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