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,
|
default=0.5,
|
||||||
help="Adversarial gate: kill se fees/gross_pnl > soglia (default 0.5, ablation 0.7)",
|
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()
|
return p.parse_args()
|
||||||
|
|
||||||
|
|
||||||
@@ -98,6 +104,7 @@ def main() -> None:
|
|||||||
db_path=settings.db_path,
|
db_path=settings.db_path,
|
||||||
prompt_mutation_weight=args.prompt_mutation_weight,
|
prompt_mutation_weight=args.prompt_mutation_weight,
|
||||||
fees_eat_alpha_threshold=args.fees_eat_alpha_threshold,
|
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)
|
run_id = run_phase1(cfg, ohlcv=ohlcv, llm=llm)
|
||||||
|
|||||||
@@ -63,9 +63,11 @@ class AdversarialAgent:
|
|||||||
self,
|
self,
|
||||||
fees_bp: float = 5.0,
|
fees_bp: float = 5.0,
|
||||||
fees_eat_alpha_threshold: float = 0.5,
|
fees_eat_alpha_threshold: float = 0.5,
|
||||||
|
flat_too_long_threshold: float = 0.95,
|
||||||
) -> None:
|
) -> None:
|
||||||
self._engine = BacktestEngine(fees_bp=fees_bp)
|
self._engine = BacktestEngine(fees_bp=fees_bp)
|
||||||
self._fees_eat_alpha_threshold = fees_eat_alpha_threshold
|
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:
|
def review(self, strategy: Strategy, ohlcv: pd.DataFrame) -> AdversarialReport:
|
||||||
signal_fn = compile_strategy(strategy)
|
signal_fn = compile_strategy(strategy)
|
||||||
@@ -133,12 +135,15 @@ class AdversarialAgent:
|
|||||||
n_active = int(((signals == Side.LONG) | (signals == Side.SHORT)).sum())
|
n_active = int(((signals == Side.LONG) | (signals == Side.SHORT)).sum())
|
||||||
n_flat_or_nan = n_bars - n_active
|
n_flat_or_nan = n_bars - n_active
|
||||||
flat_ratio = n_flat_or_nan / n_bars if n_bars > 0 else 1.0
|
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(
|
report.findings.append(
|
||||||
Finding(
|
Finding(
|
||||||
name="flat_too_long",
|
name="flat_too_long",
|
||||||
severity=Severity.HIGH,
|
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"))
|
db_path: Path = field(default_factory=lambda: Path("./runs.db"))
|
||||||
prompt_mutation_weight: float = 0.0 # Phase 2.5: opt-in LLM mutator
|
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
|
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(
|
def run_phase1(
|
||||||
@@ -82,6 +83,7 @@ def run_phase1(
|
|||||||
adversarial_agent = AdversarialAgent(
|
adversarial_agent = AdversarialAgent(
|
||||||
fees_bp=cfg.fees_bp,
|
fees_bp=cfg.fees_bp,
|
||||||
fees_eat_alpha_threshold=cfg.fees_eat_alpha_threshold,
|
fees_eat_alpha_threshold=cfg.fees_eat_alpha_threshold,
|
||||||
|
flat_too_long_threshold=cfg.flat_too_long_threshold,
|
||||||
)
|
)
|
||||||
cost_tracker = CostTracker()
|
cost_tracker = CostTracker()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user