research: 3-year stake-sizing campaign + violent-move studies (nothing changes signals)
Walk-forward "second opinion" stake multiplier (meta-labelling on 65 decision-time features, 3yr Oanda books @ $0.65) FAILED the pre-registered rule on M5 (clean null) and M15 (lift inside the label-shuffle null, explained by BUY-vs-SELL). Nothing that changes numbers ships; the 12-month M5 book re-run is byte-identical to the pinned one. Behaviour-neutral plumbing: - position_sizing.build_features (65 features; multiplier path returns 1.0, no artifact) - SignalReport.penalty_tags / penalty_total / momentum_fading (record-keeping) - TradeSetup.stake_multiplier = None (reserved) - gpu_backtest --export-features (default export stays the pinned 63 columns; extras forced to the tail even when the first trade lacks an indicator) Research tools (backtest/research): book_report, burst_map, burst_precursors, open_trade_bursts, calendar_impact, burst_indicator_map, sizing_model, watch_rule_eval. Findings recorded in backtest/research/sizing_campaign_2026-09.md: M5 stretch measures are positive in both halves; every open-trade protection against violent bars loses on both TFs; high-impact USD releases cause 20-31% of violent bars but trades held through them do better; violent bars come when price is already at an extreme, not from calm. Module 29 tests (13). Suite: 435 passed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RWV2yLt3YXHHGVYHSQU2EE
This commit is contained in:
@@ -70,6 +70,10 @@ class TradeSetup:
|
||||
# platform defaults; 0.0 means trailing is genuinely disabled.
|
||||
trail_arm_r: Optional[float] = None
|
||||
trail_dist_r: Optional[float] = None
|
||||
# Post-decision stake multiplier from position_sizing (advisory
|
||||
# overlay). None = unsized — every gate/exit path ignores it, and
|
||||
# the backtester's R accounting is unaffected unless it opts in.
|
||||
stake_multiplier: Optional[float] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -128,6 +132,13 @@ class SignalReport:
|
||||
market_regime: str = ""
|
||||
volatility_regime: str = ""
|
||||
exhaustion_count: int = 0
|
||||
# Record-keeping only (no gate reads these): the penalty tags that
|
||||
# fired in _compute_final_signal (tag -> RAW pre-decay amount), the
|
||||
# decayed total actually subtracted from confidence, and whether the
|
||||
# momentum-fading half of the exhaustion block was true.
|
||||
penalty_tags: Dict[str, float] = field(default_factory=dict)
|
||||
penalty_total: float = 0.0
|
||||
momentum_fading: bool = False
|
||||
|
||||
|
||||
class SignalAggregator:
|
||||
@@ -1552,6 +1563,12 @@ class SignalAggregator:
|
||||
if total_penalty >= MAX_PENALTY:
|
||||
break
|
||||
|
||||
# Record-keeping for position_sizing / signal-log forensics.
|
||||
# Purely additive — nothing below reads these fields.
|
||||
report.penalty_tags = {name: float(amount) for name, amount in penalties}
|
||||
report.penalty_total = float(total_penalty)
|
||||
report.momentum_fading = bool(momentum_fading)
|
||||
|
||||
confidence -= total_penalty
|
||||
confidence = max(0.0, min(95.0, confidence))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user