Part 1 — long-only. The momentum edge is long top-momentum; the gate was
qualifying shorts on high-momentum names (fighting the trend), which showed as
the -0.13R Short(qual.) drag. While the gate is active, shorts no longer qualify
(backend qualification, backtest _momentum_qualifies, and the frontend mirror).
Part 2 — production wiring. Live setups now carry a real momentum rank, so the
dashboard, the Track Record's qualified stats, and outcome evaluation all gate on
the same value instead of deferring to floors:
- new momentum_service.compute_momentum_percentiles: 12-1 momentum per ticker,
ranked across the universe into a {symbol: percentile} map.
- the daily R:R scan ranks the universe up front and stores each setup's
percentile (new trade_setups.momentum_percentile column, migration 010).
- enhance_trade_setup mutates the same row, so the percentile is preserved;
_trade_setup_to_dict + TradeSetupResponse expose it to the API.
Until a fresh scan runs, pre-existing setups have a null percentile and the gate
falls back to floors for them (longs) / excludes them (shorts) — they fill in on
the next scan. 341 backend tests pass; frontend build clean.
Needs the alembic upgrade (migration 010) on deploy.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Answers "why does a too-far-progressed setup still show": setups are only
recalculated by the scheduled R:R scan and manual fetch; at creation
entry == current price (0% progress), so over-progression is a
between-scans drift effect and must be judged at read time.
- /trades now attaches current_price (latest close per ticker).
- Qualification drops setups whose R:R recomputed from the current price
falls below min_rr — i.e. price already ran toward target (reward
consumed) or through the stop. Reuses the existing min_rr threshold
instead of a separate progress %; far cleaner (a 3:1 is already ~1:1
by 33% progress). Skipped for historical setups (no current_price).
- Fix: useFetchSymbolData now invalidates the trades queries, so a fetch/
recompute actually refreshes confidence/setups in the UI (was the cause
of the stale 100% confidence lingering after recompute).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- JWT now carries a username claim; sidebar shows "Signed in as <name>"
instead of the bare user id (sub). Re-login required for the new claim.
- Signals: Min R:R / Min Confidence inputs reflect the effective filter —
auto-filled from the activation gate when "Qualified only" is on, reset
to 0 when off (no more misleading 0 while the gate is active).
- Signals layout: Run Scanner moved to its own action row (it's a job
trigger, not a filter); qualified toggle grouped with the refinement
filters under one Filters panel.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Closes the feedback loop on R:R scanner signals:
- Nightly outcome_evaluator job replays unresolved setups against daily
OHLCV bars: target_hit / stop_hit / ambiguous (same-bar, counted as
loss) / expired after OUTCOME_EVALUATION_MAX_BARS (default 30)
- Migration 004: evaluated_at + outcome_date on trade_setups
- GET /trades/performance: hit rate, expectancy (avg R), total R with
breakdowns by direction, recommended action, and confidence bucket
- New Performance page (stat cards, breakdown tables, Evaluate Now,
methodology disclosure) wired into sidebar and mobile nav
- 17 new unit tests for evaluation logic and stats aggregation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>