99 Commits (cccbf88067bb6c3e9e02b7967f400d538289eaa0)

Author SHA1 Message Date
I Luk Kim 2560556c72 Fix: NameError 'sym' undefined in parking overlay gate check
Should be actual_sym (defined in _parking_evaluate_gate scope),
not sym (which is only defined in _parking_buy).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 04bb11f9bb Add Oracle health pre-check in run_pre_screen to warn before bar-fetch timeout
If Oracle is unreachable at 09:20 ET, without this check the pre-screen silently
waits through 5 chunks × 15s timeout (75s) before logging the empty-bars warning.
The new 3s probe logs a CRITICAL warning immediately so the operator can restart
Oracle before the market opens.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim ee2f1f6f84 Fix zombie daemon blocking ORB restart in ORBDaemonController
os.kill(pid, 0) returns success for zombie (defunct) processes.
Added ps stat check — if process is in Z state, treat as dead and
clean up PID file so subsequent start() calls work correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim b98442b28a Add paper trader improvements, web GUI updates, and experiment registry cleanup
- Paper trader: Alpaca broker fixes, catchup-thread state improvements
- Web GUI: intraday backtest duplicate run button, paper trading fixes
- Experiment registry: cleanup old v15/v16 experiments, update index
- Tests: Oracle client test additions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 6489b22c89 Fix: paper trader missing TQQQ overlay evaluation in parking gate
_parking_evaluate_gate() was returning only QQQM or SGOV, never TQQQ,
because the low-vol overlay logic (_evaluate_low_vol_overlay_target in
backtester) was not implemented in the live engine. Add overlay check
at the end of the gate: vol threshold, temperature, entropy, and Hurst
R/S analysis — matching the backtester's conditions exactly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim cbeb463685 Fix stale hardcoded schedule references in run_breakout_check
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 2d6da31bd5 Fix module-level docstring: run_stop_check schedule description
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim ba491b2a0d Fix EOD DB sweep bug and add regression test
Bug: pending candidate DB records were only swept inside the "has open
positions" branch of run_eod_exit, so a server restart mid-day (ORB
detection ran, no breakouts, no positions) left candidates as "pending"
forever.

Fix: move the in-memory and DB pending sweep to run unconditionally before
the positions check.

Test: TestEodDbSweep verifies both code paths (no-position + in-memory).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim ecbff65ea4 Update run_stop_check docstring to reflect dynamic sim_bar_minutes schedule
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 1f1fd4299f Move function-scope library imports to module top in orb engine
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 713d04deef Fix EOD exit: sweep DB for stale pending candidates after restart; remove misleading compound_returns override
- run_eod_exit now queries DB for any pending candidates not in-memory
  (guards against server restart mid-day leaving zombie pending records)
- Remove compound_returns=True force-override; live engine doesn't use it
  and V23 config has compound_returns=false

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 6ae4588a62 Move inline stdlib imports (copy, time) to module top in orb engine
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 190f7a47fa Fix V23 live engine: regime filter, sizing fidelity, trailing tighten
- screener.py: add min_atr_pct/max_atr_pct filters to live_pre_screen
- engine.py: prepend regime ticker (QQQ) to bar fetch so regime filter works
- engine.py: add rolling loss + account circuit breaker at run_orb_detection
- engine.py: patch today_open from first 1-min bar so regime gap is real
- engine.py: explicit regime + breadth filter before compute_orb_candidates
- engine.py: max_simultaneous_entries guard in run_breakout_check
- engine.py: _compute_sizing_capital with daily_budget_reset (fixed $10k base),
  drawdown governor, and streak sizing (win bonus / loss penalty)
- engine.py: trailing_tighten_at_r in run_stop_check (tight multiplier at 2R)

Bug fixes in _compute_sizing_capital:
- streak direction: remove reversed() so outcomes[0] = newest trade
- daily reset: use initial_equity as base (not growing equity), matching V23

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 41462c9e2c Fix: parking buy fill confirmation blocks event loop with 5s hard timeout
Make _parking_buy async (asyncio.sleep instead of time.sleep) and extend
confirmation from 5s to 30s per attempt with up to 3 retries on timeout.
On timeout, cancels the stale order before retrying with fresh price data.

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 27b8a49d77 Fix: paper trader ignores per-engine overrides in build_planned_order
All five build_planned_order() calls in apps/paper_trader/engine.py were
missing execution_config, causing build_planned_order to fall back to
base config.execution and silently ignore per-engine target_1_r_override,
target_1_fraction_override, max_holding_days, tiered-target settings, etc.

The backtester has always passed execution_config=_build_effective_execution_config()
(run.py:2210). This divergence caused paper trading to compute wrong target
prices and partial-exit fractions — e.g. AVGO entered with target_r=1.5
(base) instead of 3.0 (engine override), triggering a premature partial
exit on 4/15 that the backtest never produced.

Fix: add execution_config=build_effective_execution_config(candidate, self._config)
to all five call sites and hoist the function to the module-level import.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim fdd56ff683 Add duplicate run button and improve RunConfigPopup UX in Intraday ORB Backtest
- Add Copy button per run row in Run History table; opens pre-filled RunConfigPopup
  with original run's period/universe/compound_returns settings
- Make RunConfigPopup larger (500px wide) and more spacious with proper header/X button
- Prevent dialog from closing on backdrop click; only Cancel or X button closes it
- Range date inputs laid out side-by-side in 2-column grid

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim ebfada24cb Fix: don't permanently reject events when parking sell fails transiently
- When _parking_liquidate_for_event returns False but parking had shares
  (sell failed due to transient Alpaca error), skip without recording so
  event retries on next run_next_open instead of being permanently blocked

- Improve parking_sell_for_event_failed log: include exc_type, symbol, qty
  so we can see the actual exception (was silently swallowed with empty msg)

- Also deleted AVGO from processed_events so it retries tomorrow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
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