76 Commits (e46395bfeb09995b71f58536f3b93b02844e57ea)

Author SHA1 Message Date
I Luk Kim 261f67dfde Promote PeerSympathy v2 reaction_close sweep10 — corr 0.80 + top_n 1
Threshold sweep over PeerSympathy v2 reaction_close found a viable config.
After 10 ablation runs (correlation_min, leader_reaction_min, top_n_peers,
event_types, VIX gate), the dominant lever is correlation_min:

  baseline 0.55:  -2.35% / MDD 24.5% / SQS 30.2 / PF 0.977
  sweep01 0.65: +11.10% / MDD 16.1% / SQS 50.2 / PF 1.204
  sweep02 0.75: +15.76% / MDD  9.1% / SQS 62.5 / PF 1.835
  sweep07 0.80: +14.11% / MDD  3.8% / SQS 80.2 / PF 2.198
  sweep10 0.80 + top_n 1: +15.35% / MDD 3.83% / SQS 81.5 / PF 2.46
  sweep09 0.85:  +0.98% / MDD  3.8% / SQS 21.4 / PF 1.156 (cliff)

Mechanical insight: pushing correlation gate from 0.55 → 0.80 trims
coincidental ETF-shared peers and concentrates on structurally-clean
pairs where the leader's print is new information for the peer's
fundamentals — NOT the noisy NVDA/AVGO/AMD/MU semis cluster (their
mutual corr usually 0.55-0.70 with high cross-noise from competing
product cycles).

Sympathy pairs that surfaced in sweep10:
  Utilities      VST → CEG (independent power generators)
  Ag equipment   AGCO → DE
  Industrials    DAL → CAT, HWM → GE
  Software       DT → CRM, IT → INTU
  Datacenter     VRT → PWR (electrification)
  Energy         OVV/OXY/EOG/COP cluster

Hits all PROMOTE thresholds: SQS 81.5 > 50, return +15.35% > +5%,
MDD 3.83% < 25%. Sub-scores: profitability 100, risk 62.9, consistency
84.3, robustness 59.3 (lowest — sample-size discount). Sharpe 0.721,
Calmar 0.906, expectancy 0.072 R, avg holding 3.56 days, win rate 55.6%.

CAVEATS:
- N=27 trades is thin. Bootstrap PF 95% CI is [0.68, 10.82]. Treat as
  directional candidate, not production. Robustness 59.3 reflects this.
- corr_min cliff at 0.85 (universe runs out). 0.80 is at the inside edge
  of viable; re-baseline on snapshot shifts.
- macro_vix_max=30 was a no-op in sweep08. Either no candidates on
  VIX>30 days, or gate not wired to peer_sympathy in selector.py.
  Falsified hypothesis; not promoted as a lever.

Run: bt_return_max_long_v1_midlarge-liq_20260509072637244341_e1c62252.
Parent: peer_sympathy_poc_v2_reaction_close (commit 68102f1).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
3 months ago
I Luk Kim 68102f1b3a Add reaction_close salvage variant to PeerSympathy engine
Add peer_sympathy_entry_timing_policy ("next_open"|"reaction_close") and
peer_sympathy_leader_filing_time_buckets to StrategyEngineConfig. The
reaction_close variant enters peers at peer's T 16:00 ET close on the
SAME trading day as the leader's print, addressing the v1 hypothesis
failure where T+1 gap had already absorbed the news overnight.

Lookahead defenses tightened for the new branch: cutoff is T 16:00 ET
(_bar_close_timestamp(decision_date)) instead of T+1 09:30 ET; bucket
allow-list excludes AMC filings (which under PEAD's reaction_date=T+1
convention pass the timestamp check but defeat same-session sympathy).
LeaderPrint now carries filing_time_bucket from the runner.

Runner: split _schedule_peer_sympathy_candidates into two phases.
reaction_close fires BEFORE _select_candidates_for_date(date) and emits
into _scheduled_add_ons[date]; next_open keeps the existing tail-of-loop
position emitting into _scheduled_delayed_entries[next_date].

v2 backtest (1052 trading days, midlarge-liquid-long-v1 snapshot):
  trades 256→120, return -52.9%→-2.4%, MDD 61.6%→24.5%, SQS 19.6→30.2.

