first commit
This commit is contained in:
parent
517f564386
commit
401c762683
57
.gitignore
vendored
Normal file
57
.gitignore
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# ========================================
|
||||||
|
# Python 관련 무시 파일
|
||||||
|
# ========================================
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# 가상환경 (venv)
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
|
||||||
|
# 테스트, 로그, 데이터
|
||||||
|
*.log
|
||||||
|
*.tmp
|
||||||
|
*.db
|
||||||
|
*.sqlite3
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
|
||||||
|
# 환경 설정 / API 키 / 민감 정보
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
*.secret
|
||||||
|
secrets.json
|
||||||
|
config.json
|
||||||
|
api_keys.txt
|
||||||
|
|
||||||
|
# 파이썬 배포 파일
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
*.egg-info/
|
||||||
|
|
||||||
|
# Jupyter Notebook 체크포인트
|
||||||
|
.ipynb_checkpoints/
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# 에디터/IDE 관련
|
||||||
|
# ========================================
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.code-workspace
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# 시스템 파일
|
||||||
|
# ========================================
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
desktop.ini
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# Git 자체 관련 (안전)
|
||||||
|
# ========================================
|
||||||
|
*.orig
|
||||||
|
*.rej
|
35
data_analysis_engine/analyzer.py
Normal file
35
data_analysis_engine/analyzer.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
|
||||||
|
from data_analysis_engine.dataset_builder import add_technical_indicators
|
||||||
|
from data_analysis_engine.models.xgboost_model import XGBoostModel
|
||||||
|
import pandas as pd
|
||||||
|
import os
|
||||||
|
import glob
|
||||||
|
|
||||||
|
def analyze_stocks_pipeline(cds_dir, model_path, top_n=10, output_path="prediction_result.csv"):
|
||||||
|
model = XGBoostModel()
|
||||||
|
model.load_model(model_path)
|
||||||
|
|
||||||
|
results = []
|
||||||
|
|
||||||
|
csv_files = glob.glob(os.path.join(cds_dir, "*_ohlcv.csv"))
|
||||||
|
for file in csv_files:
|
||||||
|
symbol = os.path.basename(file).replace("_ohlcv.csv", "")
|
||||||
|
df = pd.read_csv(file)
|
||||||
|
df = add_technical_indicators(df)
|
||||||
|
|
||||||
|
feature_cols = [
|
||||||
|
'open', 'high', 'low', 'close', 'volume',
|
||||||
|
'sma_5', 'sma_10', 'rsi_14', 'macd',
|
||||||
|
'boll_upper', 'boll_lower'
|
||||||
|
]
|
||||||
|
X = df[feature_cols].dropna()
|
||||||
|
if X.empty:
|
||||||
|
continue
|
||||||
|
|
||||||
|
prob = model.predict_proba(X.tail(1)) # 이미 float 값임
|
||||||
|
results.append({"symbol": symbol, "predicted_score": prob})
|
||||||
|
|
||||||
|
result_df = pd.DataFrame(results)
|
||||||
|
result_df.sort_values(by="predicted_score", ascending=False, inplace=True)
|
||||||
|
result_df.head(top_n).to_csv(output_path, index=False)
|
||||||
|
print("✅ 예측 결과 저장 완료:", output_path)
|
66
data_analysis_engine/dataset_builder.py
Normal file
66
data_analysis_engine/dataset_builder.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
'''
|
||||||
|
📁 data_analysis_engine/dataset_builder.py
|
||||||
|
|
||||||
|
Complete Data Set(CDS)를 학습용 피처(X)와 타깍(y)으로 변환
|
||||||
|
기술 지표 포함
|
||||||
|
'''
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
# 기술 지표 계산 함수
|
||||||
|
def add_technical_indicators(df: pd.DataFrame) -> pd.DataFrame:
|
||||||
|
df = df.copy()
|
||||||
|
|
||||||
|
# 이동 평균선
|
||||||
|
df['sma_5'] = df['close'].rolling(window=5).mean()
|
||||||
|
df['sma_10'] = df['close'].rolling(window=10).mean()
|
||||||
|
|
||||||
|
# RSI (상대 강도 지수)
|
||||||
|
delta = df['close'].diff()
|
||||||
|
gain = delta.where(delta > 0, 0)
|
||||||
|
loss = -delta.where(delta < 0, 0)
|
||||||
|
avg_gain = gain.rolling(window=14).mean()
|
||||||
|
avg_loss = loss.rolling(window=14).mean()
|
||||||
|
rs = avg_gain / (avg_loss + 1e-6)
|
||||||
|
df['rsi_14'] = 100 - (100 / (1 + rs))
|
||||||
|
|
||||||
|
# MACD
|
||||||
|
ema12 = df['close'].ewm(span=12, adjust=False).mean()
|
||||||
|
ema26 = df['close'].ewm(span=26, adjust=False).mean()
|
||||||
|
df['macd'] = ema12 - ema26
|
||||||
|
|
||||||
|
# Bollinger Bands
|
||||||
|
ma20 = df['close'].rolling(window=20).mean()
|
||||||
|
std20 = df['close'].rolling(window=20).std()
|
||||||
|
df['boll_upper'] = ma20 + (std20 * 2)
|
||||||
|
df['boll_lower'] = ma20 - (std20 * 2)
|
||||||
|
|
||||||
|
return df
|
||||||
|
|
||||||
|
def build_dataset(df: pd.DataFrame):
|
||||||
|
"""
|
||||||
|
CDS를 기반으로 피처(X)와 타깍(y)를 생성
|
||||||
|
기술 지표 포함
|
||||||
|
"""
|
||||||
|
df = df.copy()
|
||||||
|
df = add_technical_indicators(df)
|
||||||
|
|
||||||
|
df['target'] = (df['close'].shift(-1) > df['close']).astype(int)
|
||||||
|
df.dropna(inplace=True)
|
||||||
|
|
||||||
|
feature_cols = [
|
||||||
|
'open', 'high', 'low', 'close', 'volume',
|
||||||
|
'sma_5', 'sma_10', 'rsi_14', 'macd',
|
||||||
|
'boll_upper', 'boll_lower'
|
||||||
|
]
|
||||||
|
|
||||||
|
X = df[feature_cols]
|
||||||
|
y = df['target']
|
||||||
|
return X, y
|
||||||
|
|
||||||
|
def build_dataset_with_indicators(df: pd.DataFrame):
|
||||||
|
"""
|
||||||
|
build_dataset() 같은 기능을 행하지만, 필요없을 경우를 위해 또 다른 이름으로 제공
|
||||||
|
"""
|
||||||
|
return build_dataset(df)
|
34
data_analysis_engine/models/xgboost_model.py
Normal file
34
data_analysis_engine/models/xgboost_model.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
'''
|
||||||
|
📁 data_analysis_engine/models/xgboost_model.py
|
||||||
|
|
||||||
|
XGBoost 모델 클래스: 학습, 예측, 저장, 로드, 전처리 지원
|
||||||
|
'''
|
||||||
|
|
||||||
|
import os
|
||||||
|
import xgboost as xgb
|
||||||
|
import pandas as pd
|
||||||
|
from data_analysis_engine.dataset_builder import build_dataset
|
||||||
|
|
||||||
|
class XGBoostModel:
|
||||||
|
def __init__(self):
|
||||||
|
self.model = xgb.XGBClassifier(use_label_encoder=False, eval_metric='logloss')
|
||||||
|
|
||||||
|
def fit(self, X: pd.DataFrame, y: pd.Series):
|
||||||
|
self.model.fit(X, y)
|
||||||
|
|
||||||
|
def predict_proba(self, X: pd.DataFrame) -> float:
|
||||||
|
return float(self.model.predict_proba(X)[-1][1])
|
||||||
|
|
||||||
|
def save_model(self, path: str):
|
||||||
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||||
|
self.model.save_model(path)
|
||||||
|
|
||||||
|
def load_model(self, path: str):
|
||||||
|
self.model.load_model(path)
|
||||||
|
|
||||||
|
def preprocess(self, df: pd.DataFrame) -> pd.DataFrame:
|
||||||
|
"""
|
||||||
|
예측 시 사용할 피처만 추출하는 전처리 함수
|
||||||
|
"""
|
||||||
|
X, _ = build_dataset(df)
|
||||||
|
return X
|
48
data_analysis_engine/predict.py
Normal file
48
data_analysis_engine/predict.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
'''
|
||||||
|
data_analysis_engine/predict.py
|
||||||
|
|
||||||
|
분석 엔진 전체 흐름을 실행하는 진입점 모듈입니다.
|
||||||
|
|
||||||
|
- CLI 또는 스크립트 기반 실행 가능
|
||||||
|
- 입력: CDS CSV 파일들이 저장된 폴더 경로, 모델 파일 경로, 상위 추출 수
|
||||||
|
- 처리: analyzer.analyze_stocks() 호출
|
||||||
|
- 출력: 예측 결과를 콘솔에 출력하고 CSV로 저장
|
||||||
|
'''
|
||||||
|
|
||||||
|
import os
|
||||||
|
import glob
|
||||||
|
import argparse
|
||||||
|
import pandas as pd
|
||||||
|
from data_analysis_engine.analyzer import analyze_stocks
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="SightRay 분석 엔진 실행")
|
||||||
|
parser.add_argument('--cds_dir', type=str, required=False, default=None, help='CDS CSV 파일들이 있는 디렉토리 경로')
|
||||||
|
parser.add_argument('--model_path', type=str, required=False, default=None, help='저장된 XGBoost 모델 파일 경로')
|
||||||
|
parser.add_argument('--top_n', type=int, default=5, help='상위 예측 종목 수 (기본 5개)')
|
||||||
|
parser.add_argument('--output_path', type=str, default='prediction_result.csv', help='결과 저장 파일명')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
cds_dir = args.cds_dir or input("CDS 디렉토리 경로를 입력하세요 (예: data/CS/2024-12-31): ").strip()
|
||||||
|
model_path = args.model_path or input("모델 파일 경로를 입력하세요 (예: data_analysis_engine/models/model_2024-04-17.json): ").strip()
|
||||||
|
|
||||||
|
# CDS 경로 리스트 생성 (*.csv)
|
||||||
|
cds_files = glob.glob(os.path.join(cds_dir, '*.csv'))
|
||||||
|
if not cds_files:
|
||||||
|
print("[오류] CDS 파일이 존재하지 않습니다.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 분석 실행
|
||||||
|
result_df = analyze_stocks(cds_files, model_path, args.top_n)
|
||||||
|
|
||||||
|
# 결과 출력 및 저장
|
||||||
|
print("\n[예측 결과 요약]")
|
||||||
|
print(result_df)
|
||||||
|
|
||||||
|
result_df.rename(columns={'probability': 'predicted_score'}, inplace=True)
|
||||||
|
result_df.to_csv(args.output_path, index=False)
|
||||||
|
print(f"\n결과가 다음 위치에 저장되었습니다: {args.output_path}")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
113
data_analysis_engine/train_model.py
Normal file
113
data_analysis_engine/train_model.py
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
|
||||||
|
"""
|
||||||
|
📁 data_analysis_engine/train_model.py
|
||||||
|
|
||||||
|
Complete Data Set(CDS)를 기반으로 XGBoost 모델을 학습 후 저장하는 스크립트
|
||||||
|
하이퍼파라미터 튜닝 (GridSearchCV) 포함 + 학습 로그 자동 저장 + logloss 및 Precision@TopN 출력
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import pandas as pd
|
||||||
|
from datetime import datetime
|
||||||
|
from sklearn.metrics import accuracy_score, roc_auc_score, f1_score, log_loss
|
||||||
|
from sklearn.model_selection import GridSearchCV
|
||||||
|
from xgboost import XGBClassifier
|
||||||
|
from data_analysis_engine.dataset_builder import build_dataset_with_indicators
|
||||||
|
|
||||||
|
def precision_at_top_n(y_true, y_score, top_n=50):
|
||||||
|
top_indices = pd.Series(y_score).nlargest(top_n).index
|
||||||
|
top_preds = pd.Series(y_true).iloc[top_indices]
|
||||||
|
return top_preds.sum() / top_n
|
||||||
|
|
||||||
|
def train_model(cds_dir: str, output_model_path: str = None):
|
||||||
|
"""
|
||||||
|
CDS 디렉토리에서 데이터를 읽고 모델 학습 + 최적 파라미터 탐색 후 저장
|
||||||
|
"""
|
||||||
|
X_total, y_total = [], []
|
||||||
|
|
||||||
|
for file in os.listdir(cds_dir):
|
||||||
|
if not file.endswith("_ohlcv.csv"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
df = pd.read_csv(os.path.join(cds_dir, file))
|
||||||
|
if df.empty:
|
||||||
|
continue
|
||||||
|
|
||||||
|
X, y = build_dataset_with_indicators(df)
|
||||||
|
X_total.append(X)
|
||||||
|
y_total.append(y)
|
||||||
|
|
||||||
|
if not X_total:
|
||||||
|
raise ValueError("CDS 파일에서 학습 가능한 데이터가 없습니다.")
|
||||||
|
|
||||||
|
X_all = pd.concat(X_total, ignore_index=True)
|
||||||
|
y_all = pd.concat(y_total, ignore_index=True)
|
||||||
|
|
||||||
|
# ✅ 피처 이름 보존 확인
|
||||||
|
X_all.columns = X_all.columns.astype(str)
|
||||||
|
print("📎 학습 피처:", list(X_all.columns))
|
||||||
|
|
||||||
|
# 클래스 비율 확인
|
||||||
|
class_counts = y_all.value_counts().to_dict()
|
||||||
|
print(f"🎯 타깃 분포: {class_counts}")
|
||||||
|
weight = class_counts.get(0, 1) / class_counts.get(1, 1)
|
||||||
|
|
||||||
|
# 기본 모델 + 튜닝 대상 설정
|
||||||
|
base_model = XGBClassifier(
|
||||||
|
use_label_encoder=False,
|
||||||
|
eval_metric='logloss',
|
||||||
|
scale_pos_weight=weight,
|
||||||
|
verbosity=0
|
||||||
|
)
|
||||||
|
|
||||||
|
param_grid = {
|
||||||
|
'max_depth': [3, 5],
|
||||||
|
'learning_rate': [0.1, 0.01],
|
||||||
|
'n_estimators': [100, 300]
|
||||||
|
}
|
||||||
|
|
||||||
|
grid_search = GridSearchCV(base_model, param_grid, cv=3, scoring='roc_auc', n_jobs=-1, verbose=1)
|
||||||
|
grid_search.fit(X_all, y_all)
|
||||||
|
best_model = grid_search.best_estimator_
|
||||||
|
|
||||||
|
preds = best_model.predict(X_all)
|
||||||
|
probas = best_model.predict_proba(X_all)[:, 1]
|
||||||
|
|
||||||
|
acc = accuracy_score(y_all, preds)
|
||||||
|
auc = roc_auc_score(y_all, probas)
|
||||||
|
f1 = f1_score(y_all, preds)
|
||||||
|
logloss_val = log_loss(y_all, probas)
|
||||||
|
p_at_50 = precision_at_top_n(y_all, probas, top_n=50)
|
||||||
|
|
||||||
|
print(f"📊 정확도: {acc:.2%} | AUC: {auc:.3f} | F1: {f1:.3f} | LogLoss: {logloss_val:.4f} | P@50: {p_at_50:.3f} | 샘플 수: {len(X_all):,}")
|
||||||
|
print(f"🏆 최적 하이퍼파라미터: {grid_search.best_params_}")
|
||||||
|
|
||||||
|
# 로그 저장
|
||||||
|
date_tag = datetime.now().strftime("%Y-%m-%d")
|
||||||
|
log_path = "data_analysis_engine/train_log.csv"
|
||||||
|
log_exists = os.path.exists(log_path)
|
||||||
|
log_df = pd.DataFrame([{
|
||||||
|
"date": date_tag,
|
||||||
|
"accuracy": round(acc, 4),
|
||||||
|
"auc": round(auc, 4),
|
||||||
|
"f1_score": round(f1, 4),
|
||||||
|
"logloss": round(logloss_val, 4),
|
||||||
|
"precision@50": round(p_at_50, 4),
|
||||||
|
"samples": len(X_all),
|
||||||
|
"best_params": str(grid_search.best_params_),
|
||||||
|
"cds_dir": cds_dir
|
||||||
|
}])
|
||||||
|
log_df.to_csv(log_path, mode='a', index=False, header=not log_exists)
|
||||||
|
|
||||||
|
# 모델 저장
|
||||||
|
from xgboost import Booster
|
||||||
|
model_dir = "data_analysis_engine/models"
|
||||||
|
os.makedirs(model_dir, exist_ok=True)
|
||||||
|
versioned_path = os.path.join(model_dir, f"model_{date_tag}.json")
|
||||||
|
best_model.save_model(versioned_path)
|
||||||
|
print(f"✅ 모델 저장 완료: {versioned_path}")
|
||||||
|
print(f"📝 학습 로그 저장 완료: {log_path}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
cds_dir = input("CDS 데이터 디렉토리 [기본: data]: ").strip() or "data"
|
||||||
|
train_model(cds_dir)
|
71
data_analysis_engine/train_model_concept.txt
Normal file
71
data_analysis_engine/train_model_concept.txt
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
## 📄 train_model.py - Concept 설명 문서
|
||||||
|
|
||||||
|
### ✅ 목적
|
||||||
|
CDS(Complete Data Set)를 기반으로 한 학습용 데이터셋을 생성하고, XGBoost 모델을 학습시켜 상위 분석 파이프라인에서 활용할 수 있도록 모델을 저장하는 스크립트입니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 📂 입력 데이터
|
||||||
|
- `cds_dir`: `_ohlcv.csv` 형식의 종목별 CDS가 저장된 디렉토리
|
||||||
|
- 예: `AAPL_ohlcv.csv`, `MSFT_ohlcv.csv`
|
||||||
|
- 파일 구조는 OHLCV(Time Series) 형태
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ⚙️ 주요 기능
|
||||||
|
1. **데이터 적재 및 통합**
|
||||||
|
- 종목별 CDS 파일을 모두 읽어 피처(X), 타깃(y)으로 변환
|
||||||
|
- `build_dataset()` 호출 → 기술 지표 피처 등 포함 가능
|
||||||
|
|
||||||
|
2. **클래스 분포 확인 및 불균형 보정**
|
||||||
|
- `y_total`의 클래스 비율(상승/하락) 출력
|
||||||
|
- `scale_pos_weight` 자동 조정 → 불균형에 강한 학습 구조 지원
|
||||||
|
|
||||||
|
3. **XGBoost 모델 학습 + 하이퍼파라미터 튜닝**
|
||||||
|
- `GridSearchCV`로 최적 파라미터 탐색
|
||||||
|
- 튜닝 대상: `max_depth`, `learning_rate`, `n_estimators`
|
||||||
|
|
||||||
|
4. **모델 평가 지표 출력**
|
||||||
|
- 정확도 (`accuracy_score`)
|
||||||
|
- AUC (`roc_auc_score`)
|
||||||
|
- F1 점수 (`f1_score`)
|
||||||
|
- LogLoss (`log_loss`)
|
||||||
|
- Precision@TopN (`precision@TopN`, 예: P@50)
|
||||||
|
|
||||||
|
5. **모델 저장 (버전 관리 포함)**
|
||||||
|
- 저장 경로: `data_analysis_engine/models/model_YYYY-MM-DD.json`
|
||||||
|
- 날짜 기반 버전 관리 자동 수행
|
||||||
|
|
||||||
|
6. **학습 로그 자동 기록**
|
||||||
|
- `train_log.csv`에 날짜, 성능 지표, 파라미터, 샘플 수 기록
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🧪 사용 방법
|
||||||
|
```bash
|
||||||
|
python -m data_analysis_engine.train_model
|
||||||
|
```
|
||||||
|
또는 내부에서 import 후 `train_model("data")` 호출
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🧠 향후 확장 가능성
|
||||||
|
- Optuna 기반 자동 하이퍼파라미터 탐색
|
||||||
|
- K-fold 교차 검증 평가 구조 도입
|
||||||
|
- 예측 기반 ROI 피처 학습 (Target 다양화)
|
||||||
|
- 외부 평가 세트 적용 및 모델 비교 리포트 생성
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ⚠️ 주의사항
|
||||||
|
- 모든 CDS 파일은 비어 있지 않아야 하며, `_ohlcv.csv` 확장자를 따라야 함
|
||||||
|
- 피처 수가 변하면 모델 구조도 반드시 재학습 필요
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 📌 관련 파일
|
||||||
|
- `dataset_builder.py`: X, y 전처리 생성 및 기술 지표 포함
|
||||||
|
- `xgboost_model.py`: 모델 클래스 정의 및 저장/불러오기
|
||||||
|
- `X_total.csv`, `y_total.csv`: 통합 학습 데이터
|
||||||
|
- `model_YYYY-MM-DD.json`: 학습된 XGBoost 모델
|
||||||
|
- `train_log.csv`: 학습 결과 누적 로그
|
123
data_collection_engine/collect_all_cds.py
Normal file
123
data_collection_engine/collect_all_cds.py
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
'''
|
||||||
|
📁 data_collection_engine/collect_all_cds.py
|
||||||
|
|
||||||
|
S&P500 등 여러 종목에 대해 CDS 데이터를 일괄 수집하고,
|
||||||
|
XGBoost 학습을 위한 통합 데이터셋도 자동 생성하는 스크립트
|
||||||
|
'''
|
||||||
|
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import pandas as pd
|
||||||
|
from data_collection_engine.engine import DataCollectionEngine
|
||||||
|
from data_analysis_engine.dataset_builder import build_dataset
|
||||||
|
|
||||||
|
def collect_all_cds(ticker_list_path: str, start: str, end: str, save_dir: str = "data", delay_sec: int = 13):
|
||||||
|
"""
|
||||||
|
여러 종목의 CDS 데이터를 일괄 수집하여 저장하고, 학습용 통합 데이터셋도 생성
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
ticker_list_path (str): 수집할 종목 코드가 들어 있는 CSV 파일 경로
|
||||||
|
start (str): 시작일 (YYYY-MM-DD)
|
||||||
|
end (str): 종료일 (YYYY-MM-DD)
|
||||||
|
save_dir (str): 저장 디렉토리
|
||||||
|
delay_sec (int): API 호출 간격 (기본 13초)
|
||||||
|
"""
|
||||||
|
df = pd.read_csv(ticker_list_path)
|
||||||
|
|
||||||
|
# 사용자에게 타입 선택 옵션 제공
|
||||||
|
print("\n🔍 선택 가능한 종목 유형:")
|
||||||
|
type_mapping = {
|
||||||
|
"CS": "Common Stock - 일반 주식 (가장 보편적인 기업 지분)",
|
||||||
|
"ETF": "Exchange Traded Fund - 지수 추종형 상장지수펀드",
|
||||||
|
"ETN": "Exchange Traded Note - 채권 기반의 상장지수증권",
|
||||||
|
"ADR": "American Depository Receipt - 해외 주식의 미국 상장 버전",
|
||||||
|
"PREF": "Preferred Stock - 배당 우선주",
|
||||||
|
"UNIT": "Unit - ETF 구성 단위 또는 묶음",
|
||||||
|
"RIGHT": "Rights - 신주인수권, 일정 기간 내 주식 매입 권리",
|
||||||
|
"FUND": "Mutual Fund - 공모펀드 또는 투자신탁",
|
||||||
|
"SP": "Structured Product - 파생결합증권",
|
||||||
|
"WARRANT": "Warrant - 일정 가격에 주식 구매 권한을 부여하는 증서",
|
||||||
|
"INDEX": "Market Index - 종합 주가지수 등",
|
||||||
|
"BOND": "Bond - 회사채 또는 정부채 등 고정수익 상품"
|
||||||
|
}
|
||||||
|
for k, v in type_mapping.items():
|
||||||
|
print(f"{k:<7}: {v}")
|
||||||
|
type_input = input("\n✅ 수집할 종목 유형을 입력하세요 (예: CS): ").strip().upper()
|
||||||
|
|
||||||
|
if "type" not in df.columns:
|
||||||
|
print("❗ 'type' 컬럼이 존재하지 않습니다. 전체 데이터 사용.")
|
||||||
|
filtered_df = df.copy()
|
||||||
|
else:
|
||||||
|
filtered_df = df[df["type"] == type_input].copy()
|
||||||
|
|
||||||
|
total_available = len(filtered_df)
|
||||||
|
print(f"\n총 {total_available}개의 종목이 선택된 유형({type_input})에 해당합니다.")
|
||||||
|
estimated_time = total_available * delay_sec / 60
|
||||||
|
print(f"⏳ 예상 소요 시간: 약 {estimated_time:.1f}분 (요금제 기준 {delay_sec}초 간격)")
|
||||||
|
count_limit = input(f"몇 개를 수집하시겠습니까? [기본: {total_available}]: ").strip()
|
||||||
|
count = int(count_limit) if count_limit else total_available
|
||||||
|
|
||||||
|
tickers = filtered_df.head(count)
|
||||||
|
symbol_col = "symbol" if "symbol" in tickers.columns else "ticker"
|
||||||
|
ticker_list = tickers[symbol_col].dropna().unique()
|
||||||
|
|
||||||
|
# 종료 날짜 기준으로 저장 디렉토리 세분화 (예: data/CS/2024-12-31)
|
||||||
|
dated_dir = os.path.join(save_dir, type_input, end)
|
||||||
|
engine = DataCollectionEngine(data_dir=dated_dir)
|
||||||
|
os.makedirs(dated_dir, exist_ok=True)
|
||||||
|
|
||||||
|
failed = []
|
||||||
|
X_all, y_all = [], []
|
||||||
|
error_429_count = 0
|
||||||
|
|
||||||
|
for i, symbol in enumerate(ticker_list):
|
||||||
|
try:
|
||||||
|
print(f"[{i+1}/{len(ticker_list)}] {symbol} 수집 중...")
|
||||||
|
df = engine.collect(symbol.strip().upper(), start, end)
|
||||||
|
X, y = build_dataset(df)
|
||||||
|
X_all.append(X)
|
||||||
|
y_all.append(y)
|
||||||
|
error_429_count = 0
|
||||||
|
time.sleep(delay_sec)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ {symbol} 수집 실패: {e}")
|
||||||
|
failed.append(symbol)
|
||||||
|
if "429" in str(e):
|
||||||
|
error_429_count += 1
|
||||||
|
if error_429_count >= 2:
|
||||||
|
print("⏸️ 연속된 429 오류 감지 → 60초 대기 중...")
|
||||||
|
time.sleep(60)
|
||||||
|
|
||||||
|
if failed:
|
||||||
|
print("\n⚠️ 일부 종목 수집 실패:", failed)
|
||||||
|
failed_df = pd.DataFrame(failed, columns=["ticker"])
|
||||||
|
failed_df.to_csv(os.path.join(dated_dir, "failed_tickers.csv"), index=False)
|
||||||
|
print("📄 실패 종목 목록 저장 완료: failed_tickers.csv")
|
||||||
|
else:
|
||||||
|
print("\n✅ 전체 종목 수집 완료!")
|
||||||
|
|
||||||
|
if X_all:
|
||||||
|
X_total = pd.concat(X_all, ignore_index=True)
|
||||||
|
y_total = pd.concat(y_all, ignore_index=True)
|
||||||
|
X_total.to_csv(os.path.join(dated_dir, "X_total.csv"), index=False)
|
||||||
|
y_total.to_csv(os.path.join(dated_dir, "y_total.csv"), index=False)
|
||||||
|
print("✅ 통합 학습용 데이터 저장 완료: X_total.csv, y_total.csv")
|
||||||
|
else:
|
||||||
|
print("❗ 유효한 CDS 데이터가 없어 학습용 데이터셋을 생성하지 못했습니다.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("CDS 일괄 수집을 시작합니다. 아래 정보를 입력해 주세요:")
|
||||||
|
ticker_list_path = input("티커 리스트 CSV 경로 [기본: sp500_tickers.csv]: ").strip() or "sp500_tickers.csv"
|
||||||
|
start = input("시작일 (YYYY-MM-DD) [기본: 2023-01-01]: ").strip() or "2023-01-01"
|
||||||
|
end = input("종료일 (YYYY-MM-DD) [기본: 2024-12-31]: ").strip() or "2024-12-31"
|
||||||
|
save_dir = input("저장 디렉토리 이름 [기본: data]: ").strip() or "data"
|
||||||
|
delay = input("API 호출 간격 (초) [기본: 13]: ").strip()
|
||||||
|
delay_sec = int(delay) if delay else 13
|
||||||
|
|
||||||
|
collect_all_cds(
|
||||||
|
ticker_list_path=ticker_list_path,
|
||||||
|
start=start,
|
||||||
|
end=end,
|
||||||
|
save_dir=save_dir,
|
||||||
|
delay_sec=delay_sec
|
||||||
|
)
|
48
data_collection_engine/engine.py
Normal file
48
data_collection_engine/engine.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# os 모듈은 폴더 생성, 파일 경로 등 운영체제와 관련된 기능을 다룹니다
|
||||||
|
try:
|
||||||
|
from data_collection_engine.modules.fetchers.polygon import fetch_ohlcv
|
||||||
|
from data_collection_engine.modules.preprocessors import clean_ohlcv
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
from modules.fetchers.polygon import fetch_ohlcv
|
||||||
|
from modules.preprocessors import clean_ohlcv
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Polygon API를 통해 주가 데이터를 가져오는 함수 (fetcher 모듈에 정의됨)
|
||||||
|
|
||||||
|
|
||||||
|
# 가져온 데이터를 정제(clean)하는 함수 (preprocessor 모듈에 정의됨)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 데이터 수집을 담당하는 클래스
|
||||||
|
class DataCollectionEngine:
|
||||||
|
# 생성자 함수: 객체가 만들어질 때 실행됨
|
||||||
|
def __init__(self, data_dir="data"):
|
||||||
|
self.data_dir = data_dir # 수집한 데이터를 저장할 디렉토리 경로
|
||||||
|
|
||||||
|
# 데이터 저장 폴더가 없으면 새로 생성함
|
||||||
|
os.makedirs(data_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# 실제 데이터를 수집하고 저장하는 메인 함수
|
||||||
|
def collect(self, symbol: str, start_date: str, end_date: str):
|
||||||
|
# 사용자에게 현재 어떤 종목의 데이터를 수집 중인지 출력
|
||||||
|
print(f"{symbol} 데이터를 수집 중입니다...")
|
||||||
|
|
||||||
|
# fetch_ohlcv 함수를 사용하여 API로부터 주가 원본 데이터(raw data)를 수집
|
||||||
|
raw_data = fetch_ohlcv(symbol, start_date, end_date)
|
||||||
|
|
||||||
|
# 수집한 데이터를 정제(cleaning)하여 DataFrame으로 변환
|
||||||
|
df = clean_ohlcv(raw_data)
|
||||||
|
|
||||||
|
# 저장할 파일 경로를 지정함 (예: data/AAPL_ohlcv.csv)
|
||||||
|
save_path = os.path.join(self.data_dir, f"{symbol}_ohlcv.csv")
|
||||||
|
|
||||||
|
# DataFrame을 CSV 파일로 저장
|
||||||
|
df.to_csv(save_path, index=False)
|
||||||
|
|
||||||
|
# 사용자에게 저장 완료 메시지를 출력
|
||||||
|
print(f"{symbol} 데이터 저장 완료: {save_path}")
|
||||||
|
|
||||||
|
# 수집한 데이터프레임을 반환 (다른 곳에서 쓸 수 있도록)
|
||||||
|
return df
|
25
data_collection_engine/main.py
Normal file
25
data_collection_engine/main.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# 데이터 수집 엔진을 불러옵니다 (engine.py에 있는 DataCollectionEngine 클래스 사용)
|
||||||
|
from engine import DataCollectionEngine
|
||||||
|
|
||||||
|
# 프로그램의 시작 지점을 정의하는 함수
|
||||||
|
def main():
|
||||||
|
# 사용자에게 종목 코드를 입력받습니다 (예: AAPL, TSLA 등)
|
||||||
|
# 입력값에서 양쪽 공백을 제거하고 대문자로 변환합니다
|
||||||
|
symbol = input("조회할 주식 종목 코드를 입력하세요 (예: AAPL, TSLA): ").strip().upper()
|
||||||
|
|
||||||
|
# 사용자에게 수집할 시작 날짜를 입력받습니다 (형식: 2024-01-01)
|
||||||
|
start = input("시작 날짜를 입력하세요 (YYYY-MM-DD): ").strip()
|
||||||
|
|
||||||
|
# 사용자에게 수집할 종료 날짜를 입력받습니다 (형식: 2024-12-31)
|
||||||
|
end = input("종료 날짜를 입력하세요 (YYYY-MM-DD): ").strip()
|
||||||
|
|
||||||
|
# 데이터 수집 엔진 객체를 생성합니다
|
||||||
|
engine = DataCollectionEngine()
|
||||||
|
|
||||||
|
# 입력받은 종목, 날짜에 따라 데이터를 수집하고 저장합니다
|
||||||
|
engine.collect(symbol, start, end)
|
||||||
|
|
||||||
|
# 이 파일이 직접 실행되었을 때만 main() 함수를 실행합니다
|
||||||
|
# (다른 파일에서 이 파일을 import 하면 실행되지 않도록 하기 위함)
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
40
data_collection_engine/modules/fetchers/polygon.py
Normal file
40
data_collection_engine/modules/fetchers/polygon.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# 운영체제 관련 기능 (예: 환경변수 읽기, 경로 등)을 위한 모듈
|
||||||
|
import os
|
||||||
|
|
||||||
|
# HTTP 요청을 보내기 위한 외부 라이브러리 (API 호출용)
|
||||||
|
import requests
|
||||||
|
|
||||||
|
# .env 파일에 저장된 환경변수를 불러오기 위한 도구
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
# .env 파일에서 환경변수들을 로드합니다 (예: API 키)
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
# 환경변수에서 Polygon API 키를 읽어옵니다
|
||||||
|
API_KEY = os.getenv("POLYGON_API_KEY")
|
||||||
|
|
||||||
|
|
||||||
|
# 주어진 종목(symbol)에 대해 Polygon API를 사용하여 OHLCV 데이터를 수집하는 함수
|
||||||
|
# 예: fetch_ohlcv("AAPL", "2024-01-01", "2024-03-01")
|
||||||
|
def fetch_ohlcv(symbol: str, start_date: str, end_date: str, interval: str = "day") -> list:
|
||||||
|
# Polygon API의 엔드포인트 URL 구성
|
||||||
|
url = f"https://api.polygon.io/v2/aggs/ticker/{symbol}/range/1/{interval}/{start_date}/{end_date}"
|
||||||
|
|
||||||
|
# API 호출에 필요한 파라미터 정의
|
||||||
|
params = {
|
||||||
|
"adjusted": "true", # 분할, 배당 등을 반영한 데이터 사용 여부
|
||||||
|
"sort": "asc", # 날짜 기준 오름차순 정렬
|
||||||
|
"limit": 50000, # 한 번에 최대 수집 가능한 데이터 수
|
||||||
|
"apiKey": API_KEY # 인증을 위한 API 키
|
||||||
|
}
|
||||||
|
|
||||||
|
# GET 방식으로 API 호출을 수행
|
||||||
|
res = requests.get(url, params=params)
|
||||||
|
|
||||||
|
# 응답 상태 코드가 200이 아니면 예외를 발생시킴 (에러 메시지도 함께 출력)
|
||||||
|
if res.status_code != 200:
|
||||||
|
raise Exception(f"Polygon API error: {res.status_code} - {res.text}")
|
||||||
|
|
||||||
|
# JSON 응답 중 'results' 키에 들어있는 실제 데이터를 추출하여 반환
|
||||||
|
data = res.json()
|
||||||
|
return data.get("results", [])
|
35
data_collection_engine/modules/preprocessors.py
Normal file
35
data_collection_engine/modules/preprocessors.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# pandas는 데이터를 표 형태(DataFrame)로 다루기 위한 라이브러리입니다
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
# 이 함수는 Polygon API에서 받아온 주식 데이터를 CDS(Complete Data Set)로 정리합니다
|
||||||
|
def clean_ohlcv(raw_data: list) -> pd.DataFrame:
|
||||||
|
# 입력된 raw_data (딕셔너리 리스트)를 pandas의 DataFrame으로 변환
|
||||||
|
df = pd.DataFrame(raw_data)
|
||||||
|
|
||||||
|
# 't' 컬럼은 timestamp인데 밀리초 단위이므로 datetime 형식으로 변환
|
||||||
|
df['t'] = pd.to_datetime(df['t'], unit='ms')
|
||||||
|
|
||||||
|
# 컬럼 이름을 사람이 이해하기 쉬운 형태로 바꿉니다
|
||||||
|
df.rename(columns={
|
||||||
|
't': 'timestamp', # 날짜 및 시간
|
||||||
|
'o': 'open', # 시가
|
||||||
|
'h': 'high', # 고가
|
||||||
|
'l': 'low', # 저가
|
||||||
|
'c': 'close', # 종가
|
||||||
|
'v': 'volume' # 거래량
|
||||||
|
}, inplace=True)
|
||||||
|
|
||||||
|
# 중복된 행이 있다면 제거
|
||||||
|
df.drop_duplicates(inplace=True)
|
||||||
|
|
||||||
|
# 결측값(NaN)이 포함된 행이 있다면 제거
|
||||||
|
df.dropna(inplace=True)
|
||||||
|
|
||||||
|
# 날짜(timestamp)를 기준으로 오름차순 정렬
|
||||||
|
df.sort_values(by='timestamp', inplace=True)
|
||||||
|
|
||||||
|
# 인덱스를 다시 0부터 순서대로 지정
|
||||||
|
df.reset_index(drop=True, inplace=True)
|
||||||
|
|
||||||
|
# 최종적으로 정제된 CDS(DataFrame)를 반환
|
||||||
|
return df
|
@ -0,0 +1,45 @@
|
|||||||
|
## 📘 거래량 가중 평균가 (VWAP: Volume Weighted Average Price) 개념 정리
|
||||||
|
|
||||||
|
### ✅ VWAP란?
|
||||||
|
VWAP는 특정 시간 구간 내에서 **거래량을 고려한 평균 가격**을 의미합니다. 단순 평균가보다 실제 체결 강도와 시장 중심가를 더 잘 반영하므로, 기관 투자자와 알고리즘 트레이딩 시스템에서 핵심적으로 활용됩니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🧮 계산식
|
||||||
|
VWAP는 다음 수식으로 계산됩니다:
|
||||||
|
|
||||||
|
\[
|
||||||
|
\text{VWAP} = \frac{\sum (\text{가격} \times \text{거래량})}{\sum \text{거래량}}
|
||||||
|
\]
|
||||||
|
|
||||||
|
※ Polygon.io API에서는 이 값을 미리 계산해 `vw` 컬럼으로 제공합니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 📊 SightRay에서의 활용
|
||||||
|
- CDS 생성 시 API로부터 받은 `vw` 값을 그대로 포함
|
||||||
|
- 분석 엔진이나 모델 학습 단계에서 **독립 피처로 활용 가능**
|
||||||
|
- 특정 시점의 종가(close)와 VWAP 간 차이로 **시장 심리나 평균 체결 강도** 해석 가능
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🧠 예측 모델에서의 장점
|
||||||
|
| 항목 | 효과 |
|
||||||
|
|------|------|
|
||||||
|
| 평균보다 실제 중심가 반영 | 단기 가격 왜곡을 필터링 가능 |
|
||||||
|
| 종가 대비 VWAP 분석 | 매수/매도 강도 판단에 활용 가능 |
|
||||||
|
| 거래량 포함 | 수급 기반 전략 반영에 유리 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 💡 활용 아이디어
|
||||||
|
- `close > VWAP` 여부를 바이너리 피처로 추가
|
||||||
|
- `close - VWAP` 값을 연속형 피처로 사용
|
||||||
|
- VWAP 상하 이격률을 통해 **기관 중심 매매 강도** 파악
|
||||||
|
- VWAP을 기준선으로 한 기술적 전략 (ex: VWAP Breakout)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 📌 정리
|
||||||
|
VWAP는 단순 가격 평균보다 정보량이 풍부하고, 실제 거래 강도를 반영하는 핵심적인 기술적 데이터입니다. SightRay에서는 CDS 수집 시 이를 포함하고, 모델 학습 피처로도 적극 활용 가능하도록 설계되어 있습니다.
|
||||||
|
|
96
developement_advice/data_analysis_engine_summary.txt
Normal file
96
developement_advice/data_analysis_engine_summary.txt
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
# 🧠 SightRay 데이터 분석 엔진 – 기능 요약 및 MVP 충족도 평가
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 엔진 개요
|
||||||
|
|
||||||
|
**SightRay 데이터 분석 엔진**은 CDS(Complete Data Set)를 입력으로 받아
|
||||||
|
주가의 상승 여부 또는 ROI(수익률)를 예측하고, 상승 가능성이 높은 종목을 선별하는 핵심 예측 시스템입니다.
|
||||||
|
|
||||||
|
- **입력:** OHLCV 기반의 CDS 파일 (CSV)
|
||||||
|
- **출력:** 예측 결과 + 상승 확률 + 상위 종목 리스트 (CSV 저장)
|
||||||
|
- **연계 대상:** 리스크 관리 엔진
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧩 디렉토리 구조 및 구성 파일
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
sightray/
|
||||||
|
├── data_analysis_engine/
|
||||||
|
│ ├── dataset_builder.py # CDS → 학습용 피처/타깃 변환
|
||||||
|
│ ├── models/
|
||||||
|
│ │ └── xgboost_model.py # XGBoost 모델 정의 및 예측 기능
|
||||||
|
│ ├── analyzer.py # 다중 종목 예측 + 상위 추출
|
||||||
|
│ └── predict.py # CLI 실행 진입점 (결과 저장)
|
||||||
|
```
|
||||||
|
|
||||||
|
| 파일명 | 역할 | 계층 |
|
||||||
|
|--------|------|------|
|
||||||
|
| `dataset_builder.py` | CDS → 학습용 데이터셋 변환 | Module Layer |
|
||||||
|
| `xgboost_model.py` | 모델 정의, 학습, 예측 | Module Layer |
|
||||||
|
| `analyzer.py` | 종목 순회, 예측, 정렬 | Engine Layer |
|
||||||
|
| `predict.py` | 분석 흐름 실행 CLI | Application Layer |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔁 데이터 흐름
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
[CDS CSV 파일]
|
||||||
|
↓
|
||||||
|
dataset_builder.py → 학습용 피처 + 타깃 생성
|
||||||
|
↓
|
||||||
|
xgboost_model.py → 모델 로드 + 예측 수행
|
||||||
|
↓
|
||||||
|
analyzer.py → 종목별 예측 결과 정리
|
||||||
|
↓
|
||||||
|
predict.py → 결과 출력 및 prediction_result.csv 저장
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 구현된 기능 목록
|
||||||
|
|
||||||
|
| 기능 항목 | 구현 여부 | 설명 |
|
||||||
|
|-----------|-----------|--------|
|
||||||
|
| CDS 입력 처리 | ✅ | CDS → 학습용 데이터로 변환 |
|
||||||
|
| 단일 모델 예측 (XGBoost) | ✅ | 분류 모델로 상승 확률 예측 |
|
||||||
|
| 다중 종목 분석 | ✅ | CDS 파일 목록을 순회하며 예측 수행 |
|
||||||
|
| 상위 종목 추출 | ✅ | 상승 확률 기준 top-N 종목 정렬 |
|
||||||
|
| CLI 실행 및 결과 저장 | ✅ | `predict.py`에서 CSV로 저장 가능 |
|
||||||
|
| 결과 포맷 | ✅ | `symbol, prediction, probability` 포함한 DataFrame |
|
||||||
|
| 계층화된 구조 | ✅ | 분석 모듈 구조 분리 및 재사용성 확보 |
|
||||||
|
| 문서화 가능성 | ✅ | 각 모듈별 역할 및 흐름 주석 포함 완료 |
|
||||||
|
| 평가 지표, ROC 등 | ❌ | (추후 확장 예정) |
|
||||||
|
| LSTM 또는 앙상블 | ❌ | (MVP 이후 단계에서 추가 예정) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📈 예측 결과 예시
|
||||||
|
|
||||||
|
| symbol | prediction | probability |
|
||||||
|
|--------|------------|-------------|
|
||||||
|
| AAPL | 1 | 0.84 |
|
||||||
|
| TSLA | 0 | 0.48 |
|
||||||
|
| NVDA | 1 | 0.76 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 MVP 충족도 평가
|
||||||
|
|
||||||
|
| 항목 | 달성률 | 설명 |
|
||||||
|
|-------|---------|------------------------|
|
||||||
|
| 핵심 기능 구현 | **85%+** | CDS 분석 → 상위 종목 추출까지 구현 완료 |
|
||||||
|
| 예측 정확도 개선 | 🚧 | LSTM, 앙상블은 추후 도입 예정 |
|
||||||
|
| 평가/리포트 기능 | 🚧 | 성능 평가 지표 및 시각화는 향후 추가 예정 |
|
||||||
|
| 연계 준비도 | ✅ | 리스크 엔진에 결과 연동 가능 상태 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 결론
|
||||||
|
|
||||||
|
SightRay의 데이터 분석 엔진은 현재 단계에서 **MVP 수준의 주요 기능을 모두 구현 완료**하였으며,
|
||||||
|
이제 리스크 관리 엔진과의 연계 또는 예측 성능 향상(모델 고도화)으로 나아갈 수 있는 준비가 완료된 상태입니다.
|
||||||
|
|
||||||
|
> 🚀 다음 단계로는 LSTM 또는 앙상블 모델 추가, 성능 평가 지표 도입, 실거래 적용 테스트가 가능합니다.
|
105
developement_advice/data_collection_engine_summary.txt
Normal file
105
developement_advice/data_collection_engine_summary.txt
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
# 📦 SightRay 데이터 수집 엔진 – 기능 요약 및 MVP 충족도 평가
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 엔진 개요
|
||||||
|
|
||||||
|
**SightRay 데이터 수집 엔진**은 외부 주가 API(Polygon 등)를 통해 종목별 OHLCV 데이터를 수집하고,
|
||||||
|
분석 엔진이 바로 사용할 수 있도록 정제된 CDS(Complete Data Set)를 생성하는 핵심 모듈입니다.
|
||||||
|
|
||||||
|
- **입력:** 종목 코드, 날짜 범위
|
||||||
|
- **출력:** 정제된 CDS 파일 (CSV 형식)
|
||||||
|
- **사용자 입력 기반 동작 (CLI)**
|
||||||
|
- **기술 지표 생성은 포함하지 않음 → 분석 엔진에서 처리**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧩 디렉토리 구조 및 구성 파일
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
sightray/
|
||||||
|
└── data_collection_engine/
|
||||||
|
├── engine.py # 수집 실행을 담당하는 통합 엔진 클래스
|
||||||
|
└── modules/
|
||||||
|
├── fetchers/
|
||||||
|
│ └── polygon.py # Polygon API를 통한 주가 수집 기능
|
||||||
|
└── preprocessors.py # 결측/중복 제거, timestamp 정제 등 전처리
|
||||||
|
```
|
||||||
|
|
||||||
|
| 파일명 | 역할 | 계층 |
|
||||||
|
|--------|------|------|
|
||||||
|
| `engine.py` | 사용자 입력 → 수집 → 전처리 → 저장 흐름 실행 | Engine Layer |
|
||||||
|
| `fetchers/polygon.py` | Polygon API로 OHLCV 데이터 수집 | Module Layer |
|
||||||
|
| `preprocessors.py` | 데이터 클렌징 및 CDS 구조 정제 | Module Layer |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔁 데이터 흐름
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
[사용자 입력: 종목, 날짜 범위]
|
||||||
|
↓
|
||||||
|
fetchers/polygon.py → Polygon API 호출
|
||||||
|
↓
|
||||||
|
preprocessors.py → DataFrame 정제 및 컬럼 리네이밍
|
||||||
|
↓
|
||||||
|
engine.py → 전체 흐름 제어 및 CSV 저장 (data/SYMBOL_ohlcv.csv)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 구현된 기능 목록
|
||||||
|
|
||||||
|
| 기능 항목 | 구현 여부 | 설명 |
|
||||||
|
|-----------|-----------|--------|
|
||||||
|
| 사용자 입력 처리 | ✅ | 종목코드, 날짜 범위 입력 가능 (CLI 기반) |
|
||||||
|
| 외부 API 연동 | ✅ | Polygon API 연동 및 에러 핸들링 포함 |
|
||||||
|
| 결측치/중복 제거 | ✅ | DataFrame 전처리 수행 |
|
||||||
|
| 컬럼 정제 및 타입 변환 | ✅ | 컬럼 이름 변경, datetime 변환 포함 |
|
||||||
|
| 정렬 및 저장 | ✅ | timestamp 기준 정렬 후 CSV 저장 |
|
||||||
|
| 디렉토리 구조화 | ✅ | fetcher / preprocessor 모듈 분리 구조 적용 |
|
||||||
|
| 환경변수 관리 | ✅ | `.env` 파일로 API 키 분리 |
|
||||||
|
| CDS 정의 준수 | ✅ | 분석 엔진이 바로 사용할 수 있는 포맷 제공 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📄 생성되는 CDS 예시
|
||||||
|
|
||||||
|
| timestamp | open | high | low | close | volume | vw | n |
|
||||||
|
|-----------|------|------|-----|-------|--------|----|---|
|
||||||
|
| 2024-01-02 | 187.15 | 188.44 | 183.88 | 185.64 | 81964874 | 185.9465 | 1008871 |
|
||||||
|
|
||||||
|
- timestamp는 정렬 및 datetime 형식 변환 완료
|
||||||
|
- 분석용 OHLCV 컬럼 포함
|
||||||
|
- 거래량(volume), 체결 수(n), 거래량 가중 평균가(vw) 포함
|
||||||
|
|
||||||
|
### 🔍 `vw` 컬럼 (거래량 가중 평균가, VWAP)
|
||||||
|
- 해당 기간 동안 체결된 가격과 거래량을 기반으로 산출된 **실질적인 평균 매수/매도 가격**
|
||||||
|
- 계산식 예시:
|
||||||
|
\[
|
||||||
|
VWAP = \\frac{\\sum (\\text{가격} \\times \\text{거래량})}{\\sum \\text{거래량}}
|
||||||
|
\]
|
||||||
|
- **기술 지표로도 활용됨** (지지선/저항선 판단 근거로 자주 사용)
|
||||||
|
- `Polygon.io`의 응답에 포함된 기본 컬럼으로, SightRay는 그대로 CDS에 반영
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 MVP 충족도 평가
|
||||||
|
|
||||||
|
| 항목 | 달성률 | 설명 |
|
||||||
|
|-------|---------|------------------------|
|
||||||
|
| CDS 수집 및 저장 | ✅ 100% | 분석에 활용 가능한 형식으로 저장 완료 |
|
||||||
|
| CLI 실행 및 구조화 | ✅ | 사용자 친화적 구조로 엔진 실행 가능 |
|
||||||
|
| 지표 생성 미포함 | ⛔ | 지표는 분석 엔진에서 처리 예정 |
|
||||||
|
| API 오류 대응 | ✅ | 응답 코드 체크 및 예외 처리 구현 |
|
||||||
|
| 확장 가능성 (다중 종목, 자동화) | 🔜 | 향후 배치 처리 및 스케줄링 가능성 고려됨 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 결론
|
||||||
|
|
||||||
|
SightRay의 데이터 수집 엔진은 MVP 기준에서 요구되는 **CDS 생성 기능을 완전히 충족**하고 있으며,
|
||||||
|
다음 단계인 분석 엔진, 리스크 엔진으로 원활하게 데이터를 공급할 수 있는 상태입니다.
|
||||||
|
|
||||||
|
> 🚀 이후 확장 과제로는 다중 종목 수집, 자동화 실행 스케줄링, DB 저장 연계 기능이 고려될 수 있습니다.
|
140
developement_advice/instruction.txt
Normal file
140
developement_advice/instruction.txt
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
### SightRay 핵심 디렉토리 구조 설명 (MVP 기준)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 프로젝트 루트 구조 예시
|
||||||
|
|
||||||
|
```
|
||||||
|
sightray/
|
||||||
|
├── data_collection_engine/ # 데이터 수집 및 CDS 생성 담당
|
||||||
|
│ ├── engine.py # 수집 + 정제 + 저장 실행 흐름
|
||||||
|
│ └── modules/
|
||||||
|
│ ├── fetchers/
|
||||||
|
│ │ └── polygon.py # Polygon API 연동
|
||||||
|
│ └── preprocessors.py # CDS 정제 및 전처리
|
||||||
|
│
|
||||||
|
└── data_analysis_engine/ # CDS를 기반으로 주가 예측 담당
|
||||||
|
│ ├── dataset_builder.py # CDS → 학습용 데이터로 변환
|
||||||
|
│ ├── models/
|
||||||
|
│ │ └── xgboost_model.py # XGBoost 모델 정의, 학습, 예측
|
||||||
|
│ ├── analyzer.py # 여러 종목 예측, 상위 선정
|
||||||
|
│ ├── predict.py # 전체 분석 흐름 실행 CLI
|
||||||
|
│
|
||||||
|
├── risk_manage_engine/ # 예측된 종목의 리스크를 정량적으로 평가
|
||||||
|
│ ├── calculators/ # 리스크 지표 계산 모듈 집합
|
||||||
|
│ │ ├── var.py # Value at Risk 계산
|
||||||
|
│ │ ├── svar.py # Stressed Value at Risk 계산
|
||||||
|
│ │ ├── monte_carlo.py # Monte Carlo 시뮬레이션
|
||||||
|
│ │ └── atr.py # ATR 지표 계산
|
||||||
|
│ ├── risk_calculator.py # 모든 지표를 불러와 계산하는 통합 인터페이스
|
||||||
|
│ ├── risk_scorer.py # risk_score 산출 (0~100)
|
||||||
|
│ ├── filter.py # 투자 적격 종목 필터링
|
||||||
|
│ └── evaluate.py # 전체 리스크 평가 흐름 실행
|
||||||
|
│
|
||||||
|
├── strategy_engine/ # 리스크 필터링된 종목을 기반으로 실제 투자 전략 수립
|
||||||
|
│ ├── templates/ # 다양한 전략 유형 정의 (기본, 리스크 기반 등)
|
||||||
|
│ │ ├── basic_strategy.py # TP/SL 기준의 기본 전략
|
||||||
|
│ │ ├── risk_adjusted_strategy.py # 리스크 점수 기반 투자 강도 조절 전략
|
||||||
|
│ ├── selector.py # tradable 종목 중 전략 조건 부합 종목만 선택
|
||||||
|
│ ├── position_manager.py # 진입/청산 시점 판단, 자금 배분 전략 실행
|
||||||
|
│ └── run_strategy.py # 전략 엔진 실행 진입점
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 데이터 수집 엔진 (data_collection_engine)
|
||||||
|
|
||||||
|
### engine.py
|
||||||
|
- CDS 생성을 위한 메인 클래스 (`DataCollectionEngine`) 구현
|
||||||
|
- fetcher와 preprocessor를 조합하여 데이터 수집 → 정제 → CSV 저장
|
||||||
|
|
||||||
|
### modules/fetchers/polygon.py
|
||||||
|
- Polygon API에서 주식의 OHLCV 데이터를 요청하고 받아오는 로직 구현
|
||||||
|
- API 키는 `.env` 파일을 통해 안전하게 불러옴
|
||||||
|
|
||||||
|
### modules/preprocessors.py
|
||||||
|
- API로 받은 원본 데이터를 CDS 형식으로 정제
|
||||||
|
- 결측치 제거, 중복 제거, 컬럼명 정리, 정렬, 인덱스 리셋 등 포함
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 데이터 분석 엔진 (data_analysis_engine)
|
||||||
|
|
||||||
|
### dataset_builder.py
|
||||||
|
"""
|
||||||
|
파일 설명:
|
||||||
|
CDS (OHLCV) 데이터를 머신러닝 학습용 구조로 변환
|
||||||
|
- 입력: CDS CSV 파일 (DataFrame)
|
||||||
|
- 출력: XGBoost 학습용 피처 + 타깃 컬럼 포함 DataFrame
|
||||||
|
- 타깃: 다음날 종가가 오르면 1, 내리면 0
|
||||||
|
"""
|
||||||
|
|
||||||
|
### models/xgboost_model.py
|
||||||
|
"""
|
||||||
|
파일 설명:
|
||||||
|
XGBoost 모델 학습, 저장, 예측을 포함한 클래스 정의
|
||||||
|
- fit(): 학습
|
||||||
|
- predict(): 예측 결과 및 상위 종목 정렬 반환
|
||||||
|
- save_model(), load_model(): 모델 파일 저장 및 로드
|
||||||
|
"""
|
||||||
|
|
||||||
|
### analyzer.py
|
||||||
|
"""
|
||||||
|
파일 설명:
|
||||||
|
각 종목의 CDS를 불러와 XGBoost 예측 실행
|
||||||
|
- ROI, 상승 확률 등을 기반으로 상위 종목 필터링
|
||||||
|
"""
|
||||||
|
|
||||||
|
### predict.py
|
||||||
|
"""
|
||||||
|
파일 설명:
|
||||||
|
전체 분석 흐름을 실행하는 엔트리 포인트
|
||||||
|
- CLI 또는 스크립트로 사용
|
||||||
|
- analyzer.py를 통해 결과 생성 후 저장
|
||||||
|
"""
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 리스크 관리 엔진 (risk_manage_engine)
|
||||||
|
|
||||||
|
### calculators/
|
||||||
|
- var.py: Value at Risk 계산 로직
|
||||||
|
- svar.py: Stressed Value at Risk 계산 로직
|
||||||
|
- monte_carlo.py: Monte Carlo Simulation을 통한 리스크 추정
|
||||||
|
- atr.py: Average True Range 계산을 통한 변동성 측정
|
||||||
|
|
||||||
|
### risk_calculator.py
|
||||||
|
- 위의 리스크 지표 계산 모듈들을 통합 호출
|
||||||
|
- 분석 결과와 CDS를 입력받아 리스크 요소 계산
|
||||||
|
|
||||||
|
### risk_scorer.py
|
||||||
|
- 여러 리스크 지표를 통합하여 종합 risk_score(0~100)를 산출
|
||||||
|
- 사용자 정의 가중치를 적용한 리스크 평가 기준 가능
|
||||||
|
|
||||||
|
### filter.py
|
||||||
|
- risk_score 기반으로 투자 적격 종목을 필터링
|
||||||
|
- 기준치 이하 종목은 제외
|
||||||
|
|
||||||
|
### evaluate.py
|
||||||
|
- 분석 엔진에서 넘어온 종목에 대해 전체 리스크 평가 파이프라인 실행
|
||||||
|
- 결과를 CSV로 저장하거나 전략 엔진에 전달
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 전략 엔진 (strategy_engine)
|
||||||
|
|
||||||
|
### templates/
|
||||||
|
- basic_strategy.py: TP/SL 기준의 기본 전략 로직 정의
|
||||||
|
- risk_adjusted_strategy.py: 리스크 점수 기반으로 자금 배분 전략 설정
|
||||||
|
|
||||||
|
### selector.py
|
||||||
|
- tradable 종목 중 전략 조건에 부합하는 종목만 선별
|
||||||
|
|
||||||
|
### position_manager.py
|
||||||
|
- 각 종목에 대해 실제 매수 시점, 목표 수익률(TP), 손절 기준(SL) 설정
|
||||||
|
- 자산 배분 로직 포함
|
||||||
|
|
||||||
|
### run_strategy.py
|
||||||
|
- 전략 템플릿을 실행하고 결과를 저장하는 전략 엔진 실행 진입점
|
98
developement_advice/risk_manage_engine_summary.txt
Normal file
98
developement_advice/risk_manage_engine_summary.txt
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
# ⚠️ SightRay 리스크 관리 엔진 요약 (`risk_manage_engine`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 엔진 목적
|
||||||
|
|
||||||
|
리스크 관리 엔진은 데이터 분석 엔진에서 예측된 종목들에 대해,
|
||||||
|
**실제로 투자 가능한 종목인지 판단하기 위한 정량적 위험 평가**를 수행합니다.
|
||||||
|
|
||||||
|
> 🎯 핵심 목표: `리스크 점수 산출 + 종목 필터링`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 주요 기능
|
||||||
|
|
||||||
|
| 기능 | 설명 |
|
||||||
|
|------|------|
|
||||||
|
| **리스크 지표 계산** | VaR, SVaR, Monte Carlo, ATR 등 계산하여 종목별 리스크 정량화 |
|
||||||
|
| **risk_score 산출** | 여러 지표를 종합하여 0~100점의 리스크 점수 계산 |
|
||||||
|
| **종목 필터링** | 기준치 이상의 종목만 `tradable = True` 로 설정 |
|
||||||
|
| **전략 엔진 연결** | `risk_filtered_result.csv`로 전략 엔진에 전달 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 디렉토리 구조
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
risk_manage_engine/
|
||||||
|
├── calculators/
|
||||||
|
│ ├── var.py # Value at Risk 계산
|
||||||
|
│ ├── svar.py # Stressed VaR 계산
|
||||||
|
│ ├── monte_carlo.py # Monte Carlo Simulation 계산
|
||||||
|
│ └── atr.py # ATR (Average True Range) 계산
|
||||||
|
├── risk_calculator.py # 네 가지 지표를 통합 호출하는 계산기
|
||||||
|
├── risk_scorer.py # risk_score(0~100) 산출
|
||||||
|
├── filter.py # 기준치 미만 종목 제외
|
||||||
|
└── evaluate.py # 전체 파이프라인 실행 (분석 결과 + CDS 평가)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔁 실행 흐름
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
[prediction_result.csv + CDS 파일들]
|
||||||
|
↓
|
||||||
|
risk_calculator.py → 종목별 risk 요소 계산
|
||||||
|
↓
|
||||||
|
risk_scorer.py → 종합 점수화
|
||||||
|
↓
|
||||||
|
filter.py → tradable 종목만 필터링
|
||||||
|
↓
|
||||||
|
[evaluate.py 실행] → risk_filtered_result.csv 저장
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 출력 예시
|
||||||
|
|
||||||
|
| symbol | prediction | probability | risk_score | tradable |
|
||||||
|
|--------|------------|-------------|------------|----------|
|
||||||
|
| AAPL | 1 | 0.83 | 78 | ✅ |
|
||||||
|
| TSLA | 1 | 0.79 | 64 | ❌ |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📌 구성 파일 역할 요약
|
||||||
|
|
||||||
|
| 파일 | 설명 |
|
||||||
|
|------|------|
|
||||||
|
| `var.py` | 히스토리컬 수익률 기반 Value at Risk 계산 |
|
||||||
|
| `svar.py` | 최근 급락 구간만 추출해 VaR을 재계산 |
|
||||||
|
| `monte_carlo.py` | 정규분포 기반 무작위 시뮬레이션으로 미래 손실 예측 |
|
||||||
|
| `atr.py` | 고가/저가/종가 기반 변동성 계산 |
|
||||||
|
| `risk_calculator.py` | 위 4개 계산기 통합 호출하여 dict 반환 |
|
||||||
|
| `risk_scorer.py` | 역수 + 가중치 방식으로 0~100 점수화 |
|
||||||
|
| `filter.py` | 점수 기준치 이상인 종목만 `tradable = True` 설정 |
|
||||||
|
| `evaluate.py` | 전체 실행 파이프라인 연결 진입점 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 정적 vs 동적 모델
|
||||||
|
|
||||||
|
현재 리스크 엔진은 **정적 통계 기반 모델**로 구성되어 있으며:
|
||||||
|
- 학습 없이 과거 데이터를 기반으로 즉시 계산
|
||||||
|
- 빠르고 해석 가능함
|
||||||
|
|
||||||
|
향후 확장 계획은 `risk_engine_future_plan.txt`에 명시되어 있음:
|
||||||
|
- ML 기반 예측 리스크 모델 (LSTM, XGBoost 등)
|
||||||
|
- risk_score 예측 기반 강화 전략 연계 가능
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 요약 정리
|
||||||
|
|
||||||
|
SightRay의 리스크 관리 엔진은 종목의 상승 가능성만이 아닌,
|
||||||
|
**투자 안정성과 리스크 수용 범위**를 정량적으로 판단하여,
|
||||||
|
실제로 **투자 가능한 종목(tradable)** 만 전략 엔진으로 넘겨주는 핵심 필터 역할을 수행합니다.
|
99
developement_advice/strategy_engine_summary.txt
Normal file
99
developement_advice/strategy_engine_summary.txt
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
# 📈 SightRay 전략 엔진 요약 (`strategy_engine`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 엔진 목적
|
||||||
|
|
||||||
|
전략 엔진은 리스크 관리 엔진에서 통과된 `tradable 종목들`에 대해,
|
||||||
|
**실제 투자 전략을 구성하고, 자금 배분 및 매수 실행 조건을 설정하는 역할**을 수행합니다.
|
||||||
|
|
||||||
|
> 🎯 핵심 목표: "이 종목을 얼마에, 얼마나, 어떤 조건으로 살 것인가?"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 주요 기능
|
||||||
|
|
||||||
|
| 기능 | 설명 |
|
||||||
|
|------|------|
|
||||||
|
| **전략 템플릿 적용** | TP/SL 기준 전략(`basic`), risk_score 기반 전략(`adjusted`) 선택 적용 |
|
||||||
|
| **자금 배분 및 수량 계산** | total_capital을 종목 수 또는 전략 기준에 따라 분배 |
|
||||||
|
| **실행 결과 저장** | 포지션, 전략 조건 포함 최종 전략 테이블 생성 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 디렉토리 구조
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
strategy_engine/
|
||||||
|
├── templates/
|
||||||
|
│ ├── basic_strategy.py # 고정 TP/SL 전략
|
||||||
|
│ └── risk_adjusted_strategy.py # 리스크 점수 기반 전략
|
||||||
|
├── selector.py # tradable 종목 중 전략 조건 필터링
|
||||||
|
├── position_manager.py # 자금 배분 및 포지션 수량 계산
|
||||||
|
└── run_strategy.py # 전체 전략 흐름 실행 (CLI 지원)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧠 전략 템플릿 비교
|
||||||
|
|
||||||
|
| 전략 | 설명 | TP | SL | Action 기준 |
|
||||||
|
|--------|------|----|----|----------------|
|
||||||
|
| `basic` | 고정 비율 설정 | +5% | -3% | 모두 Buy |
|
||||||
|
| `adjusted` | risk_score 기준 조정 | +3~7% | -2~3% | score < 60 → 제외 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 실행 흐름
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
[risk_filtered_result.csv]
|
||||||
|
↓
|
||||||
|
selector.py → 전략 조건 만족 종목 추출
|
||||||
|
↓
|
||||||
|
templates/ 전략 템플릿 적용 (TP/SL 설정)
|
||||||
|
↓
|
||||||
|
position_manager.py → 자금 배분, 포지션 수량 결정
|
||||||
|
↓
|
||||||
|
run_strategy.py → 결과 저장: final_strategy_result.csv
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 최종 출력 예시
|
||||||
|
|
||||||
|
| symbol | entry_price | target_price | stop_loss | position_size | capital_allocated | action |
|
||||||
|
|--------|-------------|--------------|-----------|----------------|--------------------|--------|
|
||||||
|
| AAPL | 186.5 | 199.55 | 180.9 | 1340 | 250000 | Buy |
|
||||||
|
| TSLA | 241.3 | — | — | 0 | 0 | None |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ 실행 예시 (CLI)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python run_strategy.py \
|
||||||
|
--input risk_filtered_result.csv \
|
||||||
|
--capital 1000000 \
|
||||||
|
--strategy adjusted \
|
||||||
|
--output final_strategy_result.csv
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📌 구성 파일 요약
|
||||||
|
|
||||||
|
| 파일 | 역할 |
|
||||||
|
|------|------|
|
||||||
|
| `selector.py` | tradable 종목 중 전략 조건 만족 종목 필터링 |
|
||||||
|
| `basic_strategy.py` | 고정 비율 기반 TP/SL 설정 전략 |
|
||||||
|
| `risk_adjusted_strategy.py` | risk_score에 따라 전략을 조절하는 템플릿 |
|
||||||
|
| `position_manager.py` | 자본 분배 및 실제 매수 수량 결정 |
|
||||||
|
| `run_strategy.py` | 전체 전략 실행 컨트롤러 + CLI 지원 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 정리
|
||||||
|
|
||||||
|
SightRay 전략 엔진은 분석/리스크 단계를 통과한 종목에 대해 **실행 전략을 자동 구성**하며,
|
||||||
|
**전략 템플릿 기반 + 자금 배분 + 매수 조건 설정**의 완전한 실행 구조를 갖춘 엔진입니다.
|
11
prediction_result.csv
Normal file
11
prediction_result.csv
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
symbol,predicted_score
|
||||||
|
TSM,0.5220013856887817
|
||||||
|
TSLA,0.5193219780921936
|
||||||
|
GOOG,0.5187392234802246
|
||||||
|
PG,0.5162783265113831
|
||||||
|
AMZN,0.5112144947052002
|
||||||
|
MSFT,0.49892768263816833
|
||||||
|
MMM,0.49567362666130066
|
||||||
|
AAPL,0.4697483777999878
|
||||||
|
NVDA,0.4667884111404419
|
||||||
|
ADBE,0.3825182616710663
|
|
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pandas
|
||||||
|
requests
|
||||||
|
python-dotenv
|
||||||
|
xgboost
|
11
risk_filtered_result.csv
Normal file
11
risk_filtered_result.csv
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
symbol,predicted_score,risk_score,tradable
|
||||||
|
TSM,0.5220013856887817,-5326.75,False
|
||||||
|
TSLA,0.5193219780921936,-4104.57,False
|
||||||
|
GOOG,0.5187392234802246,-3846.49,False
|
||||||
|
PG,0.5162783265113831,-2485.12,False
|
||||||
|
AMZN,0.5112144947052002,-6766.41,False
|
||||||
|
MSFT,0.4989276826381683,-9061.16,False
|
||||||
|
MMM,0.4956736266613006,-3985.47,False
|
||||||
|
AAPL,0.4697483777999878,-7455.38,False
|
||||||
|
NVDA,0.4667884111404419,-4811.97,False
|
||||||
|
ADBE,0.3825182616710663,-8660.04,False
|
|
37
risk_manage_engine/calculators/atr.py
Normal file
37
risk_manage_engine/calculators/atr.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
'''
|
||||||
|
risk_manage_engine/calculators/atr.py
|
||||||
|
|
||||||
|
ATR (Average True Range)를 계산하는 함수 모듈입니다.
|
||||||
|
|
||||||
|
- 목적: 주가의 평균적인 일일 변동폭을 계산하여 **변동성 기반 리스크**를 수치화함
|
||||||
|
- 특징: 종가 대비 고가/저가 차이, 전일 종가와의 차이 등을 종합적으로 고려한 지표
|
||||||
|
- 출력: 지정된 기간 동안의 평균 TR (변동폭)
|
||||||
|
'''
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
def calculate_atr(df: pd.DataFrame, period: int = 14) -> float:
|
||||||
|
"""
|
||||||
|
Average True Range (ATR) 계산 함수
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
df (pd.DataFrame): CDS 데이터 (필수: 'high', 'low', 'close')
|
||||||
|
period (int): ATR 계산에 사용할 기간 (기본: 14일)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: 평균 True Range (최근 period일 기준)
|
||||||
|
"""
|
||||||
|
df = df.copy()
|
||||||
|
df['prev_close'] = df['close'].shift(1)
|
||||||
|
|
||||||
|
# True Range 계산: 세 가지 중 가장 큰 값
|
||||||
|
df['tr1'] = df['high'] - df['low']
|
||||||
|
df['tr2'] = (df['high'] - df['prev_close']).abs()
|
||||||
|
df['tr3'] = (df['low'] - df['prev_close']).abs()
|
||||||
|
df['true_range'] = df[['tr1', 'tr2', 'tr3']].max(axis=1)
|
||||||
|
|
||||||
|
# ATR: True Range의 이동 평균
|
||||||
|
atr = df['true_range'].rolling(window=period).mean().iloc[-1]
|
||||||
|
|
||||||
|
return round(atr, 4) if not np.isnan(atr) else 0.0
|
97
risk_manage_engine/calculators/concept/atr_concept.txt
Normal file
97
risk_manage_engine/calculators/concept/atr_concept.txt
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
# 📈 ATR (Average True Range) 개념 정리
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ ATR이란?
|
||||||
|
|
||||||
|
**ATR(Average True Range)**는 자산의 **일일 평균 가격 변동폭**을 나타내는 기술적 지표입니다.
|
||||||
|
|
||||||
|
> "이 종목은 하루에 평균적으로 얼마나 출렁이는가?"
|
||||||
|
|
||||||
|
1978년 J. Welles Wilder가 고안했으며, 변동성 중심의 트레이딩 전략에 널리 사용됩니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 True Range(TR) 계산 방식
|
||||||
|
|
||||||
|
True Range는 단순히 고가 - 저가가 아니라 다음 세 가지 중 **가장 큰 값**을 사용합니다:
|
||||||
|
|
||||||
|
1. 고가 - 저가
|
||||||
|
2. |고가 - 전일 종가|
|
||||||
|
3. |저가 - 전일 종가|
|
||||||
|
|
||||||
|
> 이유: 갭 상승/하락까지 포함한 **진짜 변동폭**을 측정하기 위해
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔢 공식
|
||||||
|
|
||||||
|
- True Range(TR): `max(high - low, |high - prev_close|, |low - prev_close|)`
|
||||||
|
- Average True Range(ATR): `TR의 N일 이동평균`
|
||||||
|
|
||||||
|
```python
|
||||||
|
# ATR 계산 예시
|
||||||
|
tr1 = high - low
|
||||||
|
tr2 = abs(high - prev_close)
|
||||||
|
tr3 = abs(low - prev_close)
|
||||||
|
true_range = max(tr1, tr2, tr3)
|
||||||
|
atr = true_range.rolling(N).mean()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💡 사용 목적
|
||||||
|
|
||||||
|
| 목적 | 설명 |
|
||||||
|
|------|------|
|
||||||
|
| **변동성 측정** | 일일 가격의 출렁임 정도(리스크)를 수치화 |
|
||||||
|
| **손절/익절 기준** | 트레일링 스탑 설정 시 활용 가능 (예: 1.5 * ATR) |
|
||||||
|
| **시장 과열 판단** | 갑작스런 ATR 상승은 과도한 움직임의 신호로 해석 가능 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ SightRay에서의 활용 방식
|
||||||
|
|
||||||
|
- CDS를 기반으로 최신 14일 기준 ATR을 계산
|
||||||
|
- ATR이 높을수록 리스크 점수 증가
|
||||||
|
- ROI 대비 ATR이 지나치게 크면 **변동성이 과도한 종목으로 간주**
|
||||||
|
|
||||||
|
```python
|
||||||
|
from atr import calculate_atr
|
||||||
|
atr = calculate_atr(cds_df, period=14)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📈 예시 그래프 개념
|
||||||
|
|
||||||
|
```
|
||||||
|
가격 차트 vs ATR 지표
|
||||||
|
|
||||||
|
▲ 가격 ▲ ATR
|
||||||
|
│ │
|
||||||
|
200 ──┐ ┌── 3.0 ──┐ ┌──
|
||||||
|
150 ──┘┌──┘ 상승장 2.0 ──┘┌──┘ 상승 추세
|
||||||
|
100 ───────────────▶ 1.0 ───────────▶
|
||||||
|
시간 시간
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚧 한계점
|
||||||
|
|
||||||
|
| 항목 | 설명 |
|
||||||
|
|------|------|
|
||||||
|
| **방향성 정보 없음** | ATR은 변동성만 측정하며 상승/하락 구분 불가 |
|
||||||
|
| **추세 없는 시장에 민감** | 박스권 장세에서 ATR이 낮아질 수 있음 |
|
||||||
|
| **종목 특성 반영 어려움** | 일부 종목은 원래부터 ATR이 높은 경향이 있음 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 정리
|
||||||
|
|
||||||
|
ATR은 가격의 **안정성 또는 출렁임을 정량적으로 측정**하는 지표이며,
|
||||||
|
SightRay에서는 ROI와 함께 고려하여 **리스크 점수화에 포함**되는 중요한 요소 중 하나입니다.
|
||||||
|
|
||||||
|
> 📘 기본 설정: 14일 기준 ATR 사용
|
||||||
|
> → 변동성이 과도한 종목은 자동 필터링 또는 감점 처리 가능
|
@ -0,0 +1,88 @@
|
|||||||
|
# 🎲 Monte Carlo Simulation (MCS) 개념 정리
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Monte Carlo Simulation이란?
|
||||||
|
|
||||||
|
Monte Carlo Simulation(MCS)은 **확률적인 요소를 가진 문제를 수많은 무작위 시뮬레이션을 통해 해결**하는 기법입니다.
|
||||||
|
금융에서는 **미래의 자산 가격 변동 경로를 예측**하는 데 자주 사용됩니다.
|
||||||
|
|
||||||
|
> "미래에 주가가 어떻게 움직일지 알 수 없다면, 수천 번 랜덤하게 시뮬레이션해서 확률적으로 평가해보자!"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 기본 아이디어
|
||||||
|
|
||||||
|
1. 과거 데이터를 통해 수익률의 평균(μ)과 표준편차(σ)를 구함
|
||||||
|
2. **정규분포 N(μ, σ)**를 가정하고 랜덤 수익률 생성
|
||||||
|
3. 해당 수익률로 미래 가격 시뮬레이션 (ex. 1일 후 가격)
|
||||||
|
4. 이걸 수천 번 반복하여 손실 분포 생성
|
||||||
|
5. 이 분포의 하위 (1-신뢰수준)% 값을 **VaR 유사 값**으로 사용
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔢 수식 요약
|
||||||
|
|
||||||
|
- 수익률 생성: \( r_i \sim N(\mu, \sigma) \)
|
||||||
|
- 시뮬레이션 가격: \( P_{i} = P_{0} \times (1 + r_i)^d \)
|
||||||
|
- 손실 계산: \( L_i = P_0 - P_i \)
|
||||||
|
- 손실 정렬 후 하위 α% 값을 VaR로 간주
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💡 SightRay에서의 사용 방식
|
||||||
|
|
||||||
|
- 입력: CDS (OHLCV) 데이터
|
||||||
|
- 수익률: `df['close'].pct_change()`로 계산
|
||||||
|
- 시뮬레이션: `np.random.normal(mean, std, N)`
|
||||||
|
- 출력: 손실 비율 (예: 0.045 → 4.5% 손실 가능성)
|
||||||
|
|
||||||
|
```python
|
||||||
|
simulated_returns = np.random.normal(mu, sigma, size=1000)
|
||||||
|
simulated_prices = P0 * (1 + simulated_returns)
|
||||||
|
losses = P0 - simulated_prices
|
||||||
|
MonteCarlo_VaR = np.percentile(losses, 5) # 95% 신뢰수준
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📈 시각적 예시
|
||||||
|
|
||||||
|
```
|
||||||
|
1000번 시뮬레이션 결과 (가격 경로)
|
||||||
|
|
||||||
|
P(t) ↑
|
||||||
|
250┤ . . .
|
||||||
|
200┤ . . . .
|
||||||
|
150┤ . . . .
|
||||||
|
100┤──────────────────────────────▶ 시간 t
|
||||||
|
↖ 손실 구간은 좌측 하단 영역
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 사용 목적 (SightRay 기준)
|
||||||
|
|
||||||
|
- 기존 VaR이 수익률 분포만 보는 것과 달리, **미래 가격 경로까지 시뮬레이션** 가능
|
||||||
|
- 급변동 상황을 반영하는 **더 현실적인 리스크 평가 가능**
|
||||||
|
- SVaR과 함께 **리스크 점수화에 기여하는 핵심 요소 중 하나**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚧 한계점
|
||||||
|
|
||||||
|
| 항목 | 설명 |
|
||||||
|
|------|------|
|
||||||
|
| 정규분포 가정 | 실제 수익률은 비대칭적일 수 있음 (Fat tail 등) |
|
||||||
|
| 계산량 | 시뮬레이션 횟수가 많을수록 느림 (CPU 부담) |
|
||||||
|
| 파라미터 민감성 | 평균/표준편차 설정에 따라 결과가 크게 달라짐 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 정리
|
||||||
|
|
||||||
|
Monte Carlo Simulation은 **불확실한 미래 가격의 확률 분포를 직접 생성**하여,
|
||||||
|
보다 직관적이고 현실적인 리스크 추정이 가능한 고급 리스크 평가 기법입니다.
|
||||||
|
|
||||||
|
> 📘 SightRay에서는 1000회 이상 시뮬레이션을 통해 1일 후 가격 손실 가능성을 평가하고,
|
||||||
|
> 이를 리스크 점수 산출의 중요한 피처로 사용합니다.
|
80
risk_manage_engine/calculators/concept/svar_concept.txt
Normal file
80
risk_manage_engine/calculators/concept/svar_concept.txt
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
# 📉 SVaR (Stressed Value at Risk) 개념 정리
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ SVaR이란?
|
||||||
|
|
||||||
|
**Stressed Value at Risk (SVaR)**는 일반적인 VaR보다 더 **극단적인 시장 상황(= 스트레스 시나리오)**을 가정하여
|
||||||
|
리스크를 측정하는 방식입니다.
|
||||||
|
|
||||||
|
> "만약 시장이 최근과 같은 스트레스 상황에 빠진다면, 우리는 어느 정도 손실을 감당해야 하는가?"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧠 SVaR과 VaR의 차이점
|
||||||
|
|
||||||
|
| 항목 | VaR | SVaR |
|
||||||
|
|------|-----|------|
|
||||||
|
| 기준 수익률 | 전체 수익률 분포 | **특정 스트레스 기간** 수익률만 사용 |
|
||||||
|
| 위험 수준 | 일반적인 리스크 측정 | **비정상적이고 급격한 시장 상황에 초점** |
|
||||||
|
| 보수성 | 보통 수준 | **보다 보수적인 손실 예측** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📌 예시 (실제 의미)
|
||||||
|
|
||||||
|
- VaR 95% = -2.8% → “95% 확률로 -2.8% 이상 손해는 안 본다.”
|
||||||
|
- SVaR 95% = -4.7% → “급락장이 온다면, 최대 -4.7%까지 손실을 볼 수 있다.”
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔢 SightRay에서의 계산 방식 (MVP)
|
||||||
|
|
||||||
|
- 최근 N일(기본: 30일)을 **스트레스 구간**으로 간주
|
||||||
|
- 이 구간의 수익률 분포만으로 VaR을 다시 계산
|
||||||
|
- 계산 방식은 히스토리컬 방식과 동일
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 스트레스 수익률 기준 SVaR
|
||||||
|
stress_returns = df['return'].iloc[-30:]
|
||||||
|
sorted_returns = np.sort(stress_returns)
|
||||||
|
svar = abs(sorted_returns[int((1 - 0.95) * len(sorted_returns))])
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📈 시각적 차이 (개념)
|
||||||
|
|
||||||
|
```
|
||||||
|
전체 수익률 분포 스트레스 기간 수익률 분포
|
||||||
|
─────────────── ───────────────
|
||||||
|
▁▁▂▃▅▆██▇▅▃▂ ▁▂▃▅▇██▇▅▃▁
|
||||||
|
↑ VaR ↑ SVaR
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ 사용 목적 (SightRay 기준)
|
||||||
|
|
||||||
|
- 데이터 분석 엔진의 결과 종목 중 **최근 급락에 취약한 종목 필터링**
|
||||||
|
- 일반 VaR보다 더 **보수적인 리스크 기준** 적용
|
||||||
|
- 리스크 점수 산출 시 가중치를 크게 부여 가능
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚧 한계점
|
||||||
|
|
||||||
|
| 항목 | 설명 |
|
||||||
|
|------|------|
|
||||||
|
| 스트레스 정의 주관성 | 어느 기간을 스트레스로 볼 것인가는 분석가 판단에 따라 달라질 수 있음 |
|
||||||
|
| 시장 구조 변화 반영 부족 | 과거 스트레스가 미래 스트레스로 반복될 것이라는 보장은 없음 |
|
||||||
|
| 기간 설정 영향 큼 | window 값을 20, 30, 60일 등으로 설정할 때 결과가 많이 달라짐 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 정리
|
||||||
|
|
||||||
|
**SVaR은 극단적인 시장 환경을 가정한 리스크 관리 수단이며**, SightRay에서는 **최근 급변 구간 수익률**만을 이용해 VaR을 다시 계산하는 방식으로 적용됩니다.
|
||||||
|
|
||||||
|
> 📘 기본 설정: 30일 스트레스 구간, 95% 신뢰 수준
|
||||||
|
> → 향후: 위기 기간 자동 탐지 기능 등으로 확장 가능
|
87
risk_manage_engine/calculators/concept/var_concept.txt
Normal file
87
risk_manage_engine/calculators/concept/var_concept.txt
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# 📉 VaR (Value at Risk) 개념 정리
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ VaR이란?
|
||||||
|
|
||||||
|
**Value at Risk (VaR)**는 일정 기간 동안 특정 신뢰 수준(confidence level)에서
|
||||||
|
금융 자산이나 포트폴리오가 감수할 수 있는 **최대 손실 금액**을 추정하는 지표입니다.
|
||||||
|
|
||||||
|
> 쉽게 말해:
|
||||||
|
> "95% 확률로, 내일 이 자산의 손실이 **3%를 넘지 않을 것이다."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📌 공식 정의
|
||||||
|
|
||||||
|
> **VaR(신뢰수준 α)** = 수익률 분포에서 **하위 (1-α)% 지점의 손실 크기**
|
||||||
|
|
||||||
|
예를 들어:
|
||||||
|
- 95% 신뢰 수준 → 하위 5% 수익률 지점의 절대값
|
||||||
|
- 99% 신뢰 수준 → 하위 1% 수익률 지점의 절대값
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💡 사용 목적
|
||||||
|
|
||||||
|
| 목적 | 설명 |
|
||||||
|
|------|------|
|
||||||
|
| **위험 평가** | 특정 포지션 또는 포트폴리오의 손실 한계치를 수치화 |
|
||||||
|
| **투자 판단 기준** | 리스크 관리 기준으로 활용 (리스크가 과도한 종목 배제 등) |
|
||||||
|
| **자본 관리** | 기관의 자기자본 대비 리스크 노출 통제 수단 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔢 계산 방식
|
||||||
|
|
||||||
|
1. 과거 수익률(%)을 계산
|
||||||
|
2. 수익률들을 정렬 (오름차순)
|
||||||
|
3. 하위 (1 - confidence level)% 위치의 값 추출
|
||||||
|
4. 절대값을 취해 **손실 크기**로 사용
|
||||||
|
|
||||||
|
예시 (Python 기준):
|
||||||
|
```python
|
||||||
|
import numpy as np
|
||||||
|
sorted_returns = np.sort(df['return'].values)
|
||||||
|
var_index = int((1 - 0.95) * len(sorted_returns))
|
||||||
|
VaR = abs(sorted_returns[var_index])
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 시각적 이해
|
||||||
|
|
||||||
|
```
|
||||||
|
수익률 분포 (히스토그램)
|
||||||
|
|
||||||
|
▲
|
||||||
|
│ ▁▆█▇▆▅▄▃▂
|
||||||
|
│ ▂▃▅▆█▇▇▅▃▂
|
||||||
|
VaR → ▁▂▃ ← 손실 구간 (좌측 꼬리)
|
||||||
|
│
|
||||||
|
──────────┼────────────────────▶ 수익률
|
||||||
|
│ ↑
|
||||||
|
신뢰 수준 (예: 95%)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚧 한계점
|
||||||
|
|
||||||
|
| 항목 | 설명 |
|
||||||
|
|------|------|
|
||||||
|
| **정규분포 가정 문제** | 실무에서는 수익률 분포가 비대칭/두꺼운 꼬리(heavy-tail)인 경우가 많음 |
|
||||||
|
| **극단 리스크 무시** | 신뢰 수준 바깥(1% 이하)의 극단적인 손실은 반영하지 않음 |
|
||||||
|
| **과거 기반** | 과거 수익률만을 기반으로 미래 위험을 추정함 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ SightRay에서의 사용 목적
|
||||||
|
|
||||||
|
- 분석 엔진에서 선별한 종목들에 대해 **예측 ROI에 비해 감수할 리스크가 과도한지 판단**
|
||||||
|
- `risk_score`에 반영되어 **전략 엔진으로 전달 여부 결정**
|
||||||
|
- 향후 SVaR, Monte Carlo와 함께 **리스크 통합 점수화 기준 중 하나**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> 📘 참고: SightRay는 VaR을 히스토리컬 방식으로 계산하며, 신뢰 수준은 기본 95%로 설정되어 있습니다.
|
45
risk_manage_engine/calculators/monte_carlo.py
Normal file
45
risk_manage_engine/calculators/monte_carlo.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
'''
|
||||||
|
risk_manage_engine/calculators/monte_carlo.py
|
||||||
|
|
||||||
|
Monte Carlo Simulation을 기반으로 향후 주가 경로를 시뮬레이션하고,
|
||||||
|
미래 손실 가능성을 추정하는 함수 모듈입니다.
|
||||||
|
|
||||||
|
- 목적: 수익률 분포를 정규분포로 가정하여 미래 경로를 확률적으로 생성
|
||||||
|
- 출력: 특정 신뢰수준 하에서의 최대 손실 (VaR 유사)
|
||||||
|
'''
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
def monte_carlo_var(df: pd.DataFrame, simulations: int = 1000, days: int = 1, confidence_level: float = 0.95) -> float:
|
||||||
|
"""
|
||||||
|
Monte Carlo Simulation 기반 VaR 추정
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
df (pd.DataFrame): CDS 데이터 (필수: 'close' 컬럼)
|
||||||
|
simulations (int): 시뮬레이션 횟수 (기본 1000회)
|
||||||
|
days (int): 몇 일 후까지 예측할지 (기본 1일)
|
||||||
|
confidence_level (float): 신뢰 수준 (기본 95%)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: 시뮬레이션 기반 손실 추정치 (VaR 유사)
|
||||||
|
"""
|
||||||
|
df = df.copy()
|
||||||
|
df['return'] = df['close'].pct_change()
|
||||||
|
df.dropna(inplace=True)
|
||||||
|
|
||||||
|
mu = df['return'].mean()
|
||||||
|
sigma = df['return'].std()
|
||||||
|
|
||||||
|
# 시뮬레이션된 수익률 (정규분포 기반)
|
||||||
|
simulated_returns = np.random.normal(loc=mu, scale=sigma, size=simulations)
|
||||||
|
|
||||||
|
# 누적 수익률로 변환 (1일 예측 기준)
|
||||||
|
simulated_prices = df['close'].iloc[-1] * (1 + simulated_returns) ** days
|
||||||
|
losses = df['close'].iloc[-1] - simulated_prices
|
||||||
|
losses = np.sort(losses)
|
||||||
|
|
||||||
|
var_index = int((1 - confidence_level) * simulations)
|
||||||
|
var_value = losses[var_index]
|
||||||
|
|
||||||
|
return round(abs(var_value / df['close'].iloc[-1]), 4)
|
36
risk_manage_engine/calculators/svar.py
Normal file
36
risk_manage_engine/calculators/svar.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
'''
|
||||||
|
risk_manage_engine/calculators/svar.py
|
||||||
|
|
||||||
|
Stressed Value at Risk (SVaR)를 계산하는 함수 모듈입니다.
|
||||||
|
|
||||||
|
- SVaR은 일반적인 VaR보다 더 극단적인 시장 상황(스트레스 조건)을 고려한 리스크 측정 방식입니다.
|
||||||
|
- MVP 기준에서는 "특정 구간(예: 최근 급락 구간)의 수익률만을 기준으로 VaR을 재계산"하는 방식으로 간단하게 구현합니다.
|
||||||
|
'''
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
def calculate_svar(df: pd.DataFrame, confidence_level: float = 0.95, window: int = 30) -> float:
|
||||||
|
"""
|
||||||
|
Stressed Value at Risk (SVaR)를 계산하는 함수
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
df (pd.DataFrame): CDS 데이터 (필수: 'close' 컬럼)
|
||||||
|
confidence_level (float): 신뢰 수준 (기본 95%)
|
||||||
|
window (int): 최근 며칠간의 스트레스 구간만 사용할지 (기본: 30일)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: SVaR 값 (양수, 손실 한계치)
|
||||||
|
"""
|
||||||
|
df = df.copy()
|
||||||
|
df['return'] = df['close'].pct_change()
|
||||||
|
df.dropna(inplace=True)
|
||||||
|
|
||||||
|
# 최근 window일간의 스트레스 구간 수익률만 사용
|
||||||
|
stress_returns = df['return'].iloc[-window:]
|
||||||
|
sorted_returns = np.sort(stress_returns.values)
|
||||||
|
|
||||||
|
svar_index = int((1 - confidence_level) * len(sorted_returns))
|
||||||
|
svar_value = abs(sorted_returns[svar_index])
|
||||||
|
|
||||||
|
return round(svar_value, 4)
|
38
risk_manage_engine/calculators/var.py
Normal file
38
risk_manage_engine/calculators/var.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
'''
|
||||||
|
risk_manage_engine/calculators/var.py
|
||||||
|
|
||||||
|
Value at Risk (VaR)를 계산하는 함수 모듈입니다.
|
||||||
|
|
||||||
|
- 목적: 일정 기간 내 손실이 특정 확률(신뢰수준) 이상 발생하지 않을 것이라는 수준을 계산
|
||||||
|
- 방법: 수익률 분포 기반, 정규분포 가정 또는 히스토리 기반 가능
|
||||||
|
|
||||||
|
MVP 단계에서는 히스토리컬 방식 사용
|
||||||
|
'''
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
def calculate_var(df: pd.DataFrame, confidence_level: float = 0.95) -> float:
|
||||||
|
"""
|
||||||
|
Value at Risk (VaR)를 계산하는 함수
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
df (pd.DataFrame): CDS 데이터 (필수: 'close' 컬럼)
|
||||||
|
confidence_level (float): 신뢰 수준 (기본 95%)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: VaR 값 (양수, 손실 한계치)
|
||||||
|
"""
|
||||||
|
# 수익률 계산: 오늘 대비 내일 수익률
|
||||||
|
df = df.copy()
|
||||||
|
df['return'] = df['close'].pct_change()
|
||||||
|
df.dropna(inplace=True)
|
||||||
|
|
||||||
|
# 수익률들을 오름차순 정렬
|
||||||
|
sorted_returns = np.sort(df['return'].values)
|
||||||
|
|
||||||
|
# VaR은 하위 (1 - 신뢰수준)% 위치에 해당하는 손실
|
||||||
|
var_index = int((1 - confidence_level) * len(sorted_returns))
|
||||||
|
var_value = abs(sorted_returns[var_index])
|
||||||
|
|
||||||
|
return round(var_value, 4)
|
52
risk_manage_engine/evaluate.py
Normal file
52
risk_manage_engine/evaluate.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
import pandas as pd
|
||||||
|
from risk_manage_engine.risk_calculator import calculate_all_risks
|
||||||
|
from risk_manage_engine.risk_scorer import compute_risk_score
|
||||||
|
from risk_manage_engine.filter import filter_by_risk
|
||||||
|
|
||||||
|
def evaluate_predictions(prediction_file, cds_dir, output_path="risk_filtered_result.csv"):
|
||||||
|
df = pd.read_csv(prediction_file)
|
||||||
|
|
||||||
|
results = []
|
||||||
|
for _, row in df.iterrows():
|
||||||
|
symbol = row["symbol"]
|
||||||
|
score = row["predicted_score"]
|
||||||
|
cds_path = os.path.join(cds_dir, f"{symbol}_ohlcv.csv")
|
||||||
|
if not os.path.exists(cds_path):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
df_cds = pd.read_csv(cds_path)
|
||||||
|
risk_factors = calculate_all_risks(df_cds)
|
||||||
|
risk_score = compute_risk_score(risk_factors)
|
||||||
|
results.append({
|
||||||
|
"symbol": symbol,
|
||||||
|
"predicted_score": score,
|
||||||
|
"risk_score": risk_score,
|
||||||
|
"tradable": risk_score >= 60 # 기준점은 조정 가능
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ {symbol} 리스크 평가 중 오류 발생: {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
result_df = pd.DataFrame(results)
|
||||||
|
|
||||||
|
print("\n[리스크 평가 요약]")
|
||||||
|
print(result_df)
|
||||||
|
|
||||||
|
result_df.to_csv(output_path, index=False)
|
||||||
|
print(f"\n결과가 다음 위치에 저장되었습니다: {output_path}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description="SightRay 리스크 평가 실행")
|
||||||
|
parser.add_argument('--cds_dir', type=str, required=True, help='CDS 디렉토리 경로')
|
||||||
|
parser.add_argument('--prediction_file', type=str, required=True, help='예측 결과 CSV 파일 경로')
|
||||||
|
parser.add_argument('--output_file', type=str, default='risk_filtered_result.csv', help='리스크 평가 결과 저장 경로')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
evaluate_predictions(
|
||||||
|
prediction_file=args.prediction_file,
|
||||||
|
cds_dir=args.cds_dir,
|
||||||
|
output_path=args.output_file
|
||||||
|
)
|
41
risk_manage_engine/filter.py
Normal file
41
risk_manage_engine/filter.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
|
||||||
|
"""
|
||||||
|
risk_manage_engine/filter.py
|
||||||
|
|
||||||
|
리스크 점수(risk_score)를 기반으로 투자 적격 종목만 필터링하는 모듈입니다.
|
||||||
|
|
||||||
|
- 기준치 이상의 종목만 `tradable = True` 표시
|
||||||
|
- 분석 결과와 리스크 평가 결과를 결합해 출력
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
def apply_risk_filter(predictions: pd.DataFrame, risk_scores: dict, threshold: int = 70) -> pd.DataFrame:
|
||||||
|
"""
|
||||||
|
예측 결과와 리스크 점수를 결합하여 필터링 결과 반환
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
predictions (pd.DataFrame): symbol, prediction, probability 등의 예측 결과
|
||||||
|
risk_scores (dict): {symbol: score} 형태의 리스크 점수
|
||||||
|
threshold (int): 투자 적격 기준치 (기본 70)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
pd.DataFrame: 종목 + 예측 결과 + risk_score + tradable 여부 포함
|
||||||
|
"""
|
||||||
|
predictions = predictions.copy()
|
||||||
|
predictions['risk_score'] = predictions['symbol'].map(risk_scores)
|
||||||
|
predictions['tradable'] = predictions['risk_score'] >= threshold
|
||||||
|
return predictions
|
||||||
|
|
||||||
|
def filter_by_risk(df: pd.DataFrame, threshold: int = 60) -> pd.DataFrame:
|
||||||
|
"""
|
||||||
|
risk_score 컬럼을 기반으로 필터링
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
df (pd.DataFrame): risk_score 컬럼이 포함된 데이터프레임
|
||||||
|
threshold (int): 필터링 기준점 (기본 60)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
pd.DataFrame: risk_score >= threshold 인 종목만 반환
|
||||||
|
"""
|
||||||
|
return df[df["risk_score"] >= threshold].reset_index(drop=True)
|
33
risk_manage_engine/risk_calculator.py
Normal file
33
risk_manage_engine/risk_calculator.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
'''
|
||||||
|
risk_manage_engine/risk_calculator.py
|
||||||
|
|
||||||
|
리스크 관리 엔진의 통합 계산기 모듈입니다.
|
||||||
|
|
||||||
|
- 개별 리스크 지표 모듈들을 불러와 한 번에 계산
|
||||||
|
- 분석 엔진에서 전달된 종목 CDS에 대해 VaR, SVaR, MCS, ATR 계산 수행
|
||||||
|
- 결과는 하나의 딕셔너리로 반환
|
||||||
|
'''
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
from risk_manage_engine.calculators.var import calculate_var
|
||||||
|
from risk_manage_engine.calculators.svar import calculate_svar
|
||||||
|
from risk_manage_engine.calculators.monte_carlo import monte_carlo_var
|
||||||
|
from risk_manage_engine.calculators.atr import calculate_atr
|
||||||
|
|
||||||
|
def calculate_all_risks(df: pd.DataFrame, confidence_level: float = 0.95) -> dict:
|
||||||
|
"""
|
||||||
|
CDS에 대해 모든 리스크 지표 계산
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
df (pd.DataFrame): 단일 종목의 CDS
|
||||||
|
confidence_level (float): 신뢰 수준 (기본 95%)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: {'var': ..., 'svar': ..., 'mcs': ..., 'atr': ...}
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
'var': calculate_var(df, confidence_level),
|
||||||
|
'svar': calculate_svar(df, confidence_level),
|
||||||
|
'mcs': monte_carlo_var(df, confidence_level=confidence_level),
|
||||||
|
'atr': calculate_atr(df)
|
||||||
|
}
|
95
risk_manage_engine/risk_calculator_concept.txt
Normal file
95
risk_manage_engine/risk_calculator_concept.txt
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
# 🧮 리스크 통합 계산기 (`risk_calculator.py`) 개념 정리
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 목적
|
||||||
|
|
||||||
|
`risk_calculator.py`는 SightRay의 리스크 관리 엔진 내에서
|
||||||
|
**VaR, SVaR, Monte Carlo, ATR 등 여러 리스크 지표를 하나의 함수로 통합 계산**하는 모듈입니다.
|
||||||
|
|
||||||
|
> 📌 각 지표는 별도의 모듈로 계산되며, 이 파일은 해당 지표들을 조합해서 일괄 계산하는 "통합 계산기" 역할을 수행합니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 포함 지표 목록
|
||||||
|
|
||||||
|
| 지표 | 설명 | 파일명 |
|
||||||
|
|------|------|--------|
|
||||||
|
| **VaR** | Value at Risk – 통계 기반 손실 한계 추정 | `var.py` |
|
||||||
|
| **SVaR** | Stressed VaR – 스트레스 구간 기반 VaR | `svar.py` |
|
||||||
|
| **MCS** | Monte Carlo Simulation – 시뮬레이션 기반 손실 예측 | `monte_carlo.py` |
|
||||||
|
| **ATR** | Average True Range – 기술적 변동성 지표 | `atr.py` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧠 내부 구조
|
||||||
|
|
||||||
|
```python
|
||||||
|
from var import calculate_var
|
||||||
|
from svar import calculate_svar
|
||||||
|
from monte_carlo import monte_carlo_var
|
||||||
|
from atr import calculate_atr
|
||||||
|
|
||||||
|
def calculate_all_risks(df):
|
||||||
|
return {
|
||||||
|
'var': calculate_var(df),
|
||||||
|
'svar': calculate_svar(df),
|
||||||
|
'mcs': monte_carlo_var(df),
|
||||||
|
'atr': calculate_atr(df)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔁 작동 흐름
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
[단일 종목의 CDS 입력 (DataFrame)]
|
||||||
|
↓
|
||||||
|
calculate_var(df)
|
||||||
|
calculate_svar(df)
|
||||||
|
monte_carlo_var(df)
|
||||||
|
calculate_atr(df)
|
||||||
|
↓
|
||||||
|
통합된 dict 형태로 반환
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 출력 예시
|
||||||
|
|
||||||
|
```python
|
||||||
|
{
|
||||||
|
'var': 0.0312,
|
||||||
|
'svar': 0.0443,
|
||||||
|
'mcs': 0.0375,
|
||||||
|
'atr': 2.91
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- **각 값은 비율 또는 포인트 단위의 손실 위험을 의미**하며,
|
||||||
|
- 이후 `risk_score` 산출 모듈에서 가중치 등을 통해 통합 점수로 변환됩니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ SightRay에서의 역할
|
||||||
|
|
||||||
|
| 기능 | 설명 |
|
||||||
|
|------|------|
|
||||||
|
| 지표 통합 | 분석된 종목 하나에 대해 리스크 지표를 일괄 계산 |
|
||||||
|
| 스코어 입력 | `risk_scorer.py`에서 사용할 기초 입력 값 제공 |
|
||||||
|
| 확장성 확보 | 새로운 지표를 추가해도 이 모듈에서 간단히 조정 가능 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 다음 단계와 연계
|
||||||
|
|
||||||
|
- 다음 파일인 `risk_scorer.py`에서 `calculate_all_risks()` 결과를 받아
|
||||||
|
종합 리스크 점수(`risk_score`)를 계산하게 됩니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 요약
|
||||||
|
|
||||||
|
- `risk_calculator.py`는 **지표 계산을 하나의 인터페이스로 통합**해주는 모듈
|
||||||
|
- 향후 리스크 엔진 전체의 모듈화와 유지보수 효율성을 높이는 **핵심 연결 지점**입니다
|
83
risk_manage_engine/risk_manage_engine_future_plan.txt
Normal file
83
risk_manage_engine/risk_manage_engine_future_plan.txt
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
# 🔮 SightRay 리스크 관리 엔진 – 정적 모델 기반 → 동적 모델 확장 계획
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 현재 구조 요약: 정적 모델 기반 리스크 엔진
|
||||||
|
|
||||||
|
| 구성 요소 | 방식 | 설명 |
|
||||||
|
|------------|-------|------|
|
||||||
|
| VaR | 히스토리컬 방식 | 수익률 정렬 기반 손실 한계치 계산 |
|
||||||
|
| SVaR | 최근 급락 구간 기반 VaR | 보수적 리스크 추정 |
|
||||||
|
| MCS | 정규분포 기반 시뮬레이션 | 확률적 미래 손실 분포 예측 |
|
||||||
|
| ATR | 기술적 지표 (변동성) | 평균 변동폭 기반 위험도 평가 |
|
||||||
|
| Risk Score | 역수 + 가중 평균 | 정규화된 0~100 점수 산출 |
|
||||||
|
|
||||||
|
> ☑️ 장점: 구현이 쉽고 해석 가능, 규제 친화적, MVP에 적합
|
||||||
|
> ⚠️ 한계: 시장 변화에 적응 불가, 예외 상황 예측 한계
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 향후 확장 목표: 동적 학습 기반 리스크 엔진
|
||||||
|
|
||||||
|
### 🔄 목적
|
||||||
|
- 고정된 수치가 아니라, **시장 환경/데이터 흐름에 따라 리스크 예측이 변화하도록 설계**
|
||||||
|
|
||||||
|
### 🧠 핵심 방향
|
||||||
|
| 목표 기능 | 설명 |
|
||||||
|
|-----------|------|
|
||||||
|
| **리스크 예측 모델** | 미래 손실 확률 자체를 ML 모델로 학습 및 예측 (Regression) |
|
||||||
|
| **상호작용 학습** | ATR, ROI, VaR 등의 관계를 모델이 자동으로 파악 |
|
||||||
|
| **시나리오 기반 강화 학습** | 다양한 시장 조건에서 최적의 리스크 대응 전략 학습 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧩 구조적 확장 계획
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
risk_manage_engine/
|
||||||
|
├── predictors/ ← 새로운 하위 디렉토리
|
||||||
|
│ ├── risk_lstm.py # 시계열 기반 리스크 예측 모델
|
||||||
|
│ └── risk_xgboost.py # 지도학습 기반 예측 모델 (회귀 or 분류)
|
||||||
|
├── risk_dataset_builder.py # CDS + 리스크 지표 → 학습용 피처 구성
|
||||||
|
├── risk_evaluator.py # 실제 예측 vs 계산 비교 평가
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔬 사용할 모델 후보
|
||||||
|
|
||||||
|
| 모델 | 목적 | 특성 |
|
||||||
|
|------|------|------|
|
||||||
|
| LSTM | 시계열 리스크 추정 | 과거 변화 추세를 고려한 예측 |
|
||||||
|
| XGBoost | 리스크 점수 회귀 | 고정 피처 기반 예측, 빠른 학습 |
|
||||||
|
| Autoencoder | 이상 감지 | 갑작스러운 위험 신호 탐지 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 출력 구조 (예시)
|
||||||
|
|
||||||
|
| symbol | pred_risk_score | actual_risk_score | diff | alert |
|
||||||
|
|--------|------------------|-------------------|------|--------|
|
||||||
|
| AAPL | 75 | 68 | -7 | ⚠️ |
|
||||||
|
| TSLA | 62 | 64 | +2 | |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 적용 순서 제안
|
||||||
|
|
||||||
|
1. **CDS + 기존 risk_score → 학습용 데이터 구성** (`risk_dataset_builder.py`)
|
||||||
|
2. **단일 모델(XGBoost 등)로 `risk_score` 예측 회귀 모델 구축**
|
||||||
|
3. **실제 risk_score와 비교하여 예측 정확도 평가** (`risk_evaluator.py`)
|
||||||
|
4. **LSTM 기반 시계열 예측 추가**
|
||||||
|
5. **전략 엔진과 연계하여 실시간 대응 체계 구축**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 요약
|
||||||
|
|
||||||
|
- 현재: **정적 모델 기반**으로 안정성과 해석성을 확보
|
||||||
|
- 미래: **동적·학습 기반**으로 예외 상황 적응성과 예측 정밀도 확보
|
||||||
|
- SightRay는 이 둘을 유기적으로 결합하여 진화 가능한 구조를 갖추고 있음
|
||||||
|
|
||||||
|
> 📘 정적 리스크 모델은 기본 평가 수단으로 유지하되,
|
||||||
|
> 예측력 향상과 고도화를 위해 동적 모델을 확장 적용해 나갈 계획
|
50
risk_manage_engine/risk_scorer.py
Normal file
50
risk_manage_engine/risk_scorer.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
'''
|
||||||
|
📁 risk_manage_engine/risk_scorer.py
|
||||||
|
|
||||||
|
여러 리스크 지표를 종합하여 종합 리스크 점수를 계산하는 모듈
|
||||||
|
정규화 및 안정성 고려한 계산 방식 적용
|
||||||
|
'''
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
# 안정성 유지를 위한 정규화 함수
|
||||||
|
def normalize(value, min_val, max_val):
|
||||||
|
if max_val == min_val:
|
||||||
|
return 0.0 # 모든 값이 동일한 경우 → 중립값
|
||||||
|
return (value - min_val) / (max_val - min_val)
|
||||||
|
|
||||||
|
def compute_risk_score(risk_values: dict) -> float:
|
||||||
|
"""
|
||||||
|
입력된 리스크 지표들을 기반으로 종합 리스크 점수를 계산합니다.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
risk_values (dict): 각 리스크 지표들의 원시 값
|
||||||
|
예: {'var': 0.02, 'svar': 0.03, 'atr': 0.015, 'monte_carlo': 0.05}
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
score (float): 0 ~ 100 사이의 리스크 점수 (높을수록 안정적)
|
||||||
|
"""
|
||||||
|
# 기준 범위 지정 (샘플 기준)
|
||||||
|
expected_ranges = {
|
||||||
|
'var': (0.01, 0.10),
|
||||||
|
'svar': (0.01, 0.12),
|
||||||
|
'atr': (0.005, 0.05),
|
||||||
|
'monte_carlo': (0.01, 0.20)
|
||||||
|
}
|
||||||
|
|
||||||
|
weights = {
|
||||||
|
'var': 0.25,
|
||||||
|
'svar': 0.25,
|
||||||
|
'atr': 0.25,
|
||||||
|
'monte_carlo': 0.25
|
||||||
|
}
|
||||||
|
|
||||||
|
score = 0
|
||||||
|
for key, value in risk_values.items():
|
||||||
|
min_v, max_v = expected_ranges.get(key, (0, 1))
|
||||||
|
norm = normalize(value, min_v, max_v)
|
||||||
|
inverted = 1 - norm # 위험이 낮을수록 점수는 높아야 함
|
||||||
|
weighted = inverted * weights.get(key, 0.25)
|
||||||
|
score += weighted
|
||||||
|
|
||||||
|
return round(score * 100, 2) # 점수를 0~100 범위로 환산
|
87
risk_manage_engine/risk_scorer_concept.txt
Normal file
87
risk_manage_engine/risk_scorer_concept.txt
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# 🧮 종합 리스크 점수 산출기 (`risk_scorer.py`) 개념 정리
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 목적
|
||||||
|
|
||||||
|
`risk_scorer.py`는 리스크 관리 엔진에서 계산된 다양한 리스크 지표(VaR, SVaR, MCS, ATR)를 기반으로,
|
||||||
|
**0 ~ 100 사이의 단일 리스크 점수(`risk_score`)**를 산출하는 모듈입니다.
|
||||||
|
|
||||||
|
> 📌 이 점수는 종목의 전반적인 투자 안정성을 나타내며, 전략 엔진에서 매매 대상 필터링 기준으로 사용됩니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔢 점수 계산 방식
|
||||||
|
|
||||||
|
1. 각 지표 값(`var`, `svar`, `mcs`, `atr`)을 입력으로 받음
|
||||||
|
2. 위험이 클수록 점수는 낮아야 하므로 **역수 방식**으로 가공
|
||||||
|
3. 각 지표에 대해 **가중치(weight)** 적용
|
||||||
|
4. 모든 점수 합산 후 0~100 범위로 **정규화 (스케일링)**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ 내부 구조 요약
|
||||||
|
|
||||||
|
```python
|
||||||
|
def calculate_risk_score(risks: dict, weights: dict = None) -> int:
|
||||||
|
# 기본 가중치 설정
|
||||||
|
weights = {
|
||||||
|
'var': 0.25,
|
||||||
|
'svar': 0.30,
|
||||||
|
'mcs': 0.25,
|
||||||
|
'atr': 0.20
|
||||||
|
}
|
||||||
|
|
||||||
|
# 점수 계산 (역수 방식)
|
||||||
|
score = sum(weights[key] * (1 / risks[key]) for key in risks)
|
||||||
|
|
||||||
|
# 정규화: 0~100 범위
|
||||||
|
return int(round(min(max(score * 10, 0), 100)))
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚖️ 기본 가중치 설정
|
||||||
|
|
||||||
|
| 지표 | 가중치 | 이유 |
|
||||||
|
|------|--------|------|
|
||||||
|
| VaR | 0.25 | 기본적인 손실 한계 예측 |
|
||||||
|
| SVaR | 0.30 | 극단 리스크 고려 (더 보수적으로 반영) |
|
||||||
|
| MCS | 0.25 | 확률적 시나리오 기반 손실 예측 |
|
||||||
|
| ATR | 0.20 | 기술적 변동성 고려 (짧은 구간 민감도) |
|
||||||
|
|
||||||
|
> ⚠️ 향후 사용자 또는 운영자가 이 가중치를 조절할 수 있도록 설계되어 있음
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 출력 예시
|
||||||
|
|
||||||
|
```python
|
||||||
|
input: {'var': 0.032, 'svar': 0.045, 'mcs': 0.037, 'atr': 2.8}
|
||||||
|
output: risk_score = 74
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ SightRay에서의 역할
|
||||||
|
|
||||||
|
| 기능 | 설명 |
|
||||||
|
|------|------|
|
||||||
|
| 리스크 정량화 | 다양한 위험 요소를 하나의 숫자로 요약 |
|
||||||
|
| 전략 엔진 연계 | 기준치 이상일 때만 매수 대상 종목으로 전송 |
|
||||||
|
| 기준치 예: `risk_score >= 70` | 이하는 자동 제외 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 이후 확장 가능성
|
||||||
|
|
||||||
|
- **비선형 스코어링 함수 적용** (예: log scale, 비율 변화율 기반 등)
|
||||||
|
- **산업군별 위험 기준 조정**
|
||||||
|
- **리스크 대 ROI 비율 기반 스코어링**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 요약
|
||||||
|
|
||||||
|
`risk_scorer.py`는 리스크 엔진의 **지표 계산 → 투자 결정** 사이를 연결하는 핵심 모듈로,
|
||||||
|
여러 지표를 통합한 종합 점수 기반으로 **투자 적격 여부 판단**을 가능하게 만듭니다.
|
142
sightray.py
Normal file
142
sightray.py
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
"""
|
||||||
|
📁 sightray/sightray.py
|
||||||
|
|
||||||
|
SightRay 전체 통합 실행 스크립트
|
||||||
|
- 사용자가 종목 코드, 날짜, 자본금만 입력하면 전체 파이프라인 자동 실행
|
||||||
|
- 수집 → 학습 → 분석 → 리스크 평가 → 전략 실행까지 한 번에 완료
|
||||||
|
- 실행 전 필요한 라이브러리 설치 여부 확인
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import importlib.util
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import glob
|
||||||
|
from dotenv import load_dotenv, set_key
|
||||||
|
|
||||||
|
# 필요한 패키지 목록 (import명: 설치명)
|
||||||
|
REQUIRED_PACKAGES = {
|
||||||
|
"pandas": "pandas",
|
||||||
|
"requests": "requests",
|
||||||
|
"xgboost": "xgboost",
|
||||||
|
"dotenv": "python-dotenv"
|
||||||
|
}
|
||||||
|
|
||||||
|
def check_and_install_packages():
|
||||||
|
missing = []
|
||||||
|
for import_name, pip_name in REQUIRED_PACKAGES.items():
|
||||||
|
if importlib.util.find_spec(import_name) is None:
|
||||||
|
missing.append(pip_name)
|
||||||
|
if not missing:
|
||||||
|
return
|
||||||
|
print(f"❗ 필수 라이브러리 누락: {', '.join(missing)}")
|
||||||
|
confirm = input("자동으로 설치할까요? (y/n): ").strip().lower()
|
||||||
|
if confirm == 'y':
|
||||||
|
subprocess.check_call([sys.executable, "-m", "pip", "install"] + missing)
|
||||||
|
print("✅ 설치 완료. 다시 실행해 주세요.")
|
||||||
|
sys.exit()
|
||||||
|
else:
|
||||||
|
print("❌ 설치되지 않았습니다. 수동으로 설치 후 다시 실행하세요.")
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
# 라이브러리 설치 여부 확인
|
||||||
|
check_and_install_packages()
|
||||||
|
|
||||||
|
# API 키 확인 및 설정
|
||||||
|
|
||||||
|
# 🌐 .env 파일 체크 및 API 키 확인/생성
|
||||||
|
if not os.path.exists(".env"):
|
||||||
|
with open(".env", "w") as f:
|
||||||
|
f.write("# SightRay 환경설정 파일\n")
|
||||||
|
load_dotenv()
|
||||||
|
api_key = os.getenv("POLYGON_API_KEY")
|
||||||
|
if not api_key or '--reset-api' in sys.argv:
|
||||||
|
print("🔐 Polygon API 키가 설정되어 있지 않거나 초기화 요청이 감지되었습니다.")
|
||||||
|
new_key = input("Polygon API 키를 입력하세요: ").strip()
|
||||||
|
if new_key:
|
||||||
|
set_key(".env", "POLYGON_API_KEY", new_key)
|
||||||
|
print("✅ API 키가 .env 파일에 저장되었습니다.")
|
||||||
|
else:
|
||||||
|
raise ValueError("API 키가 입력되지 않았습니다.")
|
||||||
|
print("🔑 Polygon API 키가 설정되어 있지 않거나 초기화 요청이 감지되었습니다.")
|
||||||
|
new_key = input("Polygon API 키를 입력하세요: ").strip()
|
||||||
|
if new_key:
|
||||||
|
print("✅ API 키가 .env 파일에 저장되었습니다.")
|
||||||
|
|
||||||
|
# 엔진 import
|
||||||
|
from data_collection_engine.engine import DataCollectionEngine
|
||||||
|
from data_analysis_engine.analyzer import analyze_stocks_pipeline
|
||||||
|
from data_analysis_engine.train_model import train_model
|
||||||
|
from data_analysis_engine.dataset_builder import build_dataset
|
||||||
|
from risk_manage_engine.evaluate import evaluate_predictions
|
||||||
|
from strategy_engine.run_strategy import run_strategy
|
||||||
|
|
||||||
|
# (사용되지 않지만 보관된 build_dataset 예시)
|
||||||
|
def build_dataset(df):
|
||||||
|
df = df.copy()
|
||||||
|
df['target'] = (df['close'].shift(-1) > df['close']).astype(int)
|
||||||
|
df.dropna(inplace=True)
|
||||||
|
X = df[['open', 'high', 'low', 'close', 'volume']]
|
||||||
|
y = df['target']
|
||||||
|
return X, y
|
||||||
|
|
||||||
|
def run_sightray(symbol: str, start: str, end: str, capital: float, strategy: str = "adjusted"):
|
||||||
|
# 파일 경로 설정
|
||||||
|
data_dir = "data"
|
||||||
|
ohlcv_path = os.path.join(data_dir, f"{symbol}_ohlcv.csv")
|
||||||
|
prediction_path = "prediction_result.csv"
|
||||||
|
risk_path = "risk_filtered_result.csv"
|
||||||
|
output_path = "final_strategy_result.csv"
|
||||||
|
|
||||||
|
# 최신 모델 자동 탐색
|
||||||
|
model_dir = "data_analysis_engine/models"
|
||||||
|
model_list = sorted(glob.glob(f"{model_dir}/model_*.json"), reverse=True)
|
||||||
|
if not model_list:
|
||||||
|
raise FileNotFoundError("모델 파일이 존재하지 않습니다.")
|
||||||
|
model_path = model_list[0]
|
||||||
|
|
||||||
|
# 1️⃣ 데이터 수집
|
||||||
|
print("[1단계] 데이터 수집 중...")
|
||||||
|
engine = DataCollectionEngine(data_dir=data_dir)
|
||||||
|
engine.collect(symbol, start, end)
|
||||||
|
|
||||||
|
# 2️⃣ 모델 학습
|
||||||
|
print("[2단계] 분석 모델 학습 중...")
|
||||||
|
train_model(data_dir, output_model_path=model_path)
|
||||||
|
|
||||||
|
# 3️⃣ 분석 예측
|
||||||
|
print("[3단계] 예측 실행 중...")
|
||||||
|
analyze_stocks_pipeline(
|
||||||
|
cds_dir=data_dir,
|
||||||
|
model_path=model_path,
|
||||||
|
top_n=10,
|
||||||
|
output_path=prediction_path
|
||||||
|
)
|
||||||
|
|
||||||
|
# 4️⃣ 리스크 평가
|
||||||
|
print("[4단계] 리스크 평가 실행 중...")
|
||||||
|
evaluate_predictions(prediction_path, data_dir, output_path=risk_path)
|
||||||
|
|
||||||
|
# 5️⃣ 전략 실행
|
||||||
|
print("[5단계] 전략 생성 및 포지션 설정 중...")
|
||||||
|
run_strategy(
|
||||||
|
risk_filtered_path=risk_path,
|
||||||
|
total_capital=capital,
|
||||||
|
strategy=strategy,
|
||||||
|
output_path=output_path
|
||||||
|
)
|
||||||
|
|
||||||
|
print("\n✅ SightRay 전체 파이프라인 실행 완료!")
|
||||||
|
print(f"→ 최종 전략 결과: {output_path}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("SightRay 실행을 시작합니다. 다음 정보를 입력해 주세요:")
|
||||||
|
symbol = input("종목 코드 (예: AAPL, TSLA): ").strip().upper()
|
||||||
|
start = input("시작일 (YYYY-MM-DD): ").strip()
|
||||||
|
end = input("종료일 (YYYY-MM-DD): ").strip()
|
||||||
|
capital = float(input("총 투자 자본 (예: 1000000): ").strip())
|
||||||
|
strategy = input("전략 템플릿 (adjusted / basic) [기본: adjusted]: ").strip().lower()
|
||||||
|
if strategy not in ["basic", "adjusted"]:
|
||||||
|
strategy = "adjusted"
|
||||||
|
|
||||||
|
run_sightray(symbol, start, end, capital, strategy)
|
103
sightray_concept.txt
Normal file
103
sightray_concept.txt
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
# 🧠 SightRay 통합 실행 엔트리포인트 개념 정리 (`sightray.py`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 목적
|
||||||
|
|
||||||
|
`sightray.py`는 SightRay의 모든 엔진을 **하나의 흐름으로 통합 실행**하는 CLI 기반 진입점입니다.
|
||||||
|
사용자가 종목 코드와 날짜, 자본만 입력하면:
|
||||||
|
|
||||||
|
> 수집 → 분석 → 리스크 평가 → 전략 실행 → 결과 저장 까지 전부 자동화됩니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔁 실행 흐름 요약
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
[사용자 입력: symbol, start, end, capital]
|
||||||
|
↓
|
||||||
|
DataCollectionEngine.collect() → CDS 수집 및 저장
|
||||||
|
↓
|
||||||
|
train_model.py → 학습 및 모델 저장
|
||||||
|
↓
|
||||||
|
predict.py → 예측 결과 (prediction_result.csv)
|
||||||
|
↓
|
||||||
|
evaluate.py → 리스크 평가 + tradable 종목 선정
|
||||||
|
↓
|
||||||
|
run_strategy.py → 전략 적용 + 포지션 설정
|
||||||
|
↓
|
||||||
|
[최종 결과: final_strategy_result.csv 저장]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ 실행 인자
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python sightray.py \
|
||||||
|
--symbol AAPL \
|
||||||
|
--start 2024-01-01 \
|
||||||
|
--end 2024-04-01 \
|
||||||
|
--capital 1000000 \
|
||||||
|
--strategy adjusted
|
||||||
|
```
|
||||||
|
|
||||||
|
| 인자 | 설명 |
|
||||||
|
|------|------|
|
||||||
|
| `--symbol` | 분석할 종목 코드 (예: AAPL, TSLA) |
|
||||||
|
| `--start` | 시작일 (`YYYY-MM-DD`) |
|
||||||
|
| `--end` | 종료일 (`YYYY-MM-DD`) |
|
||||||
|
| `--capital` | 총 투자 자본 (정수 단위) |
|
||||||
|
| `--strategy` | 전략 템플릿 (`basic` 또는 `adjusted`, 기본: `adjusted`) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 내부 구성 연계
|
||||||
|
|
||||||
|
| 모듈 | 역할 |
|
||||||
|
|------|------|
|
||||||
|
| `engine.py` | 데이터 수집 및 저장 (`data/`) |
|
||||||
|
| `train_model.py` | XGBoost 모델 학습 및 저장 |
|
||||||
|
| `predict.py` | 분석 예측 수행 (상위 종목 선정) |
|
||||||
|
| `evaluate.py` | 리스크 점수 산출 및 필터링 |
|
||||||
|
| `run_strategy.py` | 전략 실행 및 결과 저장 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 API 키 설정 기능
|
||||||
|
|
||||||
|
| 기능 | 설명 |
|
||||||
|
|------|------|
|
||||||
|
| `.env` 자동 로딩 | POLYGON_API_KEY 로딩 시도 |
|
||||||
|
| 키 미입력 시 | 사용자에게 직접 입력받아 `.env`에 저장 |
|
||||||
|
| `--reset-api` | 강제로 API 키 재설정 가능 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 출력 파일 예시
|
||||||
|
|
||||||
|
| 파일명 | 내용 |
|
||||||
|
|--------|------|
|
||||||
|
| `data/AAPL_ohlcv.csv` | 수집된 OHLCV 데이터 (CDS) |
|
||||||
|
| `prediction_result.csv` | 예측된 상위 종목 및 확률 |
|
||||||
|
| `risk_filtered_result.csv` | tradable 종목 리스트 + risk_score |
|
||||||
|
| `final_strategy_result.csv` | TP/SL 및 포지션 포함된 전략 결과 ✅ |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ SightRay에서의 역할
|
||||||
|
|
||||||
|
| 기능 | 설명 |
|
||||||
|
|------|------|
|
||||||
|
| 자동화 허브 | 모든 엔진을 순차 실행하여 결과까지 자동 도출 |
|
||||||
|
| CLI 통제 포인트 | 사용자 입력 기반 실행 환경 구성 |
|
||||||
|
| 확장 인터페이스 | 추후 Web, API, GUI에서 호출할 핵심 함수로도 전환 가능 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 요약
|
||||||
|
|
||||||
|
`sightray.py`는 SightRay의 **완전 자동화 파이프라인**을 실행하기 위한 단일 진입점입니다.
|
||||||
|
사용자는 단 한 줄의 명령으로 전체 분석과 전략 실행을 완료할 수 있으며,
|
||||||
|
이 파일은 운영자, 자동화 시스템, 외부 API 호출 등에서 **실전 통합 인터페이스**로 사용됩니다.
|
||||||
|
|
504
sp500_tickers.csv
Normal file
504
sp500_tickers.csv
Normal file
@ -0,0 +1,504 @@
|
|||||||
|
symbol
|
||||||
|
MMM
|
||||||
|
AOS
|
||||||
|
ABT
|
||||||
|
ABBV
|
||||||
|
ACN
|
||||||
|
ADBE
|
||||||
|
AMD
|
||||||
|
AES
|
||||||
|
AFL
|
||||||
|
A
|
||||||
|
APD
|
||||||
|
ABNB
|
||||||
|
AKAM
|
||||||
|
ALB
|
||||||
|
ARE
|
||||||
|
ALGN
|
||||||
|
ALLE
|
||||||
|
LNT
|
||||||
|
ALL
|
||||||
|
GOOGL
|
||||||
|
GOOG
|
||||||
|
MO
|
||||||
|
AMZN
|
||||||
|
AMCR
|
||||||
|
AEE
|
||||||
|
AEP
|
||||||
|
AXP
|
||||||
|
AIG
|
||||||
|
AMT
|
||||||
|
AWK
|
||||||
|
AMP
|
||||||
|
AME
|
||||||
|
AMGN
|
||||||
|
APH
|
||||||
|
ADI
|
||||||
|
ANSS
|
||||||
|
AON
|
||||||
|
APA
|
||||||
|
APO
|
||||||
|
AAPL
|
||||||
|
AMAT
|
||||||
|
APTV
|
||||||
|
ACGL
|
||||||
|
ADM
|
||||||
|
ANET
|
||||||
|
AJG
|
||||||
|
AIZ
|
||||||
|
T
|
||||||
|
ATO
|
||||||
|
ADSK
|
||||||
|
ADP
|
||||||
|
AZO
|
||||||
|
AVB
|
||||||
|
AVY
|
||||||
|
AXON
|
||||||
|
BKR
|
||||||
|
BALL
|
||||||
|
BAC
|
||||||
|
BAX
|
||||||
|
BDX
|
||||||
|
BRK.B
|
||||||
|
BBY
|
||||||
|
TECH
|
||||||
|
BIIB
|
||||||
|
BLK
|
||||||
|
BX
|
||||||
|
BK
|
||||||
|
BA
|
||||||
|
BKNG
|
||||||
|
BSX
|
||||||
|
BMY
|
||||||
|
AVGO
|
||||||
|
BR
|
||||||
|
BRO
|
||||||
|
BF.B
|
||||||
|
BLDR
|
||||||
|
BG
|
||||||
|
BXP
|
||||||
|
CHRW
|
||||||
|
CDNS
|
||||||
|
CZR
|
||||||
|
CPT
|
||||||
|
CPB
|
||||||
|
COF
|
||||||
|
CAH
|
||||||
|
KMX
|
||||||
|
CCL
|
||||||
|
CARR
|
||||||
|
CAT
|
||||||
|
CBOE
|
||||||
|
CBRE
|
||||||
|
CDW
|
||||||
|
COR
|
||||||
|
CNC
|
||||||
|
CNP
|
||||||
|
CF
|
||||||
|
CRL
|
||||||
|
SCHW
|
||||||
|
CHTR
|
||||||
|
CVX
|
||||||
|
CMG
|
||||||
|
CB
|
||||||
|
CHD
|
||||||
|
CI
|
||||||
|
CINF
|
||||||
|
CTAS
|
||||||
|
CSCO
|
||||||
|
C
|
||||||
|
CFG
|
||||||
|
CLX
|
||||||
|
CME
|
||||||
|
CMS
|
||||||
|
KO
|
||||||
|
CTSH
|
||||||
|
CL
|
||||||
|
CMCSA
|
||||||
|
CAG
|
||||||
|
COP
|
||||||
|
ED
|
||||||
|
STZ
|
||||||
|
CEG
|
||||||
|
COO
|
||||||
|
CPRT
|
||||||
|
GLW
|
||||||
|
CPAY
|
||||||
|
CTVA
|
||||||
|
CSGP
|
||||||
|
COST
|
||||||
|
CTRA
|
||||||
|
CRWD
|
||||||
|
CCI
|
||||||
|
CSX
|
||||||
|
CMI
|
||||||
|
CVS
|
||||||
|
DHR
|
||||||
|
DRI
|
||||||
|
DVA
|
||||||
|
DAY
|
||||||
|
DECK
|
||||||
|
DE
|
||||||
|
DELL
|
||||||
|
DAL
|
||||||
|
DVN
|
||||||
|
DXCM
|
||||||
|
FANG
|
||||||
|
DLR
|
||||||
|
DFS
|
||||||
|
DG
|
||||||
|
DLTR
|
||||||
|
D
|
||||||
|
DPZ
|
||||||
|
DASH
|
||||||
|
DOV
|
||||||
|
DOW
|
||||||
|
DHI
|
||||||
|
DTE
|
||||||
|
DUK
|
||||||
|
DD
|
||||||
|
EMN
|
||||||
|
ETN
|
||||||
|
EBAY
|
||||||
|
ECL
|
||||||
|
EIX
|
||||||
|
EW
|
||||||
|
EA
|
||||||
|
ELV
|
||||||
|
EMR
|
||||||
|
ENPH
|
||||||
|
ETR
|
||||||
|
EOG
|
||||||
|
EPAM
|
||||||
|
EQT
|
||||||
|
EFX
|
||||||
|
EQIX
|
||||||
|
EQR
|
||||||
|
ERIE
|
||||||
|
ESS
|
||||||
|
EL
|
||||||
|
EG
|
||||||
|
EVRG
|
||||||
|
ES
|
||||||
|
EXC
|
||||||
|
EXE
|
||||||
|
EXPE
|
||||||
|
EXPD
|
||||||
|
EXR
|
||||||
|
XOM
|
||||||
|
FFIV
|
||||||
|
FDS
|
||||||
|
FICO
|
||||||
|
FAST
|
||||||
|
FRT
|
||||||
|
FDX
|
||||||
|
FIS
|
||||||
|
FITB
|
||||||
|
FSLR
|
||||||
|
FE
|
||||||
|
FI
|
||||||
|
F
|
||||||
|
FTNT
|
||||||
|
FTV
|
||||||
|
FOXA
|
||||||
|
FOX
|
||||||
|
BEN
|
||||||
|
FCX
|
||||||
|
GRMN
|
||||||
|
IT
|
||||||
|
GE
|
||||||
|
GEHC
|
||||||
|
GEV
|
||||||
|
GEN
|
||||||
|
GNRC
|
||||||
|
GD
|
||||||
|
GIS
|
||||||
|
GM
|
||||||
|
GPC
|
||||||
|
GILD
|
||||||
|
GPN
|
||||||
|
GL
|
||||||
|
GDDY
|
||||||
|
GS
|
||||||
|
HAL
|
||||||
|
HIG
|
||||||
|
HAS
|
||||||
|
HCA
|
||||||
|
DOC
|
||||||
|
HSIC
|
||||||
|
HSY
|
||||||
|
HES
|
||||||
|
HPE
|
||||||
|
HLT
|
||||||
|
HOLX
|
||||||
|
HD
|
||||||
|
HON
|
||||||
|
HRL
|
||||||
|
HST
|
||||||
|
HWM
|
||||||
|
HPQ
|
||||||
|
HUBB
|
||||||
|
HUM
|
||||||
|
HBAN
|
||||||
|
HII
|
||||||
|
IBM
|
||||||
|
IEX
|
||||||
|
IDXX
|
||||||
|
ITW
|
||||||
|
INCY
|
||||||
|
IR
|
||||||
|
PODD
|
||||||
|
INTC
|
||||||
|
ICE
|
||||||
|
IFF
|
||||||
|
IP
|
||||||
|
IPG
|
||||||
|
INTU
|
||||||
|
ISRG
|
||||||
|
IVZ
|
||||||
|
INVH
|
||||||
|
IQV
|
||||||
|
IRM
|
||||||
|
JBHT
|
||||||
|
JBL
|
||||||
|
JKHY
|
||||||
|
J
|
||||||
|
JNJ
|
||||||
|
JCI
|
||||||
|
JPM
|
||||||
|
JNPR
|
||||||
|
K
|
||||||
|
KVUE
|
||||||
|
KDP
|
||||||
|
KEY
|
||||||
|
KEYS
|
||||||
|
KMB
|
||||||
|
KIM
|
||||||
|
KMI
|
||||||
|
KKR
|
||||||
|
KLAC
|
||||||
|
KHC
|
||||||
|
KR
|
||||||
|
LHX
|
||||||
|
LH
|
||||||
|
LRCX
|
||||||
|
LW
|
||||||
|
LVS
|
||||||
|
LDOS
|
||||||
|
LEN
|
||||||
|
LII
|
||||||
|
LLY
|
||||||
|
LIN
|
||||||
|
LYV
|
||||||
|
LKQ
|
||||||
|
LMT
|
||||||
|
L
|
||||||
|
LOW
|
||||||
|
LULU
|
||||||
|
LYB
|
||||||
|
MTB
|
||||||
|
MPC
|
||||||
|
MKTX
|
||||||
|
MAR
|
||||||
|
MMC
|
||||||
|
MLM
|
||||||
|
MAS
|
||||||
|
MA
|
||||||
|
MTCH
|
||||||
|
MKC
|
||||||
|
MCD
|
||||||
|
MCK
|
||||||
|
MDT
|
||||||
|
MRK
|
||||||
|
META
|
||||||
|
MET
|
||||||
|
MTD
|
||||||
|
MGM
|
||||||
|
MCHP
|
||||||
|
MU
|
||||||
|
MSFT
|
||||||
|
MAA
|
||||||
|
MRNA
|
||||||
|
MHK
|
||||||
|
MOH
|
||||||
|
TAP
|
||||||
|
MDLZ
|
||||||
|
MPWR
|
||||||
|
MNST
|
||||||
|
MCO
|
||||||
|
MS
|
||||||
|
MOS
|
||||||
|
MSI
|
||||||
|
MSCI
|
||||||
|
NDAQ
|
||||||
|
NTAP
|
||||||
|
NFLX
|
||||||
|
NEM
|
||||||
|
NWSA
|
||||||
|
NWS
|
||||||
|
NEE
|
||||||
|
NKE
|
||||||
|
NI
|
||||||
|
NDSN
|
||||||
|
NSC
|
||||||
|
NTRS
|
||||||
|
NOC
|
||||||
|
NCLH
|
||||||
|
NRG
|
||||||
|
NUE
|
||||||
|
NVDA
|
||||||
|
NVR
|
||||||
|
NXPI
|
||||||
|
ORLY
|
||||||
|
OXY
|
||||||
|
ODFL
|
||||||
|
OMC
|
||||||
|
ON
|
||||||
|
OKE
|
||||||
|
ORCL
|
||||||
|
OTIS
|
||||||
|
PCAR
|
||||||
|
PKG
|
||||||
|
PLTR
|
||||||
|
PANW
|
||||||
|
PARA
|
||||||
|
PH
|
||||||
|
PAYX
|
||||||
|
PAYC
|
||||||
|
PYPL
|
||||||
|
PNR
|
||||||
|
PEP
|
||||||
|
PFE
|
||||||
|
PCG
|
||||||
|
PM
|
||||||
|
PSX
|
||||||
|
PNW
|
||||||
|
PNC
|
||||||
|
POOL
|
||||||
|
PPG
|
||||||
|
PPL
|
||||||
|
PFG
|
||||||
|
PG
|
||||||
|
PGR
|
||||||
|
PLD
|
||||||
|
PRU
|
||||||
|
PEG
|
||||||
|
PTC
|
||||||
|
PSA
|
||||||
|
PHM
|
||||||
|
PWR
|
||||||
|
QCOM
|
||||||
|
DGX
|
||||||
|
RL
|
||||||
|
RJF
|
||||||
|
RTX
|
||||||
|
O
|
||||||
|
REG
|
||||||
|
REGN
|
||||||
|
RF
|
||||||
|
RSG
|
||||||
|
RMD
|
||||||
|
RVTY
|
||||||
|
ROK
|
||||||
|
ROL
|
||||||
|
ROP
|
||||||
|
ROST
|
||||||
|
RCL
|
||||||
|
SPGI
|
||||||
|
CRM
|
||||||
|
SBAC
|
||||||
|
SLB
|
||||||
|
STX
|
||||||
|
SRE
|
||||||
|
NOW
|
||||||
|
SHW
|
||||||
|
SPG
|
||||||
|
SWKS
|
||||||
|
SJM
|
||||||
|
SW
|
||||||
|
SNA
|
||||||
|
SOLV
|
||||||
|
SO
|
||||||
|
LUV
|
||||||
|
SWK
|
||||||
|
SBUX
|
||||||
|
STT
|
||||||
|
STLD
|
||||||
|
STE
|
||||||
|
SYK
|
||||||
|
SMCI
|
||||||
|
SYF
|
||||||
|
SNPS
|
||||||
|
SYY
|
||||||
|
TMUS
|
||||||
|
TROW
|
||||||
|
TTWO
|
||||||
|
TPR
|
||||||
|
TRGP
|
||||||
|
TGT
|
||||||
|
TEL
|
||||||
|
TDY
|
||||||
|
TER
|
||||||
|
TSLA
|
||||||
|
TXN
|
||||||
|
TPL
|
||||||
|
TXT
|
||||||
|
TMO
|
||||||
|
TJX
|
||||||
|
TKO
|
||||||
|
TSCO
|
||||||
|
TT
|
||||||
|
TDG
|
||||||
|
TRV
|
||||||
|
TRMB
|
||||||
|
TFC
|
||||||
|
TYL
|
||||||
|
TSN
|
||||||
|
USB
|
||||||
|
UBER
|
||||||
|
UDR
|
||||||
|
ULTA
|
||||||
|
UNP
|
||||||
|
UAL
|
||||||
|
UPS
|
||||||
|
URI
|
||||||
|
UNH
|
||||||
|
UHS
|
||||||
|
VLO
|
||||||
|
VTR
|
||||||
|
VLTO
|
||||||
|
VRSN
|
||||||
|
VRSK
|
||||||
|
VZ
|
||||||
|
VRTX
|
||||||
|
VTRS
|
||||||
|
VICI
|
||||||
|
V
|
||||||
|
VST
|
||||||
|
VMC
|
||||||
|
WRB
|
||||||
|
GWW
|
||||||
|
WAB
|
||||||
|
WBA
|
||||||
|
WMT
|
||||||
|
DIS
|
||||||
|
WBD
|
||||||
|
WM
|
||||||
|
WAT
|
||||||
|
WEC
|
||||||
|
WFC
|
||||||
|
WELL
|
||||||
|
WST
|
||||||
|
WDC
|
||||||
|
WY
|
||||||
|
WSM
|
||||||
|
WMB
|
||||||
|
WTW
|
||||||
|
WDAY
|
||||||
|
WYNN
|
||||||
|
XEL
|
||||||
|
XYL
|
||||||
|
YUM
|
||||||
|
ZBRA
|
||||||
|
ZBH
|
||||||
|
ZTS
|
|
35
strategy_engine/position_manager.py
Normal file
35
strategy_engine/position_manager.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
'''
|
||||||
|
strategy_engine/position_manager.py
|
||||||
|
|
||||||
|
전략 조건이 부여된 종목에 대해 실제 포지션(자금 배분, 수량 등)을 결정하는 모듈입니다.
|
||||||
|
- 고정 자산 총액을 기준으로 자산을 분산하여 각 종목에 배정
|
||||||
|
- 리스크 점수를 반영하여 투자 비중 조정 가능 (향후 확장)
|
||||||
|
'''
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
def assign_position(df: pd.DataFrame, total_capital: float = 0) -> pd.DataFrame:
|
||||||
|
"""
|
||||||
|
각 종목에 대해 자금 배분 및 포지션 수량 계산
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
df (pd.DataFrame): 전략 조건이 포함된 종목 테이블 (symbol, entry_price 등)
|
||||||
|
total_capital (float): 전체 투자 가능 자금 (기본: 0 → 반드시 사용자가 설정해야 함)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
pd.DataFrame: position_size, capital_allocated 컬럼 추가된 최종 포지션 테이블
|
||||||
|
"""
|
||||||
|
if total_capital <= 0:
|
||||||
|
raise ValueError("[오류] total_capital 값이 0 이하입니다. 유효한 자금 규모를 입력하세요.")
|
||||||
|
|
||||||
|
df = df.copy()
|
||||||
|
num_assets = len(df)
|
||||||
|
|
||||||
|
if num_assets == 0:
|
||||||
|
return df
|
||||||
|
|
||||||
|
capital_per_asset = total_capital / num_assets
|
||||||
|
df['capital_allocated'] = capital_per_asset
|
||||||
|
df['position_size'] = (capital_per_asset / df['entry_price']).apply(lambda x: int(x)) # 정수 주식 수량
|
||||||
|
|
||||||
|
return df
|
53
strategy_engine/run_strategy.py
Normal file
53
strategy_engine/run_strategy.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
'''
|
||||||
|
strategy_engine/run_strategy.py
|
||||||
|
|
||||||
|
전략 엔진 실행 진입점:
|
||||||
|
- tradable 종목 로딩 → 전략 템플릿 적용 → 포지션 계산 → 최종 결과 저장
|
||||||
|
- 사용자 입력: 투자 금액(total_capital), 전략 유형(strategy)
|
||||||
|
'''
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import pandas as pd
|
||||||
|
from strategy_engine.selector import select_candidates
|
||||||
|
from strategy_engine.templates.basic_strategy import apply_basic_strategy
|
||||||
|
from strategy_engine.templates.risk_adjusted_strategy import apply_risk_adjusted_strategy
|
||||||
|
from strategy_engine.position_manager import assign_position
|
||||||
|
|
||||||
|
def run_strategy(risk_filtered_path: str, total_capital: float, strategy: str = "adjusted", output_path: str = "final_strategy_result.csv"):
|
||||||
|
# 1단계: 전략 대상 종목 필터링
|
||||||
|
selected = select_candidates(risk_filtered_path)
|
||||||
|
|
||||||
|
if selected.empty:
|
||||||
|
print("[알림] 조건을 만족하는 종목이 없습니다.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 2단계: 전략 템플릿 선택 적용
|
||||||
|
if strategy == "basic":
|
||||||
|
strategy_df = apply_basic_strategy(selected)
|
||||||
|
else:
|
||||||
|
strategy_df = apply_risk_adjusted_strategy(selected)
|
||||||
|
|
||||||
|
# 3단계: 포지션 할당 (자금 배분)
|
||||||
|
final_df = pd.merge(selected, strategy_df, on="symbol")
|
||||||
|
final_df = assign_position(final_df, total_capital=total_capital)
|
||||||
|
|
||||||
|
# 4단계: 결과 저장 및 반환
|
||||||
|
final_df.to_csv(output_path, index=False)
|
||||||
|
print(f"[완료] 전략 실행 결과 저장됨 → {output_path}")
|
||||||
|
return final_df
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description="SightRay 전략 엔진 실행")
|
||||||
|
parser.add_argument("--input", type=str, required=True, help="risk_filtered_result.csv 경로")
|
||||||
|
parser.add_argument("--capital", type=float, required=True, help="총 투자 자본 (예: 1000000)")
|
||||||
|
parser.add_argument("--strategy", type=str, choices=["basic", "adjusted"], default="adjusted", help="사용할 전략 템플릿 (기본: adjusted)")
|
||||||
|
parser.add_argument("--output", type=str, default="final_strategy_result.csv", help="출력 파일명")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
run_strategy(
|
||||||
|
risk_filtered_path=args.input,
|
||||||
|
total_capital=args.capital,
|
||||||
|
strategy=args.strategy,
|
||||||
|
output_path=args.output
|
||||||
|
)
|
83
strategy_engine/run_strategy_concept.txt
Normal file
83
strategy_engine/run_strategy_concept.txt
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
# 🚀 전략 실행 컨트롤러 개념 정리 (`run_strategy.py`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 목적
|
||||||
|
|
||||||
|
`run_strategy.py`는 SightRay 전략 엔진의 실행 진입점(entrypoint)으로,
|
||||||
|
**전략 실행의 전체 흐름을 자동화**하여 사용자가 단일 명령으로 전략 결과를 생성할 수 있게 합니다.
|
||||||
|
|
||||||
|
> 📌 이 스크립트는 `selector → strategy template → position manager`까지의 모든 과정을 통합합니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔁 전체 실행 흐름 요약
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
[risk_filtered_result.csv]
|
||||||
|
↓
|
||||||
|
selector.py
|
||||||
|
→ tradable 종목 + 전략 조건 만족 종목 추출
|
||||||
|
↓
|
||||||
|
templates/basic_strategy.py
|
||||||
|
→ TP/SL 기준 설정, 매수 여부 결정
|
||||||
|
↓
|
||||||
|
position_manager.py
|
||||||
|
→ 자금 배분 및 포지션 크기 계산
|
||||||
|
↓
|
||||||
|
[final_strategy_result.csv] 저장
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧩 주요 역할별 연결 구조
|
||||||
|
|
||||||
|
| 순서 | 파일 | 역할 |
|
||||||
|
|------|------|------|
|
||||||
|
| 1 | `selector.py` | 전략 조건 만족 종목 선택 (확률, risk_score 기준) |
|
||||||
|
| 2 | `basic_strategy.py` | 전략 템플릿 적용: 진입가, 목표가, 손절가 설정 |
|
||||||
|
| 3 | `position_manager.py` | 자금 배분 및 포지션 수량 계산 |
|
||||||
|
| 4 | `run_strategy.py` | 전체 흐름 통합 및 실행 CLI 제공 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ CLI 실행 인자
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python run_strategy.py \
|
||||||
|
--input risk_filtered_result.csv \
|
||||||
|
--capital 1000000 \
|
||||||
|
--output final_strategy_result.csv
|
||||||
|
```
|
||||||
|
|
||||||
|
| 인자 | 설명 |
|
||||||
|
|------|------|
|
||||||
|
| `--input` | 필수: 분석 + 리스크 필터링 결과 파일 경로 |
|
||||||
|
| `--capital` | 필수: 총 투자 자본 (ex. 1,000,000) |
|
||||||
|
| `--output` | 선택: 결과 저장 경로 (기본값: `final_strategy_result.csv`) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 출력 예시
|
||||||
|
|
||||||
|
| symbol | entry_price | target_price | stop_loss | position_size | capital_allocated |
|
||||||
|
|--------|-------------|--------------|-----------|----------------|--------------------|
|
||||||
|
| AAPL | 186.5 | 195.8 | 180.0 | 1340 | 250000 |
|
||||||
|
| TSLA | 245.2 | 257.5 | 237.8 | 1020 | 250000 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📌 특징
|
||||||
|
|
||||||
|
- 사용자 중심 구조: 자본 입력만으로 실행 가능
|
||||||
|
- 확장 가능: 향후 다른 전략 템플릿과의 교체 용이
|
||||||
|
- CLI 환경, 자동화 배치, 대시보드 API 연동까지 고려된 진입점 설계
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 정리
|
||||||
|
|
||||||
|
`run_strategy.py`는 SightRay 전략 엔진의 핵심 실행 도구로,
|
||||||
|
**분석-리스크 결과 → 실전 전략 실행**을 연결하는 완전 자동화 인터페이스입니다.
|
||||||
|
|
||||||
|
> 🎯 운영자가 이 파일 하나만 실행하면 전략이 전부 적용된 결과를 받을 수 있습니다.
|
31
strategy_engine/selector.py
Normal file
31
strategy_engine/selector.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
'''
|
||||||
|
strategy_engine/selector.py
|
||||||
|
|
||||||
|
risk_filtered_result.csv에서 tradable 종목만 선택하고,
|
||||||
|
추가적인 전략 조건(예: 상승 확률, 리스크 점수 기준)에 따라 최종 전략 대상 종목을 선별합니다.
|
||||||
|
'''
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
def select_candidates(risk_filtered_path: str, min_probability: float = 0.7, min_risk_score: int = 70) -> pd.DataFrame:
|
||||||
|
"""
|
||||||
|
전략 실행 대상 종목을 선별하는 함수
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
risk_filtered_path (str): risk_filtered_result.csv 파일 경로
|
||||||
|
min_probability (float): 최소 상승 확률 기준 (기본 0.7)
|
||||||
|
min_risk_score (int): 최소 리스크 점수 기준 (기본 70)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
pd.DataFrame: 전략 조건을 만족하는 종목들
|
||||||
|
"""
|
||||||
|
df = pd.read_csv(risk_filtered_path)
|
||||||
|
|
||||||
|
# tradable 종목 중 전략 조건 만족하는 종목만 선별
|
||||||
|
filtered = df[
|
||||||
|
(df['tradable'] == True) &
|
||||||
|
(df['predicted_score'] >= min_probability) &
|
||||||
|
(df['risk_score'] >= min_risk_score)
|
||||||
|
].copy()
|
||||||
|
|
||||||
|
return filtered.reset_index(drop=True)
|
31
strategy_engine/templates/basic_strategy.py
Normal file
31
strategy_engine/templates/basic_strategy.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
'''
|
||||||
|
strategy_engine/templates/basic_strategy.py
|
||||||
|
|
||||||
|
기본 전략 템플릿:
|
||||||
|
- 매수 신호가 있는 tradable 종목에 대해 TP(목표가), SL(손절가)를 자동 계산
|
||||||
|
- 단순 비율 방식으로 설정 (예: +5%, -3%)
|
||||||
|
- Buy 여부 결정 포함
|
||||||
|
'''
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
def apply_basic_strategy(df: pd.DataFrame, tp_ratio: float = 0.05, sl_ratio: float = 0.03) -> pd.DataFrame:
|
||||||
|
"""
|
||||||
|
전략 종목에 대해 TP/SL 조건 및 액션(Buy) 설정
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
df (pd.DataFrame): selector에서 필터링된 종목 DataFrame (최소: symbol, close 컬럼 포함)
|
||||||
|
tp_ratio (float): 목표 수익률 비율 (기본 5%)
|
||||||
|
sl_ratio (float): 손절 비율 (기본 3%)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
pd.DataFrame: 전략 조건이 부여된 종목 테이블
|
||||||
|
"""
|
||||||
|
df = df.copy()
|
||||||
|
|
||||||
|
df['entry_price'] = df['close'] # 현재 종가를 진입가로 가정
|
||||||
|
df['target_price'] = df['entry_price'] * (1 + tp_ratio)
|
||||||
|
df['stop_loss'] = df['entry_price'] * (1 - sl_ratio)
|
||||||
|
df['action'] = 'Buy' # 현재는 모두 매수 대상으로 판단
|
||||||
|
|
||||||
|
return df[['symbol', 'entry_price', 'target_price', 'stop_loss', 'action']]
|
52
strategy_engine/templates/risk_adjusted_strategy.py
Normal file
52
strategy_engine/templates/risk_adjusted_strategy.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
'''
|
||||||
|
strategy_engine/templates/risk_adjusted_strategy.py
|
||||||
|
|
||||||
|
리스크 점수 기반 전략 템플릿:
|
||||||
|
- 종목별 risk_score에 따라 TP/SL 비율 및 action(Buy 여부)을 다르게 설정
|
||||||
|
- 리스크가 낮을수록 더 공격적인 전략 적용, 높을수록 보수적으로 필터링
|
||||||
|
'''
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
def apply_risk_adjusted_strategy(df: pd.DataFrame) -> pd.DataFrame:
|
||||||
|
"""
|
||||||
|
risk_score에 따라 전략 조건(TP/SL/Buy 여부)을 결정하는 함수
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
df (pd.DataFrame): selector에서 필터링된 종목 DataFrame (symbol, close, risk_score 포함)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
pd.DataFrame: entry_price, target_price, stop_loss, action 포함한 전략 테이블
|
||||||
|
"""
|
||||||
|
df = df.copy()
|
||||||
|
df['entry_price'] = df['close']
|
||||||
|
|
||||||
|
def compute_strategy(row):
|
||||||
|
score = row['risk_score']
|
||||||
|
price = row['entry_price']
|
||||||
|
|
||||||
|
if score >= 80:
|
||||||
|
tp = 0.07
|
||||||
|
sl = 0.03
|
||||||
|
action = 'Buy'
|
||||||
|
elif score >= 70:
|
||||||
|
tp = 0.05
|
||||||
|
sl = 0.03
|
||||||
|
action = 'Buy'
|
||||||
|
elif score >= 60:
|
||||||
|
tp = 0.03
|
||||||
|
sl = 0.02
|
||||||
|
action = 'Buy'
|
||||||
|
else:
|
||||||
|
tp = None
|
||||||
|
sl = None
|
||||||
|
action = 'None'
|
||||||
|
|
||||||
|
return pd.Series({
|
||||||
|
'target_price': price * (1 + tp) if tp is not None else None,
|
||||||
|
'stop_loss': price * (1 - sl) if sl is not None else None,
|
||||||
|
'action': action
|
||||||
|
})
|
||||||
|
|
||||||
|
strategy_info = df.apply(compute_strategy, axis=1)
|
||||||
|
return pd.concat([df[['symbol', 'entry_price']], strategy_info], axis=1)
|
71
strategy_engine/templates/risk_adjusted_strategy_concept.txt
Normal file
71
strategy_engine/templates/risk_adjusted_strategy_concept.txt
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
# 📈 리스크 점수 기반 전략 템플릿 개념 정리 (`risk_adjusted_strategy.py`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 목적
|
||||||
|
|
||||||
|
`risk_adjusted_strategy.py`는 **리스크 점수(risk_score)**를 기준으로 종목별 전략 조건을 다르게 설정하는 템플릿입니다.
|
||||||
|
|
||||||
|
> 리스크가 낮은 종목은 **더 공격적으로**, 리스크가 높은 종목은 **보수적으로 또는 제외**하는 전략 구조입니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧠 전략 설계 기준
|
||||||
|
|
||||||
|
| risk_score | TP 비율 | SL 비율 | 매수 여부 (action) |
|
||||||
|
|------------|---------|---------|---------------------|
|
||||||
|
| ≥ 80 | +7% | -3% | Buy |
|
||||||
|
| 70 ~ 79 | +5% | -3% | Buy |
|
||||||
|
| 60 ~ 69 | +3% | -2% | Buy |
|
||||||
|
| < 60 | — | — | None (매수 제외) |
|
||||||
|
|
||||||
|
- TP: Target Price (목표 수익률)
|
||||||
|
- SL: Stop Loss (손절 기준)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔁 내부 동작 흐름
|
||||||
|
|
||||||
|
```python
|
||||||
|
df['entry_price'] = df['close']
|
||||||
|
if risk_score >= 80:
|
||||||
|
target = price * 1.07
|
||||||
|
stop = price * 0.97
|
||||||
|
action = 'Buy'
|
||||||
|
elif risk_score < 60:
|
||||||
|
action = 'None'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 출력 예시
|
||||||
|
|
||||||
|
| symbol | entry_price | target_price | stop_loss | action |
|
||||||
|
|--------|-------------|--------------|-----------|--------|
|
||||||
|
| AAPL | 185.6 | 198.6 | 180.0 | Buy |
|
||||||
|
| TSLA | 241.3 | — | — | None |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ 적용 위치
|
||||||
|
|
||||||
|
- `strategy_engine/templates/` 디렉토리에 위치
|
||||||
|
- `run_strategy.py` 또는 분석 시점에서 `basic_strategy`와 교체 사용 가능
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 향후 확장 방향
|
||||||
|
|
||||||
|
| 확장 항목 | 설명 |
|
||||||
|
|-----------|------|
|
||||||
|
| TP/SL 비율 튜닝 | ROI 또는 ATR 기반으로 동적 조정 |
|
||||||
|
| Buy/Sell 조건 추가 | 상승 확률, 모멘텀, 거래량 등 복합 조건 반영 |
|
||||||
|
| 전략 유형 구분 | 보수형/공격형 등 사용자 선택 가능 구조로 개선 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 정리
|
||||||
|
|
||||||
|
- 이 전략 템플릿은 **risk_score에 따라 전략을 유연하게 조정**하는 구조입니다.
|
||||||
|
- `basic_strategy.py`보다 **한 단계 정교한 판단 로직**을 제공하며,
|
||||||
|
- **리스크 점수를 전략적으로 활용**하는 대표적인 전략 템플릿입니다.
|
11667
utils/all_polygon_tickers.csv
Normal file
11667
utils/all_polygon_tickers.csv
Normal file
File diff suppressed because it is too large
Load Diff
55
utils/ticker_downloader.py
Normal file
55
utils/ticker_downloader.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import requests
|
||||||
|
import os
|
||||||
|
import pandas as pd
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from datetime import datetime
|
||||||
|
import time
|
||||||
|
|
||||||
|
# .env 파일에서 API 키 로드
|
||||||
|
load_dotenv()
|
||||||
|
API_KEY = os.getenv("POLYGON_API_KEY")
|
||||||
|
|
||||||
|
def fetch_all_us_stock_tickers(save_path="all_polygon_tickers.csv", delay_sec=13):
|
||||||
|
"""
|
||||||
|
Polygon.io에서 모든 US 주식 티커를 가져와 CSV로 저장
|
||||||
|
사용자의 요금제에 따라 요청 간 대기 시간 조절 가능
|
||||||
|
"""
|
||||||
|
base_url = "https://api.polygon.io/v3/reference/tickers"
|
||||||
|
params = {
|
||||||
|
"market": "stocks",
|
||||||
|
"active": "true",
|
||||||
|
"limit": 1000,
|
||||||
|
"apiKey": API_KEY
|
||||||
|
}
|
||||||
|
|
||||||
|
all_tickers = []
|
||||||
|
next_url = base_url
|
||||||
|
page = 1
|
||||||
|
|
||||||
|
while next_url:
|
||||||
|
print(f"🔄 요청 중 ({page} 페이지): {next_url}")
|
||||||
|
response = requests.get(next_url, params=params if next_url == base_url else {})
|
||||||
|
if response.status_code != 200:
|
||||||
|
raise Exception(f"요청 실패: {response.status_code} - {response.text}")
|
||||||
|
|
||||||
|
data = response.json()
|
||||||
|
results = data.get("results", [])
|
||||||
|
all_tickers.extend(results)
|
||||||
|
|
||||||
|
next_url = data.get("next_url")
|
||||||
|
if next_url and "apiKey" not in next_url:
|
||||||
|
next_url += f"&apiKey={API_KEY}"
|
||||||
|
|
||||||
|
page += 1
|
||||||
|
time.sleep(delay_sec) # 요청 간 간격 조정
|
||||||
|
|
||||||
|
# DataFrame으로 정리 후 저장
|
||||||
|
df = pd.DataFrame(all_tickers)
|
||||||
|
df.to_csv(save_path, index=False)
|
||||||
|
print(f"✅ 총 {len(df)}개 종목 저장 완료 → {save_path}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("전체 US 주식 티커를 가져옵니다. 요금제에 따라 요청 간 대기 시간을 조정하세요.")
|
||||||
|
delay = input("요청 간 대기 시간 (초) [기본: 13]: ").strip()
|
||||||
|
delay_sec = int(delay) if delay else 13
|
||||||
|
fetch_all_us_stock_tickers(delay_sec=delay_sec)
|
Reference in New Issue
Block a user