14 Commits (137b413084d2d0feaf49070134511f685d9f29db)

Author SHA1 Message Date
I Luk Kim 9e622c6614 Investigate compound mode: V23 is absolute champion in all modes
- V23 pure compound (live-equivalent): 200d +148.13% DD-14.23%, 400d +209.41% DD-17.35%
- Hybrid V2 compound tested: 200d +175.41% looks promising but 400d +181.71% DD-23.72%
  loses to V23 by -27.7pp return AND -6.4pp worse DD → rejected
- Safe v9 compound 400d: +128.49% DD-14.43% — better DD but -81pp return vs V23 → rejected
- V23 tight governor compound 400d: +192.54% DD-17.32% — marginal gain, not worth config
- Live paper trader uses compound mode (engine.py:1816 session_equity = initial_equity + P&L)
- All improvement axes exhausted; V23 daily_reset declared TERMINAL

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 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 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 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 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