130 Commits (722e5cf6a946199eda34bf26ea4389005a43f7cb)

Author SHA1 Message Date
I Luk Kim 5ea8850ac2 Fix paper trader startup: run missed run_open if market still open; improve poll_error logging
- AutoScheduler._run_catchup: if server starts after 9:35 AM ET but before
  market close (16:00 ET), and run_open hasn't already run today
  (checked via processed_phases), run it immediately instead of silently
  skipping it — prevents AVGO/event entries being missed on late starts

- filing_poller: log exc_type alongside error so empty-string exceptions
  (e.g. HTTPError()) are still identifiable by their type

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 86419beeb0 Fix ORB intraday data pipeline and consolidate strategy configs
- screener: switch from non-existent single-ticker endpoint to multi-ticker
  /alpaca/intraday batch calls (grouped by date, chunk ≤ 75); fixes 0-trades
- cache: bump version 2→3 to invalidate stale IEX Parquet files
- oracle_client: add get_multi_intraday_bars_today() for IEX real-time feed
- paper_trader: use /alpaca/intraday/today for live sessions, /alpaca/intraday
  for historical (SIP)
- intraday.py: define _BUILTIN_STRATEGIES={} to fix /api/orb/strategies import
- delete orb_p1–p10_winner + variant configs; add strategies/orb_default.yaml
  (Phase 10 params) as the single registered web strategy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 564bcba27c Add ORB pre-market screening and fix Oracle/Alpaca reliability bugs
- Add run_pre_screen() at 9:20 ET: fetch daily bars + enrichment + quality filter
  before market open, narrowing universe for faster orb_detect intraday fetch
- run_orb_detection() uses cached pre-screen data when available; falls back to
  full pipeline if pre_screen missed (late start, failure)
- Add _last_trading_day() helper to skip weekends/holidays for bars_end,
  preventing Alpaca 502 on Mondays (today-1 = Sunday was causing failures)
- Fix Oracle client chunk_size 300→75: Alpaca rejects 100+ ticker URL requests
- Add pre_screen event to build_schedule() at 9:20 ET and dispatch in _run_trading()
- run_session_now() runs pre_screen before orb_detect for efficiency
- Add ORB daemon, engine, models, state, screener, and intraday strategy configs
- Add intraday library (libs/intraday/) and web routes for ORB/intraday trading

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim f93ed2cfb5 Fix paper engine: prevent permanent rejection on transient order failures
Three bugs fixed:

1. _verify_order_fill now distinguishes alpaca_rejected vs order_timeout:
   - alpaca_rejected → record processed_event permanently (real problem)
   - order_timeout → do NOT record, allows retry on next run_next_open

2. Add _is_market_open() guard before every market buy submission:
   skips without recording so event retries when market opens

3. _parking_liquidate_for_event: sleep 1s → 3s after SGOV sell to give
   Alpaca time to settle cash; if plan still shows insufficient_cash after
   parking freed (race condition), skip without recording instead of
   permanently rejecting the event

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 658a741017 Route ORB bar data through Oracle API instead of calling Alpaca SDK directly
- libs/oracle_client/alpaca.py: Added get_multi_daily_bars() and
  get_multi_intraday_bars() helpers that call Oracle's /api/v1/price/data
  and /api/v1/alpaca/intraday endpoints respectively. Oracle handles
  symbol normalization (e.g. BF-B → BF.B) internally, so symbols like
  BF-B no longer crash the screening chunk.
- apps/paper_trader/alpaca_broker.py: get_bars() and get_intraday_bars()
  now use the new Oracle client helpers instead of the Alpaca SDK
  StockBarsRequest, eliminating direct Alpaca bar API calls from broker.
- apps/orb_trader/engine.py: Removed per-symbol BF-B workaround (now
  unnecessary since Oracle normalizes the symbol server-side); kept outer
  try/except for chunk-level resilience.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim aaa960c556 Handle invalid symbols (e.g. BF-B) gracefully in ORB detection
Daily bars chunk loop: on failure, retry symbol-by-symbol to isolate
and skip the bad ticker rather than crashing the entire detection.
Intraday bars chunk loop: catch and log failures, continue with rest.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 84e80fca75 Fix run_session_now: log all exceptions, never silently drop errors
- First log call moved to very top of run_session_now() so user always
  sees the task started, even if subsequent DB/engine calls fail
- Entire function body wrapped in try/except: errors go to scheduler log
  instead of vanishing in asyncio's unhandled-exception machinery
- Endpoint wraps create_task() in a logging shim (_task()) for the same reason
- 'already ran' path now logs a visible warning instead of silent return

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 407238e5d5 Fix '지금 시작': immediately run breakout check after ORB detection
Previously run_session_now() only ran orb_detect and injected future
scheduled events, but scheduled breakout windows were already past so
no breakout check ever fired.

Now: after orb_detect, immediately run one breakout check with current
snapshot prices (the core of '지금 시작'). All scheduled breakout events
are marked completed. Only stop_check and eod_exit events are injected
into the live schedule going forward.

Also improve alert message to clarify background execution (~1-2 min).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 55a9618b20 Add scheduler log clear button
- POST /orb/auto/clear-log: clears in-memory log lines and deletes
  the orb_scheduler.log file on disk
- ORBAutoScheduler.clear_log(): implements the wipe
- Log panel header now has an Eraser icon button on the right;
  disabled when log is empty

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 1d97595607 Fix ran_today: compare phase to 'idle' default instead of bool()
ORBDailyStateRow.phase defaults to 'idle' even when no DB row exists,
so bool(daily.phase) was True for brand-new sessions, hiding the
'지금 시작' button. Fix: ran_today = phase not in ('idle', '', None).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 76a5d70004 Add "지금 시작" button for late-added ORB sessions
- GET /orb/sessions now returns ran_today boolean (true if daily_state.phase
  is set, meaning engine ran ORB detection for today)
- POST /orb/sessions/{id}/run_today: fires ORB detection in background and
  injects remaining today-events (breakout, stop, EOD) into the live schedule
- ORBAutoScheduler.run_session_now(): coroutine that runs detection then
  splices session's future events into self._today_schedule
- Session card shows a cyan "지금 시작 (현재 가격 기준)" button when
  ran_today === false; hides it once detection has run

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 5bc648f4c7 Fix three ORB paper trading correctness bugs
- Breakout check interval: change from every-1-min to every sim_bar_minutes,
  matching the backtest bar aggregation frequency; align timeout base to
  market open (consistent with orb_simulator.py)
- Rejected/cancelled orders: add order_rejected flag so cancelled orders no
  longer fall through to position creation (phantom positions)
- Stop/EOD exit fill price: poll broker fill price after close_position()
  instead of recording at current_stop, capturing gap-through losses
- Stop/EOD close_position: pass qty=int(pos.shares) so multi-session
  same-ticker scenarios only close the current session's share count

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim a4cc280550 Fix Strategies page layout for narrow windows and remove alias chips
- Remove alias chips next to strategy names
- Use clamp() for responsive padding
- Add flex-wrap to header so title/buttons wrap on narrow screens
- Add flexShrink:0 to button group
- Use min() for search input width

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 426de9038d Fix lookback entry bugs: current-price sizing + MHD expiration filter
Bug #2 (paper trader): lookback entries sized using historical entry_price_est
but filled at current market price, causing cash overdraft. Fix: override
entry_price_est with get_latest_bars() close before entering _process_entries.

Bug #3 (paper trader + backtester): paper trader was missing the per-candidate
MHD expiration check that the backtester already had. Also adds
lookback_min_remaining_days (default 3) to reject candidates with too little
holding time remaining — prevents entering a position the day before forced exit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 236148de2e Remove momentum breakout sleeve (overfitting, valid -31%) and revert related code
- Delete v7.360-v7.363 experiment configs (rotation/momentum tests)
- Remove _schedule_momentum_breakout_candidates() from backtester run.py
- Remove MomentumBreakoutConfig from domain.py
- Delete momentum_calendar.py, momentum_screener.py, build_momentum_calendar.py
- Delete data/momentum_calendar/ parquet data

Valid period performance was -31.36% vs +152.4% baseline — sleeve is not viable
without walk-forward validation. Abandoning for now.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim b3ba914a8d Optimize v7 strategy: v7.356 achieves CW 2159% + SQS 90.7 (Pareto improvement over v7.314)
Key changes from v7.314 baseline (CW 2012%, SQS 90.0):
- max_position_value_pct 15→25, non_a_tier_target_1_fraction 0.2→0
- max_daily_new_risk_pct 30→50 (via v7.330, CW champion 2148%)
- bullish_raised_recovery per_trade_risk_pct 0.71→0.55 (DD improvement)
- bullish_raised_recovery max_holding_days 12→10 (sweet spot, +98pp CW)

Result: v7.356 CW 2159% (+147pp), SQS 90.7 (+0.7), risk 66.2 (+2.9), robustness 94.3 (+0.5)
All metrics improved simultaneously — return increase AND DD reduction achieved.

Also includes: web UI updates, pipeline scripts, v16/v17/v18/v19 experiment pruning,
Form4 preset additions, snapshot registry updates, domain.py enhancements.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 5496059b6c Add v7.120 composed GLD experiment updates 4 months ago
I Luk Kim 1c46569f75 Expand CLI help to include all missing commands
누락된 명령어(overfit-check, scenario-test, refresh, web, check-duplicate,
compute-sqs, rescore-public, attach-* 5개)를 모두 추가하고,
섹션별(저널·첨부·분석·트레이딩·실험관리)로 그룹화하여 가독성 개선

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim f21caf23cb Prune Form4 experimental presets 4 months ago
I Luk Kim 4b1d9afde7 Prune unused sleeve presets and trim web preset lists 4 months ago
I Luk Kim e415743444 Use SnapshotStore for paper trader next_open candidate selection
PaperTradingEngine now accepts an optional SnapshotStore and uses it
for run_next_open candidate fetching, ensuring live candidate selection
matches the backtester's pre-computed scores exactly. run_reaction_close
keeps EventDetector for real-time intraday event detection. Adds
load_snapshot_store_for_session() helper with auto-refresh logic.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 969dedc635 Speed up snapshot refresh: batch prefetch, unbuffered output, incremental-first
- enrich_tier2: prefetch price bars (parallel ThreadPool) and short ratio
  (single batch DB query) instead of per-row HTTP/DB calls (~20min → ~2min)
- canonical_snapshots: add PYTHONUNBUFFERED=1 to enrichment subprocesses
  so progress output is visible in real time
- backtest_sim: use incremental_update_canonical_snapshot when existing
  snapshot is present, falling back to full rebuild only when needed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 5cb2b9fcb8 Add non-core allocator v2 and Form4 freshness presets 4 months ago
I Luk Kim e38c314a09 Add ownership/risk-off sleeves, v17-v19 experiments, and web app restructure
New features:
- Ownership 13D/13G residual-cash sleeve with PIT calendar and quality filters
- Risk-off alpha sleeve (GLD/DBC rotation on crisis regime signals)
- Crisis relay target in parking: evaluates before defensive relay
- Bearish symbol allocation split (bearish_alloc_pct + sgov remainder)
- Alternative defensive ETF candidate (cash_parking_defensive_alt_symbol)
- Composite eval and engine ablation tools
- experiment and overfit CLI apps

New experiments:
- v17.x series (v17.1 champion SQS 78.4; v17.5–v17.129 exhausted)
- v18.x and v19.x families from v12.8 OOT defense branch
- v7.119 composed variants (idle alpha + ownership + risk-off sleeves)
- parking_only configs: bufb, jepq, merix, regime_tiered
- empty_strategy baseline config

Web app:
- Restructured into routers/services modules (experiments, leaderboard, runs, sqs, docs)
- Ownership sleeve and risk-off sleeve controls in backtest UI
- Frontend: ComposeStrategy page, tradeSleeves lib, idle decomposition display