Sample sympathy plays: GOOGL on META +7.7%, AVGO on COHR +6.3%,
SLB on HAL +5.5%, GE on HWM +5.1%. Profit factor 0.977 (one tweak
from breakeven). Verdict: VIABLE BUT WEAK — salvage hypothesis
empirically validated, near breakeven, not promoted yet.

35/35 peer_sympathy unit tests pass (29 pre-existing + 6 new for
reaction_close path).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
3 months ago
I Luk Kim 2a8e9c526e Add v7.356+EarningsRunup composite config (NEUTRAL — not promoted)
Falsifies the "EarningsRunup as PEAD sleeve adjunct" hypothesis on
v7.356 base. Backtest results:
  v7.356 baseline:   +2356% / MDD 23.55% / Sharpe 2.69
  ER standalone:     +254%  / MDD 23.97% / Sharpe 1.10
  v7.356 + ER comp:  +2746% / MDD 25.21% / Sharpe 2.63

Headline composite gain (+390pp) is portfolio-luck distributed across
other sleeves (bullish_raised +$19.8k, parking +$11.2k, idle_form4
+$8.2k). EarningsRunup engine itself contributed -$7.6k in composite.

Symbol orthogonality CONFIRMED (0 ticker overlap between ER/PEAD
within ±14 days). Capital orthogonality FALSIFIED — composite missed
30 of 121 standalone ER entries (25%) due to insufficient_cash;
v7.356 base is already capital-saturated (47% idle baseline → 41%
with ER, indicating cash pressure).

Sharpe and profit_factor BOTH worse in composite, MDD slightly higher.

Verdict: do not integrate at engine_risk_budget=0.20. Engine kept
in-tree as falsification evidence + scaffolding for alternate
integration paths (lower budget, v7.364 base, or fully separate
capital bucket).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
3 months ago
I Luk Kim 465e248cb4 Wire pct-trailing end-to-end for EarningsRunup; tune v2 risk profile
Fix EarningsRunup trailing config that was previously captured in
Candidate.features only. Add activation-gated pct trailing to
update_trailing_stop with entry-relative giveback semantics
("lock in peak − giveback% of entry"). Plumb through
Candidate → ExecutionConfig → simulator. Default None preserves
legacy pct_X behavior — no impact on engines that don't opt in.

Add earnings_runup_poc_v2_tuned config: per_trade_risk_pct 0.65→0.30,
max_positions 30→8, max_positions_per_sector 30→4, macro_vix_max=30,
trailing_warmup_days 7→0 (activation gate replaces warmup).

v2 backtest (4y, 121 trades) vs v1 baseline (119 trades):
  Total return:  +37.27%  →  +254.69%
  Max drawdown:  53.23%   →  23.97%
  SQS:           45.2     →  69.7
  Robustness:    50.1     →  100.0
  Risk score:    23.5     →  35.0
  STOP r-mult:   −0.41    →  +0.65 (trailing-locked winners)

Promotion thresholds met: MDD < 25%, return preserved, SQS > 55.
Recommend integration as PEAD sleeve adjunct (orthogonal entry timing:
pre-print attention runup vs post-print drift) rather than standalone.

7 new pct-trailing tests: activation gate, ratchet, no-ratchet-down,
reversal-stop, legacy compat, end-to-end engine wiring. 28 passed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
3 months ago
I Luk Kim 956cc78f1b Add 3 candidate engine classes beyond PEAD: EarningsRunup, PeerSympathy, VolBreakout52w
Adds three new synthetic-Candidate emitter engines parallel to the
existing leader_follower scheduler hook, plus look-ahead defenses
(LookaheadViolationError + per-engine assertions). Each engine is
covered by a standalone PoC config (no PEAD/parking/idle alpha) for
isolation backtests against the midlarge or broad snapshot.

Engines:

