# 컬럼명 매핑 가이드 ## 개요 현재 프로젝트는 **하이브리드 컬럼명 방식**을 사용합니다: - **PostgreSQL 테이블**: 영문 컬럼명 - **DataFrame (전략 코드)**: 한글 컬럼명 이는 DB 표준(영문)과 make-quant-py 호환성(한글)을 동시에 만족하기 위한 설계입니다. --- ## 1. Asset (종목 정보) ### PostgreSQL 테이블: `assets` | DB 컬럼명 (영문) | 타입 | 설명 | |----------------|------|------| | id | UUID | 고유 ID | | ticker | String(20) | 종목코드 | | name | String(100) | 종목명 | | market | String(20) | 시장 (KOSPI/KOSDAQ) | | market_cap | BigInteger | 시가총액 | | stock_type | String(20) | 주식 분류 | | sector | String(100) | 섹터 | | last_price | Numeric(15,2) | 최종 가격 | | eps | Numeric(15,2) | 주당순이익 | | bps | Numeric(15,2) | 주당순자산 | | dividend_per_share | Numeric(15,2) | 주당배당금 | | base_date | Date | 기준일 | | is_active | Boolean | 활성 여부 | ### DataFrame 컬럼 (한글) ```python # data_helpers.get_ticker_list() 반환 { '종목코드': ticker, '종목명': name, '시장': market, '섹터': sector } ``` | DataFrame 컬럼 (한글) | DB 컬럼 (영문) | |---------------------|---------------| | 종목코드 | ticker | | 종목명 | name | | 시장 | market | | 섹터 | sector | --- ## 2. PriceData (가격 데이터) ### PostgreSQL 테이블: `price_data` | DB 컬럼명 (영문) | 타입 | 설명 | |----------------|------|------| | ticker | String(20) | 종목코드 | | timestamp | DateTime | 일시 | | open | Numeric(15,2) | 시가 | | high | Numeric(15,2) | 고가 | | low | Numeric(15,2) | 저가 | | close | Numeric(15,2) | 종가 | | volume | BigInteger | 거래량 | ### DataFrame 컬럼 (한글) ```python # data_helpers.get_price_data() 반환 { '종목코드': ticker, '날짜': timestamp, '시가': open, '고가': high, '저가': low, '종가': close, '거래량': volume } ``` | DataFrame 컬럼 (한글) | DB 컬럼 (영문) | |---------------------|---------------| | 종목코드 | ticker | | 날짜 | timestamp | | 시가 | open | | 고가 | high | | 저가 | low | | 종가 | close | | 거래량 | volume | --- ## 3. FinancialStatement (재무제표) ### PostgreSQL 테이블: `financial_statements` | DB 컬럼명 (영문) | 타입 | 설명 | |----------------|------|------| | id | UUID | 고유 ID | | ticker | String(20) | 종목코드 | | account | String(100) | 계정명 | | base_date | Date | 기준일 | | value | Numeric(20,2) | 값 | | disclosure_type | Char(1) | 공시 유형 (Y/Q) | ### DataFrame 컬럼 (한글) ```python # data_helpers.get_financial_statements() 반환 { '종목코드': ticker, '계정': account, '기준일': base_date, '값': value } ``` | DataFrame 컬럼 (한글) | DB 컬럼 (영문) | |---------------------|---------------| | 종목코드 | ticker | | 계정 | account | | 기준일 | base_date | | 값 | value | --- ## 4. 전략에서 사용하는 파생 컬럼 전략 코드에서 계산되는 추가 컬럼들 (모두 한글): ### Multi-Factor 전략 **Quality 팩터**: - `ROE` - 자기자본이익률 - `GPA` - Gross Profit / Assets - `CFO` - 영업활동현금흐름 **Value 팩터**: - `PER` - 주가수익비율 - `PBR` - 주가순자산비율 - `PCR` - 주가현금흐름비율 - `PSR` - 주가매출액비율 - `DY` - 배당수익률 **Momentum 팩터**: - `12M_Return` - 12개월 수익률 - `K_Ratio` - K-Ratio (모멘텀 지속성) ### Magic Formula 전략 - `magic_ebit` - EBIT (영업이익) - `magic_ev` - Enterprise Value - `magic_ic` - Invested Capital - `magic_ey` - Earnings Yield (EBIT / EV) - `magic_roc` - Return on Capital (EBIT / IC) - `magic_rank` - 종합 순위 ### F-Score 전략 - `f_score` - F-Score (0-9점) - `분류` - 시가총액 분류 (대형주/중형주/소형주) --- ## 5. 변환 로직 위치 모든 영문 → 한글 변환은 **`app/utils/data_helpers.py`**에서 수행됩니다. ```python # app/utils/data_helpers.py def get_ticker_list(db_session: Session) -> pd.DataFrame: """종목 리스트 조회 (영문 → 한글 변환)""" assets = db_session.query(Asset).filter(Asset.is_active == True).all() data = [{ '종목코드': asset.ticker, # ticker → 종목코드 '종목명': asset.name, # name → 종목명 '시장': asset.market, # market → 시장 '섹터': asset.sector # sector → 섹터 } for asset in assets] return pd.DataFrame(data) def get_price_data(...) -> pd.DataFrame: """가격 데이터 조회 (영문 → 한글 변환)""" # ... data = [{ '종목코드': p.ticker, # ticker → 종목코드 '날짜': p.timestamp, # timestamp → 날짜 '시가': float(p.open), # open → 시가 '고가': float(p.high), # high → 고가 '저가': float(p.low), # low → 저가 '종가': float(p.close), # close → 종가 '거래량': p.volume # volume → 거래량 } for p in prices] return pd.DataFrame(data) def get_financial_statements(...) -> pd.DataFrame: """재무제표 조회 (영문 → 한글 변환)""" # ... data = [{ '종목코드': fs.ticker, # ticker → 종목코드 '계정': fs.account, # account → 계정 '기준일': fs.base_date, # base_date → 기준일 '값': float(fs.value) # value → 값 } for fs in fs_data] return pd.DataFrame(data) def get_value_indicators(...) -> pd.DataFrame: """밸류 지표 조회""" # ... data = [{ '종목코드': ticker, '지표': indicator_name, # PER, PBR, PCR, PSR, DY '값': value }] return pd.DataFrame(data) ``` --- ## 6. 새로운 컬럼 추가 시 주의사항 ### Step 1: DB 모델에 영문 컬럼 추가 ```python # app/models/asset.py class Asset(Base): # ... new_field = Column(String(50)) # 영문 컬럼명 ``` ### Step 2: Alembic 마이그레이션 ```bash alembic revision --autogenerate -m "Add new_field to assets" alembic upgrade head ``` ### Step 3: data_helpers.py에 매핑 추가 ```python # app/utils/data_helpers.py def get_ticker_list(db_session): data = [{ '종목코드': asset.ticker, '종목명': asset.name, # ... '새필드': asset.new_field # 한글 컬럼명 추가 } for asset in assets] ``` ### Step 4: 전략 코드에서 사용 ```python # app/strategies/composite/my_strategy.py ticker_list['새필드'].tolist() # 한글 컬럼명 사용 ``` --- ## 7. 일관성 검증 ### 테스트 코드 예시 ```python # tests/test_column_mapping.py def test_ticker_list_columns(): """종목 리스트 컬럼명 검증""" df = get_ticker_list(db_session) # 한글 컬럼명 확인 assert '종목코드' in df.columns assert '종목명' in df.columns assert '시장' in df.columns assert '섹터' in df.columns def test_price_data_columns(): """가격 데이터 컬럼명 검증""" df = get_price_data(db_session, ['005930'], start_date, end_date) # 한글 컬럼명 확인 assert '종목코드' in df.columns assert '날짜' in df.columns assert '시가' in df.columns assert '고가' in df.columns assert '저가' in df.columns assert '종가' in df.columns assert '거래량' in df.columns ``` --- ## 8. 대안적 접근 (참고) ### 옵션 A: 완전 영문화 (현재 미사용) ```python # DB와 DataFrame 모두 영문 ticker_list['ticker'].tolist() data_bind[['ticker', 'name', 'sector']].copy() ``` **장점**: 일관성 **단점**: make-quant-py 코드 대대적 수정 필요 ### 옵션 B: 완전 한글화 (현재 미사용) ```python # DB도 한글 컬럼명 class Asset(Base): 종목코드 = Column(String(20)) 종목명 = Column(String(100)) ``` **장점**: 변환 불필요 **단점**: DB 표준 위반, 국제화 어려움, ORM 이슈 ### 옵션 C: 하이브리드 (현재 채택) ✅ - DB: 영문 (표준 준수) - DataFrame: 한글 (make-quant-py 호환) - 변환: data_helpers.py가 책임 **장점**: 양쪽 장점 모두 활용 **단점**: 변환 레이어 유지보수 --- ## 9. make-quant-py MySQL vs 현재 PostgreSQL ### make-quant-py (MySQL) ```sql -- kor_ticker 테이블 CREATE TABLE kor_ticker ( 종목코드 VARCHAR(20), -- 한글 컬럼명 종목명 VARCHAR(100), 시가총액 BIGINT, 분류 VARCHAR(20), 섹터 VARCHAR(100), 종가 INT, EPS DECIMAL, BPS DECIMAL, 주당배당금 DECIMAL, 종목구분 VARCHAR(20), 기준일 DATE ); -- kor_price 테이블 CREATE TABLE kor_price ( 날짜 DATE, -- 한글 컬럼명 시가 INT, 고가 INT, 저가 INT, 종가 INT, 거래량 BIGINT, 종목코드 VARCHAR(20) ); -- kor_fs 테이블 CREATE TABLE kor_fs ( 종목코드 VARCHAR(20), 계정 VARCHAR(100), 기준일 DATE, 값 DECIMAL, 공시구분 CHAR(1) ); ``` ### 현재 프로젝트 (PostgreSQL) ```sql -- assets 테이블 CREATE TABLE assets ( id UUID, ticker VARCHAR(20), -- 영문 컬럼명 name VARCHAR(100), market_cap BIGINT, stock_type VARCHAR(20), sector VARCHAR(100), last_price NUMERIC(15,2), eps NUMERIC(15,2), bps NUMERIC(15,2), dividend_per_share NUMERIC(15,2), market VARCHAR(20), base_date DATE, is_active BOOLEAN ); -- price_data 테이블 CREATE TABLE price_data ( timestamp TIMESTAMP, -- 영문 컬럼명 open NUMERIC(15,2), high NUMERIC(15,2), low NUMERIC(15,2), close NUMERIC(15,2), volume BIGINT, ticker VARCHAR(20) ); -- financial_statements 테이블 CREATE TABLE financial_statements ( id UUID, ticker VARCHAR(20), account VARCHAR(100), base_date DATE, value NUMERIC(20,2), disclosure_type CHAR(1) ); ``` ### 마이그레이션 매핑 (scripts/migrate_mysql_to_postgres.py) **kor_ticker → assets**: ```python asset = Asset( ticker=row['종목코드'], # 한글 → ticker name=row['종목명'], # 한글 → name market=row['시장구분'], # 한글 → market last_price=row['종가'], # 한글 → last_price market_cap=row['시가총액'], # 한글 → market_cap eps=row['EPS'], # 영문 → eps bps=row['BPS'], # 영문 → bps dividend_per_share=row['주당배당금'], # 한글 → dividend_per_share stock_type=row['종목구분'], # 한글 → stock_type base_date=row['기준일'], # 한글 → base_date is_active=True ) ``` **kor_price → price_data**: ```python price = PriceData( ticker=row['종목코드'], # 한글 → ticker timestamp=row['날짜'], # 한글 → timestamp open=row['시가'], # 한글 → open high=row['고가'], # 한글 → high low=row['저가'], # 한글 → low close=row['종가'], # 한글 → close volume=row['거래량'] # 한글 → volume ) ``` **kor_fs → financial_statements**: ```python fs = FinancialStatement( ticker=row['종목코드'], # 한글 → ticker account=row['계정'], # 한글 → account base_date=row['기준일'], # 한글 → base_date value=row['값'], # 한글 → value disclosure_type=row['공시구분'] # 한글 → disclosure_type ) ``` ### 마이그레이션 매핑 테이블 | 테이블 | MySQL 컬럼 (한글) | PostgreSQL 컬럼 (영문) | 타입 변경 | |--------|------------------|----------------------|----------| | **kor_ticker → assets** | | | | | | 종목코드 | ticker | VARCHAR(20) | | | 종목명 | name | VARCHAR(100) | | | 시장구분 | market | VARCHAR(20) | | | 시가총액 | market_cap | BIGINT | | | 종가 | last_price | INT → NUMERIC(15,2) | | | EPS | eps | DECIMAL → NUMERIC(15,2) | | | BPS | bps | DECIMAL → NUMERIC(15,2) | | | 주당배당금 | dividend_per_share | DECIMAL → NUMERIC(15,2) | | | 종목구분 | stock_type | VARCHAR(20) | | | 기준일 | base_date | DATE | | **kor_price → price_data** | | | | | | 종목코드 | ticker | VARCHAR(20) | | | 날짜 | timestamp | DATE → TIMESTAMP | | | 시가 | open | INT → NUMERIC(15,2) | | | 고가 | high | INT → NUMERIC(15,2) | | | 저가 | low | INT → NUMERIC(15,2) | | | 종가 | close | INT → NUMERIC(15,2) | | | 거래량 | volume | BIGINT | | **kor_fs → financial_statements** | | | | | | 종목코드 | ticker | VARCHAR(20) | | | 계정 | account | VARCHAR(100) | | | 기준일 | base_date | DATE | | | 값 | value | DECIMAL → NUMERIC(20,2) | | | 공시구분 | disclosure_type | CHAR(1) | --- ## 10. 전체 데이터 흐름 ``` ┌─────────────────────────────────────────────────────┐ │ MySQL (make-quant-py) │ │ kor_ticker: 종목코드, 종목명, 시장구분, 시가총액 │ │ kor_price: 날짜, 시가, 고가, 저가, 종가, 거래량 │ │ kor_fs: 종목코드, 계정, 기준일, 값, 공시구분 │ │ 👆 한글 컬럼명 │ └─────────────────────────────────────────────────────┘ │ │ scripts/migrate_mysql_to_postgres.py │ (한글 → 영문 매핑) │ row['종목코드'] → Asset.ticker │ row['시가'] → PriceData.open ▼ ┌─────────────────────────────────────────────────────┐ │ PostgreSQL (현재 프로젝트) │ │ assets: ticker, name, market, market_cap │ │ price_data: timestamp, open, high, low, close │ │ financial_statements: ticker, account, base_date │ │ 👆 영문 컬럼명 │ └─────────────────────────────────────────────────────┘ │ │ app/utils/data_helpers.py │ (영문 → 한글 매핑) │ asset.ticker → '종목코드' │ price.open → '시가' ▼ ┌─────────────────────────────────────────────────────┐ │ DataFrame (전략 코드) │ │ 종목코드, 종목명, 시장, 섹터 │ │ 날짜, 시가, 고가, 저가, 종가, 거래량 │ │ 종목코드, 계정, 기준일, 값 │ │ 👆 한글 컬럼명 (make-quant-py 호환) │ └─────────────────────────────────────────────────────┘ ``` ### 일관성 보장 모든 레이어에서 동일한 매핑 규칙 사용: 1. **MySQL → PostgreSQL** (마이그레이션): - `row['종목코드']` → `Asset.ticker` - `row['시가']` → `PriceData.open` 2. **PostgreSQL → DataFrame** (data_helpers): - `asset.ticker` → `'종목코드'` - `price.open` → `'시가'` 3. **결과**: make-quant-py 전략 코드가 **수정 없이** 작동! ```python # 전략 코드에서 그대로 사용 가능 ticker_list['종목코드'].tolist() price_df['시가'].mean() ``` --- ## 11. 결론 현재 프로젝트는 **하이브리드 컬럼명 방식**을 채택하여: 1. ✅ **DB 표준 준수**: PostgreSQL 영문 컬럼명 2. ✅ **make-quant-py 호환**: DataFrame 한글 컬럼명 3. ✅ **마이그레이션 일관성**: MySQL → PostgreSQL 자동 매핑 4. ✅ **명확한 책임 분리**: - `scripts/migrate_mysql_to_postgres.py` - 마이그레이션 변환 - `app/utils/data_helpers.py` - 쿼리 결과 변환 ### 개발자 가이드 - **DB 스키마 작업** → 영문 컬럼명 사용 - **전략 코드 작성** → 한글 컬럼명 사용 - **새 컬럼 추가** → 세 곳 모두 업데이트: 1. PostgreSQL 모델 (영문) 2. data_helpers.py 매핑 (영문→한글) 3. 마이그레이션 스크립트 (한글→영문) - 필요 시 ### 마이그레이션 실행 ```bash python scripts/migrate_mysql_to_postgres.py \ --mysql-host localhost \ --mysql-user root \ --mysql-password password \ --mysql-database quant_db ``` --- **문서 버전**: v1.1.0 **최종 업데이트**: 2024년 1월 (마이그레이션 매핑 추가)