poll_loop now accepts dedup + settings kwargs (backwards-compatible defaults).
After each in-window cycle (stock pull + minute momentum + optional post-close),
generate_signals is called to populate state.signals for downstream Phase 5
pickup. main.py lifespan wires _ctx.dedup + settings into the poll_loop task.
1 integration test added (anomaly-free stop_loss path via direct generate_signals
call, exercises the same code path that poll_loop runs).
56 tests pass.
- generate_signals now evaluates sell before buy; buy candidates with a same-cycle
sell signal are skipped (resolves silent overwrite of state.signals[ticker]).
- Added test_sell_signal_triggers_on_anomaly_path covering _try_anomaly path
(previously 0% covered).
- Fixed stale test comment referencing deprecated relative spread formula.
- _check_buy_hard_gate uses dict.get(..., 0) for defense against partial upstream state.
- _compute_buy_confidence clamps screener_norm to >= 0 for future Top-N changes.
ChronosBoltPipeline.predict_quantiles takes `inputs` positional, not
`context` keyword. Use positional with TypeError fallback for older
chronos versions.
FP16 caused inf overflow on Korean stock prices (e.g. 280,000원 >
FP16 max 65,504). Force FP32 for prices to avoid this. Chronos model
itself handles internal scaling.
Verified end-to-end: 60-day daily fetch → Chronos predict → quantile
output. Example 005930: median=-0.59%, q10=-8.9%, q90=+6.4%, conf=0.0
(low conf is mathematically correct when median is near zero relative
to distribution width).
45/45 tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ChronosBoltPipeline.predict() does not accept `context` kwarg; it
uses positional-only and is deterministic (no num_samples). Switch
to predict_quantiles(context, prediction_length, quantile_levels)
which returns (quantiles_tensor, mean_tensor).
Implementation: if hasattr(pipeline, "predict_quantiles") → modern
quantile branch. Else fall back to legacy sample-based predict (T5).
Tests: switch to predict_quantiles mock returning (quantiles, None)
with shape [1, 1, 3] for q10/q50/q90 directly.
45/45 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ChronosPipeline (legacy T5) does not support amazon/chronos-2 or
chronos-bolt-* (input_patch_size). Switch to BaseChronosPipeline
which auto-detects variant and returns the appropriate sub-pipeline
(ChronosBoltPipeline / Chronos2Pipeline / ChronosPipeline).
Also handle the dtype kwarg deprecation: try newer `dtype=` first,
fall back to `torch_dtype=` for older versions.
Test mock_pipeline fixture updated to patch BaseChronosPipeline.
45/45 tests pass. Verified amazon/chronos-bolt-base loads on CUDA.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
_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>
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>
- 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>
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>
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>
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>