EarningsRunup (libs/backtest/earnings_runup.py)
 - Trigger: days_to_earnings ∈ [3,7] AND attention_zscore_20d ≥ 1.5
   AND dollar_volume_20d_zscore ≥ 1.0 (all evaluated at T-1 close)
 - Entry: T+1 next_open. Exit: -4% / +8% / max_holding_days =
   days_to_earnings - buffer (forced flat by close before announcement)
 - PIT calendar: PointInTimeEarningsCalendar adapter for backtest;
   oracle_surprise_prefetch fallback when parquet calendar absent
 - PoC verdict (configs/experiments/earnings_runup_poc_v1.json):
   119 trades over 1051 days, +37.27% total return, 44.46% MDD,
   SQS 45.2 (profitability=55.5, risk=23.5, robustness=50.1).
   VIABLE BUT NEEDS WORK — signal exists; standalone risk profile
   too aggressive for v7.356 baseline (8.8% MDD on v7.364). Path
   forward: per_trade_risk reduction, VIX gate, position cap, or
   integrate as PEAD sleeve adjunct (not as standalone replacement).

PeerSympathy (libs/backtest/peer_sympathy.py)
 - Trigger: leader passes PEAD filter (earnings_release / guidance_update
   / material_contract) AND leader reaction_close ≥ +5% AND peer 60d
   correlation ≥ 0.55 over [T-65, T-5]. Top-2 peers by correlation
   from leader_follower_extra_peer_symbols_by_sector + sector ETF
   holdings.
 - Entry: T+1 next_open on peer. Exit: -3.5% / +6% / max_holding=3 /
   peer-earnings blackout
 - PoC verdict (configs/experiments/peer_sympathy_poc_v1.json):
   256 trades over 1051 days, -52.92% total return, 54.47% MDD,
   SQS 19.6 (profitability=0.0, risk=5.4, robustness=100.0).
   DEAD. The leader's catalyst is already absorbed by T+1 next_open
   — peers gap up overnight before entry. robustness=100 confirms
   the negative result is not noise. Salvage paths (not implemented):
   reaction_close entry, raised-guidance-only restriction.
 - Note: initial run_id was 0 trades due to a select_candidates
   filter mismatch (engine.event_types=['peer_sympathy'] dropping
   real event_type='earnings_release' rows). The runner adapter
   was patched to bypass strategy_engine filtering for leader
   selection; the manual peer_sympathy_leader_event_types filter
   does the gating.

VolBreakout52w (libs/backtest/vol_breakout_52w.py)
 - Trigger: close_T-1 > max(high[T-252:T-2]) AND volume_T-1 ≥
   2 × median_volume_20d_T-2 AND ATR_14_T-1/close ∈ [0.015, 0.06].
   Entry T next_open, exit -3% / +5% / max_holding=2 / MOC.
 - Honest, look-ahead-safe descendant of the retired topgainer v1-v54
   family. Five layers of strict-before assertions guard the bar
   provider, candidate construction, trigger evaluation, and feature
   timestamps. A leaky-provider proof-by-contradiction test
   demonstrates the categorical catch.
 - PoC verdict (configs/experiments/vol_breakout_52w_poc_v1.json,
   broad-liquid universe): 1,332 trades, -87.28% total return,
   88.74% MDD, SQS 24.4 (profitability=0.0, robustness=100.0).
   DEAD AND HONEST. This is the most important finding of the three
   PoCs: the topgainer v1-v54 lineage's headline returns (+267%
   Sharpe 13.73 in best variants) were 100% lookahead bug. With
   the bug removed, the 52w-high + volume + ATR signal has no real
   alpha — the lookahead-corrected -4.3% from prior memory is
   confirmed and amplified to -87% on a fuller universe and longer
   horizon. Future "revive topgainer" proposals can cite this run
   (bt_return_max_long_v1_broad-liquid_20260509042903892342_3bb473d9)
   as definitive falsification.
 - Pre-open gap guard inactive (no premarket data in broad snapshot).
   skip_if_no_gap_data=true; the +4% gap-fade guard would not move
   the result given the magnitude.

Shared infrastructure additions:
 - libs/backtest/domain.py: LookaheadViolationError class +
   StrategyEngineConfig fields (11 EarningsRunup + 11 PeerSympathy
   + 13 VolBreakout52w = 35 new fields)
 - apps/backtester/run.py: _BacktestAttentionZscoreAdapter,
   _RunnerPeerResolver, _schedule_earnings_runup_candidates,
   _schedule_peer_sympathy_candidates,
   _schedule_vol_breakout_52w_candidates wired into the daily
   scheduler block. PeerSympathy adapter bypasses strategy_engine
   filtering on leader selection (manual filter handles gating).

