feat(adversarial): flat_too_long_threshold parametrico (CLI ablation)
Estende AdversarialAgent con flat_too_long_threshold (default 0.95) configurabile, simmetrico a fees_eat_alpha_threshold. Propagato a RunConfig.flat_too_long_threshold e flag CLI --flat-too-long-threshold. Motivazione: pop30-combo ha registrato 75 finding flat_too_long HIGH (secondo killer dopo fees_eat_alpha 87). Rilassare la soglia 0.95→0.98 ammette strategie più passive ma marginalmente attive — analogo all'ablation fees già verificata (+23% stabile). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -43,6 +43,12 @@ def parse_args() -> argparse.Namespace:
|
||||
default=0.5,
|
||||
help="Adversarial gate: kill se fees/gross_pnl > soglia (default 0.5, ablation 0.7)",
|
||||
)
|
||||
p.add_argument(
|
||||
"--flat-too-long-threshold",
|
||||
type=float,
|
||||
default=0.95,
|
||||
help="Adversarial gate: kill se signal flat > soglia delle bar (default 0.95, ablation 0.98)",
|
||||
)
|
||||
return p.parse_args()
|
||||
|
||||
|
||||
@@ -98,6 +104,7 @@ def main() -> None:
|
||||
db_path=settings.db_path,
|
||||
prompt_mutation_weight=args.prompt_mutation_weight,
|
||||
fees_eat_alpha_threshold=args.fees_eat_alpha_threshold,
|
||||
flat_too_long_threshold=args.flat_too_long_threshold,
|
||||
)
|
||||
|
||||
run_id = run_phase1(cfg, ohlcv=ohlcv, llm=llm)
|
||||
|
||||
@@ -63,9 +63,11 @@ class AdversarialAgent:
|
||||
self,
|
||||
fees_bp: float = 5.0,
|
||||
fees_eat_alpha_threshold: float = 0.5,
|
||||
flat_too_long_threshold: float = 0.95,
|
||||
) -> None:
|
||||
self._engine = BacktestEngine(fees_bp=fees_bp)
|
||||
self._fees_eat_alpha_threshold = fees_eat_alpha_threshold
|
||||
self._flat_too_long_threshold = flat_too_long_threshold
|
||||
|
||||
def review(self, strategy: Strategy, ohlcv: pd.DataFrame) -> AdversarialReport:
|
||||
signal_fn = compile_strategy(strategy)
|
||||
@@ -133,12 +135,15 @@ class AdversarialAgent:
|
||||
n_active = int(((signals == Side.LONG) | (signals == Side.SHORT)).sum())
|
||||
n_flat_or_nan = n_bars - n_active
|
||||
flat_ratio = n_flat_or_nan / n_bars if n_bars > 0 else 1.0
|
||||
if flat_ratio > 0.95:
|
||||
if flat_ratio > self._flat_too_long_threshold:
|
||||
report.findings.append(
|
||||
Finding(
|
||||
name="flat_too_long",
|
||||
severity=Severity.HIGH,
|
||||
detail=f"Signal flat for {flat_ratio * 100:.1f}% of bars (>95% threshold)",
|
||||
detail=(
|
||||
f"Signal flat for {flat_ratio * 100:.1f}% of bars "
|
||||
f"(>{self._flat_too_long_threshold * 100:.0f}% threshold)"
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ class RunConfig:
|
||||
db_path: Path = field(default_factory=lambda: Path("./runs.db"))
|
||||
prompt_mutation_weight: float = 0.0 # Phase 2.5: opt-in LLM mutator
|
||||
fees_eat_alpha_threshold: float = 0.5 # adversarial gate, allenta verso 0.7-0.8
|
||||
flat_too_long_threshold: float = 0.95 # adversarial gate, allenta verso 0.98-0.99
|
||||
|
||||
|
||||
def run_phase1(
|
||||
@@ -82,6 +83,7 @@ def run_phase1(
|
||||
adversarial_agent = AdversarialAgent(
|
||||
fees_bp=cfg.fees_bp,
|
||||
fees_eat_alpha_threshold=cfg.fees_eat_alpha_threshold,
|
||||
flat_too_long_threshold=cfg.flat_too_long_threshold,
|
||||
)
|
||||
cost_tracker = CostTracker()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user