Research tools:
- Ownership 13D/13G probe and PIT cache builder
- Dividend capture probe and cache builder
- Insider Form4 idle alpha probe
- Alternative ETF parking probe, put-spread overlay probe
- Wikimedia low-attention and peer-relative idle alpha probes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 4c5798913b Extend lookback entry to paper trader and mock broker
Live paper trader (engine.py):
- On first run_next_open per daemon session, call get_candidates_for_lookback()
  to fetch events from [today - max_mhd*2, today) that are still active
- Skip gap-cap check for lookback entries (multi-day drift ≠ overnight gap)
- Initialize days_held to elapsed trading days when saving strategy state

EventDetector (event_detector.py):
- Extract shared enrichment logic into _enrich_raw_rows(raw_rows, bar_end_date, config)
- Add _fetch_events_for_date_range(start, end): single DB query with entry_date range
- Add get_candidates_for_lookback(today, start_date, config): annotates each row
  with is_lookback_entry=True and lookback_days_elapsed=N

Mock broker (backtest_sim.py):
- Extend slice_by_date_range start backward when lookback_entry_enabled, mirroring
  the same logic already present in apps/backtester/run.py main()

Verified: BX/EBAY/ENB all entered 2026-03-30 via lookback in both research
backtest and mock broker. Parking, idle_alpha, form4 sleeves unaffected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 5056295cb6 Add lookback entry feature for bounded backtests
When a backtest starts mid-stream (via --start), events that fired
before the start date but are still within their max_holding_days
window can now be entered on the first simulation day.

- Add `lookback_entry_enabled: bool = False` to ExecutionConfig
- On first sim day, _collect_lookback_candidates() gathers pre-start
  events, runs them through the same select_candidates() pipeline,
  and injects them before normal candidates
- Entry fills at the first day's open price; gap-cap check is skipped
  since the event is multi-days old
- days_held is initialized to the elapsed trading days so TIME exits
  fire at the correct time relative to the original event date
- Store slice is extended backward by max_mhd calendar buffer so
  pre-start rows survive slice_by_date_range when feature is enabled
- Enabled in return_max_long_v7.119 for testing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim eb20c32a94 Fix QQQM parking signal prefix regression 4 months ago
I Luk Kim ea9f156eeb Tune Form4 sleeve quality filters and reserve sizing 4 months ago
I Luk Kim 72681e69e5 Add Form4 residual-cash sleeve and UI support 4 months ago
I Luk Kim f2113b7e06 Fix cash parking phantom-money bug + live engine parking liquidation for events
Backtester (run.py):
- cash_available = (self._cash + parking_value) * multiplier caused trades to be
  approved even when self._cash ≈ 0 (all money in SGOV/QQQ).  Trades executed
  by deducting from self._cash → negative cash (phantom money).
- Fix: after simulate_entry, if self._cash < actual trade cost and parking exists,
  call _liquidate_parking_for_cash(shortfall) before deducting from cash.
- Verified: 2022-2026 backtest with qqqm_low_dd shows 0 cash_negative events.

Live engine (engine.py):
- Add _parking_liquidate_for_event(): frees parking cash to fund event entries.
  SGOV (virtual) reduces entry_value in DB; QQQM/QQQ sells real shares via broker.
- Both entry loops (engines mode + flat/reaction_close mode) now attempt parking
  liquidation when plan.skip_reason == "insufficient_cash" before giving up.

Also includes prior session work (accumulated since last commit):
- 6 novel parking gate signals: VRP, Market Temperature, Hurst exponent, Rolling
  Kurtosis, Return Autocorrelation, SPY-QQQ Correlation (composite risk score v2)
- QQQM parking symbol support (lower expense ratio vs QQQ)
- Snapshot auto-refresh + bar extension cache (pickle) to avoid 10-min re-fetches
- Bar extension clamps to last market-closed date (ET 4PM check)
- fithia2 refresh command; --no-refresh flag for paper backtest
- Paper backtest macro extension beyond last event date (parking-only periods)
- parking_state DB schema: 7 new columns (peak_price, gate_in_sgov,
  committed_target, pending_target, pending_days, sgov_entry_value, sold_today)
- Live engine: target confirmation (2-day), top-up drawdown gate, trailing stop,
  SGOV interest accrual, full 6-signal gate evaluation
- New PARKING_PRESETS: qqqm_low_dd, composite_v2, vv_24_vrp8, vt_24_t13, etc.
- Web GUI / CLI result parity fix (Oracle URL via get_settings().stock_oracle_url)
- Force-close uses last_exec_date (has bar data); parking liquidates at last_date

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim 9cb91ee846 Add synthetic scenario robustness testing system
Builds a full synthetic market data pipeline to test strategies against
12 diverse market regimes (bull/bear/crash/chop/rotation/liquidity drought)
that may not exist in historical data. Computes Regime Robustness Score (RRS)
to detect overfitting and environment-specific fragility.

- libs/backtest/scenarios/: price_gen, macro_gen, event_gen, coupling,
  store_builder, scenarios (12 pre-built), robustness (RRS)
- apps/scenario/cli.py: `fithia2 scenario-test` with Rich output
- apps/tracker/cli.py: scenario-test command routing
- tests/: 83 unit tests across 3 new test files
- docs/scenario_test.md: usage guide and result interpretation
- docs/research_workflow_and_handoff.md: Step 5.5 scenario test added

Fix: no_signal scenario uses drift=0% (was +10%) for fair signal integrity scoring.
Fix: synthetic candidates now carry macro_vix/macro_hy_spread from macro_by_date
     to pass selector engine filters.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim 0928eb2428 Fix direct backtest mode UX: live log, inline results, trade table sort/filter
- Fix log endpoint to serve .direct.log for direct mode tasks
- Fix _parse_dates: 4-digit start with no end now defaults to today
- Fix frontend year mode to send start=YYYY-01-01 instead of year param
- Replace DirectModePanel with DirectModeTaskView: live terminal log while
  running, inline results (metric cards + equity chart + trade blotter) on
  completion, collapsible log
- Add trade table sort/filter: symbol, engine, exit reason filters, Win/Loss
  toggle, sortable columns (No., PnL, entry/exit price), stats bar
- Add No. column showing original trade order for sort restoration
- Add BacktestDirectResultsPage at /backtest/direct-results/:taskId
- Add Results button in task list for has_direct_result tasks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim c5dea9a9a8 Add Paper Trading web GUI with ANSI terminal log rendering
- Full paper trading page (sessions sidebar, 5-tab detail view)
- Auto daemon panel: status, schedule, start/stop, live log
- Auto daemon detection for terminal-started processes via psutil scan
- Log source detection: process stdout file → web GUI log file → TTY hint
- ANSI color rendering for paper task logs and auto daemon log
- Dark terminal theme (matching backtest log style) with macOS traffic lights
- Extracted ansiToHtml to shared lib/utils.ts (deduped from Backtest.tsx)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim 8dfebcf5fe Add v6new.311-322, named strategy configs, and tracker common_window fix
- v6new.311-322: latest experiment iterations
- Named configs: baseline, conviction, core_boost, docgate, entropy_safe, quick_cut
  (derived from best-performing v6new variants for production reference)
- Rename v6new.29_mom2 → baseline_v6new.29
- tracker show: add common_window_summary field
- Journal: update leaderboard, experiment registry, improvement journal

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 98923e8620 Add Phase 6 reconciliation: position integrity, kill switch, order fill verification
- ReconciliationReport dataclass tracking orphaned/ghost positions and stale orders
- _cancel_stale_orders(): cancel leftover open orders at daily run start
- _reconcile_positions(): detect Alpaca vs local state mismatches; auto-close ghost positions with RECONCILED exit reason
- _verify_order_fill(): poll broker up to 2s to confirm market order fill before saving state
- _check_kill_switch(): activate and persist kill switch at 25% drawdown; blocks new entries
- run_daily() and _process_entries() wired with all safety checks
- 18 unit tests covering all reconciliation scenarios

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 76581ead04 Remove overlay backtesting and scoring 5 months ago
I Luk Kim 493b8a8d69 Add --overlay shorthand for lb command and gitignore *.db files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim a81b3a6ac4 Update tracker, leaderboard, docs, and overlay leaderboard
Additional tracker/leaderboard updates, overlay leaderboard, and
documentation improvements.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 2aba6418e6 Add overlay engine, ranking models, snapshot pipelines, and research tools
New libs: overlay curve builder, ranking models, continuation/merged
snapshot export, intraday features. New tools: overlay evaluator,
ranking model builder, deep evaluation, fullsplit batch runner.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 9b92ab6589 Add paper trading system: broker integration, state management, reporter
New modules for live/mock broker interface, SQLite session state,
auto-trading engine, and backtest result reporting.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim ce2150789d Fix leaderboard performance regression (60min → 12s) and clean up CLI help
Replaces per-experiment rglob with single-pass manifest/metrics indexing
and adds lru_cache. Removes rarely-used commands from help display.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 057a311572 Improve paper backtest: overlay support, --top/--rank/--year options, speed optimization
Adds overlay strategy backtesting, flexible date parsing, --no-trades flag,
--rank range selection, session management improvements, circuit breaker
for screener failures, and bars_cache passthrough for 10x speed gain.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 784c581f19 Enhance backtest engine: v11 scoring, selector expansion, snapshot store improvements
Extends selector with new scoring model support, adds execution
enhancements, and improves snapshot store loading and split handling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 57d38ecfe0 Add earnings surprise feature pipeline and snapshot export improvements
Adds earnings surprise extraction to parser/features/labeler pipeline,
improves filing fetcher robustness, and extends snapshot export with
new field support.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim e2229646e6 Add oversold bounce engine experiments (v6new.259-264) — blocked by architecture
Bounce engine (buy negative reaction, bet on mean reversion) could not
execute: system architecture ties scoring to single model per backtest,
and selector/store indexes are optimized for positive-reaction PEAD.
Negative-reaction candidates get score=0 from PEAD scoring, blocking
engine selection regardless of engine-level threshold overrides.

Implementing bounce trades requires: dual scoring model support,
selector changes for negative-reaction candidate routing, and
store indexing changes. Deferred to future refactor.

Current best CW return: 293.2% (v6new.255)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 090bfa8e36 Add contrarian feature analysis + v15 scoring (v6new.174-188)
Data analysis revealed OBV Q1 (distribution) has 56.4% WR vs Q5 51.2% —
contrarian signal confirmed. Previous OBV bonus was applied in wrong
direction. Corrected with v15 scoring models.

Best result: v6new.185 (entropy + risk 0.058) CW 274.4% but SQS 72.2,
still below v6new.122 (72.4). WFV/robustness offsets CW gains.

v6new.122 confirmed as optimal under current SQS v4 formula.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 1e66b67c7a Add technical/scientific feature experiments (v6new.106-173) and v6new.122 SQS 72.4
Tier 1: Vol/RSI/BB/OBV features — sizing scalers hurt public SQS, scoring
adjustments ineffective on 28-30 trades. Only doc_quality gate lowering
(0.66→0.55) improved results (+2 trades, +0.8 SQS).

Tier 2: Hurst exponent, Shannon entropy, sector momentum — entropy bonus
CW +10.7pp but SQS equivalent (72.3 vs 72.4). Sector momentum hurt badly.

Tier 3: OU theta, gravitational pull, market temperature — all caused
large CW return drops (-80 to -103pp). Physics-based indicators don't
fit event-driven PEAD.