Tests: 21 (EarningsRunup) + 27 (PeerSympathy) + 38 (VolBreakout52w)
= 86 new unit tests, all passing. Broader unit suite: 1392 passed,
2 pre-existing failures unrelated.

Net engine state: EarningsRunup is the only viable new engine class.
PeerSympathy and VolBreakout52w are kept in-tree as falsification
evidence, not as production engines.

Co-Authored-By: Claude Opus 4.7 <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 2b6cea57b2 Paper trader Phase 1 fixes: multi-session isolation, pipeline halt, snapshot refresh unblock
- v7.356 config: swap dataset_snapshot_id from manual_only ftb_fix_v2 to
  auto_full_rebuild base canonical so paper trader can refresh snapshot
  (root cause of processed_events=0 for 30 days)
- Multi-session order isolation (1.A.2/1.A.3): tag client_order_id with
  pt-{session_id[:8]}-{uuid} prefix on all entry orders; _cancel_stale_orders
  filters by own session prefix so one session no longer ghost-cancels another's
  orders on shared Alpaca account
- Pipeline halt on failure (1.B.1): _run_pipeline returns bool and stops on
  first subprocess failure instead of silently progressing with stale data
- Daemon restart window skip (2.2): run_open/run_close only marked completed
  if processed_phases DB confirms prior execution — no more trading-less days
  after mid-day restart
- event_parser: periodic batch commits every 500 docs (hypothesis fix for
  3h hangs; unverified — may just be slow serial Oracle calls)
- Tests updated for _verify_order_fill tuple return + new cross-session
  isolation test; all 23 paper_trader unit tests green

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 08e41831bc Promote ORB Gainers V46: PEAD prior-event signal (D-7 lookback, w=0.12)
V24 → V46 via PEAD (Post-Earnings Announcement Drift) signal. Stocks with
earnings_release or guidance_update in prior 7 calendar days show +14.5pp
win rate improvement and +0.348R advantage on ORB breakouts.

Phase 1 diagnostic (291 V24 200d trades):
  Pearson=+0.135, Δ=+0.348R, WR gap=+14.5pp — all gates pass.

Phase 2 validation (w=0.12, Pareto-optimal from sweep):
  200d: V46 +114.60% / -11.83% / 3.21  vs  V24 +94.78% / -11.29% / 2.83
  400d: V46 +173.78% / -14.11% / 2.60  vs  V24 +162.1% / -13.70% / 2.471

Code changes:
- libs/intraday/domain.py: add prior_event_lookback_days: int = 0 param
- libs/intraday/orb_simulator.py: fix bug — weight_event_catalyst now wired
  for gainers_leader engine (was restricted to stocks_in_play_dual_regime only)
- apps/intraday_bt/run.py: _prefetch_prior_event_features_db() helper +
  DB routing in both catalyst trigger blocks when prior_event_lookback_days>0

V24 → status: superseded. V46 → status: live_champion.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 16f49411cb Add V34-V44 ORB diagnostic scripts; wire obv_slope_5 + min_obv_slope_20d infra
Signal axes tested (V34-V44, all failed G2 ≥ 0.30R gate):
- V34 obv_slope_5 (5d): G2=0.083R (null)
- V35 obv_slope composite (5d+20d): regime artifact (200d +18pp, 400d -12pp)
- V36 RSI-14: G2=0.148R, G5a=0.742 (redundant with OBV)
- V37 BB %B / BB width: G2=0.186R
- V38 dollar_vol_trend / sleep_streak / prior_day / vol_trend: all fail G2
- V39 premarket acceleration + hold ratio: G2=0.013R (null)
- V40 prior-day market breadth: G2=0.008R (null)
- V41 min_obv_slope_20d=0.0 hard filter: -15pp (OBV as gate too aggressive)
- V42 52w-high proximity + range position: G2=0.200R (best near-miss, fails)
- V43 30-min ORB window: -14.79% (catastrophic)
- V44 trailing multiplier sweep 0.6-1.0: 0.80 confirmed global optimum

