19 Commits (2edf0457106c24fa0705bae73266bc8d038aec6b)

Author SHA1 Message Date
I Luk Kim d000731cc7 PEAD session: add 4 candidate engines + Oracle vocab normalizer + auto-refresh disable
New engines (configurable but not promoted by default):
- libs/backtest/earnings_runup.py: pre-earnings drift entry (T-7 to T-3, attention+volume z-scores)
- libs/backtest/peer_sympathy.py: peer reaction trade after leader earnings, with reaction_close variant
- libs/backtest/vol_breakout_52w.py: 52-week high volume breakout
- libs/backtest/cross_sectional_momentum.py: 12-1 momentum with VIX/SPY-50dma regime filter

Pipeline additions:
- libs/parser/event_type_normalizer.py: normalize Oracle fallback raw vocab to strategy
  vocabulary (earnings_result→earnings_release, regulation_fd→guidance_update, etc.).
  Wired into apps/pipeline/event_parser/main.py oracle-fallback path.
- libs/labeler/label_generator.py: preserve future entry_dates as label_status='pending'
  instead of dropping as 'unavailable'.
- libs/export/snapshot_export.py: include 'pending' labels in snapshot export.
- libs/parser/rule_parser.py: harden 8-K item-code classifier against dirty input strings.

Infrastructure:
- libs/backtest/snapshot_store.py: _PRICE_FEATURE_WARMUP_DAYS 120→400 (needed for
  xsmom 12-1 lookback of 273 trading days).
- apps/backtester/run.py + apps/paper_trader/backtest_sim.py: disable auto-refresh
  of snapshots (user request — auto-refresh was silently rebuilding snapshots with
  current code, making historical backtests irreproducible across DB mutations).

Engine config support:
- libs/backtest/domain.py: add fields for ER/PS/VolBO/xsmom engine configs.
- libs/backtest/execution.py: wire pct-trailing for EarningsRunup.
- libs/backtest/scoring.py: synthetic candidate scoring for new engines.

Configs (POC + sweeps, none promoted as active strategy):
- Phase A-E PEAD baseline comparisons (no PEAD / +ER / +xsmom / +sleeves variants)
- Phase F1-F8 silo allocation sweep (v7.356 + ER/xsmom silos 15-60%)
- xsmom_poc_v1, xsmom_v2_regime_mdd
- v7.356_plus_er_silo_05/10/15/20/25/30 ranges
- earnings_runup_* / peer_sympathy_* / vol_breakout_52w_* POCs

Tests: 16-tests-each for each new engine + event_type_normalizer tests.

Note: backtest reproducibility infrastructure remains broken — see
_backup_2026-05-10/HANDOFF.md for details on the +12,592% reference run that
cannot be reproduced after rebuilds. Followup work needed before trusting any
specific backtest number.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
I Luk Kim fcf379c759 Fix snapshot refresh: enable auto-rebuild, fix date-range edge cases, log incremental failures
- registry.json: change _ftb_fix_v2 from manual_only to auto_full_rebuild so
  backtest auto-refreshes when snapshot doesn't cover the requested period
- run.py: return [] (not all_trading_days fallback) when parking cap pushes
  requested_end before requested_start, preventing silent wrong-date-range runs
- run.py: allow 1-trading-day lag tolerance in parking cap so a single lagging
  symbol (e.g. QQQM shortly after close) doesn't cap the whole simulation
- backtest_sim.py: log incremental_update_failed_falling_back warning so
  silent fallback to full rebuild is visible in direct-mode logs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 9095b376d9 Clean up superseded configs and commit accumulated R&D infrastructure
Key changes:
- Delete superseded strategy configs: orb_gainers safe_v2-v9, orb_pullback, vwap_reclaim, hypergap, leader_safe
- Add V46 prior_event_types param to domain.py + run.py event type wiring
- Major simulator.py enhancements: sector thrust sleeve, sector proxy mapping, helper functions
- Improve screener.py with better scoring/filtering
- Add new test coverage: test_simulator.py (776 lines) + test_screener.py (313 lines)
- Add V24.1 research candidate configs (w002/w003/w004/entrycap/losscap010 variants)
- Add leader momentum research configs and sweep files
- Update configs/snapshots/registry.json with new strategy registrations
- Add docs/leader_intraday_momentum_workflow.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 0cae86aa87 Revert: remove daily_budget_reset from PEAD backtest
Feature was added to wrong system (PEAD backtester). Fully reverted.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 189aa58343 Add daily_budget_reset mode for research backtesting
New mode (risk.daily_budget_reset=True) where cash_available and sizing
equity reset to initial_equity at the start of each day, regardless of
how many open positions or realized P&L exist. Unlike fixed_capital_sizing
(단리, sizing only), this also treats buying power as if no positions are
held — useful for evaluating signal quality independent of capital constraints.

- domain.py: daily_budget_reset field on RiskConfig
- run.py: _daily_budget_reset flag; _sizing_equity / _sleeve_equity_est /
  _build_portfolio_state all honor the new flag
- backtest_sim.py: daily_budget_reset param threaded through
- direct_runner.py: --daily-budget-reset CLI flag
- routers/backtest.py: BacktestRequest field + cmd arg
- client.ts: BacktestParams / BacktestTask types updated
- Backtest.tsx: checkbox in form + DBR badge in task list

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 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 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 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 76581ead04 Remove overlay backtesting and scoring 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 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