590 lines
17 KiB
Markdown
590 lines
17 KiB
Markdown
# 컬럼명 매핑 가이드
|
|
|
|
## 개요
|
|
|
|
현재 프로젝트는 **하이브리드 컬럼명 방식**을 사용합니다:
|
|
- **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월 (마이그레이션 매핑 추가)
|