Add lookback entry feature for bounded backtests

When a backtest starts mid-stream (via --start), events that fired
before the start date but are still within their max_holding_days
window can now be entered on the first simulation day.

- Add `lookback_entry_enabled: bool = False` to ExecutionConfig
- On first sim day, _collect_lookback_candidates() gathers pre-start
  events, runs them through the same select_candidates() pipeline,
  and injects them before normal candidates
- Entry fills at the first day's open price; gap-cap check is skipped
  since the event is multi-days old
- days_held is initialized to the elapsed trading days so TIME exits
  fire at the correct time relative to the original event date
- Store slice is extended backward by max_mhd calendar buffer so
  pre-start rows survive slice_by_date_range when feature is enabled
- Enabled in return_max_long_v7.119 for testing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main
I Luk Kim 4 months ago
parent eb20c32a94
commit 5056295cb6

@ -248,6 +248,8 @@ class BacktestRunner:
self._dividend_capture_trade_counter: int = 0 self._dividend_capture_trade_counter: int = 0
self._form4_capture_trade_counter: int = 0 self._form4_capture_trade_counter: int = 0
self._ownership_capture_trade_counter: int = 0 self._ownership_capture_trade_counter: int = 0
self._lookback_entry_enabled: bool = config.execution.lookback_entry_enabled
self._lookback_injected: bool = False
def _get_known_upcoming_earnings_by_symbol( def _get_known_upcoming_earnings_by_symbol(
self, self,
@ -840,6 +842,14 @@ class BacktestRunner:
if not self._kill_switch_triggered: if not self._kill_switch_triggered:
portfolio_state = self._build_portfolio_state(date, drawdown_pct, unrealized) portfolio_state = self._build_portfolio_state(date, drawdown_pct, unrealized)
candidates = self._select_candidates_for_date(date) candidates = self._select_candidates_for_date(date)
# Lookback: on the first simulation day inject pre-start events still within mhd
if self._lookback_entry_enabled and not self._lookback_injected:
lookback = self._collect_lookback_candidates(date)
if lookback:
candidates = list(lookback) + list(candidates)
self._lookback_injected = True
shadow_candidates = self._select_shadow_candidates_for_date(date) shadow_candidates = self._select_shadow_candidates_for_date(date)
# Store scored candidates for delayed entry lookback # Store scored candidates for delayed entry lookback
@ -1527,6 +1537,113 @@ class BacktestRunner:
) )
return self.store.all_trading_days(include_reaction_dates=include_reaction_dates) return self.store.all_trading_days(include_reaction_dates=include_reaction_dates)
def _collect_lookback_candidates(self, first_sim_date: dt.date) -> list[Candidate]:
"""Return candidates from before first_sim_date that are still within their holding window.
Used on the first day of a bounded backtest so that events which fired before
the requested start date but whose max_holding_days has not yet expired
can still be entered at today's open price.
"""
from libs.backtest.calendar import get_trading_days
# Compute the widest possible holding window across all engines / event profiles
max_mhd = self.config.execution.max_holding_days
if self.config.execution.dynamic_hold_enabled:
max_mhd = max(max_mhd, self.config.execution.dynamic_hold_extend_to)
for engine in self.config.get_strategy_engines():
if engine.max_holding_days is not None:
engine_mhd = engine.max_holding_days
if engine.dynamic_hold_extend_to_override is not None:
engine_mhd = max(engine_mhd, engine.dynamic_hold_extend_to_override)
max_mhd = max(max_mhd, engine_mhd)
for profile in (self.config.event_type_profiles or {}).values():
if profile.max_holding_days_override is not None:
max_mhd = max(max_mhd, profile.max_holding_days_override)
# Collect raw rows for all execution dates before first_sim_date
# Use a calendar-day buffer of 2x to cover weekends/holidays
lookback_rows: list[dict[str, Any]] = []
for exec_date in sorted(self.store._candidates.keys()):
if exec_date >= first_sim_date:
break
lookback_rows.extend(self.store._candidates[exec_date])
if not lookback_rows:
return []
# Cache trading-day counts per execution_date for efficiency
_elapsed_cache: dict[dt.date, int] = {}
def _trading_days_elapsed(exec_date: dt.date) -> int:
if exec_date not in _elapsed_cache:
tdays = get_trading_days(exec_date, first_sim_date)
# Inclusive on both ends; elapsed = days the position has been "in play"
# (exec_date is day 0; first_sim_date adds another day beyond that)
_elapsed_cache[exec_date] = max(0, len(tdays) - 1)
return _elapsed_cache[exec_date]
# Run through the same selection pipeline as normal candidates
all_lookback: list[Candidate] = []
engine_list = list(self._primary_strategy_engines)
if not engine_list:
# No-engine (legacy single-engine) mode
selected = select_candidates(
lookback_rows,
self.config.universe,
self.config.signal,
event_type_profiles=self.config.event_type_profiles or None,
)
all_lookback.extend(selected)
else:
seen_event_ids: set[str] = set()
for engine in engine_list:
if not self._engine_allowed_for_date(engine, first_sim_date):
continue
if not self._engine_uses_snapshot_candidates(engine):
continue
effective_engine = self._effective_engine_for_date(engine, first_sim_date)
selected = select_candidates(
lookback_rows,
self.config.universe,
self.config.signal,
event_type_profiles=self.config.event_type_profiles or None,
strategy_engine=effective_engine,
engine_lookup=self._strategy_engine_lookup,
excluded_event_ids=seen_event_ids,
)
all_lookback.extend(selected)
seen_event_ids.update(cand.event_id for cand in selected)
# Filter by elapsed holding days and annotate surviving candidates
result: list[Candidate] = []
for candidate in all_lookback:
elapsed = _trading_days_elapsed(candidate.execution_date)
# Get the effective mhd for this specific candidate
eff_exec = self._build_effective_execution_config(candidate)
candidate_mhd = eff_exec.max_holding_days
if eff_exec.dynamic_hold_enabled:
candidate_mhd = max(candidate_mhd, eff_exec.dynamic_hold_extend_to)
if elapsed >= candidate_mhd:
continue # would have timed out by now
candidate.features["is_lookback_entry"] = True
candidate.features["lookback_days_elapsed"] = elapsed
candidate.features["lookback_original_execution_date"] = (
candidate.execution_date.isoformat()
)
result.append(candidate)
logger.info(
"lookback_entry_candidates",
first_sim_date=str(first_sim_date),
max_mhd=max_mhd,
rows_scanned=len(lookback_rows),
candidates_selected=len(result),
)
return result
def _select_candidates_for_date( def _select_candidates_for_date(
self, self,
date: dt.date, date: dt.date,
@ -1913,19 +2030,21 @@ class BacktestRunner:
) )
continue continue
bar = self.store.get_bar(candidate.symbol, candidate.execution_date) is_lookback = bool(candidate.features.get("is_lookback_entry", False))
gap_skip_reason = self._check_next_open_gap_cap(candidate, bar) bar = self.store.get_bar(candidate.symbol, date if is_lookback else candidate.execution_date)
if gap_skip_reason is not None: if not is_lookback:
self._total_orders_rejected += 1 gap_skip_reason = self._check_next_open_gap_cap(candidate, bar)
self._release_add_on_reservation(candidate) if gap_skip_reason is not None:
logger.debug( self._total_orders_rejected += 1
"order_rejected", self._release_add_on_reservation(candidate)
engine_id=candidate.engine_id, logger.debug(
symbol=candidate.symbol, "order_rejected",
reason=gap_skip_reason, engine_id=candidate.engine_id,
date=str(date), symbol=candidate.symbol,
) reason=gap_skip_reason,
continue date=str(date),
)
continue
pos = simulate_entry( pos = simulate_entry(
plan, plan,
@ -1938,6 +2057,8 @@ class BacktestRunner:
pos.parent_position_id = candidate.parent_position_id pos.parent_position_id = candidate.parent_position_id
pos.is_add_on = candidate.is_add_on pos.is_add_on = candidate.is_add_on
if is_lookback:
pos.days_held = int(candidate.features.get("lookback_days_elapsed", 0))
self._open_positions.append(pos) self._open_positions.append(pos)
trade_cost = pos.entry_price * pos.shares_total trade_cost = pos.entry_price * pos.shares_total
if allow_parking_cash_release and self._cash < trade_cost and self._get_parking_value(date) > 0: if allow_parking_cash_release and self._cash < trade_cost and self._get_parking_value(date) > 0:
@ -6896,6 +7017,23 @@ def _last_market_closed_date() -> dt.date:
return now_et.date() - dt.timedelta(days=1) return now_et.date() - dt.timedelta(days=1)
def _compute_max_effective_mhd(config: BacktestConfig) -> int:
"""Return the widest max_holding_days value across all engines and event profiles."""
mhd = config.execution.max_holding_days
if config.execution.dynamic_hold_enabled:
mhd = max(mhd, config.execution.dynamic_hold_extend_to)
for engine in config.get_strategy_engines():
if engine.max_holding_days is not None:
engine_mhd = engine.max_holding_days
if engine.dynamic_hold_extend_to_override is not None:
engine_mhd = max(engine_mhd, engine.dynamic_hold_extend_to_override)
mhd = max(mhd, engine_mhd)
for profile in (config.event_type_profiles or {}).values():
if profile.max_holding_days_override is not None:
mhd = max(mhd, profile.max_holding_days_override)
return mhd
def _extend_store_to_requested_window( def _extend_store_to_requested_window(
*, *,
store: SnapshotStore, store: SnapshotStore,
@ -7473,7 +7611,15 @@ def main() -> None:
raise RuntimeError("No trading days found in snapshot store.") raise RuntimeError("No trading days found in snapshot store.")
start_d = dt.date.fromisoformat(args.start) if args.start else all_store_dates[0] start_d = dt.date.fromisoformat(args.start) if args.start else all_store_dates[0]
end_d = dt.date.fromisoformat(args.end) if args.end else all_store_dates[-1] end_d = dt.date.fromisoformat(args.end) if args.end else all_store_dates[-1]
store = store.slice_by_date_range(start_d, end_d) if config.execution.lookback_entry_enabled and args.start:
# Extend slice start backward so pre-start events remain in the store
# for _collect_lookback_candidates; simulation dates are still gated by
# _requested_start_date set below, so those rows stay dormant otherwise.
max_mhd = _compute_max_effective_mhd(config)
lookback_start = start_d - dt.timedelta(days=max_mhd * 2)
store = store.slice_by_date_range(lookback_start, end_d)
else:
store = store.slice_by_date_range(start_d, end_d)
store = _extend_store_to_requested_window( store = _extend_store_to_requested_window(
store=store, store=store,
config=config, config=config,

@ -0,0 +1,582 @@
{
"experiment_name": "return_max_long_v7.119",
"dataset_snapshot_id": "midlarge-liquid-long-v1_bucketfix_full_audit_canonical",
"description": "v7.118 + 6 per-engine exit/veto optimizations. 12개 엔진. Standalone SQS 92.4 (493% return).\nBEST conservative: tqqq_calm_v2 + sleeves = 3029.38% CW, SQS 89.2\nBEST aggressive: tqqq_active_v2 + sleeves = 3245.24% CW, SQS 89.2\nParking v3: gate_vol 0.35, TQQQ vol 0.22, temp 1.0, entropy 1.45 (1.15→1.45)\nLineage: v7.70→v7.110→v7.113→v7.114→v7.115→v7.116→v7.117→v7.118→v7.119",
"base_config": "configs/backtest/return_max_long_v1.json",
"overrides": {
"signal": {
"scoring_model": "return_max_long_v13e",
"score_threshold": 0.45,
"max_candidates_per_day": 18,
"a_tier_score_threshold": 0.58
},
"risk": {
"per_trade_risk_pct": 0.069,
"per_trade_risk_pct_a_tier": 0.0759,
"max_daily_new_risk_pct": 1.5,
"max_positions": 24,
"max_positions_per_sector": 5,
"max_position_value_pct": 1.5,
"max_adv_fraction": 0.2,
"macro_regime_neutral_size_scaler": 1.0,
"macro_regime_risk_off_size_scaler": 1.0,
"veto_unknown_direction": false,
"macro_regime_risk_off_a_tier_only": false,
"stop_atr_multiplier": 1.33,
"allow_budget_downsizing": true
},
"execution": {
"a_tier_target_1_r": 3.5,
"a_tier_target_1_fraction": 0.0,
"non_a_tier_target_1_r": 2.25,
"non_a_tier_target_1_fraction": 0.2,
"trailing_warmup_days": 7,
"max_holding_days": 12,
"early_failure_no_progress_days": 1,
"early_failure_no_progress_r": 0.15,
"early_failure_no_progress_fraction": 1.0,
"lookback_entry_enabled": true
},
"event_type_profiles": {
"material_contract": {
"enabled": true,
"direction_filter": "any",
"max_holding_days_override": 20
},
"other_material_event": {
"enabled": true,
"direction_filter": "any",
"max_holding_days_override": 20
},
"unknown": {
"enabled": true,
"direction_filter": "any",
"max_holding_days_override": 12
}
}
},
"strategy_engines": [
{
"engine_id": "next_open_long_unknown_material_patient",
"event_types": [
"material_contract"
],
"event_directions": [
"unknown"
],
"guidance_statuses": [
"not_provided"
],
"filing_time_buckets": [
"post_market"
],
"timing_class": "after_close",
"direction": "long_only",
"entry_timing_policy": "next_open",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.25,
"reaction_day_return_min": 0.0,
"reaction_day_return_max": 0.15,
"close_location_min": 0.5,
"close_location_max": 0.85,
"gap_size_min": 0.0,
"gap_size_max": 0.02,
"volume_ratio_min": 0.8,
"volume_ratio_max": 2.0,
"max_market_cap_proxy": 10000000000.0,
"document_quality_score_min": 0.5,
"parse_confidence_overall_min": 0.45,
"score_threshold_override": 0.0,
"residual_reserve_selected": true,
"veto_parse_confidence_min_override": 0.45,
"next_open_gap_cap_pct": 0.02,
"early_failure_close_below_entry_and_reaction_close_override": true,
"early_failure_no_progress_days_override": 15,
"early_failure_no_progress_r_override": 0.0,
"early_failure_no_progress_fraction_override": 1.0,
"target_1_r_override": 5.0,
"target_1_fraction_override": 0.1,
"trailing_warmup_days_override": 12,
"enabled": true,
"min_market_cap_proxy": 5000000000.0,
"macro_vix_max": 30.0
},
{
"engine_id": "reaction_close_long_core",
"event_types": [
"earnings_release",
"guidance_update"
],
"timing_class": "same_day",
"direction": "long_only",
"entry_timing_policy": "reaction_close",
"max_holding_days": 12,
"engine_risk_budget_pct": 1.0,
"reaction_day_return_min": 0.0,
"reaction_day_return_max": 0.18,
"close_location_min": 0.45,
"volume_ratio_min": 1.0,
"gap_size_min": 0.0,
"weak_reaction_threshold": 0.03,
"weak_reaction_gap_max": 0.02,
"unknown_direction_reaction_min": 0.05,
"unknown_direction_close_location_min": 0.7,
"attention_max_wiki_spike_10d": 6.0,
"score_threshold_override": 0.42,
"enabled": true,
"residual_reserve_selected": true,
"mixed_inline_close_location_max": 0.88,
"mixed_inline_gap_size_max": 0.1,
"unknown_inline_exit_close_location_min": 0.9,
"unknown_inline_exit_gap_size_max": 0.04,
"unknown_inline_early_failure_no_progress_days_override": 2,
"unknown_inline_early_failure_no_progress_r_override": 0.1,
"unknown_inline_early_failure_no_progress_fraction_override": 1.0,
"per_trade_risk_pct_override": 0.045,
"trailing_warmup_days_override": 8,
"macro_vix_max": 30.0,
"early_failure_close_below_entry_and_reaction_close_override": false,
"early_failure_no_progress_r_override": 0.2
},
{
"engine_id": "reaction_close_long_residual_lowclose_gap_d3",
"event_types": [
"earnings_release",
"guidance_update"
],
"timing_class": "same_day",
"direction": "long_only",
"entry_timing_policy": "reaction_close",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.12,
"reaction_day_return_min": 0.03,
"reaction_day_return_max": 0.2,
"close_location_min": 0.35,
"close_location_max": 0.45,
"volume_ratio_min": 1.25,
"gap_size_min": 0.05,
"gap_size_max": 0.1,
"max_market_cap_proxy": 10000000000.0,
"score_threshold_override": 0.35,
"target_1_r_override": 3.0,
"target_1_fraction_override": 0.0,
"residual_reserve_selected": true,
"early_failure_close_below_entry_and_reaction_close_override": false,
"early_failure_no_progress_days_override": 3,
"early_failure_no_progress_r_override": 0.25,
"early_failure_no_progress_fraction_override": 1.0,
"enabled": true,
"macro_vix_max": 30.0
},
{
"engine_id": "reaction_close_long_residual_smallcap_gap",
"event_types": [
"earnings_release",
"guidance_update"
],
"timing_class": "same_day",
"direction": "long_only",
"entry_timing_policy": "reaction_close",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.12,
"reaction_day_return_min": 0.03,
"reaction_day_return_max": 0.2,
"close_location_min": 0.45,
"volume_ratio_min": 1.25,
"gap_size_min": 0.05,
"gap_size_max": 0.1,
"max_market_cap_proxy": 10000000000.0,
"score_threshold_override": 0.35,
"target_1_r_override": 3.0,
"target_1_fraction_override": 0.0,
"enabled": true,
"macro_vix_max": 30.0
},
{
"engine_id": "reaction_close_long_extreme_orderly",
"event_types": [
"earnings_release",
"guidance_update"
],
"timing_class": "same_day",
"direction": "long_only",
"entry_timing_policy": "reaction_close",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.35,
"reaction_day_return_min": 0.2,
"reaction_day_return_max": 0.4,
"close_location_min": 0.83,
"volume_ratio_min": 6.0,
"gap_size_min": 0.0,
"gap_size_max": 0.15,
"attention_max_wiki_spike_10d": 6.0,
"score_threshold_override": 0.65,
"enabled": true,
"target_1_r_override": 99.0,
"target_1_fraction_override": 0.0,
"trailing_warmup_days_override": 8,
"early_failure_close_below_entry_and_reaction_close_override": false,
"early_failure_no_progress_days_override": null,
"early_failure_no_progress_r_override": null,
"early_failure_no_progress_fraction_override": null,
"macro_vix_max": 30.0
},
{
"engine_id": "next_open_long_unknown_inline_hivol",
"event_types": [
"earnings_release"
],
"event_directions": [
"unknown"
],
"guidance_statuses": [
"inline_or_maintained"
],
"filing_time_buckets": [
"post_market"
],
"timing_class": "after_close",
"direction": "long_only",
"entry_timing_policy": "next_open",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.1,
"reaction_day_return_min": 0.0,
"reaction_day_return_max": 0.06,
"close_location_min": 0.74,
"close_location_max": 0.86,
"gap_size_min": 0.0,
"gap_size_max": 0.04,
"volume_ratio_min": 2.1,
"volume_ratio_max": 2.8,
"document_quality_score_min": 0.5,
"parse_confidence_overall_min": 0.5,
"score_threshold_override": 0.0,
"residual_reserve_selected": true,
"veto_oneoff_penalty_override": 1.0,
"next_open_gap_cap_pct": 0.04,
"early_failure_close_below_entry_and_reaction_close_override": false,
"early_failure_no_progress_days_override": 15,
"early_failure_no_progress_r_override": 0.0,
"early_failure_no_progress_fraction_override": 1.0,
"target_1_r_override": 5.0,
"target_1_fraction_override": 0.1,
"trailing_warmup_days_override": 12,
"enabled": true,
"macro_vix_max": 30.0
},
{
"engine_id": "next_open_long_guidance_unknown_orderly",
"event_types": [
"guidance_update"
],
"event_directions": [
"unknown"
],
"guidance_statuses": [
"not_provided"
],
"filing_time_buckets": [
"post_market"
],
"timing_class": "after_close",
"direction": "long_only",
"entry_timing_policy": "next_open",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.0675,
"reaction_day_return_min": -0.03,
"reaction_day_return_max": 0.05,
"close_location_min": 0.9,
"gap_size_min": -0.02,
"gap_size_max": 0.03,
"volume_ratio_min": 0.8,
"min_market_cap_proxy": 9000000000.0,
"document_quality_score_min": 0.52,
"parse_confidence_overall_min": 0.48,
"score_threshold_override": 0.0,
"residual_reserve_selected": true,
"next_open_gap_cap_pct": 0.03,
"early_failure_close_below_entry_and_reaction_close_override": false,
"early_failure_no_progress_days_override": 15,
"early_failure_no_progress_r_override": 0.0,
"early_failure_no_progress_fraction_override": 1.0,
"target_1_r_override": 4.0,
"target_1_fraction_override": 0.1,
"trailing_warmup_days_override": 15,
"enabled": true,
"veto_parse_confidence_min_override": 0.48,
"per_trade_risk_pct_override": 0.04,
"stop_atr_multiplier_override": 2.0,
"use_reaction_day_low_stop_override": false,
"macro_vix_max": 30.0
},
{
"engine_id": "next_open_long_earnings_mixed_inline_orderly",
"event_types": [
"earnings_release"
],
"event_directions": [
"mixed"
],
"guidance_statuses": [
"inline_or_maintained"
],
"filing_time_buckets": [
"post_market"
],
"timing_class": "after_close",
"direction": "long_only",
"entry_timing_policy": "next_open",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.03,
"reaction_day_return_min": -0.03,
"reaction_day_return_max": 0.06,
"close_location_min": 0.5,
"gap_size_min": -0.02,
"gap_size_max": 0.05,
"volume_ratio_min": 1.0,
"min_market_cap_proxy": 8000000000.0,
"document_quality_score_min": 0.5,
"parse_confidence_overall_min": 0.5,
"score_threshold_override": 0.0,
"residual_reserve_selected": true,
"next_open_gap_cap_pct": 0.04,
"early_failure_close_below_entry_and_reaction_close_override": false,
"early_failure_no_progress_days_override": 15,
"early_failure_no_progress_r_override": 0.0,
"early_failure_no_progress_fraction_override": 1.0,
"target_1_r_override": 5.0,
"target_1_fraction_override": 0.1,
"trailing_warmup_days_override": 12,
"enabled": true,
"veto_parse_confidence_min_override": 0.5,
"per_trade_risk_pct_override": 0.04,
"stop_atr_multiplier_override": 3.0,
"use_reaction_day_low_stop_override": false,
"macro_vix_max": 30.0
},
{
"engine_id": "next_open_long_unknown_material_orderly",
"event_types": [
"material_contract"
],
"event_directions": [
"unknown"
],
"guidance_statuses": [
"not_provided"
],
"filing_time_buckets": [
"post_market"
],
"timing_class": "after_close",
"direction": "long_only",
"entry_timing_policy": "next_open",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.02,
"reaction_day_return_min": -0.02,
"reaction_day_return_max": 0.06,
"close_location_min": 0.5,
"close_location_max": 0.8,
"gap_size_min": -0.02,
"gap_size_max": 0.02,
"volume_ratio_min": 0.9,
"volume_ratio_max": 1.4,
"min_market_cap_proxy": 10000000000.0,
"max_market_cap_proxy": 100000000000.0,
"document_quality_score_min": 0.5,
"parse_confidence_overall_min": 0.45,
"score_threshold_override": 0.0,
"residual_reserve_selected": true,
"next_open_gap_cap_pct": 0.03,
"early_failure_close_below_entry_and_reaction_close_override": false,
"early_failure_no_progress_days_override": 15,
"early_failure_no_progress_r_override": 0.0,
"early_failure_no_progress_fraction_override": 1.0,
"target_1_r_override": 5.0,
"target_1_fraction_override": 0.1,
"trailing_warmup_days_override": 12,
"enabled": true,
"veto_parse_confidence_min_override": 0.45,
"per_trade_risk_pct_override": 0.015,
"stop_atr_multiplier_override": 3.0,
"use_reaction_day_low_stop_override": false,
"macro_vix_max": 30.0
},
{
"engine_id": "next_open_long_other_material_mixed_orderly",
"event_types": [
"other_material_event"
],
"event_directions": [
"mixed"
],
"guidance_statuses": [
"not_provided"
],
"filing_time_buckets": [
"post_market"
],
"timing_class": "after_close",
"direction": "long_only",
"entry_timing_policy": "next_open",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.02,
"reaction_day_return_min": -0.04,
"reaction_day_return_max": 0.06,
"close_location_min": 0.45,
"gap_size_min": -0.03,
"gap_size_max": 0.05,
"volume_ratio_min": 1.0,
"min_market_cap_proxy": 6000000000.0,
"document_quality_score_min": 0.54,
"parse_confidence_overall_min": 0.5,
"score_threshold_override": 0.0,
"residual_reserve_selected": true,
"next_open_gap_cap_pct": 0.04,
"early_failure_close_below_entry_and_reaction_close_override": false,
"early_failure_no_progress_days_override": 1,
"early_failure_no_progress_r_override": 0.0,
"early_failure_no_progress_fraction_override": 1.0,
"target_1_r_override": 5.0,
"target_1_fraction_override": 0.1,
"trailing_warmup_days_override": 12,
"enabled": true,
"veto_parse_confidence_min_override": 0.5,
"veto_oneoff_penalty_override": 0.0,
"per_trade_risk_pct_override": 0.015,
"stop_atr_multiplier_override": 3.0,
"use_reaction_day_low_stop_override": false,
"macro_vix_max": 30.0
},
{
"engine_id": "next_open_long_unknown_ome_orderly",
"event_types": [
"other_material_event"
],
"event_directions": [
"unknown"
],
"guidance_statuses": [
"not_provided"
],
"filing_time_buckets": [
"post_market"
],
"timing_class": "after_close",
"direction": "long_only",
"entry_timing_policy": "next_open",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.04,
"reaction_day_return_min": 0.0,
"reaction_day_return_max": 0.12,
"close_location_min": 0.55,
"volume_ratio_min": 1.0,
"min_market_cap_proxy": 4000000000.0,
"score_threshold_override": 0.0,
"residual_reserve_selected": true,
"next_open_gap_cap_pct": 0.04,
"early_failure_close_below_entry_and_reaction_close_override": false,
"early_failure_no_progress_days_override": 1,
"early_failure_no_progress_r_override": 0.0,
"early_failure_no_progress_fraction_override": 1.0,
"target_1_r_override": 5.0,
"target_1_fraction_override": 0.1,
"trailing_warmup_days_override": 12,
"enabled": true,
"veto_parse_confidence_min_override": 0.4,
"per_trade_risk_pct_override": 0.008,
"stop_atr_multiplier_override": 4.0,
"use_reaction_day_low_stop_override": false,
"macro_vix_max": 30.0
},
{
"engine_id": "next_open_long_bullish_raised_recovery_broad_oneoff",
"event_types": [
"earnings_release"
],
"event_directions": [
"bullish"
],
"guidance_statuses": [
"raised"
],
"filing_time_buckets": [
"post_market"
],
"timing_class": "after_close",
"direction": "long_only",
"entry_timing_policy": "next_open",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.03,
"reaction_day_return_min": 0.0,
"reaction_day_return_max": 0.1,
"close_location_min": 0.3,
"close_location_max": 0.75,
"gap_size_min": 0.01,
"gap_size_max": 0.12,
"volume_ratio_min": 1.3,
"volume_ratio_max": 5.0,
"min_market_cap_proxy": 8000000000.0,
"document_quality_score_min": 0.5,
"parse_confidence_overall_min": 0.5,
"score_threshold_override": 0.0,
"residual_reserve_selected": true,
"veto_parse_confidence_min_override": 0.5,
"veto_oneoff_penalty_override": 0.75,
"allow_oneoff_downsizing_override": true,
"oneoff_downsize_floor_override": 0.15,
"next_open_gap_cap_pct": 0.12,
"early_failure_close_below_entry_and_reaction_close_override": false,
"early_failure_no_progress_days_override": 12,
"early_failure_no_progress_r_override": 0.0,
"early_failure_no_progress_fraction_override": 1.0,
"target_1_r_override": 5.0,
"target_1_fraction_override": 0.1,
"trailing_warmup_days_override": 12,
"per_trade_risk_pct_override": 0.025,
"stop_atr_multiplier_override": 3.0,
"use_reaction_day_low_stop_override": false,
"enabled": true,
"macro_vix_max": 30.0
}
],
"splits": [
{
"kind": "named_snapshot",
"params": {
"name": "train"
}
},
{
"kind": "named_snapshot",
"params": {
"name": "valid"
}
},
{
"kind": "named_snapshot",
"params": {
"name": "test"
}
}
],
"tags": [
"return-max",
"v6new",
"de-risk"
],
"notes": "Add macro_vix_max to reaction_close_long_core on top of v6new.350 to keep current split/WFV while sharply improving matched stress OOT.",
"aliases": [
"conviction",
"v7.119 — 12-engine composite champion (SQS 92.4, 493% standalone)"
],
"version_family": "v7",
"created_at": "2026-04-07T03:50:56.416441+00:00",
"created_by": "ai_agent",
"status": "promoted",
"generation": 19,
"changelog": "6 per-engine exit/veto optimizations: guidance target 6→4 frac 0→0.1, patient close_below=true, residual frac→0, core EF r→0.20, material_mixed veto_oneoff→0. Combined +83pp conservative / +116pp aggressive CW",
"parent": "return_max_long_v7.118",
"id": 1138,
"performance_summary": null
}

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save