# Trading AI — Claude Code 프로젝트 가이드 > **역할**: 워렌 버핏 스타일 한국 주식 AI 투자 분석 전문가로 행동할 것. > 가치투자 관점(ROE·영업이익률·부채비율·FCF)을 최우선으로 판단한다. > 이 파일은 매 대화 시작 시 자동 로드됨 — 파일 탐색 없이 이 내용만으로 작업 시작. --- ## Claude 행동 원칙 ### 1. 먼저 생각, 그 다음 코딩 - 가정은 명시적으로 밝힐 것. 불확실하면 질문. - 해석이 여러 개면 나열하고 고를 것 — 혼자 결정 금지. - 더 단순한 방법이 있으면 말할 것. 불필요한 복잡성에 반론. ### 2. 단순함 우선 - 요청한 것만 구현. 추측성 기능·추상화·유연성 추가 금지. - "나중에 필요할 수도" 코드 금지. 200줄이 50줄로 가능하면 다시 짤 것. - 불가능한 시나리오를 위한 에러 핸들링 금지. ### 3. 정밀한 변경 - 요청한 부분만 수정. 인접 코드 "개선" 금지. - 기존 스타일 유지 (내 방식이 달라도). - 내 변경으로 생긴 불필요한 import/변수/함수만 제거. 기존 dead code는 언급만. ### 4. 검증 기준 명시 - 다단계 작업 시 각 단계 완료 조건을 먼저 정의. - Docker 서비스 변경 → 로그 확인 필수. - DB 변경 → 쿼리로 확인 필수. ### 이 프로젝트 특이사항 - sudo 비밀번호 입력 불가 → sudo 필요 작업은 `! sudo ` 형태로 유저에게 요청. - n8n 마이그레이션 충돌 이력 있음 → n8n 스키마 변경 시 반드시 백업 먼저. - NAS fstab은 한 줄 유지 필수 (nfsvers=3). - 상장폐지 필터(`is_active=true`) 빠지면 추천 결과 오염됨. --- ## 시스템 개요 뉴스 수집 → 형태소분석 → 임베딩 → 중복제거 → AI분석 → 재무스코어링 → 기술분석 → 매수/매도 추천 → 텔레그램 알림 --- ## 인프라 | 항목 | 값 | |------|-----| | 서버 OS | Ubuntu 22.04 | | GPU | GPU0: RTX 3060 12GB (EXAONE 추론) / GPU1: RTX 3070 8GB (bge-m3 임베딩) | | NAS | `192.168.0.36:/volume1/trading → /mnt/nas` (nfsvers=3 필수) | | DB 연결 | `postgres:5432`, DB명 `trading_ai`, user `kyu` | | 환경변수 | `/home/kyu/trading/.env` | | Docker | `docker compose` (파일: `/home/kyu/trading/docker-compose.yml`) | | PostgreSQL 데이터 | `/mnt/nas/postgresql/data` (NAS 저장) | --- ## 서비스 목록 | 서비스 | 컨테이너 | 포트 | 내부IP | 역할 | |--------|---------|------|--------|------| | news-collector | trading-news-collector | 8787 | 172.30.0.16 | 뉴스수집+형태소+임베딩+Ollama분석 | | bareunaapi | trading-bareunaapi | 5757 | 172.30.0.12 | 한국어 형태소분석 FastAPI 래퍼 | | bareun | trading-bareun | 5656/9902 | 172.30.0.15 | 바른 NLP gRPC 서버 | | dart-collector | trading-dart-collector | 8888 | 172.30.0.17 | DART 공시/재무제표 수집 | | score-engine | trading-score-engine | 8686 | 172.30.0.19 | 종합 투자 스코어 계산+텔레그램 | | ta-engine | trading-ta-engine | 8484 | 172.30.0.23 | 기술적분석 (MA/RSI/MACD/볼밴/스토캐스틱) | | dashboard-api | trading-dashboard-api | 8989 | 172.30.0.22 | 대시보드 REST API | | kis-api | trading-kis-api | 8585 | 172.30.0.18 | 네이버모바일 주가수집 + 매매시그널 | | ollama | trading-ollama | 11434 | 172.30.0.13 | EXAONE 3.5 7.8B 추론(GPU0) + bge-m3 임베딩(GPU1) | | n8n | trading-n8n | 5678 | 172.30.0.20 | 워크플로우 자동화 | | n8n-worker | trading-n8n-worker | - | 172.30.0.21 | n8n Queue Worker | | postgres | trading-postgres | 5432 | - | 메인 DB | | redis | trading-redis | 6379 | 172.30.0.10 | 캐시/중복제거 | | qdrant | trading-qdrant | 6333 | 172.30.0.11 | 뉴스 벡터 유사도 필터 | | aux-signal | trading-aux-signal | 8282 | 172.30.0.25 | 보조 데이터: 네이버 컨센서스·기관/외국인 수급 + ECOS 매크로(USD/KRW, 국고채10년) | | us-market | trading-us-market | 8383 | 172.30.0.24 | 미증시→한국 동조 시그널 (섹터ETF 14개 ±5점, 개별 페어 회귀 ±10점, 자동 페어발굴) | | graph-engine | trading-graph-engine | 9090 | 172.30.0.27 | GAT 그래프 신경망 (12피처 노드 × 가격상관/섹터/뉴스공기 엣지) → graph_score | | telegram-bot | trading-telegram-bot | - | 172.30.0.26 | 텔레그램 명령 처리 (/buy /sell /stock /deep /market) | --- ## 파일 구조 & 핵심 함수 ### `/home/kyu/trading/news-collector/main.py` (21KB) 뉴스 수집 + AI 분석 파이프라인. 포트 8787. | 함수/클래스 | 역할 | |------------|------| | `class S` | 전역 상태 (DB pool, Redis, Qdrant client) | | `nhash(title, url)` | SHA256[:16] 뉴스 중복 해시 | | `is_korean(text)` | 한글 포함 여부 체크 | | `parse_rss_date(date_str)` | RSS pubDate → ISO timestamp | | `@app.post("/collect/market")` | 네이버 금융 메인 뉴스 수집 | | `@app.post("/collect/stocks")` | 종목별 네이버 뉴스 수집 | | `@app.post("/collect/rss")` | 18개 RSS 피드 수집 | | `@app.get("/sources")` | RSS 소스 목록 | **파이프라인 순서** (함수 내부): 1. 바른API `/analyze` → 형태소분석 + 종목감지 2. Ollama `bge-m3` → 1024차원 임베딩 3. Qdrant → 코사인 유사도 ≥0.92 중복 제거 4. Ollama `exaone3.5:7.8b` (GPU0) → 호재/악재/중립 + intensity(1~5) + catalyst분류 5. PostgreSQL `news_analysis` 저장 **EXAONE 프롬프트**: 버핏 관점 (실적/수주/배당/리스크/모멘텀/기타 catalyst 분류) **RSS 소스 (28개)**: 한국경제, 매일경제, 머니투데이, 이데일리, 연합뉴스, 조선비즈, 헤럴드경제, 아시아경제, 파이낸셜뉴스, SBS Biz, 뉴스1, 뉴시스 등 + 네이버금융 직접 크롤링 **스케줄러**: - 평일 RSS: 8-18시 5분마다 (`rss_weekday`) - 주말 RSS: 8-22시 15분마다 (`rss_weekend`) — 누적 학습용 - 평일 마켓: 9-17시 10분마다 (`market`, 네이버 금융 메인) - raw 분석: 24시간 30분마다 200건 batch (`process_raw`) — 백로그 소화용으로 확장 - 주간 raw 백필: 일요일 02:00 전체 종목 × 50페이지 (`historical_raw_weekly`) **수집/분석 분리** (대규모 백필용): - `POST /collect/historical-raw?count=0&max_pages=100` — raw만 빠르게 수집 (LLM 스킵) - `POST /process/raw?batch_size=200` — news_raw → news_analysis 점진 분석 - `GET /raw/stats` — 백필 진행률 (total/unprocessed/processed) --- ### `/home/kyu/trading/bareunaapi/main.py` (9.5KB) 한국어 형태소분석 FastAPI 래퍼. 포트 5757. | 함수/클래스 | 역할 | |------------|------| | `class AppState` | bareun gRPC stub + Redis + 종목사전 | | `class AnalyzeRequest / AnalyzeResponse` | 분석 요청/응답 모델 | | `news_hash(title, url)` | Redis 중복제거용 해시 | | `extract_morphemes(text)` | 바른API → 명사 추출 | | `extract_stocks(text)` | 텍스트에서 종목코드 감지 | | `build_filtered(nouns, stocks)` | 필터링된 텍스트 구성 | | `scan_finance_terms(text)` | finance_dict.py 금융용어 매칭 | | `_analyze(req)` | 핵심 분석 로직 | | `@app.post("/analyze")` | 단건 분석 | | `@app.post("/analyze/batch")` | 배치 분석 | | `@app.get("/stocks")` | 로드된 종목 목록 | | `@app.post("/stocks/refresh")` | KRX→Naver 종목 새로고침 | | `@app.delete("/dedup/flush")` | Redis 중복제거 캐시 초기화 | **관련 파일**: - `bareunaapi/finance_dict.py` — 주식/금융 전문 용어 사전 - `bareunaapi/stock_loader.py` — KRX→Naver 폴백, ~3768 종목 로드 --- ### `/home/kyu/trading/dart-collector/main.py` (34KB) DART 공시/재무제표 수집. 포트 8888. | 함수/클래스 | 역할 | |------------|------| | `class Stats` | 수집 통계 | | `get_corp_code(stock_code)` | 종목코드 → DART corp_code 변환 | | `get_corp_name(stock_code)` | 종목코드 → 기업명 | | `calc_financial_ratios(key_items, prev_revenue)` | ROE/영업이익률/부채비율/FCF/매출성장률 계산 | | `@app.post("/collect/disclosures")` | 공시 수집 | | `@app.post("/collect/financials")` | 재무제표 수집 (300개 종목) | | `@app.post("/collect/major")` | 주요 대형주 수집 | | `@app.post("/collect/corps")` | DART 기업목록 갱신 | | `@app.get("/corps")` | 기업목록 조회 | | `@app.get("/corps/{stock_code}")` | 특정 기업 조회 | | `@app.get("/financials/{stock_code}")` | 종목 재무데이터 | | `@app.get("/stats")` | 수집 통계 | --- ### `/home/kyu/trading/score-engine/main.py` (27KB) 종합 투자 스코어 계산 + 텔레그램 알림. 포트 8686. | 함수/클래스 | 역할 | |------------|------| | `get_recommendation(score)` | 점수→등급 변환 | | `calc_fundamental_score(fin, per, pbr)` | 재무점수 산출 (-100~100) | | `calc_magic_formula(fin, market_cap)` | 그린블라트 매직 포뮬러 (ROC + EY) 0~30점 | | `calc_piotroski_score(curr, prev)` | 피오트로스키 F-Score 7신호 → -15~+15 | | `calc_altman_z(fin, market_cap)` | 알트만 Z-Score 단순화 → 매수/매도/관망 신호 | | `calc_peg(curr, prev, per)` | 린치 GARP — PER/이익성장률 → 신호 | | `calc_momentum(conn, code)` | AQR 12-1개월 가격 모멘텀 → 신호 | | `calc_beneish_simplified(curr, prev)` | Beneish M-Score 단순화 (분식 의심도) | | `aggregate_signals(signals)` | 6공식 보팅 다수결 → (요약, 카운트) | | `calc_trend_score(conn, code)` | 5년 ROE 일관성·추세 -30~+30 | | `calc_dcf(fin, market_cap)` | 간이 DCF 내재가치 + 안전마진 | | `calc_earnings_quality(fin)` | CFO/영업이익 비율 (분식의심 패널티) | | `is_value_investable(fin, per, pbr, market_cap)` | 버핏 필터 통과 여부 | | `@app.post("/score/calculate")` | 종목 점수 계산 실행 | | `@app.post("/briefing/send")` | 텔레그램 브리핑 전송 | | `@app.get("/ranking")` | 전체 종목 랭킹 | | `@app.get("/recommendations")` | 추천 종목 목록 | | `@app.get("/stock/{code}")` | 특정 종목 점수 상세 | | `@app.get("/backtest")` | 추천 성과 백테스트 (수익률/승률/샤프/MDD/알파) | | `@app.post("/learn-weights")` | 공식별 신호 → 7d 수익률 회귀로 가중치 학습 | | `@app.get("/learn-weights")` | 현재 적용 중인 공식 가중치 조회 | | `@app.post("/learn-pricing")` | D+E: 점수→30d 수익률 선형회귀 + Random Forest 학습 | | `@app.get("/predict-price/{code}")` | 학습된 모델로 종목 30일 후 예상 수익률·가격 | | `@app.get("/sector/concentration")` | 섹터 집중도 + 30% 초과 경고 | **스코어링 공식 (실제 가중치)**: ``` 종합점수 = 펀더멘털통합×0.24 + 뉴스(catalyst가중)×0.18 + 기술×0.15 + DART공시×0.10 + 외국인수급×0.14 + 공매도×0.06 + 가격모멘텀×0.03 + DCF안전마진×0.10 + 시장레짐 보정(±5~10) + 앙상블 보팅(±18) ← 6공식 매수/매도 합 × 학습가중치 ``` **10공식 앙상블 보팅** — 각 공식이 독립 신호 발신, 학습 가중치로 결합: | 공식 | 출처/논문 | 데이터 | 매수 신호 | |------|----------|--------|----------| | 매직포뮬러 | Greenblatt | ROC + EY | magic_score ≥ 20 | | F-Score | Piotroski 2000 | 7신호 (전년 대비) | f_score ≥ 6 | | 알트만 Z | Altman 1968 | 6.72×ROA + 1.05×(시총/부채) | Z ≥ 2.6 | | PEG | Lynch GARP | PER / 이익성장률 | PEG ≤ 1.5 | | 12-1 모멘텀 | AQR (Carhart 1997) | (P_-21 / P_-252) - 1 | ≥ 10% | | Beneish | Beneish 1999 | TATA·SGI·CFO/NI | 의심도 < 50 + CFO/NI > 1 | | **GP/A** | Novy-Marx 2013 | 영업이익/총자산 (대체) | ≥ 15% | | **G-Score** | Mohanram 2005 | 5신호 vs 섹터 중앙값 | ≥ 4 | | **Amihud** | Amihud 2002 | avg(\|return\|/거래대금) | ≥ 100 (소형 알파) | | **베타(BAB)** | Frazzini-Pedersen 2014 | 종목 vs KOSPI 60일 회귀 | β < 0.7 (저베타 알파) | **학습 가중치** (`weight_config` 테이블): - `POST /learn-weights?days=90` — 백테스트로 공식별 매수vs매도 그룹 7d 수익률 차이(edge) 측정 - edge가 큰 공식일수록 가중치 ↑ (정규화 후 합 = 6, 균등 시 각 1.0) - 다음 점수 계산부터 자동 적용. 표본 부족 시 default(균등 1.0) **펀더멘털통합** = `clip(buffett_score + trend_score + earnings_quality + magic_score + f_score_adj, -100, 100)` - `buffett_score`: ROE / 영업이익률 / 부채비율 / 매출성장 / PER / PBR / FCF / 배당 - `trend_score`: 5년 ROE 일관성·추세 (-30~+30) - `earnings_quality`: CFO/영업이익 ≥1 가산, <0.7 분식의심 패널티 - `magic_score`: ROC(영업이익/총자산) + EY(영업이익/EV) 임계값 합산 (0~30) - `f_score_adj`: F-Score ≥6 +15, 5 +8, 4 +3, ≤2 -15 (가치함정 회피) **매직 포뮬러 임계값** (한국 시장 보정): - ROC: ≥25% +15 / ≥15% +10 / ≥8% +5 - EY: ≥15% +15 / ≥10% +10 / ≥6% +5 **피오트로스키 F-Score 7신호** (현재/전년 사업보고서 비교, 9중 2개는 데이터 부재로 생략): 1. ROA(NI/총자산) > 0 2. CFO > 0 3. ΔROA > 0 4. CFO > NI 5. Δ부채비율 < 0 6. Δ영업이익률 > 0 7. Δ자산회전율 > 0 **추천 등급** (점수 + 다수공식 동의 강제): - 강력매수: 점수 ≥70 AND 매수보팅 ≥3 - 매수관심: 점수 ≥40 AND 매수보팅 ≥1 AND 매도보팅 <2 - 매도관심: 점수 ≤-30 OR 매도보팅 ≥3 - 강력매도: 점수 ≤-60 OR 매도보팅 ≥4 - 관망: 그 외 **버핏 가치투자 필터** (`is_value_investable`): - `operating_profit > 0` 영업적자 제외 - ROE ≥ 10% - 부채비율 ≤ 200% - PER ≤ 60 - 시총 ≥ 100억 - `dart_corps.is_active=true` (상장폐지 제외) --- ### `/home/kyu/trading/ta-engine/main.py` (37KB) 기술적 분석 엔진. 포트 8484. | 함수/클래스 | 역할 | |------------|------| | `_ema_series(values, period)` | EMA 계산 | | `_ma(closes, n)` | 단순이동평균 | | `_rsi(closes, period=14)` | RSI | | `_macd(closes)` | MACD (12/26/9) | | `_bollinger(closes, period=20)` | 볼린저밴드 | | `_stochastic(highs, lows, closes, period=14)` | 스토캐스틱 K/D | | `_vol_ratio(volumes, period=20)` | 거래량비율 | | `calc_indicators(ohlcv)` | 전체 지표 계산 | | `calc_tech_score(ind)` | 기술점수 (-100~100) | | `calc_price_targets(price, ind, sig)` | 목표가/손절가 계산 | | `analyze_position(price, buy_price, qty, ...)` | 포지션 분석 | | `@app.get("/technical/{code}")` | 종목 기술분석 | | `@app.get("/ranking")` | 기술점수 랭킹 | | `@app.get("/buy-candidates")` | 매수 후보 | | `@app.post("/analyze/all")` | 전체 종목 분석 | | `@app.post("/analyze/{code}")` | 특정 종목 분석 | | `@app.post("/position")` | 포지션 분석 | | `@app.get("/report/{code}")` | 종합 리포트 | **목표가 계산**: - 진입가: `min(현재가, MA20×0.99)` - T1: `max(볼밴상단, 현재가×1.05)` - T2: `max((현재가+52주고가)/2, 현재가×1.10)` - T3: `max(52주고가×0.97, 현재가×1.20)` - 손절가: `min(MA60×0.98, 현재가×0.95)` --- ### `/home/kyu/trading/dashboard-api/main.py` (19KB) 대시보드 REST API. 포트 8989. | 엔드포인트 | 역할 | |-----------|------| | `GET /api/summary` | 전체 요약 통계 | | `GET /api/recent` | 최근 뉴스 분석 | | `GET /api/ranking` | 종합 랭킹 | | `GET /api/recommendations` | 추천 종목 | | `GET /api/avoid` | 회피 종목 | | `GET /api/signals` | 매매 시그널 | | `GET /api/technical/{code}` | 기술분석 데이터 | | `GET /api/buy-candidates` | 매수 후보 | | `GET /api/alerts` | 알림 목록 | | `GET /api/timeline` | 타임라인 | | `GET /api/stock/{code}` | 종목 상세 | | `GET /api/search` | 종목 검색 | | `POST /api/position` | 포지션 등록 | | `GET /api/report/{code}` | 종목 리포트 | | `GET /api/fundamentals` | 전체 재무데이터 | | `GET /api/fundamentals/{code}` | 종목 재무데이터 | | `GET /api/name/{code}` | 종목명 조회 | | `GET /` | 대시보드 HTML | --- ### `/home/kyu/trading/kis-api/main.py` (11KB) 네이버 모바일 주가수집 + 매매시그널. 포트 8585. | 함수/엔드포인트 | 역할 | |---------------|------| | `class Stats` | 수집 통계 | | `calc_signal(p, news_sc, dart_sc)` | 뉴스+공시점수 → 매매시그널 | | `GET /price/{code}` | 단일 종목 주가 | | `GET /prices` | 전체 주가 목록 | | `GET /signals` | 전체 시그널 | | `GET /signals/{code}` | 종목별 시그널 | | `POST /collect` | 주가 수집 실행 | **Redis DB 할당**: - db=0: n8n Queue - db=1: bareunaapi 중복제거 - db=3: kis-api/score-engine (`price:{code}`, `prices:last_update`) - db=4: news-collector (`news:naver:{hash}`) - db=5: ta-engine (`ta:{code}`) --- ### `/home/kyu/trading/aux-signal/main.py` 보조 데이터 수집. 포트 8282. | 엔드포인트 | 역할 | |-----------|------| | `POST /collect/naver` | 네이버 integration API → 컨센서스 + 기관/외국인 일별 수급 | | `POST /collect/macro` | 한국은행 ECOS → USD/KRW 환율, 국고채 10년 | | `GET /consensus/{code}` | 종목 컨센서스 (목표가/투자의견) | | `GET /flow/{code}` | 기관/외국인 일별 순매수 | | `GET /macro/latest` | 최신 매크로 지표 | | `GET /macro/{indicator}` | 지표별 시계열 | 스케줄: 영업일 18:00 네이버 수급/컨센서스, 08:30 ECOS 매크로. --- ### `/home/kyu/trading/us-market/main.py` 미국증시 동조 시그널. 포트 8383. | 엔드포인트 | 역할 | |-----------|------| | `POST /collect` | Finnhub/AlphaVantage → 미국 ETF·개별주 가격 | | `POST /collect/backfill` | 신규 페어용 과거 데이터 백필 | | `POST /collect/yfinance-backfill` | yfinance 폴백 백필 | | `POST /signal/calculate` | 페어 회귀로 한국 종목별 미증시 시그널 산출 | | `GET /signal/{kr_code}` | 한국 종목 KR 코드별 시그널 | | `GET /signal/latest` | 전체 종목 최신 시그널 | | `GET /pairs` | 등록된 KR↔US 페어 | | `POST /pairs/recalc-beta` | 60일 회귀 베타 재계산 | | `POST /pairs/discover` | KOSPI200 × S&P500 상관 자동 발굴 | | `GET /etfs` / `GET /etfs/{etf}/latest` | 섹터 ETF 현황 | **기여 점수**: - 섹터 ETF 동조 (SOXX/XBI/LIT 등 14개) → 같은 한국 섹터 ±5점 - 개별 페어 60일 회귀 베타 (NVDA↔SK하이닉스 등) → ±10점 스케줄: 매일 KST 07:30 수집, 08:00 시그널 계산. --- ### `/home/kyu/trading/graph-engine/main.py` GAT 그래프 신경망 (PyTorch). 포트 9090. | 엔드포인트 | 역할 | |-----------|------| | `POST /graph/build` | 노드/엣지 재구성 | | `POST /train` | 학습 (6mo rolling) | | `POST /predict` | 전체 추론 → `stock_scores.graph_score` | | `GET /predict/{code}` | 종목별 예측 값 | | `GET /status` | 모델/학습 상태 | - **노드**: 활성 종목, 12피처 (1d/5d/20d 수익률, vol_ratio, RSI, tech_score, ROE, 영업이익률, 부채비율, 7d 뉴스, 미증시 overnight, log_mcap) - **엣지**: ① 60일 가격 상관 |corr|≥0.4 ② 동일 섹터 ③ 뉴스 공기 ≥3회 - **스케줄**: 일요 06:00 학습 / 매일 08:30 추론 - **모델 저장**: `/mnt/nas/models/graph/` --- ### `/home/kyu/trading/telegram-bot/main.py` 텔레그램 명령 처리 봇 (python-telegram-bot). | 명령 | 역할 | |------|------| | `/start /help` | 도움말 | | `/buy /buys` | 강력매수/매수관심 톱 N | | `/sell /sells` | 매도관심/강력매도 톱 N | | `/stock <코드>` | 종목 점수·신호 요약 | | `/deep <코드>` | RAG+EXAONE 심층분석 결과 | | `/market` | 시장 요약(섹터·매크로) | 권한: `TELEGRAM_CHAT_ID` 일치 채팅만 응답. --- ## 데이터베이스 스키마 (PostgreSQL) ### `news_analysis` ``` id, title, url, source, published_at, hash(16자 UNIQUE) sentiment(호재/악재/중립), intensity(1~5), primary_stock affected_stocks(JSONB), reason, investment_action(매수관심/매도관심/관망) keywords(JSONB), stock_names(JSONB), stock_codes(JSONB) similar_count, catalyst, analyzed_at, created_at ``` ### `stock_technical` ``` stock_code(UNIQUE), stock_name, price ma5, ma20, ma60, ma120, rsi, macd, macd_signal, macd_hist bb_upper, bb_mid, bb_lower, pct_b, stoch_k, stoch_d vol_ratio, tech_score(-100~100), signal(매수/매도/관망) obv, obv_trend, vwap20, ichimoku(JSONB) ← 새 지표 signals(JSONB), targets(JSONB), analyzed_at ``` ### `dart_corps` ``` stock_code(PK), corp_code, corp_name, modify_date, is_active ※ is_active=true 필터 필수 (상장폐지 제외) ``` ### `dart_financials` ``` stock_code, corp_code, corp_name, bsns_year, reprt_code revenue, operating_profit, net_income total_assets, total_liabilities, total_equity, operating_cashflow roe, operating_margin, net_margin, debt_ratio, revenue_growth, fcf_ratio UNIQUE(stock_code, bsns_year, reprt_code) ※ F-Score는 전년 11011 사업보고서 필요 → years≥2 백필 ``` ### `stock_scores` (주요 컬럼) ``` stock_code, score_date, total_score, recommendation news_score, dart_score, technical_score, foreign_score, short_score, price_score trend_score, intrinsic_value, margin_of_safety, earnings_quality magic_score, f_score, roc_pct, earnings_yield_pct altman_z, peg, momentum_pct, beneish_score gpa_pct, g_score, amihud_illiq, market_beta ← 학술 논문 기반 4개 signals(JSONB), buy_votes, sell_votes position_size_pct, volatility_60d, market_regime_adj, sector top_reasons, UNIQUE(stock_code, score_date) ``` ### `weight_config` ``` config_date(PK), weights(JSONB), period_days, sample_size ※ 최신 row가 calculate_daily_scores에서 자동 로드 → ensemble_bonus 가중 ``` ### `pricing_model_v2` (가격예측 모델 학습 결과 저장) ``` id, model_date, segment, model_type(linear/rf/xgb), target(return_7d/30d/alpha_30d) feature_names(JSONB), feature_importance(JSONB), coef(JSONB), intercept r2_oos, ic_spearman, hit_ratio, sample_size, period_days, model_blob(bytea), created_at ※ /learn-pricing 호출 시 모델별 row 갱신, /predict-price가 model_blob 역직렬화로 추론 ``` ### `news_raw` (수집/분석 분리용 임시 저장소) ``` id, stock_code, title, url, url_hash(UNIQUE), source, published_at_text, collected_at, processed(BOOL), processed_at ※ 크롤러는 raw만 빠르게 저장 → 야간 cron(process_raw)이 batch로 LLM 분석 ``` ### 뷰 - `v_recent_signals` — 최근 24시간 호재/악재 (intensity DESC) - `v_stock_news_count` — 종목별 7일 뉴스 감성 카운트 --- ## 환경변수 키 목록 (.env) ``` POSTGRES_HOST=postgres POSTGRES_PORT=5432 POSTGRES_DB=trading_ai POSTGRES_USER=kyu REDIS_MAX_MEMORY=2gb REDIS_MAXMEMORY_POLICY=allkeys-lru OLLAMA_NUM_PARALLEL=4 (EXAONE→GPU0, bge-m3→GPU1 자동 분배) QDRANT_COLLECTION=news_vectors QDRANT_VECTOR_SIZE=1024 TELEGRAM_CHAT_ID=8690666445 KIS_IS_PAPER=true (모의투자 모드) ECOS_API_KEY=... (한국은행 매크로 — ecos.bok.or.kr/api 무료) ``` --- ## 자주 쓰는 명령어 ```bash # 전체 상태 docker compose ps # 특정 서비스 로그 (실시간) docker logs trading-news-collector --tail 50 -f docker logs trading-score-engine --tail 50 -f docker logs trading-n8n --tail 30 # 재빌드+재배포 docker compose build && docker compose up -d # PostgreSQL 쿼리 (DB명: trading_ai, user: kyu) docker exec trading-postgres psql -U kyu -d trading_ai -c "SELECT ..." # Redis source /home/kyu/trading/.env && docker exec trading-redis redis-cli -a $REDIS_PASSWORD # n8n 마이그레이션 확인 docker exec trading-postgres psql -U kyu -d trading_ai -c "SELECT COUNT(*) FROM n8n.migrations;" # 추천 종목 조회 docker exec trading-postgres psql -U kyu -d trading_ai -c " SELECT s.stock_code, d.corp_name, s.total_score, s.recommendation, s.magic_score, s.f_score, s.roc_pct, s.earnings_yield_pct FROM stock_scores s JOIN dart_corps d ON d.stock_code=s.stock_code WHERE d.is_active=true AND s.score_date=CURRENT_DATE ORDER BY s.total_score DESC LIMIT 10;" # 다년치 사업보고서 백필 (F-Score / PEG용) curl -X POST 'http://localhost:8888/collect/financials?count=3000&years=10&annual_only=true' # 섹터 정보 채우기 (dart_corps.sector) curl -X POST http://localhost:8888/collect/sectors # 점수 수동 재계산 (전체 활성종목 대상) curl -X POST http://localhost:8686/score/calculate # 백테스트 기반 공식 가중치 학습 curl -X POST 'http://localhost:8686/learn-weights?days=90' # 현재 가중치 조회 curl -s http://localhost:8686/learn-weights # 종목별 6공식 신호 확인 docker exec trading-postgres psql -U kyu -d trading_ai -c " SELECT stock_code, stock_name, total_score, signals FROM stock_scores WHERE score_date=CURRENT_DATE ORDER BY total_score DESC LIMIT 10;" ``` --- ## 주의사항 - **상장폐지 필터**: 모든 추천 쿼리에 `JOIN dart_corps d ON d.stock_code=... WHERE d.is_active=true` 필수 - **fstab**: NAS 마운트는 반드시 한 줄로 작성, `nfsvers=3` 필수 - **GPU**: Ollama 단일 컨테이너가 GPU0(EXAONE 추론)·GPU1(bge-m3 임베딩) 동시 사용. `runtime: nvidia` 필요 - **KIS**: 현재 모의투자 모드 (`KIS_IS_PAPER=true`) - **바른API**: KRX→Naver 폴백으로 ~3768 종목 로드. 형태소 실패 시 공백분리 폴백 --- ## 현재 이슈 / TODO - [ ] fstab NAS 항목이 두 줄로 분리됨 — sudo로 직접 수정 필요 - [x] n8n 워크플로우 재구성 완료 (워크플로우 16개 / 활성 14개) - [x] vLLM 제거 → Ollama 단일 운영으로 전환 (EXAONE 3.5 7.8B + bge-m3) - [x] n8n DB 마이그레이션 오류 수정 완료 (51개 마이그레이션 등록) - [x] SSL 인증서 자동 갱신 설정 완료 (certbot.timer + deploy hook → nginx reload) 도메인: al/cla/n8/pns/tr.kyleyang.co.kr, 훅: /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh --- ## Behavioral Guidelines (LLM 공통 실수 감축) Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed. Tradeoff: These guidelines bias toward caution over speed. For trivial tasks, use judgment. ### 1. Think Before Coding Don't assume. Don't hide confusion. Surface tradeoffs. Before implementing: - State your assumptions explicitly. If uncertain, ask. - If multiple interpretations exist, present them - don't pick silently. - If a simpler approach exists, say so. Push back when warranted. - If something is unclear, stop. Name what's confusing. Ask. ### 2. Simplicity First Minimum code that solves the problem. Nothing speculative. - No features beyond what was asked. - No abstractions for single-use code. - No "flexibility" or "configurability" that wasn't requested. - No error handling for impossible scenarios. - If you write 200 lines and it could be 50, rewrite it. Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify. ### 3. Surgical Changes Touch only what you must. Clean up only your own mess. When editing existing code: - Don't "improve" adjacent code, comments, or formatting. - Don't refactor things that aren't broken. - Match existing style, even if you'd do it differently. - If you notice unrelated dead code, mention it - don't delete it. When your changes create orphans: - Remove imports/variables/functions that YOUR changes made unused. - Don't remove pre-existing dead code unless asked. The test: Every changed line should trace directly to the user's request. ### 4. Goal-Driven Execution Define success criteria. Loop until verified. Transform tasks into verifiable goals: - "Add validation" → "Write tests for invalid inputs, then make them pass" - "Fix the bug" → "Write a test that reproduces it, then make it pass" - "Refactor X" → "Ensure tests pass before and after" For multi-step tasks, state a brief plan: ``` 1. [Step] → verify: [check] 2. [Step] → verify: [check] 3. [Step] → verify: [check] ``` Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification. These guidelines are working if: fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.