Infrastructure added (backward-compatible, V24 parity preserved):
- features.py: obv_slope_5 enrichment key
- domain.py: weight_obv_slope_5=0.0, min_obv_slope_20d=None
- orb_simulator.py: 5 wiring sites for obv_slope_5; min_obv_slope_20d gate

V24 remains live champion. 20 signal axes exhausted.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 4b8a157a67 Promote V24 ORB Gainers: add OBV-slope(20d) accumulation quality weight
Phase 1 diagnostic (diag_orb_quality_features.py) on V23 200d trade set found
obv_slope_20 passes all edge gates: Pearson=+0.2349 with r_multiple, top-tercile
WR 75% vs bottom 59.4% (+15.6pp), avg_R gap +0.394R. Hurst_60 and OU-θ_60 failed.

Weight sweep: 0.05 is Pareto-dominant (0.10/0.15 blow DD).

200d (same window): V24 +94.8% DD-11.3% Sharpe 2.83 vs V23 +85.0% DD-11.6% Sharpe 2.66
400d (same window): V24 +162.1% DD-13.7% Sharpe 2.47 vs V23 +149.4% DD-13.7% Sharpe 2.36
V24 Pareto-dominates V23 on both windows. V23 marked superseded.

Code changes:
- libs/intraday/features.py: add compute_obv_slope_approx() + enrich_daily_bars field
- libs/intraday/domain.py: add weight_obv_slope field to ORBStrategyParams
- libs/intraday/orb_simulator.py: wire obv_slope_20 read/store/score in gainers_leader branch
- configs: orb_gainers_v24_quality_overlay.yaml (new champion, live_readiness: experimental)
- configs: orb_gainers_v23.yaml status → superseded

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
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 cccbf88067 Document V23 TRUE 400d result and fix Safe v9 framing
V23 400d TRUE (correct pipeline, 2026-04-21): +146.09%, DD -13.66%, Sharpe 2.33.
Prior result (+120.87%, DD -23.97%) was from buggy AH-close pipeline; data fix
improved DD by 10.31pp. V23 now strictly dominates Safe v9 on ALL 400d metrics
(+45pp return, +3.5pp DD, +0.34 Sharpe). Updated Safe v9 status to
validated_200d_only and clarified Korean framing to remove the misleading
"최종 검증 전략" without qualification.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 65afc95c7e Clean up: reduce Oracle timeout, fix company endpoint, archive old v7/v15/v16 experiments
- libs/oracle_client/alpaca.py: reduce bar-fetch timeout 90s→15s (fail fast on Oracle outage)
- libs/oracle_client/client.py: add health_check_fast() for cheap liveness probe; fix health path
- libs/oracle_client/company.py + financial.py: use /api/v1/company/{symbol} (newer endpoint)
- libs/oracle_client/__init__.py: re-export AlpacaSnapshot/get_snapshot/get_snapshots at package level
- configs/experiments: delete archived v15.x, v16.x, v7.119–v7.358 experiment configs (superseded)
- README.md: fix absolute path → relative path for ORB docs link

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim d7bfda039b Add expanded ORB simulator features and metrics
- orb_simulator.py: min_abs_gap_pct filter, premarket dollar vol filter,
  rolling_loss circuit breaker, drawdown_governor, streak_sizing,
  trailing_tighten_at_r, allow_doji/red_to_green breakout, abs_gap scoring
  for gainers_leader, ORBSimulationState, run_orb_simulation_with_state API
- metrics.py: loss_containment_score and related metrics
- features.py: enrich_daily_bars with gap_zscore, ATR ratio, range compression
- domain.py: extended ORBStrategyParams with new fields
- cache.py: DailyBarCache with merged parquet storage and coverage metadata
- simulator.py: base simulator updates for new entry/exit mechanics
- configs/intraday: updated orb_gainers_v23.yaml with canonical params
- Added BLD to midlarge symbol snapshot

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 3d3ac5cd0e Purge 1,062 obsolete experiment configs (v6~v19 R&D families)
Keep only active families:
- v7 (130 files): current champion lineage, #1 SQS 91.6
- v15 (3 files) / v16 (7 files): leaderboard #3/#4
- empty_strategy.json: web utility

