스캐너 후보 종목뿐 아니라 현재 보유 종목도 매 사이클마다 평가해
stop-loss / take-profit이 실제로 동작하도록 개선.
- db.py: get_open_positions_by_market() 추가
- net BUY - SELL 집계 쿼리로 실제 보유 종목 코드 목록 반환
- 단순 "최신 레코드 = BUY" 방식보다 안전 (이중 매도 방지)
- main.py: 실시간 루프에서 스캐너 후보 + 보유 종목을 union으로 구성
- dict.fromkeys로 순서 유지하며 중복 제거
- 스캐너에 없는 보유 종목은 로그로 명시
- 보유 종목은 Playbook 없으면 HOLD → stop-loss/take-profit 체크
- tests/test_db.py: get_open_positions_by_market 테스트 5개 추가
- net 양수 종목 포함, 전량 매도 제외, 부분 매도 포함
- 마켓 범위 격리, 거래 없을 때 빈 리스트
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add /api/pnl/history endpoint to app.py for daily P&L history charting
- Rewrite index.html as full SPA with Chart.js bar chart, summary cards,
and decisions log table with market filter tabs and 30s auto-refresh
- Add test_pnl_history_all_markets and test_pnl_history_market_filter tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
**Problem 1 — Current price always 0**
get_orderbook() used inquire-asking-price-exp-ccn which has no stck_prpr
in output1 (only askp/bidp data). This caused every domestic BUY to be
skipped with "no affordable quantity (cash=..., price=0.00)".
**Problem 2 — KRX tick unit error on limit orders**
Limit order prices were passed unrounded, triggering 호가단위 오류 in VTS.
Also ORD_DVSN was wrongly set to "01" (시장가) for limit orders.
**Fix**
- Add kr_tick_unit(price) and kr_round_down(price) module-level helpers
implementing KRX 7-tier price tick rules (1/5/10/50/100/500/1000원).
- Add get_current_price(stock_code) → (price, change_pct, foreigner_net)
using FHKST01010100 / inquire-price API (works in VTS, returns correct
stck_prpr, prdy_ctrt, frgn_ntby_qty).
- Fix send_order() ORD_DVSN: "00"=지정가, "01"=시장가 (was "01"/"06").
- Apply kr_round_down() to limit order price inside send_order().
- Replace both get_orderbook() calls in main.py with get_current_price().
- Update all 4 test_main.py mock sites to use get_current_price AsyncMock.
**Tests added** (25 new tests, all 646 pass)
- TestKrTickUnit: 13 parametrized boundary cases + 7 round-down cases
- TestGetCurrentPrice: correct fields, correct API path/TR_ID, HTTP error
- TestSendOrderTickRounding: tick rounding, ORD_DVSN 00/01
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three bugs found by comparing against KIS official GitHub examples:
1. FID_COND_SCR_DIV_CODE: "20001" → "20171" (volume-rank screen code)
2. FID_TRGT_EXLS_CLS_CODE: "000000" (6-digit) → "0000000000" (10-digit)
3. fluctuation ranking:
- TR_ID: "FHPST01710100" (invalid) → "FHPST01700000"
- path: /quotations/volume-rank → /ranking/fluctuation
- params: volume-rank params → lowercase fluctuation-specific params
(fid_rank_sort_cls_code, fid_input_cnt_1, fid_prc_cls_code,
fid_rsfl_rate1, fid_rsfl_rate2, etc.)
Note: VTS (paper trading) does not return data from ranking APIs regardless
of parameter correctness — this is a KIS policy restriction, not a code bug.
These fixes ensure correct behavior when switching to a live account.
Tests: TestFetchMarketRankings (3 tests) added to test_broker.py
Closes#155
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three fixes for overseas stock trading failures:
1. Price API exchange code mapping:
- get_overseas_price() now applies _PRICE_EXCHANGE_MAP (NASD→NAS, NYSE→NYS, AMEX→AMS)
- Price API HHDFS00000300 requires short exchange codes same as ranking API
2. rt_cd check in send_overseas_order():
- Log WARNING (not INFO) when rt_cd != "0" (e.g., "주문가능금액이 부족합니다")
- Caller (main.py) checks rt_cd == "0" before calling log_trade()
- Prevents DB from recording failed orders as successful trades
3. Limit order price premium for BUY:
- BUY limit price = current_price * 1.005 (0.5% premium)
- SELL limit price = current_price (no premium)
- Improves fill probability: KIS VTS only accepts limit orders,
and last price is typically at or below ask
4. PAPER_OVERSEAS_CASH fallback (config + main.py):
- New setting: PAPER_OVERSEAS_CASH = 50000.0 (USD)
- When VTS overseas balance API fails/returns 0, use this as simulated cash
- Applied in both trading_cycle() and run_daily_session()
5. Candidate price fallback:
- If price API returns 0, use scanner candidate price as fallback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
KIS VTS (paper trading) rejects overseas market orders with:
"모의투자 주문처리가 안되었습니다(지정가만 가능한 상품입니다)"
Root cause: send_overseas_order() was called with price=0.0 (market order)
in both trading_cycle() and run_daily_session(), even though current_price
was already computed correctly by Fix#147 (exchange code mapping).
Fix: pass current_price as the limit order price in both call sites.
Domestic broker send_order() keeps price=0 (market orders are fine on KRX).
Adds regression test TestOverseasBalanceParsing::test_overseas_buy_order_uses_limit_price
verifying price=182.5 is passed, not 0.0.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Apply _PRICE_EXCHANGE_MAP in get_overseas_price() to send short codes
(NASD→NAS, NYSE→NYS, AMEX→AMS) required by HHDFS00000300 price API
- Add PAPER_OVERSEAS_CASH config setting (default $50,000) for simulated
USD balance when VTS overseas balance API returns 0 in paper mode
- Fall back to scan candidate price when live price API returns 0
- Both fixes together resolve "no affordable quantity (cash=0, price=0)"
which was preventing all overseas trade execution
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When gemini-2.5-flash quota is exhausted (20 RPD free tier), generate_playbook()
fell back to _defensive_playbook() which only had price_change_pct_below: -3.0 SELL
conditions — no BUY conditions — causing zero trades on US market despite scanner
finding strong momentum/oversold candidates.
Changes:
- Add _smart_fallback_playbook() that uses scanner signals to build BUY conditions:
- momentum signal: BUY when volume_ratio_above=VOL_MULTIPLIER
- oversold signal: BUY when rsi_below=RSI_OVERSOLD_THRESHOLD
- always: SELL stop-loss at price_change_pct_below=-3.0
- Use _smart_fallback_playbook() instead of _defensive_playbook() on Gemini failure
- Add 10 new tests for _smart_fallback_playbook() covering momentum/oversold/empty cases
- Update existing test_gemini_failure_returns_defensive to match new behavior
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
decide() ignored market_data["prompt_override"], always building a generic
trade-decision prompt. This caused pre_market_planner playbook generation
to fail with JSONDecodeError on every market, falling back to defensive
playbooks. Now prompt_override takes priority over both optimization and
standard prompt building.
Closes#143
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The overseas ranking API was returning 404 for all exchanges because the
TR_IDs, API paths, and exchange codes were all incorrect. Updated to match
KIS official API documentation:
- TR_ID: HHDFS76290000 (updown-rate), HHDFS76270000 (volume-surge)
- Path: /uapi/overseas-stock/v1/ranking/{updown-rate,volume-surge}
- Exchange codes: NASD→NAS, NYSE→NYS, AMEX→AMS via ranking-specific mapping
Fixes#141
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- prefer .venv/bin/python when APP_CMD is unset\n- pass DASHBOARD_PORT into launch command (default 8080)\n- target tmux window by name instead of fixed index\n\nRefs #137
- 아키텍처 다이어그램에 v2 컴포넌트 (Strategy, Context, Evolution) 추가
- 핵심 모듈 테이블: 6개 → 14개 모듈 반영
- 테스트: 35개/3파일 → 551개/25파일
- 지원 시장 10개 거래소 테이블 추가
- 텔레그램 양방향 명령어 9종 레퍼런스
- 프로젝트 구조 트리 전면 갱신
- 문서 링크 섹션 추가
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- testing.md: 54 tests/4 files → 551 tests/25 files 반영, 전체 테스트 파일 설명
- architecture.md: v2 컴포넌트 추가 (Strategy, Context, Dashboard, Decision Logger 등),
Playbook Mode 데이터 플로우, DB 스키마 5개 테이블, v2 환경변수
- commands.md: Dashboard 실행, Telegram 명령어 9종 레퍼런스
- CLAUDE.md: Project Structure 확장, 테스트 수 업데이트, --dashboard 플래그
- skills.md: DB 파일명 trades.db로 통일, Dashboard 명령어 추가
- requirements-log.md: 2026-02-16 문서 v2 동기화 요구사항 기록
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- MARKET_SHORTHAND + expand_market_codes()로 config "US" → schedule "US_NASDAQ/NYSE/AMEX" 자동 확장
- /report, /scenarios, /review, /dashboard 텔레그램 명령 추가
- price_change_pct를 trading_cycle과 run_daily_session에 주입
- HOLD시 get_open_position 기반 손절 모니터링 및 자동 SELL 오버라이드
- 대시보드 /api/status 동적 market 조회로 변경
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add --dashboard CLI flag and DASHBOARD_ENABLED env var to start
FastAPI dashboard in a daemon thread alongside the trading loop.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add observability dashboard: status, playbook, scorecard, performance,
context browser, decisions, and active scenarios endpoints.
SQLite read-only on separate connections from trading loop.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap evolution notification in try/except so telegram failures don't
crash the evolution loop. Add integration tests for market close flow.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Run EvolutionOptimizer.evolve() at US market close, skip for other
markets, and notify via Telegram when a strategy PR is generated.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>