diff --git a/README.md b/README.md index 0b1b51a..14570a2 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,49 @@ # QUANT TRADER -![Python](https://img.shields.io/badge/Python-3.11%20%7C%203.12-3776AB?logo=python&logoColor=white) -![Tests](https://img.shields.io/badge/tests-1%2C683%20passed-2EA043) +![Python](https://img.shields.io/badge/Python-3.11%2B-3776AB?logo=python&logoColor=white) +![Tests](https://img.shields.io/badge/tests-1%2C600%2B-2EA043) ![Market](https://img.shields.io/badge/Market-KR%20stocks-0F766E) -![Mode](https://img.shields.io/badge/Modes-backtest%20%7C%20paper%20%7C%20live--gated-7C3AED) -![Focus](https://img.shields.io/badge/Focus-risk%20first-E11D48) +![Mode](https://img.shields.io/badge/mode-paper%20%EC%9A%B4%EC%98%81%20%EC%A4%91-7C3AED) -한국 주식 자동매매를 **백테스트 → 페이퍼 트레이딩 → 실전 게이트**까지 한 저장소에서 굴리는 개인 프로젝트입니다. -데이터 수집, 전략 검증, 리스크 관리, 일일 자동 운영, 웹 대시보드, KIS API 연동까지 전부 재현 가능하게 구성했습니다. +한국 주식 자동매매 개인 프로젝트. 데이터 수집부터 백테스트, 모의투자(paper), 리스크 관리, +일일 자동 운영, 웹 대시보드, KIS API 연동까지 한 저장소에서 돌린다. -이 프로젝트가 다른 자동매매 저장소와 다른 점은 **결론이 정직하다는 것**입니다. +처음엔 단타 알파를 찾는 게 목표였다. 몇 달 동안 후보군을 바꿔가며 체계적으로 돌려봤고, +결론은 "내 규모에서 시장 예측으로 초과수익은 안 나온다"였다. 그 실패 기록은 지우지 않고 +[연구 로그](docs/RESEARCH_LOG.md)에 전부 남겨뒀다. 지금은 방향을 바꿔서 예측 없이 먹을 수 +있는 것만 조합해 굴리는 중이다. 지수 ETF 분산 + 유휴 현금은 CD금리 파킹 + 거래비용 최소화 ++ 월 적립. 대신 실전 주문 경로는 검증을 통과하기 전까지 전부 막아뒀다(fail-closed). -- 능동 알파(시장 예측으로 초과수익)를 candidate family 단위로 수개월 체계적으로 탐색했고, 전부 승격 게이트를 넘지 못했습니다. 그 실패 기록 전체를 [연구 로그](docs/RESEARCH_LOG.md)로 남겼습니다. -- 그래서 수익은 예측이 아니라 **구조**에서 얻습니다 — 분산 베타 보유 + 유휴 현금의 이자 회수 + 비용 최소화(ETF 매도세 면제 반영) + 월 적립 복리. -- 안전이 항상 우선입니다. 실전 주문 경로는 여러 단계의 **fail-closed 게이트**를 전부 통과해야만 열립니다. +> 학습용 프로젝트고 투자 조언이 아님. -> 학습·연구용 프로젝트입니다. 투자 조언이 아닙니다. - -## 운영 대시보드 +## 운영 화면 ![운영 대시보드](docs/images/dashboard-main.png) -*실제 운영 화면 (2026-07-10). 트랙별 평가금·TWR 수익률·MDD·주식 배치율·보유 종목을 한눈에 보고, 월 적립 입금도 우측 상단 버튼으로 기록합니다. 입금은 시간가중수익률(TWR)로 중화되어 수익률을 왜곡하지 않습니다.* +운영 대시보드 (2026-07-10 캡처). 트랙별 평가금, 수익률(TWR), MDD, 주식 배치율, 보유 종목을 +한 화면에서 본다. 월 적립은 우측 상단 버튼으로 기록하는데, 입금은 시간가중수익률로 중화돼서 +수익률이 왜곡되지 않는다. 대시보드에서 할 수 있는 쓰기 작업은 입금 기록 하나뿐이고 +매매나 설정 변경은 못 한다. ```bash python main.py --mode dashboard # http://127.0.0.1:8080 ``` +## 지금 굴리는 트랙 (2026-07-10 기준) + +| 트랙 | 구성 | 자본 | 진행 | +|------|------|------|------| +| `kr_pocket` 소액 적립 | KODEX 200 47.5% + CD금리 파킹 ETF 47.5% + 현금 5% | 30만 시작, 월 10만 적립 | paper 1/60일 | +| `kr_diversified_hold` | 대형주 10종목 균등 buy&hold, 저회전 | 1,000만 (paper) | 23/60일, 관찰용 | +| 단타 샌드박스 | paper 전용. 실돈은 60일 게이트 통과 후 따로 결정 | - | 대기 | + +실제로 돈이 들어갈 트랙은 `kr_pocket` 하나다. + +- KODEX 200 1주면 그 자체로 200종목 분산이라 소액에서 유일하게 말이 되는 분산 수단 +- 나머지 절반을 그냥 현금으로 두면 이자 0이라, CD금리 누적형 ETF에 파킹 (연 3%대, 가격 변동 사실상 없음) +- 국내 상장 ETF는 매도 거래세 면제. 이걸 체결 비용 모델에도 반영해서 페이퍼 성적이 가짜 비용으로 깎이지 않게 함 +- 위험자산을 총자산의 절반으로 고정. 백테스트 기준 MDD가 주식 100% 대비 절반 수준 + ## 시스템 구성 ```mermaid @@ -36,79 +53,61 @@ flowchart LR RB["바스켓 리밸런서
drift 트리거 · 회전 상한
1일 1매매 가드"] --> RG RG["리스크 가드
MDD·일손실 한도
유동성·중복주문 차단"] --> EX EX["주문 실행기
paper 체결 모델
live는 gate 통과 시에만"] --> DB[("SQLite WAL
거래·포지션·스냅샷·입금")] - DB --> OBS["관측성
헬스체크 · 디스코드 알림
일일/주간 리포트"] + DB --> OBS["관측성
헬스체크 · 디스코드
일일/주간 리포트"] DB --> WEB["웹 대시보드
TWR · 입금 기록"] ``` -체결 반영은 원장 정합성을 보장합니다 — 포지션 저장이 실패하면 방금 저장한 매매 기록을 보상 롤백해 "현금만 차감된 반쪽 원장"이 남지 않고, 예외성 주문 실패는 디스코드 critical로 즉시 승격됩니다. - -## 운영 트랙 (2026-07-10 기준) - -| 트랙 | 구성 | 자본 | 진행 | -|------|------|------|------| -| **소액 적립** `kr_pocket` | KODEX 200 47.5% + CD금리 파킹 ETF 47.5% + 현금 5% | 30만 시작 + 월 10만 적립 | 페이퍼 1/60일 | -| **분산 대형주** `kr_diversified_hold` | 대형주 10종목 균등 buy&hold (저회전) | 1,000만 (페이퍼) | 23/60일 · 관찰용 | -| 단타 샌드박스 | 페이퍼 전용 (실돈 투입은 60일 게이트 통과 후 별도 결정) | — | 대기 | - -소액 트랙이 핵심입니다. 예측 없이 얻을 수 있는 것만 조합했습니다: - -- **지수 절반**: KODEX 200 1주가 그 자체로 200종목 분산 — 소액에서 유일한 분산 수단 -- **파킹 절반**: 유휴 현금을 CD금리 누적형 ETF로 — 시장 위험 없이 연 3%대, 종전 "이자 0 현금"의 캐시 드래그 제거 -- **비용 정합**: 국내 상장 ETF 매도 거래세 면제를 체결 모델에도 반영 — 가짜 비용으로 승격 판정이 왜곡되지 않게 -- 낙폭에 민감한 설계: 위험자산을 총자산의 절반으로 고정해 MDD를 주식 100% 대비 절반 수준으로 억제 - -## 페이퍼 → 실전 승격 파이프라인 +## paper → 실전 승격 ```mermaid flowchart LR - BT["백테스트
비용·슬리피지·look-ahead 가드"] --> PP["페이퍼 운영
60영업일 트랙레코드"] - PP -->|"스냅샷 커버리지 ≥95%
dead-letter 0건
비용 드래그 ≤1%/년"| PC["PASS_CANDIDATE"] - PC --> LG{"live gate
(fail-closed)"} + BT["백테스트
비용·슬리피지·look-ahead 가드"] --> PP["paper 운영
60영업일 트랙레코드"] + PP -->|"스냅샷 커버리지 95% 이상
dead-letter 0건
비용 드래그 연 1% 이하"| PC["PASS_CANDIDATE"] + PC --> LG{"live gate"} LG -->|"KIS 연결·잔고 동기화
blockers 통과 + 운영자 확인"| LIVE["실계좌 소액"] LG -->|미충족| PP ``` -실계좌는 페이퍼 트랙레코드가 기준을 통과하기 전에는 **어떤 조합으로도 열리지 않습니다**. `--force-live` 같은 우회 플래그는 제거했습니다. +paper 트랙레코드가 기준을 못 넘으면 실계좌는 안 열린다. 예전에 있던 `--force-live` 같은 +우회 플래그는 지웠다. -## 일일 운영 (자동) +## 일일 운영 -매 영업일 오전 10시에 한 사이클이 자동으로 돕니다: +평일 오전 10시에 한 사이클이 자동으로 돈다. ``` -리밸런싱 판단 → (필요 시) 주문 → NAV 스냅샷 → DB 백업 → 승격 진행률 리포트 → 디스코드 카드 +리밸런싱 판단 → (필요 시) 주문 → NAV 스냅샷 → DB 백업 → 승격 진행률 → 디스코드 카드 ``` -- 스냅샷 결측은 당일 critical 경보 (KST 기준 영업일 판정) -- 금요일에는 백업 **복구 리허설**까지 자동 수행 — 백업이 실제로 복구되는지 매주 검증 -- 운영자 점검은 명령 하나면 됩니다: +- 당일 스냅샷이 빠지면 critical 경보 (영업일 판정은 KST 기준) +- 금요일엔 백업 복구 리허설까지 돈다. 백업 파일이 실제로 복구되는지 매주 확인하는 용도 +- 평소 점검은 이거 하나로 끝: ```bash python main.py --mode health -# 종료코드 0=OK / 1=ATTENTION / 2=BLOCKED — 모니터링 스크립트에서 분기 가능 -# 승격 대기 같은 파이프라인의 '예상 상태'는 라벨로만 표시하고 경보로 올리지 않는다 -# — 매일 노란불이면 진짜 장애를 못 알아본다 +# 종료코드 0=OK / 1=ATTENTION / 2=BLOCKED +# 승격 대기 같은 '원래 그런 상태'는 라벨로만 찍히고 경보로 안 올라온다 ``` -## Quick Start +## 시작하기 ```bash python -m venv .venv .venv\Scripts\activate pip install -r requirements.txt -# 설정: config/settings.yaml.example → settings.yaml, .env.example → .env -# 디스코드 알림을 쓰려면 .env의 DISCORD_WEBHOOK_URL 설정 +# config/settings.yaml.example → settings.yaml 복사, .env.example 참고해서 .env 작성 +# 디스코드 알림 쓰려면 .env에 DISCORD_WEBHOOK_URL 필요 -python main.py --mode guide # 실행 모드 안내 +python main.py --mode guide # 실행 모드 목록 python main.py --mode backtest --strategy scoring --symbol 005930 -python main.py --mode rebalance --dry-run # 바스켓 리밸런싱 (계획만) -python main.py --mode health # 운영 통합 헬스 점검 -python main.py --mode weekly_report # 주간 요약 (성과·귀속 분해) -python main.py --mode dashboard # 웹 대시보드 -pytest tests/ -q # 테스트 1,683개 +python main.py --mode rebalance --dry-run # 리밸런싱 계획만 확인 +python main.py --mode health # 운영 점검 +python main.py --mode weekly_report # 주간 요약 +pytest tests/ -q ``` -월 적립 기록은 대시보드의 **적립 입금** 버튼이 제일 편하고, CLI도 있습니다: +월 적립 기록은 대시보드 버튼이 편하고, CLI도 있다. ```bash python tools/record_deposit.py --basket kr_pocket --amount 100000 @@ -116,66 +115,61 @@ python tools/record_deposit.py --basket kr_pocket --amount 100000 ## 안전장치 -실전 경험(과 사고)에서 나온 것들이라 목록이 깁니다. 설계 원칙은 세 가지입니다. +한 번씩 데인 뒤에 추가된 것들이라 목록이 길다. 기본 방침은 이렇다. -1. **fail-closed** — 데이터 조회 실패, 상태 불명, 검증 불가면 주문하지 않는다 -2. **원장 정합** — 거래·포지션·현금이 어긋난 반쪽 상태를 남기지 않는다 (보상 롤백) -3. **경보 위생** — 파이프라인의 예상 상태는 라벨, 진짜 장애만 경보 (매일 울리는 경보는 없는 것과 같다) +- 데이터 조회 실패, 상태 불명, 검증 불가면 주문 안 한다 (fail-closed) +- 거래·포지션·현금이 어긋난 반쪽 원장을 남기지 않는다. 포지션 저장이 실패하면 방금 쓴 매매 기록을 되돌리고 실패를 위로 올린다 +- 파이프라인상 원래 그런 상태는 라벨, 진짜 장애만 경보. 매일 울리는 경보는 결국 아무도 안 본다
-세부 안전장치 목록 펼치기 +세부 목록 펼치기 - 포트폴리오 MDD·일손실 한도 도달 시 신규 매수 차단 (손절·청산 SELL은 유지) -- 미체결/중복 주문 방지 — live 미체결 조회 실패는 "미체결 있음"으로 간주 (fail-closed) +- 미체결/중복 주문 방지. live 미체결 조회가 실패하면 "미체결 있음"으로 간주 - 신규 매수 직전 유동성(평균 거래량·거래대금) 재검증, 누락 시 차단 - 갭 리스크·상관관계·업종 비중 확인용 데이터 조회 실패 시 신규 매수 차단 -- 주문/청산 판단 가격이 0·NaN·누락이면 판단 보류 + 차단 이벤트 기록 -- 시장 국면 필터 데이터 불명 시 `unknown` 국면으로 신규 매수 차단 -- live 체결 확인 전 DB 반영 보류, 주문번호 불일치 시 반영 보류 +- 주문/청산 판단 가격이 0, NaN, 누락이면 판단 보류 + 차단 이벤트 기록 +- 시장 국면 필터 데이터 불명 시 unknown 국면으로 신규 매수 차단 +- live 체결 확인 전 DB 반영 보류, 주문번호 불일치 시에도 보류 - live 시작 전 KIS 연결·잔고 동기화 실패 시 스케줄러 시작 차단 -- 체결 반영 원자성: 포지션 저장 실패 시 매매 기록 보상 롤백 (반쪽 원장 방지) -- 예외성 주문 실패는 ORDER_ERROR critical 이벤트 + 디스코드 즉시 알림 -- 1일 1매매 가드 — 중복 사이클이 회전 상한을 우회하지 못하게 -- 스키마 마이그레이션은 멱등·중단 재개 가능, 행수 검증 실패 시 원본 보존 -- DB 백업 (보존 14일) + 매주 금요일 복구 리허설 자동 수행 -- 웹 대시보드 쓰기는 입금 기록 하나뿐 (매매·설정 변경 불가), CSRF 방어 적용 -- 긴급 청산은 POST 전용 · 127.0.0.1 바인드 · 토큰 검증 기본 적용 +- 주문 예외성 실패는 ORDER_ERROR critical 이벤트 + 디스코드 즉시 알림 +- 1일 1매매 가드. 사이클이 중복 실행돼도 회전 상한을 우회하지 못함 +- 스키마 마이그레이션은 멱등, 중단 지점부터 재개 가능, 행수 검증 실패 시 원본 보존 +- DB 백업 보존 14일 + 매주 금요일 복구 리허설 +- 대시보드 쓰기는 입금 기록 하나뿐, CSRF 방어 적용 +- 긴급 청산은 POST 전용, 127.0.0.1 바인드, 토큰 검증 기본 적용
-세부 파라미터는 `config/risk_params.yaml`, `config/baskets.yaml`에서 관리합니다. +파라미터는 `config/risk_params.yaml`, `config/baskets.yaml`에서 관리. -## 프로젝트 구조 +## 폴더 구조 ``` quant_trader/ ├── main.py # 모드 라우터 (backtest/rebalance/health/dashboard/...) ├── config/ # 설정 (baskets, risk_params, strategies, settings) -├── core/ # 리밸런서·리스크·주문 실행·헬스·관측성·paper 런타임 -├── strategies/ # 전략 (scoring, rotation 등 — 현재 전부 연구 보관) +├── core/ # 리밸런서, 리스크, 주문 실행, 헬스, 관측성, paper 런타임 +├── strategies/ # 전략들 (현재 전부 연구 보관 상태) ├── backtest/ # 백테스터 (비용·슬리피지·이벤트 가드 반영) -├── database/ # SQLite 모델·리포지토리·마이그레이션·백업 -├── monitoring/ # 로깅·디스코드·웹 대시보드 +├── database/ # SQLite 모델, 리포지토리, 마이그레이션, 백업 +├── monitoring/ # 로깅, 디스코드, 웹 대시보드 ├── api/ # KIS REST·WebSocket -├── tools/ # 운영 도구 (입금 기록·평가·트랙 재시작·시뮬레이터) -├── scripts/ # 검증 스크립트 (OOS·sleeve 비교·리포트) -├── deploy/ # (선택) Oracle Cloud ARM 상시 구동 (systemd) -├── tests/ # 1,683개 (외부 API는 모킹, DB는 격리) -└── docs/ # 설계·운영 문서 (+ images/ 스크린샷) +├── tools/ # 운영 도구 (입금 기록, 평가, 트랙 재시작, 시뮬레이터) +├── scripts/ # 검증 스크립트 +├── deploy/ # (선택) Oracle Cloud ARM 상시 구동 +├── tests/ # 외부 API는 모킹, DB는 격리 +└── docs/ # 문서, 스크린샷 ``` ## 문서 | 문서 | 내용 | |------|------| -| [PROFITABILITY_FINDINGS](docs/PROFITABILITY_FINDINGS.md) | **수익성 정직 점검 (먼저 읽을 것)** — 알파 탐색 결론 | -| [POCKET_TRACK_PLAN](docs/POCKET_TRACK_PLAN.md) | 소액 적립 트랙 설계 — 기대치·구성·입금·게이트 | -| [BASKET_PAPER_EVALUATION](docs/BASKET_PAPER_EVALUATION.md) | 페이퍼→실전 승격 기준과 자동 판정 | +| [PROFITABILITY_FINDINGS](docs/PROFITABILITY_FINDINGS.md) | 수익성 점검 결론. 뭘 시도했고 왜 접었는지 | +| [POCKET_TRACK_PLAN](docs/POCKET_TRACK_PLAN.md) | 소액 적립 트랙 설계. 기대치, 구성, 입금, 게이트 | +| [BASKET_PAPER_EVALUATION](docs/BASKET_PAPER_EVALUATION.md) | paper→실전 승격 기준과 자동 판정 | | [BASKET_LIVE_RUNBOOK](docs/BASKET_LIVE_RUNBOOK.md) | 실전 전환 절차 (모의서버 리허설 → 소액 → 목표 자본) | -| [PROJECT_GUIDE](docs/PROJECT_GUIDE.md) | 파일 역할·모드별 흐름·실전 전 체크리스트 | -| [RESEARCH_LOG](docs/RESEARCH_LOG.md) | 연구·운영 상세 이력 아카이브 (알파 탐색 실패 기록 포함) | -| [quant_trader_design](quant_trader_design.md) | 아키텍처·전략·리스크 설계 | - -## English Summary - -`quant_trader` is a risk-first algorithmic trading project for Korean equities: backtesting, paper trading with a 60-trading-day promotion track record, KIS API integration, TWR-based deposit accounting, and live-trading gates that fail closed. After months of systematic alpha research concluded honestly in "no edge at this scale", the deployed strategy is structural: diversified beta + parking idle cash in a CD-rate ETF + cost minimization + monthly contributions. +| [PROJECT_GUIDE](docs/PROJECT_GUIDE.md) | 파일 역할, 모드별 흐름, 실전 전 체크리스트 | +| [RESEARCH_LOG](docs/RESEARCH_LOG.md) | 연구·운영 이력 아카이브. 알파 탐색 실패 기록 포함 | +| [quant_trader_design](quant_trader_design.md) | 아키텍처, 전략, 리스크 설계 | diff --git a/docs/RESEARCH_LOG.md b/docs/RESEARCH_LOG.md index e936c59..de01051 100644 --- a/docs/RESEARCH_LOG.md +++ b/docs/RESEARCH_LOG.md @@ -1,12 +1,12 @@ # 연구·운영 로그 (아카이브) -README에 쌓이던 상세 이력을 이 파일로 옮겼다. 이 문서는 시간순 기록 원본이라 길고, -요약된 현재 결론은 [README](../README.md)와 -[PROFITABILITY_FINDINGS](PROFITABILITY_FINDINGS.md)를 먼저 보는 편이 낫다. +README에 쌓이던 상세 이력을 이 파일로 옮겨왔다. 시간순 기록 원본이라 길다. +요약된 결론만 필요하면 [README](../README.md)나 +[PROFITABILITY_FINDINGS](PROFITABILITY_FINDINGS.md)를 먼저 보는 게 낫다. -- 능동 alpha 탐색은 2026-04~05에 걸쳐 candidate family 단위로 체계적으로 수행했고, - 전부 승격 게이트(벤치마크 초과·MDD·회전율)를 넘지 못했다 — 그 실패 기록 전체가 여기 있다. -- 2026-06 이후 운영은 분산 베타(바스켓) + 소액 적립 트랙으로 전환됐다. +- 알파 탐색은 2026-04~05에 candidate family 단위로 돌렸고 전부 승격 게이트(벤치마크 + 초과, MDD, 회전율)에서 막혔다. 그 실패 기록 전체가 아래에 있다. +- 2026-06부터는 분산 베타(바스켓)와 소액 적립 트랙 운영으로 전환. ---