1. 데이터베이스 개요
Alchemetric 프로젝트는 PostgreSQL 15를 프로덕션 데이터베이스로 사용하며, 로컬 개발 환경에서는 SQLite를 지원한다. ORM은 SQLAlchemy 2.0+를 사용하여 두 DBMS 간 호환성을 유지한다.
전체 데이터베이스는 18개 테이블로 구성되며, 크게 다섯 가지 영역으로 분류
코어 데이터 5개 — 주가, 종목정보, 펀더멘털, 재무제표, 거시경제 지표
NLP / 대체 데이터 3개 — 뉴스 센티먼트, DART 공시, KRX 수급
ML 파이프라인 4개 — 피처스토어, 모델 메타데이터, 학습 이력, 예측 결과
스케줄러 3개 — 스케줄 설정, 실행 로그, ML 학습 전용 설정
백테스트 3개 — 백테스트 실행, 거래 기록, 일별 에쿼티 커브
| 테이블명 | 영역 | 용도 | 예상 Row 수 | 상태 |
|---|---|---|---|---|
stock_price |
코어 | 일봉 가격 데이터 (OHLCV) | ~10M | 운영중 |
stock_info |
코어 | 종목 마스터 정보 | ~10K | 운영중 |
stock_fundamental |
코어 | 일별 밸류에이션 + 투자자별 매매 | ~5M | 운영중 |
financial_statement |
코어 | 분기별 재무제표 (DART) | ~100K | 운영중 |
macro_indicator |
코어 | 거시경제 지표 11종 (EAV 구조) | ~30K | 운영중 |
news_sentiment |
NLP | 뉴스 기사 + KR-FinBert-SC 센티먼트 | ~500K | 운영중 |
dart_disclosure |
NLP | DART 전자공시 + 센티먼트 | ~200K | 운영중 |
krx_supply_demand |
NLP | KRX 공매도 + 프로그램 매매 | ~5M | 운영중 |
feature_store |
ML | 종목별 일별 피처 저장소 (59컬럼) | ~10M | 운영중 |
ml_model |
ML | 학습된 모델 메타데이터 + 성능 지표 | ~500 | 운영중 |
ml_training_log |
ML | 모델 학습 이력 + Optuna 하이퍼파라미터 | ~2K | 운영중 |
ml_prediction |
ML | 모델 예측 결과 + 매매 시그널 | ~1M | 운영중 |
schedule_job |
스케줄러 | 크론 기반 스케줄 설정 | ~50 | 운영중 |
schedule_log |
스케줄러 | 스케줄 실행 이력 | ~10K | 운영중 |
job_target_code |
스케줄러 | 스케줄 잡 대상 종목 리스트 (1:N) | ~200 | 운영중 |
pipeline_step_log |
스케줄러 | 파이프라인 스텝별 실행 이력 (1:N) | ~50K | 운영중 |
| backtest_run | 백테스트 | 백테스트 실행 메타데이터 + 성과 지표 | ~500 | 운영중 |
| backtest_trade | 백테스트 | 개별 거래 기록 | ~50K | 운영중 |
| backtest_daily | 백테스트 | 일별 에쿼티 커브 | ~100K | 운영중 |
2. ER 다이어그램
아래 다이어그램은 Alchemetric 데이터베이스의 전체 테이블 관계를 나타냅니다. 실선 화살표는 외래키(FK) 관계, 점선은 논리적 조인 관계를 의미
┌───────────────────────────────────────────────────────────────────────────┐ │ CORE DATA TABLES (5) │ └───────────────────────────────────────────────────────────────────────────┘ stock_info macro_indicator ┌────────────────────┐ ┌─────────────────────┐ │ id PK │ │ id PK │ │ market UQ ┐ │ │ date UQ ┐ │ │ code UQ ┘ │ │ indicator_name UQ ┘ │ │ name │ │ value │ │ sector │ │ change_pct │ │ industry │ │ source │ │ updated_at │ └─────────────────────┘ └────────┬───────────┘ : │ : │ (market, code) : (date, indicator_name) │ : ┌─────┼──────────────┐ : │ │ │ : ▼ ▼ ▼ : stock_price stock_fundamental financial_statement ┌──────────────┐ ┌──────────────────┐ ┌───────────────────┐ │ id PK │ │ id PK │ │ id PK │ │ market UQ ┐│ │ market UQ ┐ │ │ market UQ ┐ │ │ code UQ ││ │ code UQ │ │ │ code UQ │ │ │ date UQ ┘│ │ date UQ ┘ │ │ period UQ ┘ │ │ open/high │ │ per/pbr/eps │ │ period_date │ │ low/close │ │ bps/market_cap │ │ revenue │ │ volume │ │ div_yield │ │ operating_profit │ │ created_at │ │ foreign_ratio │ │ net_income │ └──────┬───────┘ │ inst_net_buy │ │ roe/roa │ : │ foreign_net_buy │ │ operating_margin │ : │ individual_net.. │ │ net_margin │ : └────────┬─────────┘ │ debt_ratio │ : : │ source │ : : └─────────┬─────────┘ : : : ┌───────────────────────────────────────────────────────────────────────────┐ │ NLP / ALTERNATIVE DATA TABLES (3) │ └───────────────────────────────────────────────────────────────────────────┘ news_sentiment dart_disclosure krx_supply_demand ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────┐ │ id PK │ │ id PK │ │ id PK │ │ date │ │ date │ │ market UQ ┐ │ │ market │ │ market │ │ code UQ │ │ │ code (nullable) │ │ code │ │ date UQ ┘ │ │ title │ │ corp_name │ │ short_selling_vol │ │ description │ │ report_nm │ │ short_selling_ratio │ │ url UQ ┐ │ │ rcept_no UQ ┐ │ │ program_buy_vol │ │ source UQ ┘ │ │ code UQ ┘ │ │ program_sell_vol │ │ sentiment_score │ │ report_type │ │ margin_balance │ │ sentiment_label │ │ type_score │ │ source │ └────────┬─────────┘ │ sentiment_score │ └──────────┬───────────┘ : │ sentiment_label │ : : └────────┬─────────┘ : : : : :·······················:·························: : : : ▼ ▼ ▼ ┌────────────────────────────────────────────────────────────────────────────┐ │ ML PIPELINE TABLES (4) │ └────────────────────────────────────────────────────────────────────────────┘ feature_store (joins: 5 core + 3 NLP/대체 데이터 테이블) ┌───────────────────────────────────────────────────────────────────────────┐ │ id PK │ market, code, date UQ │ Phase 1: 24 기술적 + Phase 2: 9 재무 │ │ │ │ + Phase 3: 11 거시 + Phase 4: 5 뉴스 │ │ │ │ + Phase 5: 10 공시/수급 + 4 targets │ │ │ │ = 59 feature columns + 4 targets │ └───────────────────────────────────────────────────────────────────────────┘ ml_model ──────────┬──── 1:N (FK) ────── ml_prediction ┌─────────────────┐ │ ┌──────────────────────┐ │ id PK │ │ │ id PK │ │ model_name UQ ┐ │ │ │ model_id FK ──────┤ CASCADE │ version UQ ┘ │ │ │ market, code │ │ model_type │ │ │ prediction_date │ │ algorithm │ │ │ target_date │ │ market │ │ │ predicted_class │ │ target_column │ │ │ probability_up/down │ │ hyperparameters │ │ │ predicted_return │ │ feature_columns │ │ │ signal │ │ train dates │ │ │ confidence │ │ accuracy/f1/auc │ │ └──────────────────────┘ │ mse/rmse/mae/r2 │ │ │ model_path │ │ │ is_active │ │ └──────────────────┘ │ │ └──── 1:N (FK) ────── ml_training_log ┌──────────────────────┐ │ id PK │ │ model_id FK ──────┤ SET NULL │ algorithm │ │ train/val dates │ │ status │ │ metrics_json │ │ feature_importance │ │ optuna_trials │ │ best_trial_value │ └──────────────────────┘ ┌────────────────────────────────────────────────────────────────────────────┐ │ SCHEDULER TABLES (5) │ └────────────────────────────────────────────────────────────────────────────┘ schedule_job ──────┬──── 1:N ──── job_step ┌──────────────────┐ │ ┌────────────────────┐ │ id PK │ │ │ id PK │ │ job_name UQ │ │ │ job_id FK ──────┤ │ market │ │ │ step_type UQ │ │ cron_expr │ │ │ step_order │ │ days_back │ │ │ enabled │ │ enabled │ │ │ config (JSON) │ │ description │ │ └────────────────────┘ └──────────────────┘ │ ├──── 1:N ──── job_target_code │ ┌────────────────────┐ │ │ id PK │ │ │ job_id FK ──────┤ │ │ code UQ │ │ │ name │ │ └────────────────────┘ │ └──── 1:N ──── schedule_log ┌────────────────────┐ │ id PK │ │ job_id FK ──────┤ │ trace_id │ │ status │ │ started/finished │ │ total_codes │ │ success/failed │ │ trigger_by │ │ message │ └─────────┬──────────┘ │ └── 1:N ── pipeline_step_log ┌──────────────────┐ │ id PK │ │ log_id FK ──────┤ │ trace_id │ │ step_type UQ │ │ step_order │ │ status │ │ duration_sec │ │ saved_count │ │ error_message │ │ log_text │ └──────────────────┘ ┌────────────────────────────────────────────────────────────────────────────┐ │ BACKTEST TABLES (3) │ └────────────────────────────────────────────────────────────────────────────┘ backtest_run ─────────┬──── 1:N (FK) ────── backtest_trade ┌──────────────────────┐ │ ┌──────────────────────┐ │ id PK │ │ │ id PK │ │ name │ │ │ run_id FK ──────┤ CASCADE │ market │ │ │ market, code │ │ strategy │ │ │ trade_date │ │ start_date/end_date │ │ │ action (BUY/SELL) │ │ config_json │ │ │ price/shares/amount │ │ initial_capital │ │ │ fee/tax │ │ transaction_fee │ │ │ signal_source │ │ tax_rate │ │ │ signal_confidence │ │ codes_json │ │ │ probability_up │ │ total_return │ │ │ cash_after │ │ annualized_return │ │ │ portfolio_value_after│ │ sharpe/sortino/calmar│ │ └──────────────────────┘ │ max_drawdown │ │ │ win_rate │ │ │ profit_factor │ │ │ total_trades │ │ │ benchmark_return │ │ │ alpha │ │ │ status │ │ └──────────────────────┘ │ │ └──── 1:N (FK) ────── backtest_daily ┌──────────────────────┐ │ id PK │ │ run_id FK ──────┤ CASCADE │ date UQ ┐ │ │ (run_id) UQ ┘ │ │ portfolio_value │ │ cash │ │ positions_value │ │ daily_return │ │ cumulative_return │ │ drawdown │ │ benchmark_value │ │ benchmark_return │ │ positions_json │ └──────────────────────┘
3. 코어 데이터 테이블
3.1 stock_price
모든 종목의 일봉(OHLCV) 가격 데이터를 저장합니다. yfinance를 통해 한국(KOSPI/KOSDAQ) 및 미국(NYSE/NASDAQ) 시장 데이터를 수집하며, (market, code, date) 조합으로 유니크 제약을 설정하여 중복 삽입을 방지합니다.
CREATE TABLE stock_price ( id SERIAL PRIMARY KEY, market VARCHAR(10) NOT NULL, -- KOSPI, KOSDAQ, NYSE, NASDAQ code VARCHAR(20) NOT NULL, -- 005930, AAPL date DATE NOT NULL, open NUMERIC(15,2), high NUMERIC(15,2), low NUMERIC(15,2), close NUMERIC(15,2), volume BIGINT, created_at TIMESTAMP DEFAULT now(), CONSTRAINT uq_stock_price UNIQUE (market, code, date) ); CREATE INDEX idx_stock_price_lookup ON stock_price (market, code, date);
3.2 stock_info
종목 마스터 테이블입니다. 종목 코드, 이름, 섹터, 산업 분류를 저장하며 (market, code)로 유니크 제약을 설정한다. 섹터별 필터링을 위해 (market, sector) 복합 인덱스를 제공
CREATE TABLE stock_info ( id SERIAL PRIMARY KEY, market VARCHAR(10) NOT NULL, code VARCHAR(20) NOT NULL, name VARCHAR(100), sector VARCHAR(50), industry VARCHAR(100), updated_at TIMESTAMP DEFAULT now(), CONSTRAINT uq_stock_info UNIQUE (market, code) ); CREATE INDEX idx_stock_info_sector ON stock_info (market, sector);
3.3 stock_fundamental
KIS API를 통해 수집하는 일별 밸류에이션 지표(PER, PBR, EPS, BPS)와 투자자별 순매수 데이터를 저장합니다. 외국인 보유 비율, 기관/외국인/개인 순매수량을 포함하여 ML 피처의 펀더멘털 영역을 구성합니다.
CREATE TABLE stock_fundamental ( id SERIAL PRIMARY KEY, market VARCHAR(10) NOT NULL, code VARCHAR(20) NOT NULL, date DATE NOT NULL, -- 밸류에이션 per NUMERIC(10,2), pbr NUMERIC(10,2), eps NUMERIC(15,2), bps NUMERIC(15,2), market_cap BIGINT, div_yield NUMERIC(8,4), -- 외국인 보유 foreign_ratio NUMERIC(8,4), -- 투자자별 순매수 inst_net_buy BIGINT, foreign_net_buy BIGINT, individual_net_buy BIGINT, created_at TIMESTAMP DEFAULT now(), CONSTRAINT uq_stock_fundamental UNIQUE (market, code, date) ); CREATE INDEX idx_fundamental_lookup ON stock_fundamental (market, code, date);
3.4 financial_statement
DART API에서 수집하는 분기별 재무제표 데이터. 손익계산서(매출, 영업이익, 순이익), 수익성 지표(ROE, ROA, 영업이익률, 순이익률), 안정성 지표(부채비율)를 포함합니다. period 컬럼은 2024Q4, 2024A 형식으로 분기 및 연간 데이터를 구분한다.
CREATE TABLE financial_statement ( id SERIAL PRIMARY KEY, market VARCHAR(10) NOT NULL, code VARCHAR(20) NOT NULL, period VARCHAR(10) NOT NULL, -- 2024Q4, 2024A period_date DATE NOT NULL, -- 분기 말일 -- 손익계산서 revenue BIGINT, operating_profit BIGINT, net_income BIGINT, -- 수익성 지표 roe NUMERIC(10,2), roa NUMERIC(10,2), operating_margin NUMERIC(10,2), net_margin NUMERIC(10,2), -- 안정성 지표 debt_ratio NUMERIC(10,2), source VARCHAR(30) DEFAULT 'dart', created_at TIMESTAMP DEFAULT now(), CONSTRAINT uq_financial_statement UNIQUE (market, code, period) ); CREATE INDEX idx_financial_lookup ON financial_statement (market, code, period_date);
3.5 macro_indicator
거시경제 지표를 EAV(Entity-Attribute-Value) 구조로 저장한다. 하나의 테이블에 11개 지표를 indicator_name 컬럼으로 구분하여 유연한 지표 확장이 가능하다. 수집 소스는 yfinance, FinanceDataReader, FRED API 3종을 사용한다. (date, indicator_name)으로 유니크 제약을 설정
CREATE TABLE macro_indicator ( id SERIAL PRIMARY KEY, date DATE NOT NULL, indicator_name VARCHAR(50) NOT NULL, -- KRW_USD, VIX, ..., FED_RATE, USD_INDEX, US_CPI value NUMERIC(15,4) NOT NULL, change_pct NUMERIC(10,6), -- 전일 대비 변화율 source VARCHAR(30), -- yfinance / fdr / fred created_at TIMESTAMP DEFAULT now(), CONSTRAINT uq_macro_indicator UNIQUE (date, indicator_name) ); CREATE INDEX idx_macro_lookup ON macro_indicator (indicator_name, date);
현재 수집 중인 11개 거시경제 지표는 다음과 같다.
| indicator_name | 수집 소스 | 티커/심볼 | 설명 |
|---|---|---|---|
KRW_USD |
yfinance | KRW=X | 원/달러 환율 |
VIX |
yfinance | ^VIX | CBOE 변동성 지수 (공포 지수) |
KOSPI |
yfinance | ^KS11 | KOSPI 종합지수 |
SP500 |
yfinance | ^GSPC | S&P 500 지수 |
US_10Y |
yfinance | ^TNX | 미국 10년 국채 금리 |
KR_3Y |
FinanceDataReader | INVESTING: | 한국 3년 국채 금리 |
WTI |
yfinance | CL=F | WTI 원유 선물 가격 |
GOLD |
yfinance | GC=F | 금 선물 가격 |
FED_RATE |
FRED API | FEDFUNDS | 미국 연방기금 기준금리 |
USD_INDEX |
FRED API | DTWEXBGS | 달러 인덱스 (무역가중) |
US_CPI |
FRED API | CPIAUCSL | 미국 소비자물가지수 (CPI) |
4. NLP / 대체 데이터 테이블
Phase 4~5에서 추가된 NLP 및 대체 데이터 테이블. 네이버 뉴스 API + KR-FinBert-SC 모델로 뉴스 센티먼트를 분석하고, DART 전자공시와 KRX 수급(공매도/프로그램매매) 데이터를 수집
4.1 news_sentiment
네이버 뉴스 API를 통해 수집한 뉴스 기사에 KR-FinBert-SC(한국어 금융 센티먼트 모델)로 감성 점수를 부여한다. code가 NULL이면 시장 전체 뉴스, 값이 있으면 종목별 뉴스
CREATE TABLE news_sentiment ( id SERIAL PRIMARY KEY, date DATE NOT NULL, market VARCHAR(10) NOT NULL DEFAULT 'KR', code VARCHAR(20), -- NULL = 시장 전체 title VARCHAR(500) NOT NULL, description TEXT, url VARCHAR(1000), source VARCHAR(30) DEFAULT 'naver', -- KR-FinBert-SC 센티먼트 sentiment_score NUMERIC(6,4), -- -1.0 ~ +1.0 sentiment_label VARCHAR(20), -- positive / negative / neutral created_at TIMESTAMP DEFAULT now(), CONSTRAINT uq_news_sentiment UNIQUE (url, code) ); CREATE INDEX idx_news_lookup ON news_sentiment (market, code, date); CREATE INDEX idx_news_date ON news_sentiment (date); CREATE INDEX idx_news_code_date ON news_sentiment (code, date);
4.2 dart_disclosure
DART API를 통해 수집하는 전자공시 데이터. 공시 유형(실적, 지분변동 등)에 따라 type_score 가중치를 부여하며, KR-FinBert-SC로 공시 제목의 센티먼트를 분석한다. rcept_no(접수번호)가 공시 고유 식별자
CREATE TABLE dart_disclosure ( id SERIAL PRIMARY KEY, date DATE NOT NULL, market VARCHAR(10) NOT NULL, code VARCHAR(20) NOT NULL, corp_name VARCHAR(100), report_nm VARCHAR(500) NOT NULL, -- 공시 제목 rcept_no VARCHAR(20) NOT NULL, -- 접수번호 flr_nm VARCHAR(100), -- 제출인 rcept_dt VARCHAR(10), -- 접수일 (YYYYMMDD) -- 공시 유형 분류 report_type VARCHAR(50), -- 실적, 지분, 기타 등 type_score NUMERIC(4,2) DEFAULT 0.2, -- 실적=1.0, 지분=0.8, ... -- 센티먼트 sentiment_score NUMERIC(6,4), sentiment_label VARCHAR(20), created_at TIMESTAMP DEFAULT now(), CONSTRAINT uq_dart_disclosure UNIQUE (rcept_no, code) ); CREATE INDEX idx_disclosure_lookup ON dart_disclosure (market, code, date); CREATE INDEX idx_disclosure_date ON dart_disclosure (date);
4.3 krx_supply_demand
KRX(한국거래소) 수급 데이터를 저장한다. 공매도(대차거래) 거래량/비율과 프로그램 매매(기관 알고리즘) 매수/매도량을 포함. pykrx 라이브러리를 통해 수집하며, 향후 신용잔고 데이터도 확장 가능
CREATE TABLE krx_supply_demand ( id SERIAL PRIMARY KEY, market VARCHAR(10) NOT NULL, code VARCHAR(20) NOT NULL, date DATE NOT NULL, -- 공매도 short_selling_volume BIGINT, short_selling_ratio NUMERIC(8,4), -- % -- 프로그램 매매 program_buy_volume BIGINT, program_sell_volume BIGINT, -- 신용잔고 (향후 확장) margin_balance BIGINT, source VARCHAR(30) DEFAULT 'pykrx', created_at TIMESTAMP DEFAULT now(), CONSTRAINT uq_krx_supply_demand UNIQUE (market, code, date) ); CREATE INDEX idx_supply_demand_lookup ON krx_supply_demand (market, code, date);
5. ML 파이프라인 테이블
5.1 feature_store
종목별 일별 피처를 저장하는 중심 테이블. Phase 5 기준 총 69개 피처 컬럼으로 구성되며, 코어 5개 테이블과 NLP/대체 데이터 3개 테이블의 데이터를 조인하여 계산
CREATE TABLE feature_store ( id SERIAL PRIMARY KEY, market VARCHAR(10) NOT NULL, code VARCHAR(20) NOT NULL, date DATE NOT NULL, -- ═══════════════════════════════════════════════════════ -- Phase 1: 기술적 지표 (24 columns) -- ═══════════════════════════════════════════════════════ -- 가격 피처 (6) close NUMERIC(15,2), return_1d NUMERIC(10,6), return_5d NUMERIC(10,6), return_20d NUMERIC(10,6), volatility_20d NUMERIC(10,6), volume_ratio NUMERIC(10,4), -- 이동평균 (3) sma_5 NUMERIC(15,4), sma_20 NUMERIC(15,4), sma_60 NUMERIC(15,4), -- 지수이동평균 (2) ema_12 NUMERIC(15,4), ema_26 NUMERIC(15,4), -- RSI (1) rsi_14 NUMERIC(8,4), -- MACD (3) macd NUMERIC(15,4), macd_signal NUMERIC(15,4), macd_histogram NUMERIC(15,4), -- 볼린저 밴드 (5) bb_upper NUMERIC(15,4), bb_middle NUMERIC(15,4), bb_lower NUMERIC(15,4), bb_width NUMERIC(10,6), bb_pctb NUMERIC(10,6), -- OBV (1) obv NUMERIC(20,2), -- 파생 피처 (4 = 24 - 20) price_to_sma20 NUMERIC(10,6), price_to_sma60 NUMERIC(10,6), golden_cross INTEGER, -- 1: 골든크로스, 0: 없음 rsi_zone INTEGER, -- -1: 과매도, 0: 중립, 1: 과매수 -- ═══════════════════════════════════════════════════════ -- Phase 2: 재무 피처 (+9 columns = 33 total) -- ═══════════════════════════════════════════════════════ per NUMERIC(10,2), pbr NUMERIC(10,2), eps NUMERIC(15,2), market_cap BIGINT, foreign_ratio NUMERIC(8,4), inst_net_buy BIGINT, foreign_net_buy BIGINT, roe NUMERIC(10,2), debt_ratio NUMERIC(10,2), -- ═══════════════════════════════════════════════════════ -- Phase 3: 거시경제 피처 (+11 columns = 44 total) -- ═══════════════════════════════════════════════════════ krw_usd NUMERIC(10,4), vix NUMERIC(8,4), kospi_index NUMERIC(10,2), us_10y NUMERIC(8,4), kr_3y NUMERIC(8,4), sp500 NUMERIC(10,2), wti NUMERIC(10,2), gold NUMERIC(10,2), fed_rate NUMERIC(8,4), -- FRED: 미국 기준금리 usd_index NUMERIC(10,4), -- FRED: 달러 인덱스 us_cpi NUMERIC(10,4), -- FRED: 미국 CPI -- ═══════════════════════════════════════════════════════ -- Phase 4: 뉴스 센티먼트 피처 (+5 columns = 49 total) -- ═══════════════════════════════════════════════════════ news_sentiment NUMERIC(6,4), -- 일평균 센티먼트 news_volume INTEGER, -- 당일 뉴스 건수 news_sentiment_std NUMERIC(6,4), -- 센티먼트 표준편차 market_sentiment NUMERIC(6,4), -- 시장 전체 센티먼트 market_news_volume INTEGER, -- 시장 전체 뉴스 건수 -- ═══════════════════════════════════════════════════════ -- Phase 5A: 공시 피처 (+5 columns = 54 total) -- ═══════════════════════════════════════════════════════ disclosure_count_30d INTEGER, -- 30일 공시 건수 days_since_disclosure INTEGER, -- 최근 공시 경과일 disclosure_sentiment NUMERIC(6,4), -- 공시 센티먼트 disclosure_type_score NUMERIC(4,2), -- 공시 유형 가중치 disclosure_volume_change NUMERIC(10,4), -- 공시 전후 거래량 변화 -- ═══════════════════════════════════════════════════════ -- Phase 5B: 수급 피처 (+5 columns = 59 total) -- ═══════════════════════════════════════════════════════ short_selling_volume BIGINT, -- 공매도 거래량 short_selling_ratio NUMERIC(8,4), -- 공매도 비율 (%) program_buy_volume BIGINT, -- 프로그램 매수량 program_sell_volume BIGINT, -- 프로그램 매도량 program_net_volume BIGINT, -- 프로그램 순매수 -- ═══════════════════════════════════════════════════════ -- Targets (4 columns) -- ═══════════════════════════════════════════════════════ target_class_1d INTEGER, -- 1일 후 상승(1)/하락(0) target_class_5d INTEGER, -- 5일 후 상승(1)/하락(0) target_return_1d NUMERIC(10,6), -- 1일 후 수익률 target_return_5d NUMERIC(10,6), -- 5일 후 수익률 created_at TIMESTAMP DEFAULT now(), CONSTRAINT uq_feature_store UNIQUE (market, code, date) ); CREATE INDEX idx_feature_store_lookup ON feature_store (market, code, date); CREATE INDEX idx_feature_store_date ON feature_store (date);
Phase 1 (24 기술적) + Phase 2 (+9 재무) + Phase 3 (+11 거시경제) + Phase 4 (+5 뉴스 센티먼트) + Phase 5 (+10 공시/수급) = 59 feature columns + 4 target columns
5.2 ml_model
학습이 완료된 ML 모델의 메타데이터를 저장한다. 분류(classification) 지표(accuracy, precision, recall, F1, AUC-ROC)와 회귀(regression) 지표(MSE, RMSE, MAE, R2)를 모두 포함하며, is_active 플래그로 서빙 중인 모델을 관리
CREATE TABLE ml_model ( id SERIAL PRIMARY KEY, model_name VARCHAR(100) NOT NULL, model_type VARCHAR(30) NOT NULL, -- classification / regression / reinforcement algorithm VARCHAR(30) NOT NULL, -- random_forest / xgboost / lightgbm / ensemble / lstm / transformer / dqn / ppo market VARCHAR(10) NOT NULL, target_column VARCHAR(50) NOT NULL, -- target_class_1d 등 -- 학습 파라미터 hyperparameters VARCHAR(2000), -- JSON feature_columns VARCHAR(2000), -- JSON train_start_date DATE, train_end_date DATE, train_sample_count INTEGER, -- 분류 성능 지표 accuracy NUMERIC(6,4), precision_score NUMERIC(6,4), recall NUMERIC(6,4), f1_score NUMERIC(6,4), auc_roc NUMERIC(6,4), -- 회귀 성능 지표 mse NUMERIC(15,6), rmse NUMERIC(15,6), mae NUMERIC(15,6), r2_score NUMERIC(8,6), -- 모델 파일 model_path VARCHAR(500), is_active BOOLEAN DEFAULT FALSE, version INTEGER DEFAULT 1, created_at TIMESTAMP DEFAULT now(), updated_at TIMESTAMP DEFAULT now(), CONSTRAINT uq_ml_model UNIQUE (model_name, version) ); CREATE INDEX idx_ml_model_active ON ml_model (market, model_type, is_active);
5.3 ml_training_log
모든 학습 시도의 이력을 기록합니다. 성공/실패와 무관하게 모든 학습 과정을 추적하며, Optuna 하이퍼파라미터 최적화 결과와 피처 중요도를 JSON으로 저장합니다. model_id는 SET NULL 외래키로, 모델 삭제 시에도 학습 로그가 보존됩니다.
CREATE TABLE ml_training_log ( id SERIAL PRIMARY KEY, model_id INTEGER REFERENCES ml_model(id) ON DELETE SET NULL, algorithm VARCHAR(30) NOT NULL, model_type VARCHAR(30) NOT NULL, market VARCHAR(10) NOT NULL, target_column VARCHAR(50) NOT NULL, -- 학습 데이터 정보 train_start_date DATE, train_end_date DATE, val_start_date DATE, val_end_date DATE, train_samples INTEGER, val_samples INTEGER, feature_count INTEGER, -- 학습 결과 status VARCHAR(20) NOT NULL DEFAULT 'running', metrics_json VARCHAR(3000), -- JSON: 성능 지표 feature_importance_json VARCHAR(5000), -- JSON: 피처 중요도 hyperparameters_json VARCHAR(2000), -- JSON: 하이퍼파라미터 -- Optuna 최적화 optuna_trials INTEGER, best_trial_value NUMERIC(10,6), started_at TIMESTAMP NOT NULL DEFAULT now(), finished_at TIMESTAMP, error_message VARCHAR(1000) ); CREATE INDEX idx_training_log_model ON ml_training_log (model_id); CREATE INDEX idx_training_log_status ON ml_training_log (status, started_at);
5.4 ml_prediction
학습된 모델의 예측 결과를 저장한다. 분류 결과(상승/하락 확률)와 회귀 결과(예상 수익률)를 모두 포함하며, 최종적으로 BUY / SELL / HOLD 시그널과 신뢰도를 제공. 모델 삭제 시 관련 예측도 CASCADE로 함께 삭제
CREATE TABLE ml_prediction ( id SERIAL PRIMARY KEY, model_id INTEGER NOT NULL REFERENCES ml_model(id) ON DELETE CASCADE, market VARCHAR(10) NOT NULL, code VARCHAR(20) NOT NULL, prediction_date DATE NOT NULL, target_date DATE NOT NULL, -- 분류 결과 predicted_class INTEGER, -- 1: 상승, 0: 하락 probability_up NUMERIC(6,4), probability_down NUMERIC(6,4), -- 회귀 결과 predicted_return NUMERIC(10,6), -- 시그널 signal VARCHAR(10), -- BUY / SELL / HOLD confidence NUMERIC(6,4), created_at TIMESTAMP DEFAULT now(), CONSTRAINT uq_ml_prediction UNIQUE (model_id, market, code, prediction_date) ); CREATE INDEX idx_prediction_lookup ON ml_prediction (market, code, prediction_date); CREATE INDEX idx_prediction_signal ON ml_prediction (signal, prediction_date);
6. 스케줄러 테이블
6.1 schedule_job
APScheduler와 연동되는 크론 기반 스케줄 설정 테이블. 작업 유형은 job_step 테이블의 스텝 조합으로 정의하며, 대상 종목은 job_target_code로 관리
CREATE TABLE schedule_job ( id SERIAL PRIMARY KEY, job_name VARCHAR(50) UNIQUE NOT NULL, market VARCHAR(10) NOT NULL, cron_expr VARCHAR(100) NOT NULL, -- "0 18 * * *" days_back INTEGER NOT NULL DEFAULT 7, enabled BOOLEAN NOT NULL DEFAULT TRUE, description VARCHAR(200), created_at TIMESTAMP DEFAULT now(), updated_at TIMESTAMP DEFAULT now(), CONSTRAINT uq_schedule_job_name UNIQUE (job_name) );
6.2 schedule_log
스케줄 실행의 상세 이력을 기록한다. trace_id로 하위 스텝 로그(pipeline_step_log)와 연결하며, 처리 대상 종목 수, 성공/실패/DB 저장 건수를 추적. 수동 실행(manual)과 자동 실행(scheduler)을 구분
CREATE TABLE schedule_log ( id SERIAL PRIMARY KEY, job_id INTEGER NOT NULL REFERENCES schedule_job(id) ON DELETE CASCADE, trace_id VARCHAR(36), -- UUID: 스텝 로그 연결 키 status VARCHAR(20) NOT NULL DEFAULT 'running', -- running / success / failed started_at TIMESTAMP NOT NULL DEFAULT now(), finished_at TIMESTAMP, total_codes INTEGER DEFAULT 0, success_count INTEGER DEFAULT 0, failed_count INTEGER DEFAULT 0, db_saved_count INTEGER DEFAULT 0, trigger_by VARCHAR(20) NOT NULL DEFAULT 'manual', -- manual / scheduler message VARCHAR(500) );
6.3 job_target_code
스케줄 잡의 대상 종목 리스트. schedule_job과 1:N 관계로, 잡 삭제 시 CASCADE로 함께 삭제된다. 동일 잡 내에서 종목 코드는 유일해야 한다
CREATE TABLE job_target_code ( id SERIAL PRIMARY KEY, job_id INTEGER NOT NULL REFERENCES schedule_job(id) ON DELETE CASCADE, code VARCHAR(20) NOT NULL, name VARCHAR(100), CONSTRAINT uq_job_target_code UNIQUE (job_id, code) );
6.4 pipeline_step_log
파이프라인 스텝별 실행 이력. schedule_log와 1:N 관계로, 각 스텝의 시작/종료 시간, 소요시간, 저장 건수, 에러 메시지, 전체 로그 텍스트를 기록한다. 동일 실행 내에서 step_type은 유일해야 한다
CREATE TABLE pipeline_step_log ( id SERIAL PRIMARY KEY, log_id INTEGER NOT NULL REFERENCES schedule_log(id) ON DELETE CASCADE, trace_id VARCHAR(36) NOT NULL, -- UUID: 실행 추적 키 step_type VARCHAR(30) NOT NULL, -- price / fundamental / feature / ml … step_order INTEGER NOT NULL, status VARCHAR(20) DEFAULT 'pending', -- pending / running / success / failed / skipped started_at TIMESTAMP, finished_at TIMESTAMP, duration_sec INTEGER, saved_count INTEGER DEFAULT 0, summary VARCHAR(500), error_message VARCHAR(2000), log_text TEXT, -- 전체 로그 텍스트 CONSTRAINT uq_step_log_type UNIQUE (log_id, step_type) );
7. 백테스트 테이블
7.1 backtest_run
백테스트 실행의 메타데이터와 성과 지표를 저장합니다. strategy 컬럼으로 ML 앙상블(ml_ensemble)과 단일 모델(single_model) 전략을 구분하며, Sharpe/Sortino/Calmar 비율, 최대 낙폭, 승률, 알파 등 핵심 성과 지표를 기록합니다.
CREATE TABLE backtest_run ( id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(200) NOT NULL, market VARCHAR(10) NOT NULL, strategy VARCHAR(50) NOT NULL, -- ml_ensemble / single_model start_date DATE NOT NULL, end_date DATE NOT NULL, config_json VARCHAR(5000), -- JSON: model_ids, aggregation_method 등 initial_capital NUMERIC(15,2) NOT NULL DEFAULT 10000000, transaction_fee NUMERIC(8,6) DEFAULT 0.00015, tax_rate NUMERIC(8,6) DEFAULT 0.0020, codes_json VARCHAR(5000), -- JSON: 종목 목록 -- 성과 지표 total_return NUMERIC(10,6), annualized_return NUMERIC(10,6), sharpe_ratio NUMERIC(10,6), sortino_ratio NUMERIC(10,6), max_drawdown NUMERIC(10,6), calmar_ratio NUMERIC(10,6), win_rate NUMERIC(8,6), profit_factor NUMERIC(10,4), total_trades INTEGER, benchmark_return NUMERIC(10,6), alpha NUMERIC(10,6), -- 실행 상태 status VARCHAR(20) DEFAULT 'running', -- running / success / failed error_message VARCHAR(1000), started_at DATETIME, finished_at DATETIME, created_at DATETIME ); CREATE INDEX idx_backtest_run_market ON backtest_run (market, status); CREATE INDEX idx_backtest_run_date ON backtest_run (start_date, end_date);
7.2 backtest_trade
백테스트 실행 중 발생한 개별 거래 기록을 저장합니다. run_id로 backtest_run과 연결되며, 매수/매도 시점의 가격, 수량, 수수료, 세금과 시그널 정보를 추적합니다. 백테스트 실행 삭제 시 관련 거래 기록도 CASCADE로 함께 삭제
CREATE TABLE backtest_trade ( id INTEGER PRIMARY KEY AUTOINCREMENT, run_id INTEGER NOT NULL REFERENCES backtest_run(id) ON DELETE CASCADE, market VARCHAR(10) NOT NULL, code VARCHAR(20) NOT NULL, trade_date DATE NOT NULL, action VARCHAR(10) NOT NULL, -- BUY / SELL price NUMERIC(15,2) NOT NULL, shares INTEGER NOT NULL, amount NUMERIC(15,2) NOT NULL, fee NUMERIC(15,4), tax NUMERIC(15,4), -- 시그널 정보 signal_source VARCHAR(100), signal_confidence NUMERIC(6,4), probability_up NUMERIC(6,4), -- 거래 후 잔고 cash_after NUMERIC(15,2), portfolio_value_after NUMERIC(15,2), created_at DATETIME ); CREATE INDEX idx_backtest_trade_run ON backtest_trade (run_id); CREATE INDEX idx_backtest_trade_code ON backtest_trade (run_id, code, trade_date);
7.3 backtest_daily
백테스트 실행의 일별 에쿼티 커브를 저장합니다. 포트폴리오 가치, 현금 잔고, 포지션 가치, 일별/누적 수익률, 낙폭, 벤치마크 대비 성과를 추적하며, positions_json에 종목별 보유 현황을 JSON으로 기록합니다. (run_id, date) 조합으로 유니크 제약을 설정
CREATE TABLE backtest_daily ( id INTEGER PRIMARY KEY AUTOINCREMENT, run_id INTEGER NOT NULL REFERENCES backtest_run(id) ON DELETE CASCADE, date DATE NOT NULL, portfolio_value NUMERIC(15,2) NOT NULL, cash NUMERIC(15,2), positions_value NUMERIC(15,2), -- 수익률 daily_return NUMERIC(10,6), cumulative_return NUMERIC(10,6), drawdown NUMERIC(10,6), -- 벤치마크 benchmark_value NUMERIC(15,2), benchmark_return NUMERIC(10,6), -- 포지션 상세 positions_json VARCHAR(5000), -- JSON: 종목별 보유 현황 created_at DATETIME, CONSTRAINT uq_backtest_daily UNIQUE (run_id, date) ); CREATE INDEX idx_backtest_daily_run ON backtest_daily (run_id, date);
8. 인덱스 전략
인덱스 설계는 두 가지 원칙을 따른다.
첫째, 유니크 제약 조건이 암묵적으로 생성하는 인덱스를 최대한 활용
둘째, 조회 패턴(종목별 기간 조회, 날짜 범위 스캔, 상태 필터)에 맞춘 복합 인덱스를 별도로 생성
| 테이블 | 인덱스명 | 컬럼 | 용도 |
|---|---|---|---|
| stock_price | uq_stock_price |
market, code, date | 중복 방지 + 종목별 기간 조회 |
| stock_price | idx_stock_price_lookup |
market, code, date | 종목별 가격 조회 최적화 |
| stock_info | uq_stock_info |
market, code | 종목 유니크 식별 |
| stock_info | idx_stock_info_sector |
market, sector | 섹터별 종목 필터링 |
| stock_fundamental | uq_stock_fundamental |
market, code, date | 중복 방지 |
| stock_fundamental | idx_fundamental_lookup |
market, code, date | 펀더멘털 데이터 조회 |
| financial_statement | uq_financial_statement |
market, code, period | 중복 방지 |
| financial_statement | idx_financial_lookup |
market, code, period_date | 날짜 기반 재무제표 조회 |
| macro_indicator | uq_macro_indicator |
date, indicator_name | 중복 방지 |
| macro_indicator | idx_macro_lookup |
indicator_name, date | 지표별 시계열 조회 |
| news_sentiment | uq_news_sentiment |
url, code | 중복 뉴스 방지 |
| news_sentiment | idx_news_lookup |
market, code, date | 종목별 뉴스 조회 |
| news_sentiment | idx_news_date |
date | 날짜별 뉴스 스캔 |
| news_sentiment | idx_news_code_date |
code, date | 종목+날짜 복합 조회 |
| dart_disclosure | uq_dart_disclosure |
rcept_no, code | 중복 공시 방지 |
| dart_disclosure | idx_disclosure_lookup |
market, code, date | 종목별 공시 조회 |
| dart_disclosure | idx_disclosure_date |
date | 날짜별 공시 스캔 |
| krx_supply_demand | uq_krx_supply_demand |
market, code, date | 중복 방지 |
| krx_supply_demand | idx_supply_demand_lookup |
market, code, date | 종목별 수급 조회 |
| feature_store | uq_feature_store |
market, code, date | 중복 방지 |
| feature_store | idx_feature_store_lookup |
market, code, date | 종목별 피처 조회 |
| feature_store | idx_feature_store_date |
date | 날짜 범위 학습 데이터 추출 |
| ml_model | uq_ml_model |
model_name, version | 모델 버전 유니크 |
| ml_model | idx_ml_model_active |
market, model_type, is_active | 활성 모델 조회 |
| ml_training_log | idx_training_log_model |
model_id | 모델별 학습 이력 조회 |
| ml_training_log | idx_training_log_status |
status, started_at | 실행 상태별 조회 |
| ml_prediction | uq_ml_prediction |
model_id, market, code, prediction_date | 중복 예측 방지 |
| ml_prediction | idx_prediction_lookup |
market, code, prediction_date | 종목별 예측 조회 |
| ml_prediction | idx_prediction_signal | signal, prediction_date | 시그널별 종목 필터링 (BUY/SELL) |
| backtest_run | idx_backtest_run_market | market, status | 시장별 백테스트 상태 조회 |
| backtest_run | idx_backtest_run_date | start_date, end_date | 기간별 백테스트 조회 |
| backtest_trade | idx_backtest_trade_run | run_id | 실행별 거래 기록 조회 |
| backtest_trade | idx_backtest_trade_code | run_id, code, trade_date | 종목별 거래 이력 조회 |
| backtest_daily | uq_backtest_daily | run_id, date | 중복 방지 |
| backtest_daily | idx_backtest_daily_run | run_id, date | 실행별 에쿼티 커브 조회 |
참고: PostgreSQL에서 UNIQUE 제약 조건은 자동으로 B-Tree 인덱스를 생성.
별도의 lookup 인덱스는 UNIQUE 제약과 컬럼 순서가 동일한 경우 중복으로 보일 수 있으나,
SQLite 호환성을 위해 명시적으로 선언.
'Project > 🚀 AI Finance 2026' 카테고리의 다른 글
| #6 [모듈1] 데이터 분석 - 개발문서 (0) | 2026.02.19 |
|---|---|
| #5 [모듈0] 데이터 수집 - 개발문서 (0) | 2026.02.19 |
| #4 Alchemetric API 정의서 (0) | 2026.02.06 |
| #2 Alchemetric 시스템 아키텍쳐 (0) | 2026.02.03 |
| #1 Alchemetric 프로젝트 개발 명세서 (0) | 2026.02.03 |