momentum gate: long-only + wire the percentile onto live setups
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>
This commit is contained in:
@@ -580,11 +580,14 @@ def _assign_momentum_percentiles(candidates: list[dict]) -> None:
|
||||
|
||||
def _momentum_qualifies(cand: dict, threshold: float) -> bool:
|
||||
"""Whether a candidate clears the floors (meets_core) and the momentum gate.
|
||||
Threshold 0 disables the momentum gate (floors only)."""
|
||||
Threshold 0 disables the momentum gate (floors only). The gate is long-only:
|
||||
while it's active, shorts (fighting the trend) never qualify."""
|
||||
if not cand["meets_core"]:
|
||||
return False
|
||||
if threshold <= 0:
|
||||
return True
|
||||
if cand["direction"] == "short":
|
||||
return False
|
||||
mp = cand.get("momentum_percentile")
|
||||
return mp is not None and mp >= threshold
|
||||
|
||||
|
||||
Reference in New Issue
Block a user