12 Commits (529477727fd59ddcfdee7a44abcaebf68324883a)

Author SHA1 Message Date
I Luk Kim 529477727f Add score and event_type to trade blotter output
FilledTrade now carries event_type and score from the Candidate.
These fields are written to trade_blotter.parquet and displayed in
paper backtest trade logs.

Previously score showed as 0.00 for all trades because the field
wasn't propagated from Candidate → FilledTrade → Parquet.

Score=0.00 is valid for trades from engines with score_threshold_override=0.0
(e.g. guidance_unknown_orderly) where engine gates, not score, determine entry.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim bb659cf7ae Add reaction-based position sizing to reduce mean-reversion loss risk
New feature: reaction_size_cap_threshold in RiskConfig
- When abs(reaction_day_return) > threshold, position size scales down
- Formula: scaler = threshold / abs(reaction) (linear inverse)
- E.g. threshold=8%: 8% reaction → 100%, 16% → 50%, 24% → 33%

Paper trading impact simulation (top 10 trades):
- PII (react +14%): loss $501 → $288 (saved $214)
- VSCO (react +18%): loss $792 → $350 (saved $442)
- RYTM (react +12%): loss $397 → $256 (saved $141)
- Winners (react <5%): unchanged (SSRM, FLS, LW, KGS all 100%)
- Total loss reduction: $2,610 → $1,813 (-30%)
- Net PnL improvement: +$797

v6new.24 backtest: Train SQS 92.5 (session best), risk=75.6.
Public SQS 52.5 (lower return due to sizing, but best risk profile).

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