44888d6ede
feat(signal_v2-phase3b): main.py lifespan loads ChronosPredictor
...
AppContext.chronos field. lifespan: if KIS_APP_KEY set, load
ChronosPredictor(model_name=settings.chronos_model). Exceptions
during load logged + signal_v2 continues without chronos (other
endpoints unaffected). poll_loop receives chronos param.
45 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 18:11:50 +09:00
9e5fecb369
feat(signal_v2-phase3b): post-close cycle + minute momentum update
...
scheduler._is_post_close_trigger: 16:00 KST ±1min detection (market day).
pull_worker:
- _run_post_close_cycle: daily fetch (60일) + chronos batch predict →
state.chronos_predictions + state.daily_ohlcv.
- update_minute_momentum_for_all: 매 cycle 마다 state.minute_momentum 갱신.
- poll_loop signature 확장 (chronos optional).
45 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 18:04:32 +09:00
28f9c8c3a6
feat(signal_v2-phase3b): chronos_predictor + 4 mock tests
...
ChronosPredictor wraps HuggingFace ChronosPipeline. Batch predict
returns ChronosPrediction(median, q10, q90, conf, as_of) per ticker.
Confidence = 1 - clamp(spread/2, 0, 1) where spread = (q90-q10) / |median|.
Lazy import of chronos lib (heavy). GPU auto-detect with FP16.
44 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 18:00:46 +09:00
c5a88fab66
feat(signal_v2-phase3b): momentum_classifier + 6 unit tests
...
aggregate_1min_to_5min: 1분봉 5개 → 5분봉 1개 (open=첫, close=마지막,
high=max, low=min, volume=sum). classify_minute_momentum: 직전 5개
5분봉 양봉 개수 + 거래량 60분 multiplier → 5-level
(strong_up/weak_up/neutral/weak_down/strong_down).
40 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 17:55:34 +09:00
7056cf2fa6
feat(signal_v2-phase3b): kis_client.get_daily_ohlcv (60 daily bars)
...
TR_ID FHKST03010100 (수정주가 일봉). KIS returns descending; client
reverses to ascending and trims to last N days.
1 new test, 34 total.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 17:49:06 +09:00
4ac7da8670
feat(signal_v2-phase3b): foundation — config + state + requirements
...
- config.py: CHRONOS_MODEL env (default amazon/chronos-2)
- state.py: PollState extended with daily_ohlcv + chronos_predictions
+ minute_momentum
- requirements.txt: transformers + chronos-forecasting
33 existing tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 17:46:09 +09:00
b690900cfc
fix(signal_v2-phase3a): V1 KIS env pattern + test isolation fix
...
config.py: kis_env_type (virtual/real) + KIS_REAL_*/KIS_VIRTUAL_* env
variables (V1 호환). kis_app_key/kis_app_secret/kis_account properties
auto-select based on env type.
main.py: KIS not-configured warning uses descriptive message including
env type + expected var name.
test_main.py: monkeypatch load_dotenv to no-op + setenv empty string
(instead of delenv) — defeats .env re-read on importlib.reload.
Pre-existing test_startup_warns_if_webai_api_key_missing also fixed.
33/33 tests pass (was 31/33).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 10:25:58 +09:00
d85512d036
feat(signal_v2-phase3a): main.py lifespan integrates KIS client + WS
...
AppContext extended with kis_client + kis_ws. lifespan:
- If KIS_APP_KEY set: create KISClient + KISWebSocket, fetch portfolio,
subscribe WebSocket H0STASP0 for holdings.
- If unset: WARNING log, signal_v2 still serves /health (no KIS data).
- Shutdown closes kis_ws → kis_client → stock client in order.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 05:21:55 +09:00
3ebe95ba29
feat(signal_v2-phase3a): pull_worker KIS minute cycle + WS callback
...
_run_kis_minute_cycle: portfolio + screener union 종목 분봉 fetch +
screener-only 종목 호가 REST fetch. WebSocket callback factory
(make_asking_price_callback).
poll_loop / _run_polling_cycle 에 kis_client optional param 추가
(Phase 5 까지 None 일 때도 정상 동작).
2 new tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 05:19:24 +09:00
163c9fb690
feat(signal_v2-phase3a): kis_websocket + 4 integration tests
...
KISWebSocket: approval_key (POST /oauth2/Approval) + H0STASP0 호가
실시간 subscribe + receive loop + exponential reconnect (1s → 2s → 4s
→ max 30s). _parse_asking_price 필드 인덱스 운영 검증 필요
(현재 가정: 마지막 2 필드가 ask_total/bid_total).
4 tests pass, 29 total.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 05:16:20 +09:00
27bf360b01
feat(signal_v2-phase3a): kis_client REST + 4 integration tests
...
KISClient: 분봉 (FHKST03010200) + 호가 (FHKST01010200) async REST.
V1 토큰 파일 (signal_v1/data/kis_token.json) read-only 공유, mtime
캐시. 초당 2회 throttle. exponential retry (max 3, 1s/2s/4s).
4 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 05:12:45 +09:00
eafa73edb1
feat(signal_v2-phase3a): scheduler NXT windows (20:00-23:30 / 04:30-07:00)
...
NXT 시간외 거래 시간대도 5분 cron 폴링 활성화. 23:30-04:30 dead zone
(KIS 점검) → 04:30 까지 skip. 기존 _seconds_until_next_market_open
(휴장일/주말용) 와 별개로 _seconds_until_nxt_or_market_open 신설.
3 new tests, scheduler suite 11 passed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 05:09:27 +09:00
68eb7b073c
feat(signal_v2-phase3a): config + state extensions for KIS data
...
- config.py: KIS_APP_KEY/SECRET/ACCOUNT/IS_VIRTUAL + V1_TOKEN_PATH env
- state.py: PollState extended with minute_bars (deque) and asking_price
- requirements.txt: websockets>=12
19 existing tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 05:06:40 +09:00
8342d38935
chore(signal_v2): sync holidays.json from stock + start.bat launcher
...
- holidays.json: authoritative copy from web-backend/stock/app/holidays.json
(replaces 13-date stub from Task 3; now 16 dates including Jan/May/Dec edges)
- start.bat: uvicorn launcher (cd to web-ai root, host 0.0.0.0, port 8001)
19 tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 03:55:41 +09:00
e47947fb69
fix(signal_v2): await cancelled poll_task + public cache_size
...
Code review fixes:
- main.py lifespan: await poll_task after cancel() to avoid client
close racing with mid-fetch task (CRITICAL).
- stock_client: add public cache_size() method; main.py /health uses
it instead of private _cache attribute (IMPORTANT).
19 tests still pass. Deferred to Phase 7 backlog:
- _ctx singleton test isolation (importlib.reload provides isolation in practice)
- poll_loop interval floor (interval >= 60 by design)
- shutdown logging
- response schema validation
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 03:52:45 +09:00
94c684bab8
feat(signal_v2): pull_worker + FastAPI app + 2 integration tests
...
poll_loop: asyncio.gather parallel fetch of 3 endpoints (portfolio,
news_sentiment, screener_preview) + state update. main.py: FastAPI
lifespan creates StockClient/SignalDedup/shutdown.Event then spawns
poll_loop as background task. GET /health reports status, last poll
times, cache size.
Signal V2 test suite: 19/19 PASS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 03:49:50 +09:00
1a6d9fcb39
feat(signal_v2): rate_limit + 3 unit tests
...
SignalDedup: 24h-rolling duplicate signal blocker. SQLite WAL +
busy_timeout=120000 standard fix (reference_sqlite_concurrency.md
pattern). PK (ticker, action) with UPSERT. Phase 4 (signal generator)
will call is_recent() before sending + record() after sending.
3 unit tests pass, total 17 signal_v2.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 03:46:59 +09:00
6cb5085118
test(signal_v2): add scheduler boundary tests at exact transitions
...
Code review noted missing boundary tests at:
- 09:00:00 (pre-market → market) → 60
- 15:30:00 (market → post-market) → 300
- 20:00:00 (post-market → overnight skip)
3 new tests, total 8 scheduler / 14 signal_v2.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 03:45:55 +09:00
fdabc69004
feat(signal_v2): scheduler + 5 unit tests
...
Time-window dispatcher: pre-market (07:00-09:00, 5min), market
(09:00-15:30, 1min), post-market (15:30-20:00, 5min), overnight skip
to next market day 07:00. Weekend + holiday detection via holidays.json.
Stub holidays.json with 13 dates. Task 6 will sync from
web-backend/stock/app/holidays.json.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 03:44:24 +09:00
90235497ae
refactor(signal_v2): narrow stock_client exception catch + remove dead code
...
Code quality review fixes:
- _cached_request: catch httpx.HTTPError instead of bare Exception
(avoid swallowing KeyboardInterrupt / asyncio.CancelledError)
- _request_with_retry: remove unused last_exc variable + dead post-loop
raise paths. Final sentinel raise preserved for mypy.
6 tests still pass. Deferred to Phase 7 backlog: cache concurrency
coalescing + __aenter__/__aexit__ context manager support.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 03:43:09 +09:00
8469bf7ffa
feat(signal_v2): stock_client + 6 integration tests
...
httpx async client with custom retry loop (max 3, exponential 1s/2s/4s),
memory dict cache (portfolio 60s / news-sentiment 300s / screener 60s),
X-WebAI-Key auth header injection. Stale fallback returns last
successful response with logger.warning on persistent failures.
6 integration tests pass with respx httpx mock.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 03:40:12 +09:00
8a2fac03a6
feat(signal_v2): foundation — config + state + requirements
...
- signal_v2/config.py: Settings dataclass loading web-ai/.env explicitly
- signal_v2/state.py: PollState dataclass + module-level singleton
- requirements.txt: httpx / fastapi / uvicorn / pytest-asyncio / respx
- .gitignore: signal_v2/data/*.db (WAL/SHM)
- empty tests/ marker
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 03:35:36 +09:00