penti/docs/COLUMN_MAPPING.md

590 lines
17 KiB
Markdown
Raw Normal View History

2026-01-31 23:30:51 +09:00
# 컬럼명 매핑 가이드
## 개요
현재 프로젝트는 **하이브리드 컬럼명 방식**을 사용합니다:
- **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월 (마이그레이션 매핑 추가)