- ensemble.predict() 가 chronos_raw / lgbm_raw 를 함께 반환
- predict_and_store() 가 매 호출마다 3종 행 적재:
model='ensemble' (user_triggered=인자)
model='chronos' (user_triggered=FALSE, shadow)
model='lgbm' (user_triggered=FALSE, shadow)
- retrain_weekly.adjust_weights(): 최근 30일 prediction_outcomes 의
chronos vs lgbm hit_rate 로 ensemble_weights upsert
w_chronos = clamp(0.1, hr_c/(hr_c+hr_l), 0.9), w_lgbm = 1 - w_chronos
모델별 표본 < 10 이면 기본값(0.6/0.4) 유지
- API 응답에 saved_shadow_ids 추가 (TS 타입도 동기화)
- README: 동작 모델 메모 섹션을 실제 구현과 일치하도록 갱신
리뷰어 지적 3번 (ensemble_weights 가 영원히 갱신 안됨, upsert_weights 미호출) 해결.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
171 lines
7.7 KiB
Markdown
171 lines
7.7 KiB
Markdown
# stock_chart_site
|
||
|
||
개인용 주식 차트 + 단기 예측 사이트. 한국 종목 검색 → 현재 차트 표시 → "예상차트 보기" 클릭 시 Chronos+LightGBM 앙상블로 향후 1~5거래일 예측을 차트에 이어 붙임. 사용자가 한 번이라도 예측을 확인한 종목은 자동 저장해서 다음날 실제 가격과 비교 → 오차/방향성 적중률을 누적 → 앙상블 가중치를 자동 보정.
|
||
|
||
스펙 원문: `/home/claude/EJClaw/groups/stock_predictor/SPEC.md` (별도 채팅 그룹).
|
||
|
||
## 빠른 시작 (Windows + Docker Desktop + RTX 3070 Ti)
|
||
|
||
전제: Docker Desktop이 이미 설치되어 있고, GPU 사용하려면 `Settings → Resources → WSL Integration → GPU support`가 켜져 있어야 합니다.
|
||
|
||
```cmd
|
||
git clone https://git.tkrmagid.kr/tkrmagid/stock_chart_site.git
|
||
cd stock_chart_site
|
||
build.bat
|
||
```
|
||
|
||
`build.bat`이 자동으로
|
||
1. `.env.example` → `.env` 복사 (없는 경우)
|
||
2. `nvidia-smi` 로 GPU 감지 → GPU 있으면 GPU 모드, 없으면 CPU 폴백
|
||
3. `docker compose build` + `up -d`
|
||
|
||
확인:
|
||
- Web: http://localhost:3000
|
||
- Backend health: http://localhost:8000/health
|
||
- DB extensions: http://localhost:8000/health/db (`timescaledb`, `vector`, `pg_trgm` 셋 다 켜져 있어야 정상)
|
||
|
||
정지:
|
||
```cmd
|
||
docker compose down
|
||
```
|
||
|
||
## 빌드 모드 (수동)
|
||
|
||
`build.bat`을 안 쓰는 경우 `.env`가 먼저 있어야 합니다.
|
||
|
||
```bash
|
||
# .env 없으면 한 번만 복사
|
||
copy .env.example .env
|
||
|
||
# GPU 모드 (RTX 3070 Ti 등 NVIDIA GPU 사용)
|
||
docker compose -f docker-compose.yml -f docker-compose.gpu.yml up -d --build
|
||
|
||
# CPU 모드
|
||
docker compose up -d --build
|
||
```
|
||
|
||
## API 키 발급 (모두 무료)
|
||
|
||
키 없어도 pykrx 기반 일봉/뉴스 RSS 만으로 일단 동작합니다. 다음 키를 받으면 데이터 품질이 좋아집니다.
|
||
|
||
### 1) 한국투자증권 KIS OpenAPI (실시간 시세 + EOD)
|
||
|
||
1. https://apiportal.koreainvestment.com 회원가입 (한국투자증권 계좌 필요)
|
||
2. 좌측 "Open API 신청" → 모의/실전 중 하나 신청
|
||
3. 발급 완료 후 마이페이지에서 App Key, App Secret, 계좌번호 확인
|
||
4. `.env` 에 입력:
|
||
```
|
||
KIS_APP_KEY=...
|
||
KIS_APP_SECRET=...
|
||
KIS_ACCOUNT_NO=...
|
||
```
|
||
|
||
### 2) DART OpenAPI (전자공시 본문)
|
||
|
||
1. https://opendart.fss.or.kr 회원가입
|
||
2. 마이페이지 → 인증키 신청 → 즉시 발급
|
||
3. `.env` 에 입력:
|
||
```
|
||
DART_API_KEY=...
|
||
```
|
||
|
||
### 3) HuggingFace (선택, 모델 다운로드 가속)
|
||
|
||
토큰 없어도 공개 모델 (`amazon/chronos-bolt-base`, `snunlp/KR-FinBert`) 다운로드가 됩니다. 토큰이 있으면 rate limit이 완화되고 첫 다운로드가 빨라집니다.
|
||
|
||
1. https://huggingface.co 회원가입
|
||
2. https://huggingface.co/settings/tokens 에서 Read 토큰 생성
|
||
3. `.env` 에 입력:
|
||
```
|
||
HUGGINGFACE_TOKEN=hf_...
|
||
```
|
||
|
||
## 학습/배치 대상 시드 종목 (10개)
|
||
|
||
검색은 KRX 전 종목을 대상으로 동작하지만, 일별 배치/재학습/메트릭 누적은 아래 10개를 우선 대상으로 합니다. 운영하면서 더 의미있는 종목이 보이면 교체합니다.
|
||
|
||
| 분류 | 종목 | 코드 |
|
||
|---|---|---|
|
||
| 대형 인기주 | 삼성전자 | 005930 |
|
||
| 대형 인기주 | SK하이닉스 | 000660 |
|
||
| 변동성 큰 종목 | 에코프로비엠 | 247540 |
|
||
| 변동성 큰 종목 | 한미반도체 | 042700 |
|
||
| 최근 인기 테마 | 두산에너빌리티 | 034020 |
|
||
| 최근 인기 테마 | 한화에어로스페이스 | 012450 |
|
||
| 최근 인기 테마 | HD현대중공업 | 329180 |
|
||
| 전통 IT/플랫폼 | NAVER | 035420 |
|
||
| 방어주/저변동 | KT&G | 033780 |
|
||
| 방어주/저변동 | 한국가스공사 | 036460 |
|
||
|
||
## 디렉토리 구조
|
||
|
||
```
|
||
stock_chart_site/
|
||
├── build.bat # Windows: 빌드+기동
|
||
├── docker-compose.yml # db + backend + web
|
||
├── docker-compose.gpu.yml # GPU 오버레이 (NVIDIA reservation)
|
||
├── .env.example # 환경 변수 템플릿
|
||
├── backend/
|
||
│ ├── Dockerfile # CUDA 12.1 + Python 3.11
|
||
│ ├── pyproject.toml
|
||
│ └── app/
|
||
│ ├── main.py # FastAPI entry
|
||
│ ├── config.py # env settings
|
||
│ ├── db/
|
||
│ │ ├── connection.py
|
||
│ │ └── migrations/
|
||
│ │ └── 001_init.sql # DB 스키마
|
||
│ ├── fetch/ # KIS / pykrx / DART / 뉴스 (Phase 1)
|
||
│ ├── models/ # Chronos / LightGBM / KR-FinBERT (Phase 2~4)
|
||
│ ├── pipelines/ # daily_batch / inference / retrain (Phase 1, 4)
|
||
│ └── api/ # FastAPI 라우터 (Phase 5)
|
||
└── web/
|
||
├── Dockerfile
|
||
├── package.json
|
||
└── app/
|
||
├── layout.tsx
|
||
└── page.tsx # 검색 + 차트 UI (Phase 6)
|
||
```
|
||
|
||
## 진행 계획
|
||
|
||
- [x] Phase 0 — 스캐폴드: Docker 환경 + DB 스키마 + FastAPI/Next.js + build.bat
|
||
- [x] Phase 1a — pykrx 데이터 파이프: 일봉/외인기관/지수 + DART + 뉴스 RSS + 거시
|
||
- [x] Phase 1b — KIS read-only EOD (스모크 통과)
|
||
- [x] Phase 2 — KR-FinBERT 감성 점수 + 일별 집계 뷰
|
||
- [x] Phase 3 — Chronos zero-shot 예측 어댑터 + 피처 빌더
|
||
- [x] Phase 4 — LightGBM walk-forward + ensemble + 매칭/재학습 잡
|
||
- [x] Phase 5 — FastAPI 엔드포인트 (검색/차트/예측/메트릭/뉴스)
|
||
- [x] Phase 6 — Next.js UI (검색 + 현재 차트 + 예상차트 overlay)
|
||
- [ ] Phase 7 (옵션) — 백테스트 페이지 + Chronos/LGBM 단독 shadow 예측
|
||
|
||
### API 엔드포인트 (요약)
|
||
|
||
| 메서드 | 경로 | 설명 |
|
||
|---|---|---|
|
||
| GET | `/health`, `/health/db`, `/health/keys` | 헬스/외부키 ping |
|
||
| POST | `/api/refresh/{code}?lookback_days=N` | 수동 갱신 |
|
||
| GET | `/api/symbols/search?q=&seed_only=` | 종목 검색 (trigram + prefix) |
|
||
| GET | `/api/symbols/{code}` | 종목 메타 |
|
||
| GET | `/api/chart/{code}?days=N` | OHLCV + 감성 + 외인기관거래대금 |
|
||
| POST | `/api/predict/{code}?horizons=1,3,5` | on-demand 앙상블 예측 (user_triggered) |
|
||
| GET | `/api/predict/{code}/latest` | 최신 base_date 예측 묶음 (UI overlay) |
|
||
| GET | `/api/metrics/{code}?window_days=N` | 종목 hit_rate / mae |
|
||
| GET | `/api/news/{code}?limit=N&source=` | 최근 뉴스/공시 + 감성 |
|
||
|
||
## 동작 모델 메모
|
||
|
||
- 예측 트리거: 사용자가 "예상차트 보기" 누른 종목에 대해 즉시 inference. 결과는 세 종류 행으로 적재:
|
||
- `model='ensemble'` (user_triggered=TRUE) — UI 가 표시하는 최종 예측
|
||
- `model='chronos'` (user_triggered=FALSE, shadow) — Chronos 단독 성능 추적용
|
||
- `model='lgbm'` (user_triggered=FALSE, shadow) — LGBM 단독 성능 추적용
|
||
- 매칭 배치: 평일 16:30 KST. `target_date <= today AND outcomes 미존재` 인 모든 행에 대해 `target_date` 이상 `today` 이하 범위의 **최초 거래일 종가**를 actual_close 로 사용 → 주말/공휴일 자동 이월. shadow 행도 함께 매칭됨.
|
||
- 주간 02:00 (일요일): 시드 10종목 × horizons LGBM 재학습. 최근 30일 prediction_outcomes 의 chronos vs lgbm hit_rate 비교 → `w_chronos = clamp(0.1, hr_c/(hr_c+hr_l), 0.9)` 공식으로 `ensemble_weights` upsert. 모델별 표본이 10 미만이면 기본값(0.6/0.4) 유지.
|
||
- DB bootstrap: 백엔드 첫 부팅 시 lifespan 에서 idempotent migration + symbols 시드(비어있을 때만 pykrx 전 종목 적재) 자동 수행. `BOOTSTRAP_DISABLED=1` 로 비활성화 가능.
|
||
|
||
## 안전/한계
|
||
|
||
- 본인 1인 개인용. 외부 공개/상업 사용 안 함.
|
||
- 자동매매 연결 없음. 예측은 참고용.
|
||
- 백테스트 정확도와 라이브 정확도는 다르며 단기 방향성 모델의 라이브 상한은 보통 55~60%.
|