Deleted families: v1.xxx, v6, v6new, v8, v9, v10, v11, v12, v13,
v14, v17, v18, v19 + all alias files (baseline_, conviction_, etc.)
+ parking_only_*, short_bearish_v1

Index rebuilt: 139 experiments.

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 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 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 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 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 76581ead04 Remove overlay backtesting and scoring 5 months ago
I Luk Kim 1d9507540b Add strategy aliases to key experiment configs
baseline(v29), docgate(v122), entropy_safe(v196), conviction(v307) etc.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 902b7d99b8 Add v6new.307: SQS 74.4 #1, CW 350.2% — high-WR engine risk boost
Boosting inline/guidance engine per_trade_risk from 0.015-0.020 to 0.040.
These engines have 73-100% WR — bigger positions on best signals.

v6new.307: SQS 74.4 (#1), CW 350.2%, Test +46.8%, MaxDD 2.4%, PF 14.74

Full progression: 262.9% → 284.4% → 312.6% → 320.5% → 350.2%

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 30eb311443 Add v6new.288: SQS 72.8 #1, CW 320.5% — core risk up + budget downsizing
Boosting core engine (WR 84%) per_trade_risk from 0.038 to 0.045 with
allow_budget_downsizing=true. Higher conviction = bigger positions on
the best engine. Budget downsizing prevents cash rejections.

v6new.288: SQS 72.8 (#1), CW 320.5%, Test +45.6%, PF 14.54

Progress: 262.9% → 284.4% → 293.2% → 312.6% → 320.5%

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 320a9d19df Clean up rank 100+ strategies and obsolete files
Removes experiment configs for strategies below leaderboard rank 100,
deletes obsolete PER-v1 strategy notes, and updates leaderboard/registry.

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 bc4ffecb10 Add v6new.275: SQS 72.7 #1, CW 312.6% — OME early fail day 1
OME engine early_failure_no_progress_days=1 (from day 2 in v272) further
improves capital recovery speed. 198 trades, 312.6% CW return.

SQS 72.7 = new #1 on leaderboard (including overlays).
Test return +45.5%, Profit Factor 19.23.

Key: cutting OME losers at day 1 instead of day 2 frees capital faster
while losing only marginal OME winners that needed >1 day to show progress.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 165424ecc2 Add v6new.272: CW 310.8% breakthrough via OME early failure cut
Trade analysis revealed OME engines had 54-60% WR with stop-heavy exits.
Adding early_failure (day 2, R=0.0) for OME engines frees capital faster,
enabling 6 more trades (194→200) and boosting CW from 293.2% to 310.8%.

Key insight: cutting low-quality engine losers early improves compounding
more than any scoring/feature/sizing change tested in this session.

v6new.272: SQS 72.4 (#6), CW 310.8%, 200 trades, test +44.9%

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 1c38415cb7 Add v6new.265-268: small-cap expansion experiments
Lowering market cap floors to 2B added 15 trades (194→209) but CW return
dropped from 293.2% to 278.5%. Small-cap PEAD events have lower average
quality — individual outliers like SEDG exist but don't compensate.

v6new.255 (293.2%) confirmed as optimal trade-quality balance.

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 74244b15d2 Add v6new.224-258: grid search risk×ATR toward 300% CW
Best: v6new.255 (293.2%) = risk 0.069 + ATR 1.45 + entropy + doc 0.50
ATR 1.45 is critical threshold — below it loses 1 trade and drops to 269%.
Risk 0.069 is max before trade loss at 0.070.

Progress: 262.9% → 284.4% → 288.3% → 290.4% → 291.8% → 293.2%

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 672098c077 Add v6new.215-223: three-way parallel search toward 300% CW
Best: v6new.220 (289.6%) = risk 0.065 + max_pos_value 1.5 + ATR 1.6.
194 trades fixed — event count is the structural bottleneck.
Management_change/oneoff relaxation hurt. Budget/leverage neutral.
300% requires fundamentally more trades or higher per-trade returns.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 6dee2c24d2 Add v6new.197-214: parameter grid search around v196
Best CW return: v6new.207 (288.3%) = v196 + risk 0.060 + ATR 1.6.
Public SQS: v6new.196 (72.6) still best — v207 is 72.5.
ATR 1.6 is optimal stop distance. Sector limit increase hurts badly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim cc953949f1 Add v6new.196: new best SQS 72.6 (#3) — v29+entropy+risk+doc_quality
v6new.196 = v29 base + entropy scoring (v13e) + per_trade_risk 0.058
+ doc_quality 0.50. CW return 284.4% (+21.5pp over v122), 194 trades.

Key finding: v29 base with doc_quality 0.50 (not 0.55) is the optimal
quality gate when combined with entropy scoring.

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 95c6b499c4 Add missing experiment configs v6new.129, 160-162
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 83c7a66f77 Add v6new.37-38: final tuning attempts — v6new.30 confirmed as optimum
v6new.37 (prune 2 weak engines): +106.55% — neutral (+0.10%)
v6new.38 (boost top engines): +96.55% — worse (-9.90%, capital starvation)

38 experiments complete. v6new.30 is the confirmed framework optimum.
Paper BT: +106.45%, 48 trades, 67% WR, MaxDD 3.35%, Sharpe 3.66

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim daa8536546 Add v6new.36: mean reversion engine for high-VIX drops
New engine: next_open_long_mean_reversion_high_vix
  Targets: react < -7%, close 0.15-0.60, bearish/mixed/unknown direction
  Signal: VIX>20 + big drop = 62.9% WR, +3.91% 5d mean (n=167)
  VIX 25-30 sweet spot: 75% WR, +5.46% 5d mean

Test split: 3 MR trades, 67% WR, +5.59% total PnL
Paper BT: 51 trades vs 48 (v6new.30), return ~equal

Also fixes _rows_to_table to handle sparse feature columns.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim e8241d2924 Test execution model + short side — both rejected, v6new.30 confirmed
Short Side (Direction 3):
  Bearish events: 45-55% WR for short — no actionable edge
  Mean reversion after large drops cancels short PEAD

Execution Model (Direction 2):
  T+2 delayed entry: loses 54% of alpha (Day 1 = 54% of 5d return)
  Wider stops (v6new.34): -0.68pp — smaller positions offset fewer stop-outs
  Tighter trailing (v6new.35): -12.48pp — cuts winners too early
  reaction_close >> next_open (86% vs 57% WR) but post_market can't use RC

Direction 1 (new data: Form 4, XBRL, Earnings Surprise) requires Oracle API
implementation. Free sources identified: SEC EDGAR, Alpha Vantage, FINRA.

v6new.30 is the confirmed framework optimum.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 85515f2db9 Add v6new.31-33: parameter tuning — all neutral/worse, v6new.30 confirmed optimal
v6new.31 (all OME risk reduced): +105.23% — worse, mixed_ome/other_material_mixed need full risk
v6new.32 (patient risk only): +108.15% — marginal, not worth
v6new.33 (core warmup 10): +108.62% — identical to v6new.30

v6new.30 (+108.62%, Sharpe 3.77, SQS 63.8) is the framework optimum.
Further parameter tuning yields diminishing/negative returns.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim b6ea2a4b50 Add v6new.29-30: OME risk experiments — v6new.30 Paper BT +108.62%
v6new.29 (OME disabled): +104.75%, 30 trades, 90% WR — high quality but fewer trades
v6new.30 (OME risk further reduced): +108.62%, 45 trades, 71% WR, Sharpe 3.77
  - unknown_ome: 0.008→0.004
  - other_material_unknown: 0.003→0.002
  SQS: 63.8, WFV 100% positive, OOT 90% positive

Leaderboard: v6new.27 #1 (63.9), v6new.30 #2 (63.8)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim dc16a1f88b Add v6new.28 (marginal) — v6new.27 confirmed as optimal at +105.35%
v6new.28: further recovery risk 0.0035→0.002 → +105.77% (marginal +0.42%)
v6new.27 is the sweet spot: +105.35%, MaxDD 3.35%, Sharpe 3.73

Session summary:
- 28 strategy experiments, 6 infrastructure fixes
- Paper trader unified with backtester (6 divergences resolved)
- Final strategy: v6new.27 (v6.100 base + OME risk reduction)
- Paper BT validated: 96% trade-by-trade match with backtester

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago