Skip to content

donggyushin/stocker

Repository files navigation

stock-agent

CI

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/회귀 검증용으로 보존.

리스크 관리 (Phase 3 모의투자 운영 기준값, ADR-0025)

항목 적용 대상
종목당 진입 금액 세션 자본의 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 통과 필수.

로드맵 (총 약 4주)

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 ORBStrategyRSIMRStrategy 교체 + 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건 제외). 정식 티커 발급 후 다음 갱신에 추가 예정.

Phase 0 체크리스트 (완료 2026-04-19)

  • 한국투자증권 비대면 계좌 개설
  • 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의 디렉토리 구조 섹션 참조.

설치 및 실행

사전 준비

  1. KIS Developers 포털에서 모의투자계좌 API 신청 완료 및 모의투자용 APP_KEY / APP_SECRET 발급
    • MTS의 "상시 모의투자 참가신청"과 별개 절차임에 주의
  2. 텔레그램 @BotFather로 봇 생성 → 토큰 및 chat_id 확보

최초 설정 — .env 준비

.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 등록 필수

백테스트용 일봉 백필 (Step E Stage 3 선결)

--strategy-type gap-reversal 백테스트는 DailyBarPrevCloseProviderdata/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 에 기입).

Walk-forward 검증 (C2, RSI 평균회귀)

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 판정.

장중 실행 (Phase 3)

실전 전 반드시 모의투자 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)
  • 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·일일 마감 체크)

책임 고지 (Disclaimer)

  • 본 프로젝트는 학습·연구 목적이며 수익을 보장하지 않습니다. 데이트레이딩은 개인 투자자 대다수가 손실을 보는 고위험 영역입니다.
  • 실전 투입 전 반드시 모의투자·백테스팅을 통해 검증하고, 본인 판단과 책임 하에 잃어도 생활에 지장 없는 소액으로 시작하십시오.
  • 증권사 API 키 및 계좌 정보는 절대 리포지토리에 커밋하지 마십시오(.env.gitignore 대상).
  • 본 코드가 원인이 된 금전적 손실에 대해 작성자는 책임을 지지 않습니다.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors