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:
@@ -2,6 +2,13 @@ import type { ActivationConfig, TradeSetup } from './types';
|
||||
|
||||
const HIGH_CONVICTION_ACTIONS = new Set(['LONG_HIGH', 'SHORT_HIGH']);
|
||||
|
||||
/**
|
||||
* Floor for the primary target's reach probability — mirrors
|
||||
* MIN_TARGET_PROBABILITY in app/services/qualification.py. A primary below
|
||||
* this is a lottery target whose distance inflates R:R past the min_rr gate.
|
||||
*/
|
||||
export const MIN_TARGET_PROBABILITY = 20;
|
||||
|
||||
function actionDirection(action: TradeSetup['recommended_action']): 'long' | 'short' | 'neutral' {
|
||||
if (!action || action === 'NEUTRAL') return 'neutral';
|
||||
if (action.startsWith('LONG')) return 'long';
|
||||
@@ -40,7 +47,7 @@ export function qualifiesSetup(setup: TradeSetup, config: ActivationConfig): boo
|
||||
return false;
|
||||
}
|
||||
const targetProbability = primaryTargetProbability(setup);
|
||||
if (targetProbability == null || targetProbability <= 0) return false;
|
||||
if (targetProbability == null || targetProbability < MIN_TARGET_PROBABILITY) return false;
|
||||
if ((setup.confidence_score ?? 0) < config.min_confidence) return false;
|
||||
// Residual cross-sectional momentum is the core selection (long-only). While
|
||||
// the gate is active, shorts never qualify; missing ranks do not qualify
|
||||
@@ -77,6 +84,9 @@ export function disqualifyReason(setup: TradeSetup, config: ActivationConfig): s
|
||||
}
|
||||
const targetProbability = primaryTargetProbability(setup);
|
||||
if (targetProbability == null || targetProbability <= 0) return 'no target probability';
|
||||
if (targetProbability < MIN_TARGET_PROBABILITY) {
|
||||
return `target probability below ${MIN_TARGET_PROBABILITY}%`;
|
||||
}
|
||||
if ((setup.confidence_score ?? 0) < config.min_confidence) {
|
||||
return `confidence below ${config.min_confidence.toFixed(0)}%`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user