Best result: v6new.122 (SQS 72.4, #3 leaderboard) = v6new.29 + doc quality
gate 0.66→0.55. Single parameter change outperformed all feature engineering.

New code:
- libs/features/market_features.py: 9 new features (vol, RSI, BB, OBV,
  Hurst, entropy, OU theta, gravitational pull, market temperature)
- libs/backtest/scoring.py: v12-v14 scoring models with technical gates
- libs/backtest/allocator.py: volatility + conviction size scalers
- libs/backtest/domain.py: volatility_size_scaler + conviction_boost config
- scripts/enrich_*.py: snapshot enrichment scripts
- 68 experiment configs (v6new.106-173)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim fecdc12007 Add earnings surprise feature pipeline and v11 scoring
New data source integration:
- EarningsSurpriseService: GET /api/v1/earnings/surprise/{symbol}
  Returns actual vs estimated EPS with surprise_percentage
- Feature builder: creates earnings_surprise_v1 snapshots for earnings events
- Backfill script runs for existing 1,273 tickers (Alpha Vantage rate limited)

New scoring (v11):
- Small beat (0-3% surprise): +10% bonus (82.4% WR in sample)
- Medium beat (3-8%): +5% bonus
- Big beat (>8%): no bonus (already priced in)
- Miss (<=0%): -5% penalty

Signal validation (n=66 sample):
  Small beat: 82.4% WR, +1.79% mean 5d return
  Big beat: 54.8% WR, +0.47%
  Miss: 55.6% WR, -0.10%

Backfill running (~4 hours). Experiment pending data completion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 07bcfbe51d Complete engine unification Phase 5-6: residual reserve + macro data
Phase 5 — Engine selection (both entry paths):
  - Added residual_reserve_selected tracking between engines
  - Added prelimit amplification (5x) for attention-requiring engines
  - Added truncate_to parameter to select_candidates calls
  Matches BacktestRunner._select_candidates_for_date() behavior.

Phase 6 — Macro data:
  - Added FRED series fetch (VIXCLS, BAMLH0A0HYM2) to _fetch_macro()
  - Matches SnapshotStore._fetch_macro() which loads from MacroObservation DB
  - Enables VIX/HY regime sizing in live paper trading

All 6 phases of BacktestRunner ↔ PaperTradingEngine unification complete.
450 unit tests pass. Multi-strategy paper backtest verified.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 26ca89c058 Integrate shared trade logic into PaperTradingEngine (Phase 3-4 complete)
PaperTradingEngine now uses:
1. AttentionFilterService after select_candidates (2 entry paths)
   - run_reaction_close: attention filtering added
   - run_next_open: attention filtering added
2. build_effective_execution_config via _resolve_execution_config delegation
   - Replaces 60-line inline implementation with shared function
   - Adds adaptive exit support (was missing)
   - Adds event_type_profile max_holding_days (was missing)
3. check_next_open_gap_cap before order submission
   - Rejects excessive gap-up entries (was missing)

This eliminates divergences #2 (attention), #3 (execution config),
and #4 (gap cap) from the unification plan. All 450 tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim fb4fec7dac Unify BacktestRunner and PaperTradingEngine trade decision logic
Phase 1-4 of engine unification to eliminate research/live divergence.

Phase 1 — Scoring (event_detector.py):
  EventDetector now uses config's scoring_model (v5/v9 etc.) when
  event_v1 features are present (parse_confidence_overall not null).
  Falls back to compute_entry_score only for incomplete events.

Phase 2 — Execution config (execution.py):
  Extracted build_effective_execution_config() as shared function.
  BacktestRunner delegates to it. PaperTradingEngine can now use
  identical per-engine overrides, adaptive exit, tiered targets.

Phase 3 — Attention filtering (attention.py):
  New AttentionFilterService class extracted from BacktestRunner.
  Provides: engine_requires_attention, apply_filters, rescoring.
  BacktestRunner now delegates to this service.
  PaperTradingEngine can import and use the same service.

Phase 4 — Gap cap (execution.py):
  check_next_open_gap_cap() shared function for next-open gap rejection.

All 450 unit tests pass. Paper backtest verified working.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim c747d5e4f0 Fix snapshot path resolution for paper backtest
Snapshots can be in data/parquet/ or data/datasets/snapshots/.
Now tries default parquet_dir first, falls back to data/datasets/snapshots/
if the snapshot exists there instead.

Fixes FileNotFoundError when running multi-strategy paper backtest with
configs that reference snapshots in the alternate directory.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 0134476632 Fix async/sync conflict in paper backtest — nested event loop error
run_backtest changed from async to sync function. Pipeline refresh
(async) runs via asyncio.run() before the sync BacktestRunner,
avoiding nested event loop when SnapshotStore.load() calls asyncio.run().

CLI updated to call run_backtest() directly (no asyncio.run wrapper).

Tested: `fithia2 paper backtest --config v6new.24 --start 2025-03-23 --end 2026-03-23` works.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 33acf12baa Remove snapshot refresh fallback — fail hard if pipeline update fails
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 3c65c72a72 Auto-refresh snapshot when paper backtest end_date exceeds snapshot coverage
When `fithia2 paper backtest --end <date>` requests a date beyond the
snapshot's latest event, automatically runs the pipeline:
1. Filing poller (discover new 8-Ks)
2. Filing fetcher (download exhibits)
3. Event parser (parse events)
4. Feature builder (compute features)
5. Label generator (compute labels)
6. Dataset export (re-generate Parquet snapshot)

Staleness check: snapshot is stale if its latest event_date is >14 days
before the requested end_date, or if the manifest is >7 days old.

If refresh fails, falls back to existing snapshot data gracefully.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim d7ecaf97dc Refactor paper backtest to use BacktestRunner — eliminates engine divergence
Major refactor: `fithia2 paper backtest` now uses the exact same
BacktestRunner + SnapshotStore pipeline as `apps/backtester/run.py`.

Before: PaperTradingEngine + EventDetector + MockBroker
  - Different scoring (compute_entry_score vs config scoring_model)
  - Different data source (DB + Oracle vs Parquet snapshot)
  - Different feature computation (real-time vs pipeline)
  → Config gate changes didn't take effect in paper backtest

After: BacktestRunner + SnapshotStore (Parquet)
  - Identical scoring, engine matching, position sizing
  - Same Parquet data as research backtester
  - Config changes work identically in both systems

Trade output format preserved for reporter.py compatibility.
PaperTradingEngine still used for live Alpaca trading (unchanged).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 0797535047 Revert DB-first feature fix — Oracle real-time enrichment must be primary
The DB-first approach (prefer feature_json over Oracle recalculation) caused:
- LMND (+$782) and M (+$1,052) trades to disappear
- TEM loss to increase from -$321 to -$535
- Overall PnL drop from +$5,948 to +$3,078

Root cause: DB features were computed at a different time with different
Oracle data. When paper trader used DB values, the feature values didn't
match what the backtester's Parquet snapshot had, causing different
engine gate outcomes.

Paper trader must use Oracle real-time enrichment as primary source
(same as the original design). The volume_ratio_20d field name fix
is retained as that was a genuine bug.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim f0baa48e7b Revert paper trader scoring to compute_entry_score — fix 2025 trade loss
The _compute_score → v5 dispatch caused v5's hard gates to reject almost
all events (v5 requires specific direction/guidance combos). This killed
all 2025 trades in paper backtest.

Root cause: BacktestRunner and PaperTradingEngine use different flows.
BacktestRunner applies scoring AFTER engine selection (engines have
score_threshold_override=0.0 that bypasses score gates). But EventDetector
applied scoring BEFORE engine matching, causing v5's hard gates to reject
events that engines would have accepted.

Fix: revert to compute_entry_score for EventDetector. Score is ranking-only
in paper trading; engine gates (reaction_min, close_min, etc.) handle filtering.

The volume_ratio_20d fix and DB-first feature fix remain in place.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 2bb5fe1c37 Fix paper trader / backtester consistency: DB-first features + config-aware scoring
Three critical inconsistencies between BacktestRunner and PaperTradingEngine
that caused gate fixes to not work in paper trading:

1. DB feature values now take priority over Oracle recalculation
   - Previously: Oracle bars always recomputed reaction_day_return etc.
   - Now: if DB feature_json has the value, Oracle fallback is skipped
   - Root cause of PII bug: DB had react=-5.3% but Oracle recomputed +13.9%
     due to different date alignment, bypassing engine reaction_min gate

2. Scoring now uses config's scoring_model (v5/v8/v9/v10 etc.)
   - Previously: always used compute_entry_score() regardless of config
   - Now: _compute_score() dispatches to the correct scoring function
   - Ensures hard gates and weights match between backtest and paper trading

3. volume_ratio_20d field name consistency (from prior commit)

These fixes ensure paper trading results match backtester behavior,
making engine gate changes (reaction_min, close_min, etc.) effective
in both systems.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 4de842ba89 Fix paper trader volume_ratio field name mismatch with selector
EventDetector computed volume_ratio as fallback but selector checks
volume_ratio_20d. When DB feature_json was missing this field, the
volume gate was silently bypassed in paper trading — allowing trades
like LKQ (vol=0.8) that the backtest correctly blocks.

Now sets both volume_ratio_20d and volume_ratio for consistency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 8dacdaab4e Add v10 macro regime scoring (VIX+HY) and FRED macro features — SQS 59.1 (rejected)
New features:
- _enrich_macro_features() in snapshot_export: adds macro_vix, macro_hy_spread from FRED
- compute_return_max_long_score_v10: +12% bonus in favorable regime (VIX>18+HY>3.25)
- _macro_regime_score(): regime-aware scoring component

Findings:
- VIX signal is statistically strong: 62.3% WR (VIX>18+HY>3.25) vs 50.8% (other)
- But scoring bonus promotes marginal trades, diluting OOS quality
- Same pattern as eps_growth, drift bonus: raw signal ≠ scoring improvement
- v6new.17 SQS 59.1 < v6new.9 SQS 63.3

v6new.9 remains best at SQS 63.3 after 17 experiments.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 85c4d98987 Add v6new experiment suite: scoring v8/v9/v9g, cross-event drift, coverage engines
Scoring additions (libs/backtest/scoring.py):
- v8: conditional financial bonus (eps_growth_qoq/revenue_growth_qoq)
- v9: cross-event drift momentum (+/-10% from prior same-ticker 5d return)
- v9g: gated variant — reject events with negative prior drift

Snapshot export (libs/export/snapshot_export.py):
- _enrich_prior_event_drift: computes prior_event_fwd5d for all snapshots
- smallcap-liquid-long-v1 universe profile ($500M-$2B)
- market_cap_max support in screener and filtering

8 experiment configs (v6new.1-v6new.8):
- v6new.1: unknown event reclassification (neutral)
- v6new.2: financial features (neutral, EPS growth is noise)
- v6new.3: small-cap (blocked, survivorship bias)
- v6new.4-6: cross-event drift variants (rejected)
- v6new.7: engine pruning (quality up, count down)
- v6new.8: coverage expansion with 2 new post-market engines (best result)

Best result: v6new.8 SQS 41.5 vs v6.29 control 32.3 on same conditions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 02542248b7 Add attention-aware PEAD caps and promote step75 5 months ago
I Luk Kim b507fbf499 Add attention client and continue PEAD research 5 months ago
I Luk Kim c646303423 Recalibrate public SQS and exposure-aware tracking 5 months ago
I Luk Kim 9ec0b26e10 Implement multi-engine PEAD strategy research workflow 5 months ago
I Luk Kim 2395a0c0c3 feat: PEAD mid-cap strategy + pipeline hardening + README cleanup
- Implement PEAD 7% Long+Short strategy with mid-cap universe expansion
- Add Stock Oracle screener/company clients, text sentiment features
- Enhance backtest engine: short-side execution, walk-forward CV, MFE/MAE analysis
- Harden pipeline: sequential Oracle API calls, scoring recalibration (event_quality 65%)
- Add experiment configs for 60+ strategy variants and journal tracking
- Add review/analysis CLI tools
- Remove obsolete dev/phase0-4 design documents and analysis scripts
- Clean README to reflect only implemented features (remove unbuilt adapters/engines)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim cb19afa87b feat: add strategy improvement tracking system (SQS + journal + leaderboard)
Track experiment cycles with SQS scoring (0-100), JSONL journal, and
auto-generated leaderboard to prevent duplicate experiments and enable
data-driven strategy decisions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim 191653394d feat: scoring cleanup — alpha-only composite, default-deny unknown events, new exit/risk features
Remove 5 non-alpha features (earnings surprise, risk penalty, parse confidence,
direction clarity, LM sentiment) from composite score to eliminate double-counting
with hard gates and noise sources. Redistribute weights to 5 alpha features.

Add default-deny for unknown event types, no-follow-through early exit (D+1),
kill switch log-only mode, macro regime size scaler. Remove SUE gate (Gate 8).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim 3779be78e3 fix: kill switch research mode reset + adjust veto threshold
- Fix kill switch reset: remove unreachable drawdown recovery condition
  (equity can't change while trading is halted), reset peak_equity and
  drawdown_pct to 0 on cooldown expiry
- Raise veto_oneoff_penalty threshold 0.5 → 0.7 (was blocking 67% of
  candidates due to high median oneoff_penalty in dataset)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim 4d0e773ba0 feat: overhaul strategy — document quality > price momentum
Flip scoring weights so event/document quality is primary signal (55%)
and market confirmation is secondary (35%). Add research mode with
kill-switch cooldown/reset, veto gates for bad events, reduced portfolio
risk, and 4 diagnostic analysis scripts.

Phase A: Research mode kill-switch reset, risk reduction (0.5%/trade,
max 4 positions), bullish-only direction for all event types.

Phase B: 2 new sub-scorers (parse_confidence, direction_clarity),
4 veto gates (oneoff risk, parse confidence, unknown/bearish direction).

Phase C: signal_quality, event_type_decomposition, kill_switch_impact,
concurrent_position analysis scripts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim cdf6ae3493 feat: Phase 5 fundamental strategy improvements
Fix core strategy flaws identified from academic research and Phase 4
backtest results (23% win rate, 0% target hits, 77% stop exits).

5A — Exit mechanics: ATR-based targets (reachable ~4.5% vs unreachable ~6-8%),
     partial profit-taking at target with breakeven stop on remainder,
     wider catastrophic stop (3.0 ATR), trailing stop enabled by default.
5B — Event-type-specific logic: EventTypeProfile with per-type overrides
     for holding days, ATR multipliers, score thresholds, direction filter.
     Disabled management_change and other_material_event (low evidence).
5C-1 — Expanded universe from 15 to 97 symbols across sectors including
       mid-cap growth where PEAD is stronger.
5C-3 — Bootstrap 95% confidence intervals for key trade metrics.
5D — SUE integration: earnings surprise scoring (eps_growth_qoq) at 10%
     weight, entry gate blocks negative EPS surprise for earnings events.
5F — Extended label horizons to 10D/20D with Alembic migration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim d4ae900286 feat: expand data pipeline and integrate event features into scoring
Data expansion:
- Poll SEC 8-K filings from 2025-10-01 to 2026-03-12 (was ~2 months)
- Pipeline: 36 new filings → 24 new events → total 44 events, 15 symbols
- Re-export with merged features (market_v1+event_v1+financial_v1)
- Parquet columns: 7 → 41 (adds signal_strength, guidance_direction,
  document_quality, oneoff_penalty, eps_growth_qoq, etc.)

Score model v2:
- Add event quality component (15% weight): signal_strength_score,
  guidance_direction_score, document_quality_score
- Add risk penalty component (10% weight): inverted oneoff_penalty
- Rebalance market weights: reaction 25%, close 25%, volume 15%, gap 10%
- Graceful degradation when event features are absent (returns 0.5)

Export pipeline:
- Add --feature-versions CLI flag to merge multiple feature types
- export_dataset_snapshot() accepts feature_versions list parameter
- Groups features by event_id and merges feature_json dicts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim d387d567c5 fix: correct equity calculation — use market value, not unrealized PnL
equity was computed as cash + unrealized_pnl where unrealized_pnl =
(close - entry) × shares. Since cash already had entry cost subtracted,
this double-counted the cost basis:

  buggy:   equity = (initial - entry×shares) + (close - entry)×shares
                  = initial + close×shares − 2×entry×shares  ← WRONG

  correct: equity = cash + market_value
                  = (initial - entry×shares) + close×shares
                  = initial + (close − entry)×shares          ← RIGHT

This caused drawdown to spike to ~73% the instant a position opened
(e.g. TSLA $330 × 222 shares → equity appeared to drop from 100k to
27k), falsely triggering the kill switch at 25% and blocking all
subsequent entries.

Before fix: 3 trades, +0.08% return, 39.2% max drawdown (fake)
After fix:  10 trades, -2.63% return, 4.24% max drawdown (real)

Also: when bar data is missing, positions now use entry_price as
fallback market value instead of treating the position as worthless.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim 867d70afae fix: correct simulation loop and equity curve calculation after real-data testing
- BacktestRunner.run() now iterates all NYSE trading days (not just candidate
  days) via SnapshotStore.all_trading_days() so stop/target/time exits are
  checked every day, not only on days with new candidates
- Record initial DailyPortfolioState before simulation loop starts so
  total_return_pct is computed relative to the true initial equity (100k),
  not the first post-entry equity snapshot
- SnapshotStore._fetch_event_metadata() now synthesises event_timestamp from
  event_date + 21:00 UTC when filed_at_utc is NULL (transparent enrichment at
  loader boundary, not silent substitution in selector)
- SnapshotStore._async_load() maps event_close → entry_price_est when the
  column is absent, and derives score from abs(reaction_day_return) when the
  Parquet snapshot has no score column
- Add --snapshot-dir CLI flag to BacktestRunner to override the default
  parquet_dir base path (needed for non-standard snapshot locations)
- Fix integration test assertion: total_trading_days >= 2 (was == 2)
- Add configs/experiments/realdata_test_v1.json for real Phase 3 snapshot runs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim 2f4d9f61f7 feat: implement Phase 4 -- event-driven backtester
Full backtesting engine that reads Parquet snapshots and simulates a
swing-trading strategy with no look-ahead bias.

## New modules (libs/backtest/)
- domain.py: All Pydantic v2 models (Candidate, PlannedOrder, FilledTrade,
  OpenPosition, DailyPortfolioState, MetricsBundle, BacktestConfig, etc.)
- calendar.py: Thin wrappers over time_utils + reaction_date
- manifests.py: Config load/deep-merge/validate, run-ID generation
- metrics.py: 21 pure-function metrics (no pandas, stdlib statistics only)
- selector.py: build_candidate(), rank_candidates() (score↓ ADV↓ symbol↑)
- allocator.py: 7-gate run_entry_gates(), ATR stop, floor() shares
- execution.py: simulate_entry/exit(), update_trailing_stop() (ratchet-up only)
- splits.py: Walk-forward windows, year/regime split utilities
- snapshot_store.py: Sync load() → asyncio.run(_async_load()), no look-ahead
- artifacts.py: Full run-dir writer (Parquet, CSV, JSON)

## App modules (apps/backtester/)
- run.py: BacktestRunner (exit-first→entry simulation, 25% kill switch) + CLI
- replay.py: Double-run determinism checker

## Config files
- configs/backtest/defaults.json: Base strategy defaults
- configs/experiments/baseline_v1.json: First experiment manifest

## Tests: 142 new tests, all passing
- 132 unit tests (no DB/HTTP required)
- 8 integration tests (synthetic SnapshotStore)
- 3 backtest determinism/replay tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim 9d3427e24e fix: disable Qwen3.5 thinking mode and switch OllamaClient to sync httpx
- Add `think: False` and `num_ctx: 8192` to Ollama payload:
  Qwen3.5 extended thinking mode generated 1300+ internal reasoning
  tokens before each response, adding 30-60s latency per LLM call.
  Disabling it reduces parse time from 600s timeout to ~13s.

- Rewrite OllamaClient to use sync httpx.Client inside asyncio.to_thread():
  Async httpx inside an active asyncpg SQLAlchemy session context on
  Python 3.13 hung indefinitely. Synchronous httpx in a thread pool
  completely isolates Ollama I/O from the asyncio event loop.

- Fix filing_poller to set issuer_id/symbol_id on Document records:
  Missing FK caused feature_builder to reject all events with
  event_no_symbol warning. Now looks up IssuerMaster/SymbolMaster
  by ticker before creating Document rows.

- Update test_llm_client to mock _sync_call instead of _client attr.
- Raise ollama_timeout default to 600s for large document processing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim bbe0e31150 feat: implement Phase 3 -- LLM enrichment, labeler, review queue, dataset export
- libs/llm/: OllamaClient (httpx async, retry), LLMCacheStore (SHA-256 DB cache),
  prompt registry (event_classifier_v1), LLMParser (cache→prompt→validate→repair)
- libs/parser/merger.py: rule+LLM canonical merge with provenance tracking,
  conflict detection (both confident + disagree), should_queue_for_review()
- libs/db/models.py: LLMCallCache, ReviewItem, EventLabel 3개 ORM 모델 추가
- libs/db/migrations/versions/0002_phase3_tables.py: Phase 3 Alembic migration
- libs/labeler/: filing_time_bucket→reaction_date, 1D/3D/5D fwd return, MFE/MAE
- libs/review/queue.py: create(dedup)/resolve/list ReviewItem
- libs/export/snapshot_export.py: temporal split + Parquet + manifest.json
- apps/: label_generator, dataset_export, review CLI, gold set evaluator
- 42개 신규 테스트 추가 (unit 32 + integration 4 + replay 1) — 152/152 통과
- libs/common/config.py: OLLAMA_URL/MODEL/TIMEOUT 설정 추가
- libs/common/logging.py: bugfix — add_logger_name incompatible with PrintLoggerFactory

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim 38ac53b597 implement Phase 2 gap items: backfill CLI, retry, financial features
- filing_poller: add --start-date/--end-date CLI args for historical backfill
  (defaults to 7 days ago when omitted)
- OracleClient.get/post: apply with_retry(max_attempts=3) so transient
  connection errors, timeouts, and 5xx responses are automatically retried
  with exponential backoff (0.1s→0.2s→fail)
- financial_features: new compute_financial_features() extracting latest_eps,
  latest_gross_margin, latest_operating_margin, eps_growth_qoq,
  revenue_growth_qoq from FinancialDataResponse
- feature_builder: wire FinancialService into build_features_for_event(),
  persisting financial_v1 FeatureSnapshot (non-fatal if unavailable)
- tests: 94 pass (81→89 unit + 5 replay); +8 new tests covering financial
  features and retry success path

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim 0471018dd8 feat: implement ACE-F v1 Phase 1 -- Stock Oracle 기반 이벤트 파이프라인
Stock Oracle (localhost:18001)을 단일 데이터 소스로 사용하는 미국 주식
이벤트 스윙 트레이딩 시스템의 Phase 1 구현체.

주요 구성:
- libs/oracle_client/: Stock Oracle REST 클라이언트 (filings, price, financial, fred, finra)
- libs/common/: config, logging, time_utils, ids, retries, file_store
- libs/db/: SQLAlchemy 2.0 모델 12개 + Alembic 마이그레이션 0001
- libs/parser/: 규칙 기반 파서 (텍스트 정규화, JSON Schema 검증, LLM 스텁)
- libs/features/: 시장/이벤트 피처 계산기
- apps/pipeline/: filing_poller → filing_fetcher → event_parser → feature_builder
- apps/sync/: macro_sync (FRED), short_volume_sync (FINRA), issuer_sync
- tests/: 단위 81개 + 리플레이 5개 전체 통과, lint clean

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago