Activation gate: primary target probability floor (>= 20%)
A qualified setup's primary target must now clear MIN_TARGET_PROBABILITY
(20%), shared with the primary-selection floor in recommendation_service
and mirrored in the frontend gate. Closes the read-time hole where a
stale pre-c7a198b row starring a far lottery target (probability pinned
at the 3% clamp floor, R:R inflated by the same distance) qualified
forever: the scanner emits no replacement row and live R:R never decays.
A/B backtest vs c7a198b baseline (same July-3 snapshot): 7 of 1096
qualified setups removed; qualified net avg R 0.202 -> 0.207, hold
Sharpe 2.00 -> 2.02, CAGR +48.8% -> +49.6%, max DD unchanged at -15.8%.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,9 +5,11 @@ performance stats (server) and mirrored on the frontend. The core selection is
|
||||
residual cross-sectional momentum: a setup's ticker must rank in the top
|
||||
``min_momentum_percentile`` of the universe by beta-adjusted 12-1 month momentum.
|
||||
R:R and confidence remain as floors, and conviction/conflict survive as optional
|
||||
tighteners (off by default). Qualified setups must also have a probability-backed
|
||||
target; otherwise a mathematically high R:R can be driven by a fragile target
|
||||
with no independent validation.
|
||||
tighteners (off by default). Qualified setups must also have a primary target
|
||||
with at least ``MIN_TARGET_PROBABILITY`` reach probability: a primary below the
|
||||
floor is a lottery target whose distance inflates R:R, so it would otherwise
|
||||
game the min_rr gate (the model clamps probabilities at 3%, and far targets pin
|
||||
there while their live R:R stays high forever).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -16,6 +18,13 @@ from typing import Any
|
||||
|
||||
HIGH_CONVICTION_ACTIONS = {"LONG_HIGH", "SHORT_HIGH"}
|
||||
|
||||
# Floor for the primary target's reach probability, shared with the primary
|
||||
# target selection in recommendation_service and mirrored in the frontend
|
||||
# (qualification.ts). Under the two-barrier model a fair-race 1.5:1 target sits
|
||||
# near ~34% before drift adjustments, so 20% only excludes targets the model
|
||||
# itself considers long shots.
|
||||
MIN_TARGET_PROBABILITY = 20.0
|
||||
|
||||
|
||||
def _action_direction(action: str | None) -> str:
|
||||
if not action or action == "NEUTRAL":
|
||||
@@ -85,7 +94,8 @@ def setup_qualifies(setup: Any, config: dict) -> bool:
|
||||
live_rr = live_risk_reward(setup, float(current_price))
|
||||
if live_rr is not None and live_rr < config["min_rr"]:
|
||||
return False
|
||||
if primary_target_probability(setup) is None:
|
||||
target_probability = primary_target_probability(setup)
|
||||
if target_probability is None or target_probability < MIN_TARGET_PROBABILITY:
|
||||
return False
|
||||
if (setup.confidence_score or 0.0) < config["min_confidence"]:
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user