#!/usr/bin/env python3 """P6 helper: emit the 140 sim_variants for the deployability sweep. Cross-product: 7 costs × 4 latencies × 5 thresholds = 140 variants. Threshold values are placeholders — the actual values come from config/ml/v2_prod_thresholds.json after the threshold-tuning step. Re-run this script post-tuning to refresh sweep_deployability.yaml with calibrated thresholds. Usage: scripts/generate_sweep_variants.py > config/ml/sweep_deployability.yaml """ from __future__ import annotations import json, sys from pathlib import Path # 7 costs (price-units / lot / side). 0.125 = 1 tick (ES), 0.0625 = 0.5 tick (best-case), # stepping through realistic retail (0.25, 0.375 = 2-3 ticks), and worst-case (1.0 = 8 ticks). COSTS = [0.0625, 0.125, 0.1875, 0.25, 0.375, 0.5, 1.0] # 4 latencies (ns). 100M = 100ms (best), 200M = realistic anchor, 300M = degraded, # 400M = stress anchor. LATENCIES_NS = [100_000_000, 200_000_000, 300_000_000, 400_000_000] # 5 thresholds — REPLACE THESE WITH THE OUTPUT OF THE THRESHOLD-TUNING PASS. # Read from config/ml/v2_prod_thresholds.json if it exists; otherwise use # the canonical p60-p95 placeholder (5 steps spanning the range). THRESH_JSON = Path(__file__).resolve().parent.parent / "config" / "ml" / "v2_prod_thresholds.json" if THRESH_JSON.exists(): blob = json.loads(THRESH_JSON.read_text()) THRESHOLDS = blob["thresholds_absolute"] # e.g. {"p60": 0.42, "p70": 0.55, ...} THRESHOLD_NAMES = list(THRESHOLDS.keys()) THRESHOLD_VALUES = list(THRESHOLDS.values()) assert len(THRESHOLD_VALUES) == 5, f"expected 5 thresholds; got {len(THRESHOLD_VALUES)}" else: print("# WARNING: v2_prod_thresholds.json missing — using p60-p95 placeholder thresholds", file=sys.stderr) THRESHOLD_NAMES = ["p60", "p70", "p80", "p90", "p95"] THRESHOLD_VALUES = [0.40, 0.55, 0.70, 0.85, 0.95] assert len(COSTS) == 7 assert len(LATENCIES_NS) == 4 print("# P6 deployability sweep — 4 windows × 140 sim_variants (7 cost × 4 latency × 5 threshold).") print("# Generated by scripts/generate_sweep_variants.py — do NOT hand-edit.") print("# Re-run after threshold-tuning to refresh threshold absolute values.") print() print("base:") print(" data_template: /mnt/training-data/futures-baseline-mbp10/ES.FUT/ES.FUT_{window}.dbn.zst") print(" predecoded_dir: /feature-cache/predecoded") print(" n_parallel: 1 # batched flow overrides with variants.len()=140") print(" decision_stride: 4") print(" latency_ns: 200000000 # default (overridden per variant)") print(" target_annual_vol_units: 50.0") print(" annualisation_factor: 825.0") print(" max_lots: 5") print(" max_events: 0") print(" seed: 12648430") print(" # __SHA__ replaced by submitter with post-trunk-grows training short-SHA.") print(" checkpoint: /feature-cache/alpha-perception-runs/__SHA__/trunk_best_h1000.bin") print(" sim_variants:") for ci, cost in enumerate(COSTS): for li, latency in enumerate(LATENCIES_NS): for ti, (tname, tval) in enumerate(zip(THRESHOLD_NAMES, THRESHOLD_VALUES)): name = f"c{ci}_l{li}_{tname}" print(f" - {{ name: {name}, threshold: {tval}, cost_per_lot_per_side: {cost}, latency_ns: {latency} }}") print() print("cells:") print(" - { name: W1, window: 2025-Q2 }") print(" - { name: W2, window: 2025-Q3 }") print(" - { name: W3, window: 2025-Q4 }") print(" - { name: W4, window: 2026-Q1 }")