Python 기반 한국주식 자동매매 시스템. 일중 데이트레이딩 가정 폐기(ADR-0021/0023) → 일봉 RSI 평균회귀(RSIMRStrategy) 1차 채택 후보 확정. Phase 2 PASS (2026-05-03 공식 선언) · Phase 3 진행 중 — main.py 모의투자 무중단 운영 착수.
KOSPI 200 대형주를 대상으로 일봉 RSI 평균회귀(RSIMRStrategy) 전략을 자동 실행하는 시스템을 구축합니다. ADR-0021/0023 결정으로 일중 데이트레이딩 가정을 폐기하고 일/월 단위 평균회귀 전략을 채택했습니다. ORB·VWAP-MR·Gap-Reversal·Cross-sectional 모멘텀·저변동성·Golden Cross·DCA 전략 코드는 baseline/회귀 검증용으로 보존됩니다. 초기에는 한국투자증권 모의투자 환경에서 검증하고, 2주 이상 무사고 운영이 확인된 뒤에만 소액(100~200만원) 실전으로 전환합니다.
왜 한국투자증권인가: 토스증권은 개인 개발자용 Open API를 제공하지 않습니다(2026-04 기준). 자동매매가 가능하면서 Mac/Python 환경에서 바로 사용할 수 있는 REST API + 모의투자 지원 증권사가 한국투자증권(KIS Developers)입니다.
- 본 프로젝트의 목표는 "수익 극대화"가 아니라 "손실을 최소화하며 데이터로 검증한 전략을 규율 있게 실행" 하는 것입니다.
- 데이트레이딩은 통계적으로 개인 투자자의 70~90%가 손실을 봅니다. 거래세(0.18%) + 수수료 + 슬리피지가 마진을 잠식합니다.
- 실전 투입 전 모의투자 → 백테스팅 → 페이퍼트레이딩 단계를 반드시 거칩니다.
RSI 평균회귀 (RSIMRStrategy) — long-only, 1차 채택 후보 (ADR-0023, 2026-05-02 확정)
- KOSPI 200 cross-sectional 일봉 RSI(14) 기반
- 과매도 RSI ≤ 30 진입 (EOD 15:35 KST 일봉 신호 생성 → 다음 영업일 09:00 후 첫 step 에서 시초가 시장가 매수)
- 과매수 RSI ≥ 70 청산 (익절 고정가 없음, RSI 조건 도달 시 청산)
- stop_loss -3% (분봉 저가 도달 시 즉시 청산 — 분봉 stop_loss 가드, ADR-0025 PR3)
- 동시 최대 10종목, 15:00 강제청산 미사용 (일봉 전략 특성상, ADR-0025)
ORB·VWAP-MR·Gap-Reversal·Cross-sectional 모멘텀·저변동성·Golden Cross·DCA 전략 코드는 baseline/회귀 검증용으로 보존.
| 항목 | 값 | 적용 대상 |
|---|---|---|
| 종목당 진입 금액 | 세션 자본의 10% | RiskConfig.position_pct |
| 동시 보유 종목 | 최대 10종목 | RiskConfig.max_positions |
| 종목당 손절 | -3% (stop_loss_pct) | RSIMRConfig |
| 일일 손실 한도 | 자본의 -2% 도달 시 당일 매매 중단 | RiskConfig.daily_loss_limit_pct |
| 일일 최대 진입 | 5회 | RiskConfig.daily_max_entries |
| 강제청산 | 운영 미사용 (일봉 전략, ORB 폐기) | — |
ORB 시절 기본값(종목당 20%, 동시 3종목, 손절 -1.5%, 익절 +3.0%, 일일 10회)은 ADR-0025 로 대체됨. 고정 익절 미사용 — RSI 과매수 도달(70) 시 청산.
- 언어: Python 3.12+
- 의존성 관리:
uv - 브로커 API:
python-kis 2.x(KIS Developers REST/WebSocket) - 시장 데이터:
pykrx(KRX 공식 과거 OHLCV),pyyaml(유니버스 설정 파일 로드) - 백테스팅: 자체 시뮬레이션 루프 (
backtest/engine.py) —backtesting.py라이브러리는 다중종목·포트폴리오 게이팅 표현 불가 + AGPL 라이센스 부담으로 폐기 (2026-04-20) - 스케줄링:
APScheduler - 알림:
python-telegram-bot 22.x - 로깅:
loguru - 설정 검증:
pydantic-settings - 저장소: SQLite (MVP)
- 테스트:
pytest,pytest-mock - 포매터/린터:
ruff(lint + format,pre-commit훅 적용). black 폐기 (ADR-0026, 2026-05-03). - CI: GitHub Actions — PR 및 main push 마다 ruff·pytest 자동 실행. main 머지는 CI job
Lint, format, test통과 필수.
| Phase | 기간 | 목표 |
|---|---|---|
| 0 | 2~3일 | 한투 계좌 개설, KIS 모의 키 발급, 텔레그램 봇 세팅, 레포 초기화 |
| 1 | 4~5일 | KIS 클라이언트(토큰/주문/조회) + 데이터 파이프라인(pykrx + 분봉 폴링) |
| 2 | 5~7일 | 전략 엔진·리스크 매니저·백테스트 엔진 구현 → 복구 로드맵(Step A~F) 완주 → RSI MR 1차 채택 후보 확정 (ADR-0023) · PASS (2026-05-03) |
| 3 | 5~7일 | 모의투자 자동 실행 루프, 텔레그램 알림, 2주 이상 무사고 운영 ← 진행 중 |
| 4 | 상시 | 소액 실전 전환 (초기 50만원 한도부터), 주간 회고 |
| 5 | 지속 | 2번째 전략 A/B, 필요 시 클라우드 VPS 이전, 대시보드 |
Phase 1 PASS · Phase 2 PASS (2026-05-03 공식 선언) · Phase 3 진행 중 — ADR-0023 C1~C4 추가 검증 전원 통과 후 Phase 3 착수. main.py 모의투자 무중단 운영 재허가. Phase 3 PR5(ADR-0027) 까지 완료 — EOD 일봉 트리거 결손 해소. pytest 2266 passed, 4 skipped.
Phase 1 PASS (코드·테스트 레벨) (2026-04-19 선언). Phase 0 환경 준비 완료. broker(KisClient + rate_limiter) + data(historical + universe + realtime) 모두 완료. paper 주문 + live 시세 하이브리드 키 정책 도입: KIS paper 도메인에 시세 API가 없어 RealtimeDataStore는 별도 실전 APP_KEY로 실전 도메인을 호출하며, 실전 키 PyKis 인스턴스에는 install_order_block_guard를 설치해 주문 경로를 구조적으로 차단한다.
Phase 2 진행 중 — ORB 전략 엔진 + 리스크 매니저 + 백테스트 엔진 코어 + CSV 분봉 어댑터 + 파라미터 민감도 그리드 + backtest.py CLI + KIS 과거 분봉 API 어댑터 + 백필 CLI 완료 (2026-04-20~22). strategy/ + risk/ + backtest/ + data/kis_minute_bars.py 완료. scripts/backtest.py·scripts/sensitivity.py 에 --loader={csv,kis} 옵션 추가. PASS 판정은 ADR-0019 세 게이트(MDD > -15% · 승률 × 손익비 > 1.0 · 연환산 샤프 > 0) 전부 충족 + walk-forward 검증 통과 조건.
Phase 2 1차 백테스트 FAIL (2026-04-24, ADR-0019). 2026-04-24 에 1년치 KIS 분봉 백필 완료 (199 심볼, 2.78 GB) + uv run python scripts/backtest.py --loader=kis --from 2025-04-22 --to 2026-04-21 1회 실행. 결과: MDD -51.36%, 총수익률 -50.05%, 샤프 -6.81, 승률 31.35%, 손익비 1.28, 트레이드당 기대값 ≈ -0.28R. Phase 2 PASS 기준 3.4배 초과 미달. 사용자 정책: 수익률 확인 전까지 Phase 3 진입 금지 (ADR-0019). 복구 5단계 로드맵 A(민감도 그리드) → B(비용 가정 재검정) → C(유니버스 필터) → D(전략 파라미터 구조 변경) → E(전략 교체) 순차 게이팅.
ADR-0019 복구 로드맵 Step A FAIL (2026-04-25). 32 조합 민감도 그리드 실행 (28/32 완료 — 4 조합 미실행, 결과 뒤집힐 가능성 0% 로 즉시 종결). 세 게이트(MDD > -15% · 승률×손익비 > 1.0 · 샤프 > 0) 동시 통과 조합 0 / 28. 최고 수익률 -40.91%, 전 조합 샤프 음수.
Step B 완료 (2026-04-29) — 3 거래일 장중 실 호가 수집 (331,530 샘플). 전체 중앙값 스프레드 0.1305% — 현행 가정 0.1% 대비 1.3× 이지만 사전 기준(0.05~0.2%) 내. ADR-0006 슬리피지 가정 0.1% 유지 결정. costs.py 변경 없음.
Step C FAIL (2026-04-30, Issue #76) — Top 50 / Top 100 유동성 서브셋 백테스트 실행 (--loader=kis, 2025-04-22 ~ 2026-04-21). 두 서브셋 모두 ADR-0019 세 게이트 전원 FAIL (Top 50: MDD -44.70%, 샤프 -6.68, 승률×손익비 0.377 / Top 100: MDD -50.13%, 샤프 -7.74, 승률×손익비 0.383). Step D (전략 파라미터 구조 변경) 진입.
Step D1 FAIL (2026-05-01) — OR 윈도 스터디. step_d1_grid 48 조합 × Top 50 / Top 100 = 96 런 전원 ADR-0019 게이트 미통과. 최선 조합: Top 50 MDD -37.18% / Top 100 MDD -35.98%. Step C 대비 MDD 소폭 개선이나 PASS 기준 -15% 까지 여전히 21~23%p 격차. 상세: docs/runbooks/step_d1_or_window_2026-05-01.md.
Step D2 FAIL (2026-05-01) — force_close_at 스터디. step_d2_grid 48 조합 × Top 50 / Top 100 = 96 런 전원 ADR-0019 게이트 미통과. 최선 조합 (force_close_at=15:20, stop=2.5%, take=5.0%): Top 50 MDD -35.02% / Top 100 MDD -37.56%. D1 vs D2 거의 동급 — stop=2.5%/take=5.0% 가 본질 개선 벡터.
Step E FAIL (2026-05-01, ADR-0021) — PR1~PR4 코드 산출물 완료 후 4 런 실행. VWAP-MR(Top 50 MDD -49.09% / Top 100 MDD -50.11%) · Gap-Reversal(Top 50 MDD -10.19% / Top 100 MDD -19.99%) 전 4 런 ADR-0019 세 게이트 동시 통과 0. VWAP-MR · Gap-Reversal 두 후보 폐기. 코드 산출물 보존.
Step F 진입 (2026-05-01, ADR-0021·ADR-0022) — 가설 풀 확장. 일중 데이트레이딩 가정 폐기 + 일/월 단위 전략 + DCA baseline 비교. ADR-0022 게이트 적용 (MDD > -25% · DCA 대비 양의 알파 · 연환산 샤프 > 0.3). PR6 종합 판정 후 정본은 docs/runbooks/step_f_summary_2026-05-02.md + docs/adr/0023-rsi-mr-strategy-adoption-conditional.md. 상세 설계와 각 Phase의 PASS 기준, 비용·위험 분석은 plan.md에 있습니다.
Step F PR1 (F1 DCA baseline) 완료 — PASS (2026-05-02). KODEX 200(069500) 월 정액 매수 DCA 전략 1년 백테스트. MDD -12.92% · 연환산 Sharpe 2.2683 · 총수익률 +51.50% mark-to-market. ADR-0022 게이트 1·3 PASS (게이트 2 N/A — 자기 자신 baseline). 후속 PR 의 DCA 대비 알파 비교 기준 확정. 런북: docs/runbooks/step_f_dca_baseline_2026-05-02.md.
Step F PR2 (F2 Golden Cross) 완료 — PASS (2026-05-02). KODEX 200(069500) 200일 SMA 추세 추종 전략 백테스트 (2024-06-01 ~ 2026-04-21). MDD -20.52% · 연환산 Sharpe 2.2753 · 총수익률 +182.36% mark-to-market. ADR-0022 게이트 3종 PASS (DCA 대비 알파 +130.86%p). 단, trades=1 (통계 신뢰도 낮음) + 069500 가격 급등 데이터 plausibility 검증 필요. 런북: docs/runbooks/step_f_golden_cross_2026-05-02.md.
Step F PR3 (F3 Cross-sectional 모멘텀) 완료 — FAIL (2026-05-02). KOSPI 200 캐시 101종목 대상 cross-sectional 모멘텀 전략 백테스트 (2025-04-01 ~ 2026-04-21, lookback 6개월, top-N 10). MDD -7.70% · 연환산 Sharpe 0.9910 · 총수익률 +11.22% mark-to-market. ADR-0022 게이트 1·3 PASS, 게이트 2(DCA 대비 알파 +11.22% - +48.18% = -36.96%p) FAIL → 종합 FAIL. 주요 caveat: 유니버스 부분집합(101/199) + lookback 단축(12개월 표준 → 6개월) + KOSPI 강세장 인덱스 베타 압도. 런북: docs/runbooks/step_f_momentum_2026-05-02.md.
Step F PR4 (F4 Low Volatility) 완료 — FAIL (2026-05-02). KOSPI 200 캐시 101종목 대상 저변동성 전략 백테스트 (2025-04-01 ~ 2026-04-21, lookback_days=60, top-N 10, rebalance_month_interval=3). MDD -9.62% · 연환산 Sharpe 1.1713 · 총수익률 +15.87% mark-to-market. ADR-0022 게이트 1·3 PASS, 게이트 2(DCA 대비 알파 +15.87% - +48.18% = -32.31%p) FAIL → 종합 FAIL. 런북: docs/runbooks/step_f_low_volatility_2026-05-02.md.
Step F PR5 (F5 RSI 평균회귀) 완료 — PASS (2026-05-02). KOSPI 200 캐시 101종목 대상 일봉 RSI(14) 평균회귀 전략 백테스트 (2025-04-01 ~ 2026-04-21, 과매도 30, 과매수 70). MDD -6.40% · 연환산 Sharpe 2.4723 · 총수익률 +56.31% mark-to-market. ADR-0022 게이트 3종 전원 PASS (DCA 대비 알파 +8.13%p, trades=175 — Step F 전체에서 통계적으로 가장 신뢰도 높은 알파 확인). 런북: docs/runbooks/step_f_rsi_mr_2026-05-02.md.
Step F PR6 (종합 판정 + ADR-0023) 완료 (2026-05-02). 5 가설 비교 표 + 시나리오 A 판정 → F5 RSI 평균회귀 (RSIMRStrategy) 를 1차 채택 후보로 확정. PR2 Golden Cross 는 단일 trade caveat 로 채택 보류 (코드 보존). PR3 모멘텀·PR4 저변동성은 본 평가 환경 한계 인정으로 후보 제외 (코드 보존). Phase 3 (모의투자 무중단 운영) 진입은 ADR-0023 의 4 추가 검증 전부 통과 후로 게이팅. 종합 런북: docs/runbooks/step_f_summary_2026-05-02.md. ADR: docs/adr/0023-rsi-mr-strategy-adoption-conditional.md.
ADR-0023 C1 통과 (2026-05-02). universe 199 종목 일봉 백필 완료 (2024-04-01~2026-04-21) + PR5 RSI MR 재평가: MDD -8.17% · Sharpe 2.2966 · 총수익률 +63.44% · DCA 알파 +15.26%p · trades=177. ADR-0022 게이트 3종 전원 PASS. 원본 PR5 의 universe 부분집합 편향 caveat 해소. 런북: docs/runbooks/c1_universe_full_backfill_2026-05-02.md.
ADR-0023 C2 통과 (2026-05-02). walk-forward 본 구현 (scripts/walk_forward_rsi_mr.py + backtest/walk_forward.py) + 다년 캐시 (2024-04-01~2026-04-21) 분할 평가. step6 (2 windows) + step3 (3 windows) 모두 ADR-0022 게이트 + degradation ≤ 0.3 (ADR-0024) 통과. 런북: docs/runbooks/c2_walk_forward_rsi_mr_2026-05-02.md.
ADR-0023 C3 통과 (2026-05-03). scripts/verify_069500_adjusted.py 진단 CLI + data/c3_verify_069500.json raw 결과. data/stock_agent.db 캐시 458 행 = pykrx adjusted=True 458 행 close 완전 일치 (Stage 3 diff 0). ETF/KOSPI 200 비율 점프 0건 (Stage 2). Google Finance · Wikipedia KOSPI 200 cross-check 정합 (Stage 4). pykrx 일봉 캐시는 수정주가 데이터로 확정 — PR1~PR5 절대 수익률은 한국 KOSPI 200 강세장 macro 의 결과. 런북: docs/runbooks/c3_069500_adjusted_plausibility_2026-05-03.md.
ADR-0023 C4 통과 (2026-05-03). RSI MR sensitivity grid 96 조합 (rsi_period 3 × oversold 2 × overbought 2 × stop_loss 4 × max_positions 2). DCA baseline +48.18% 대비 64/96 (66.67%) all_gates_pass. 현행 14/30/70/0.03/10 PASS, 현행 인접 7/8 (87.5%). 게이트 1·3 100% PASS, 게이트 2 만 32 FAIL. Phase 3 진입 게이트 판정 PASS. 런북: docs/runbooks/c4_rsi_mr_sensitivity_2026-05-03.md.
Phase 2 PASS 공식 선언 (2026-05-03). ADR-0023 의 C1~C4 추가 검증 전원 통과 → Phase 3 (모의투자 무중단 운영) 착수 재허가. main.py 모의투자 운영 가능 상태. 상세: docs/adr/0023-rsi-mr-strategy-adoption-conditional.md.
Phase 3 PR2 완료 (2026-05-03). main.py 전략 wiring ORBStrategy → RSIMRStrategy 교체 + RiskConfig ADR-0025 확정값 명시 주입. executor.py strategy 매개변수 타입을 Strategy Protocol 로 확장 + restore_session ORB/일봉 전략 분기 추가. ADR-0025 리스크 한도 운영 적용. pytest 2221 passed, 4 skipped.
Phase 3 PR3 완료 (2026-05-03). main.py _install_jobs 에 RSI MR 모드 판정 가드 추가 — RSI MR 전략일 때 15:00 force_close cron 미등록(ADR-0025, 일봉 전략 강제청산 부적합). executor.py 분봉 stop_loss 가드 추가 — _OpenLot.stop_price 보존 + bar.low ≤ stop_price 도달 시 strategy.on_bar 없이 즉시 ExitSignal(reason="stop_loss") 처리. Executor.strategy 공개 프로퍼티 신설. pytest 2231 passed, 4 skipped (+10).
Phase 3 PR4 완료 (2026-05-03). KIS WebSocket lazy subscribe 도입 — ORB 시절 main.py 가 universe 199 종목 전부를 사전 구독하던 루프(for ticker in universe.tickers: realtime_store.subscribe(ticker)) 제거. BarSource Protocol 에 subscribe/unsubscribe 추가, Executor 가 진입 체결 시 동적 구독·청산 체결 시 구독 해제·restore_session 재기동 시 재구독. 시작 시 KIS WebSocket 동시 구독 0, 운영 중 최대 10(RSI MR max_positions=10). pytest 2242 passed, 4 skipped (+11).
Phase 3 PR5 완료 (2026-05-04, ADR-0027). EOD 일봉 트리거 + pending signals queue 도입 — 운영 첫날(2026-05-04) executor.entry.filled 0건 / step processed=0 결함 확인. 근본 원인: main.py cron 에 EOD 일봉 트리거 미등록. Executor.enqueue_pending_signals 신규 공개 메서드 추가 + _pending_signals buffer flush 로직 도입. main.py _on_eod_signal 콜백(15:35 KST) 신규 추가 — universe 일봉 fetch → strategy.on_bar 호출 → 시그널 큐인. 다음 영업일 09:00 후 첫 step() 에서 buffer flush → 시초가 시장가 주문. Runtime 필드 9→10 (historical_store 추가). RSI MR 모드 cron 3→4종. pytest 2266 passed, 4 skipped (+24).
Phase 3 착수 전제 통과 (2026-04-21). 실전 시세 전용 APP_KEY 3종 발급·IP 화이트리스트 등록·평일 장중 healthcheck.py 4종 그린(WebSocket 체결 수신 OK) 완료.
Phase 3 첫 산출물 — Executor (코드·테스트 레벨) 완료 (2026-04-21). execution/ 패키지 신설 — Executor + Protocol 3종(OrderSubmitter/BalanceProvider/BarSource) + 어댑터 3종(LiveOrderSubmitter/LiveBalanceProvider/DryRunOrderSubmitter) + StepReport/ReconcileReport DTO. pytest 605건 green.
Phase 3 두 번째 산출물 — main.py + APScheduler 통합 (코드·테스트 레벨) 완료 (2026-04-21). src/stock_agent/main.py 신설 — BlockingScheduler + 4종 cron job(09:00 session_start·매분 step·15:00 force_close·15:30 daily_report, 평일 한정) + --dry-run CLI 플래그(KIS 주문 접촉 0). pytest 681건 green.
Phase 3 세 번째 산출물 — monitor/notifier.py (텔레그램 알림, 코드·테스트 레벨) 완료 (2026-04-21). src/stock_agent/monitor/ 패키지 신설 — Notifier Protocol + TelegramNotifier + NullNotifier + ErrorEvent/DailySummary DTO. 진입·청산·에러·일일 요약 4종 텔레그램 알림. 전송 실패 silent fail + 연속 실패 경보. pytest 778건 green. 의존성 추가 없음.
Phase 3 네 번째 산출물 — storage/db.py (SQLite 원장, 코드·테스트 레벨) 완료 (2026-04-22). src/stock_agent/storage/ 패키지 신설 — TradingRecorder Protocol + SqliteTradingRecorder + NullTradingRecorder + StorageError. 주문·체결·일일 PnL을 data/trading.db에 append-only 기록. 기록 실패 silent fail + 연속 실패 경보. 의존성 추가 없음(stdlib sqlite3 전용).
Phase 3 다섯 번째 산출물 — broker 체결조회 + 부분체결 정책 (코드·테스트 레벨) 완료 (2026-04-22, ADR-0015). KisClient.cancel_order 신설 + PendingOrder.qty_filled 추가. Executor._resolve_fill 이 타임아웃 시 cancel_order 호출 + 부분/0 체결 수습. 진입 부분체결 → 실체결 수량만 원장 기록. 청산 부분/0 체결 → ExecutorError 승격. 의존성 추가 없음. Phase 3 코드 산출물 전부 완료. Phase 3 PASS 선언은 모의투자 환경 연속 10영업일 무중단 운영 후.
운영 주의: KOSPI 200 구성종목은 config/universe.yaml에 수동 관리합니다. KRX KOSPI 200 정기변경(연 2회 — 매년 6월·12월의 선물·옵션 동시만기일 익영업일 기준)에 맞춰 운영자가 직접 갱신해야 합니다. 현재 KRX 정보데이터시스템 [11006] 기준 199/200 반영(2026-04-17 조회, 임시 가상 코드 1건 제외). 정식 티커 발급 후 다음 갱신에 추가 예정.
- 한국투자증권 비대면 계좌 개설
- KIS Developers 가입 및 모의투자 APP_KEY / APP_SECRET 발급
- 텔레그램 봇 생성(@BotFather) 및 chat_id 확보
- 레포 초기화(
uv init),.env.example작성 -
scripts/healthcheck.py구현 — 모의 잔고 조회 + 텔레그램 알림 수신 확인
현재 존재하는 파일 (Phase 3 PR5 완료 기준):
stock-agent/
├── .github/workflows/
│ └── ci.yml # PR·main push 시 ruff/pytest 자동 실행
├── .python-version # 3.12
├── pyproject.toml # uv 기반, ruff/pytest 설정 포함
├── uv.lock # 패키지 잠금 (pykrx 1.2.7, pyyaml 6.0.3 포함)
├── .pre-commit-config.yaml # ruff (lint + format), 기본 훅
├── .env.example # KIS·텔레그램 키 placeholder (.env는 .gitignore)
├── .gitignore
├── README.md
├── plan.md
├── config/
│ └── universe.yaml # KOSPI 200 종목코드 (수동 관리, 연 2회 정기변경)
├── src/stock_agent/
│ ├── __init__.py
│ ├── config.py # pydantic-settings Settings + get_settings() 캐시
│ ├── broker/
│ │ ├── __init__.py
│ │ ├── kis_client.py # KisClient — 토큰 관리, 잔고/주문/조회 DTO
│ │ ├── rate_limiter.py # OrderRateLimiter — 주문 경로 전용 (2 req/s, 350ms)
│ │ └── CLAUDE.md # 모듈 세부 문서
│ └── data/
│ ├── __init__.py # HistoricalDataStore, HistoricalDataError, DailyBar, KospiUniverse, UniverseLoadError, load_kospi200_universe, RealtimeDataStore, TickQuote, MinuteBar, RealtimeDataError, MinuteCsvBarLoader, MinuteCsvLoadError, KisMinuteBarLoader, KisMinuteBarLoadError, SpreadSample, SpreadSampleCollector, SpreadSampleCollectorError export
│ ├── historical.py # pykrx 일봉 SQLite 캐시 (fetch_daily_ohlcv 전용, 스키마 v3)
│ ├── universe.py # KOSPI 200 유니버스 YAML 로더 (load_kospi200_universe)
│ ├── realtime.py # 실시간 시세 (RealtimeDataStore — WebSocket 우선 + REST 폴링 fallback)
│ ├── minute_csv.py # CSV 과거 분봉 어댑터 (MinuteCsvBarLoader)
│ ├── kis_minute_bars.py # KIS API 과거 분봉 어댑터 (KisMinuteBarLoader, SQLite 캐시 data/minute_bars.db)
│ ├── spread_samples.py # KIS 호가 스프레드 스냅샷 수집기 (SpreadSampleCollector, Step B 인프라)
│ ├── calendar.py # 공휴일 캘린더 (BusinessDayCalendar, YamlBusinessDayCalendar, HolidayCalendar, load_kospi_holidays — ADR-0018)
│ ├── daily_bar_loader.py # 일봉 로더 어댑터 (DailyBarLoader, DailyBarSource — __init__.py 미재노출, 직접 import)
│ └── CLAUDE.md # 모듈 세부 문서
│ ├── strategy/
│ │ ├── __init__.py # EntrySignal, ExitReason, ExitSignal, GapReversalConfig, GapReversalStrategy, ORBStrategy, Signal, Strategy, StrategyConfig, StrategyError, VWAPMRConfig, VWAPMRStrategy export
│ │ ├── base.py # Strategy Protocol, EntrySignal/ExitSignal DTO, ExitReason Literal, KST 상수
│ │ ├── orb.py # ORBStrategy 상태 머신 + StrategyConfig (frozen dataclass)
│ │ ├── vwap_mr.py # VWAPMRStrategy + VWAPMRConfig (Step E PR2)
│ │ ├── gap_reversal.py # GapReversalStrategy + GapReversalConfig (Step E PR3)
│ │ ├── factory.py # build_strategy_factory + STRATEGY_CHOICES (Step E PR4)
│ │ ├── dca.py # DCAStrategy + DCAConfig (Step F PR1)
│ │ ├── golden_cross.py # GoldenCrossStrategy + GoldenCrossConfig (Step F PR2)
│ │ ├── momentum.py # MomentumStrategy + MomentumConfig (Step F PR3)
│ │ ├── low_volatility.py # LowVolStrategy + LowVolConfig (Step F PR4)
│ │ ├── rsi_mr.py # RSIMRStrategy + RSIMRConfig (Step F PR5)
│ │ └── CLAUDE.md # 모듈 세부 문서
│ ├── risk/
│ │ ├── __init__.py # RiskManager, RiskConfig, RiskDecision, PositionRecord, RejectReason, RiskManagerError export
│ │ ├── manager.py # RiskManager — 포지션 사이징·진입 게이팅·서킷브레이커
│ │ └── CLAUDE.md # 모듈 세부 문서
│ ├── backtest/
│ │ ├── __init__.py # BacktestEngine, BacktestConfig, BacktestResult, BacktestMetrics, TradeRecord, DailyEquity, BarLoader, InMemoryBarLoader export
│ │ ├── engine.py # BacktestEngine — 자체 시뮬레이션 루프
│ │ ├── costs.py # 슬리피지·수수료·거래세 순수 함수
│ │ ├── metrics.py # 총수익률·MDD·샤프·승률·평균손익비·일평균거래수
│ │ ├── loader.py # BarLoader Protocol + InMemoryBarLoader
│ │ ├── sensitivity.py # 파라미터 민감도 그리드
│ │ ├── prev_close.py # DailyBarPrevCloseProvider (Step E Stage 2 — GapReversalStrategy 전일 종가 주입)
│ │ ├── dca.py # DCABaselineConfig + compute_dca_baseline (Step F PR1)
│ │ ├── golden_cross.py # GoldenCrossBaselineConfig + compute_golden_cross_baseline (Step F PR2)
│ │ ├── momentum.py # MomentumBaselineConfig + compute_momentum_baseline (Step F PR3)
│ │ ├── low_volatility.py # LowVolBaselineConfig + compute_low_volatility_baseline (Step F PR4)
│ │ ├── rsi_mr.py # RSIMRBaselineConfig + compute_rsi_mr_baseline (Step F PR5)
│ │ ├── rsi_mr_sensitivity.py # RSI MR 민감도 그리드 (RSIMRParameterAxis, RSIMRSensitivityGrid, step_f_rsi_mr_grid 등 — ADR-0023 C4)
│ │ ├── walk_forward.py # walk-forward 본 구현 (WalkForwardResult, run_walk_forward — ADR-0024, C2)
│ │ └── CLAUDE.md # 모듈 세부 문서
│ ├── execution/
│ │ ├── __init__.py # Executor, ExecutorConfig, OrderSubmitter, BalanceProvider, BarSource, LiveOrderSubmitter, LiveBalanceProvider, DryRunOrderSubmitter, StepReport, ReconcileReport, EntryEvent, ExitEvent, ExecutorError export (13종)
│ │ ├── executor.py # Executor — 신호 → 주문 → 체결 추적 → 상태 동기화 루프
│ │ └── CLAUDE.md # 모듈 세부 문서
│ ├── monitor/
│ │ ├── __init__.py # Notifier, TelegramNotifier, NullNotifier, ErrorEvent, DailySummary export
│ │ ├── notifier.py # 텔레그램 알림 — Notifier Protocol + 구현체
│ │ └── CLAUDE.md # 모듈 세부 문서
│ ├── storage/
│ │ ├── __init__.py # TradingRecorder, SqliteTradingRecorder, NullTradingRecorder, StorageError export
│ │ ├── db.py # SQLite 원장 — 주문·체결·일일 PnL append-only 기록
│ │ └── CLAUDE.md # 모듈 세부 문서
│ └── main.py # 장중 실행 진입점 (BlockingScheduler + Executor 오케스트레이터)
├── tests/
│ ├── test_config.py
│ ├── test_kis_client.py
│ ├── test_safety.py
│ ├── test_rate_limiter.py
│ ├── test_historical.py # 14 케이스
│ ├── test_universe.py # 11 케이스
│ ├── test_realtime.py # 28 케이스
│ ├── test_strategy_orb.py # 36 케이스
│ ├── test_risk_manager.py # 73 케이스
│ ├── test_backtest_engine.py
│ ├── test_kis_minute_bar_loader.py # 39 케이스
│ ├── test_executor.py # 63 케이스
│ ├── test_main.py # 47 케이스 (+ 확장분 포함)
│ ├── test_notifier.py # 71 케이스
│ └── test_storage_db.py # 49 케이스 (+ 3 skip)
└── scripts/
├── healthcheck.py # KIS 모의 잔고 조회 + 텔레그램 hello (실주문 없음)
├── backtest.py # 단일 런 백테스트 CLI
├── sensitivity.py # 파라미터 민감도 그리드 CLI (--grid {default,step-d1,step-d2}, --resume 지정 시 조합 단위 incremental flush, freeze 내성)
├── backfill_minute_bars.py # KIS 과거 분봉 캐시 일괄 적재 CLI
├── backfill_daily_bars.py # pykrx 일봉 캐시 일괄 백필 CLI (Step E Stage 3 선결 — gap-reversal 결정론 보장)
├── collect_spread_samples.py # KIS 호가 스프레드 스냅샷 수집 CLI (Step B 인프라, JSONL 출력)
├── build_liquidity_ranking.py # KOSPI 200 유동성 랭킹 산출 CLI (Step C 인프라, CSV 출력)
├── build_universe_subset.py # 유동성 랭킹 CSV → KOSPI 200 서브셋 YAML 생성 (Step C 보조)
├── walk_forward_rsi_mr.py # RSI 평균회귀 walk-forward 검증 CLI (C2, step6/step3 분할, Markdown/CSV 출력)
├── c4_rsi_mr_sensitivity.py # RSI MR 민감도 그리드 96 조합 실행 CLI (ADR-0023 C4)
├── verify_069500_adjusted.py # KODEX 200 수정주가 4단계 교차 검증 CLI (ADR-0023 C3)
└── debug_kis_minute.py # KIS 과거 분봉 API 파싱 디버그 CLI (Issue #52 대응)
미착수 모듈의 청사진은 plan.md의 디렉토리 구조 섹션 참조.
- KIS Developers 포털에서 모의투자계좌 API 신청 완료 및 모의투자용 APP_KEY / APP_SECRET 발급
- MTS의 "상시 모의투자 참가신청"과 별개 절차임에 주의
- 텔레그램 @BotFather로 봇 생성 → 토큰 및 chat_id 확보
.env 는 두 경로를 순서대로 로드하고 뒤 파일이 앞을 덮는다.
| 경로 | 역할 |
|---|---|
~/.config/stocker/.env |
worktree 무관 공용 master (권장) |
repo 루트 .env |
worktree-local override (선택) |
운영자 1회 셋업:
mkdir -p ~/.config/stocker
cp .env.example ~/.config/stocker/.env
chmod 600 ~/.config/stocker/.env
# 편집기로 ~/.config/stocker/.env 를 열어 필수 값 기입:
# KIS paper 3종(APP_KEY·APP_SECRET·ACCOUNT_NO) + 텔레그램 2종(BOT_TOKEN·CHAT_ID)
# KIS 실전 3종은 Phase 3 착수 시 필요 (지금은 선택)
uv run python scripts/healthcheck.py # 통과 확인이후 claude-squad 로 새 worktree 를 만들 때 .env 를 다시 복사할 필요 없다.
worktree 단위로 다른 값이 필요하면(예: paper/live 전환) repo 루트에 .env 를 따로 작성한다.
민감정보 취급 원칙: .env 파일(두 경로 모두)은 절대 커밋하지 않는다. ~/.config/stocker/.env 는 저장소 밖 경로라 구조적으로 커밋 대상이 아니고, repo 루트 .env 는 .gitignore 의 .env 라인으로 차단된다(이 라인을 실수로 지우지 않아야 한다). 커밋·PR 전 diff 에 키 문자열이 섞여들지 않았는지 확인한다.
# 가상환경 및 의존성 설치
uv sync
# git 훅 설치 (최초 1회)
uv run pre-commit install
# worktree-local override 가 필요한 경우에만 (통상은 ~/.config/stocker/.env 로 충분)
cp .env.example .env
# .env 에서 아래 값을 채웁니다:
# KIS_ENV = paper
# KIS_HTS_ID = 한투 HTS 아이디
# KIS_APP_KEY = 모의투자 APP_KEY (36자)
# KIS_APP_SECRET = 모의투자 APP_SECRET (180자)
# KIS_ACCOUNT_NO = 계좌번호 (XXXXXXXX-XX 형식)
# TELEGRAM_BOT_TOKEN = 텔레그램 봇 토큰
# TELEGRAM_CHAT_ID = 텔레그램 chat_id
#
# --- 시세 전용 실전 키 (Phase 3 착수 전 필수, 미설정 시 healthcheck 4번 SKIP) ---
# # HTS_ID 는 paper/실전 공유 — 위 KIS_HTS_ID 재사용 (한 사람 당 하나)
# KIS_LIVE_APP_KEY = 실전 APP_KEY (36자) ← KIS Developers 포털에서 실전 앱 별도 신청
# KIS_LIVE_APP_SECRET = 실전 APP_SECRET (180자)
# KIS_LIVE_ACCOUNT_NO = 실전 계좌번호 (XXXXXXXX-XX) — paper 계좌번호와 다름
# # 실전 앱 발급 후 KIS Developers 포털 → 앱 관리 → 허용 IP 목록에 현재 공인 IP 등록 필수--strategy-type gap-reversal 백테스트는 DailyBarPrevCloseProvider 가 data/stock_agent.db 일봉 캐시를 조회한다. 미백필 상태이면 백테스트 중 pykrx 네트워크 호출이 반복되어 결정론이 깨진다. 운영자가 1회 선행 실행해야 한다.
# Top 100 유니버스 전체 1년치 일봉 백필 (Top 50 은 부분집합이라 한 번에 처리됨)
uv run python scripts/backfill_daily_bars.py \
--from 2025-04-01 --to 2026-04-21 \
--universe-yaml config/universe_top100.yaml
# 특정 심볼만 백필
uv run python scripts/backfill_daily_bars.py \
--from 2025-04-01 --to 2026-04-21 \
--symbols 005930 000660
# exit code: 0 정상 / 1 일부 심볼 HistoricalDataError / 2 입력·설정 오류 / 3 I/O 오류pykrx 1.2.7 이상은 KRX_ID / KRX_PW 환경변수가 필요하다 (~/.config/stocker/.env 에 기입).
data/stock_agent.db 일봉 캐시가 충분히 백필된 상태에서 실행한다.
# primary: step6 — 6개월 train / 6개월 test, 2 windows (non-overlap)
uv run python scripts/walk_forward_rsi_mr.py \
--from 2024-04-01 --to 2026-04-21 \
--step step6 \
--output-markdown data/c2_walk_forward_rsi_mr_step6.md \
--output-csv data/c2_walk_forward_rsi_mr_step6.csv
# secondary: step3 — 6개월 train / 6개월 test, 3 windows (overlap)
uv run python scripts/walk_forward_rsi_mr.py \
--from 2024-04-01 --to 2026-04-21 \
--step step3 \
--output-markdown data/c2_walk_forward_rsi_mr_step3.md \
--output-csv data/c2_walk_forward_rsi_mr_step3.csv
# exit code: 0 정상 / 2 입력·설정 오류 / 3 I/O 오류PASS 기준: 모든 windows 에서 ADR-0022 게이트 3종 통과 + degradation ≤ 0.3 (ADR-0024).
RSI 평균회귀(일봉 전략)의 정본 데이터 경로는 위 "백테스트용 일봉 백필" 섹션이다. 분봉 백필은 ORB·VWAP-MR·Gap-Reversal 등 분봉 기반 baseline 전략 회귀 검증용으로 보존한다.
data/minute_bars.db 에 KIS 분봉을 적재한다.
# 유니버스 전체 심볼에 대해 최근 1년치 분봉 백필
uv run python scripts/backfill_minute_bars.py \
--from 2024-04-22 --to 2025-04-22
# 특정 심볼만 백필
uv run python scripts/backfill_minute_bars.py \
--from 2024-04-22 --to 2025-04-22 --symbols 005930 000660
# exit code: 0 정상 / 1 일부 심볼 KisMinuteBarLoadError / 2 입력·설정 오류 / 3 I/O 오류백필 완료 후 scripts/backtest.py --loader=kis --from ... --to ... 로 MDD > -15% 확인 시 Phase 2 PASS 판정.
실전 전 반드시 모의투자 2주 무사고 운영을 먼저 거친다. 드라이런 모드로 동작을 먼저 검증하는 것을 권장한다.
# 드라이런 — KIS 주문 API 접촉 0, 시그널·로그만 남긴다
uv run python -m stock_agent.main --dry-run
# 실 주문 실행 (paper 또는 실전 — .env 의 KIS_ENV 에 따름)
uv run python -m stock_agent.main
# 시작 자본 명시 (기본 1,000,000원)
uv run python -m stock_agent.main --dry-run --starting-capital 2000000- 스케줄 (RSI MR 모드, 평일 KST 기준):
- 09:00 세션 시작 (
session_start) - 매분 신호 처리 (
step) — 분봉 stop_loss 가드 포함 (보유 포지션만 동적 구독, ADR-0025 PR3) - 15:30 일일 리포트 (
daily_report) - 15:35 EOD 일봉 트리거 (
on_eod_signal) — universe 일봉 fetch →strategy.on_bar호출 → pending signals 큐인 → 다음 영업일 09:00 후 첫 step 에서 시초가 시장가 주문 (ADR-0027) - 15:00 강제청산 cron 미등록 — 일봉 전략 특성상 부적합 (ADR-0025)
- 09:00 세션 시작 (
- KIS WebSocket 구독: 시작 시 0종목 → 진입 체결 시 동적 구독 · 청산 체결 시 해제 · 최대
max_positions=10(ADR-0025 PR4) - 공휴일 자동 판정 미지원 — KRX 임시공휴일은 운영자가 프로세스를 띄우지 않는 방식으로 처리
- SIGINT(Ctrl+C) / SIGTERM 모두 graceful shutdown 처리 (exit 0)
uv run python scripts/healthcheck.py
# 통과 기준:
# 1) KIS 모의투자 토큰 발급 OK — 시간대 무관
# 2) 모의 계좌 잔고 조회 OK — 시간대 무관
# 3) 텔레그램 "hello" 메시지 수신 OK — 시간대 무관
# 4) 삼성전자(005930) 현재가 조회 OK — 평일 장중(09:00~15:30 KST) 실행 필수
# 장외에서는 WebSocket 연결은 성공하지만 체결 이벤트 미수신 → 2초 타임아웃 후 실패 가능
# (실전 키 미설정 시 4번은 SKIP — Phase 3 착수 전 반드시 해결:
# 실전 키 3종 기입 + KIS Developers 포털 IP 화이트리스트 등록 필수)plan.md— 전체 설계서(Context, 전략 상세, 리스크 한도, 로드맵, Verification 기준, 비용·위험 분석)docs/operations-playbook.md— 모의투자 운영 플레이북 (장 전 체크리스트·장중 이상 대응·Kill switch·일일 마감 체크)
- 본 프로젝트는 학습·연구 목적이며 수익을 보장하지 않습니다. 데이트레이딩은 개인 투자자 대다수가 손실을 보는 고위험 영역입니다.
- 실전 투입 전 반드시 모의투자·백테스팅을 통해 검증하고, 본인 판단과 책임 하에 잃어도 생활에 지장 없는 소액으로 시작하십시오.
- 증권사 API 키 및 계좌 정보는 절대 리포지토리에 커밋하지 마십시오(
.env는.gitignore대상). - 본 코드가 원인이 된 금전적 손실에 대해 작성자는 책임을 지지 않습니다.