feat: require gate reset before post-stop reentry
This commit is contained in:
@@ -43,7 +43,7 @@ export function liveRiskReward(setup: TradeSetup, currentPrice: number): number
|
||||
* app/services/qualification.py — keep the two in sync.
|
||||
*/
|
||||
export function qualifiesSetup(setup: TradeSetup, config: ActivationConfig): boolean {
|
||||
if ((setup.reentry_lockdown_remaining_sessions ?? 0) > 0) return false;
|
||||
if (setup.reentry_gate_reset_required) return false;
|
||||
if (setup.rr_ratio < config.min_rr) return false;
|
||||
// Live R:R from current price — drops setups whose price has already run
|
||||
// toward target (reward consumed) or through the stop.
|
||||
@@ -80,9 +80,8 @@ export function qualifiesSetup(setup: TradeSetup, config: ActivationConfig): boo
|
||||
* qualifiesSetup rule-for-rule (keep the order in sync).
|
||||
*/
|
||||
export function disqualifyReason(setup: TradeSetup, config: ActivationConfig): string | null {
|
||||
const lockdownRemaining = setup.reentry_lockdown_remaining_sessions ?? 0;
|
||||
if (lockdownRemaining > 0) {
|
||||
return `post-stop lockdown · ${lockdownRemaining} session${lockdownRemaining === 1 ? '' : 's'} remaining`;
|
||||
if (setup.reentry_gate_reset_required) {
|
||||
return 'post-stop gate reset required';
|
||||
}
|
||||
if (setup.rr_ratio < config.min_rr) {
|
||||
return `R:R ${setup.rr_ratio.toFixed(1)} below gate ${config.min_rr.toFixed(1)}`;
|
||||
|
||||
@@ -144,7 +144,7 @@ export interface TradeSetup {
|
||||
momentum_percentile?: number | null;
|
||||
strategy_rank?: number | null;
|
||||
volatility_percentile?: number | null;
|
||||
reentry_lockdown_remaining_sessions?: number | null;
|
||||
reentry_gate_reset_required?: boolean;
|
||||
context_as_of?: TradeSetupContextAsOf | null;
|
||||
recommendation_summary?: RecommendationSummary;
|
||||
}
|
||||
@@ -356,10 +356,10 @@ export interface BacktestPortfolioMonitorRun extends BacktestPortfolioPolicy {
|
||||
label: string;
|
||||
description: string;
|
||||
is_production: boolean;
|
||||
comparison_arm?: 'live_no_lockdown' | 'live_lockdown_5' | null;
|
||||
comparison_arm?: 'live_immediate' | 'live_gate_reset' | null;
|
||||
entry_variant: string;
|
||||
exit_policy: string;
|
||||
reentry_lockdown_sessions?: number;
|
||||
reentry_policy?: 'immediate' | 'gate_reset';
|
||||
lookback: string;
|
||||
lookback_label: string;
|
||||
}
|
||||
@@ -371,8 +371,8 @@ export interface BacktestPortfolioMonitor {
|
||||
label: string;
|
||||
description: string;
|
||||
is_production: boolean;
|
||||
comparison_arm?: 'live_no_lockdown' | 'live_lockdown_5' | null;
|
||||
reentry_lockdown_sessions?: number;
|
||||
comparison_arm?: 'live_immediate' | 'live_gate_reset' | null;
|
||||
reentry_policy?: 'immediate' | 'gate_reset';
|
||||
}[];
|
||||
lookbacks: { lookback: string; label: string }[];
|
||||
runs: BacktestPortfolioMonitorRun[];
|
||||
@@ -415,7 +415,7 @@ export interface BacktestReport {
|
||||
target_model?: 'production_gtl' | 'structural_sr';
|
||||
target_model_label?: string;
|
||||
is_production_target_model?: boolean;
|
||||
production_reentry_lockdown_sessions?: number;
|
||||
production_reentry_policy?: 'gate_reset';
|
||||
};
|
||||
overall_qualified: BacktestBucket;
|
||||
overall_all: BacktestBucket;
|
||||
|
||||
Reference in New Issue
Block a user