from __future__ import annotations import json import multiprocessing as mp import sys from concurrent.futures import ProcessPoolExecutor, as_completed from dataclasses import asdict from pathlib import Path import pandas as pd PACKAGE_PARENT = Path(__file__).resolve().parents[2] if str(PACKAGE_PARENT) not in sys.path: sys.path.insert(0, str(PACKAGE_PARENT)) from strategy32.research.soft_router import load_component_bundle, score_candidate from strategy32.scripts.run_current_cash_learned_blocker import LearnedBlockerCandidate from strategy32.scripts.run_current_cash_learned_blocker_exact import _exact_period_worker from strategy32.scripts.run_current_relaxed_hybrid_experiment import WINDOWS, YEAR_PERIODS, YTD_START OUT_JSON = Path("/tmp/strategy32_current_cash_blocked_scale_exact_sweep.json") BASELINE_JSON = Path("/tmp/strategy32_live_combo_backtest.json") CACHE_PATH = "/tmp/strategy32_fixed66_bundle.pkl" def _candidate_space() -> list[LearnedBlockerCandidate]: scales = (0.25, 0.40, 0.50, 0.60, 0.70, 0.75, 0.80, 0.90) return [ LearnedBlockerCandidate( block_bars=42, train_min_blocks=12, lookback_blocks=24, ridge_alpha=1.0, prediction_threshold=-0.0025, blocked_scale=scale, ) for scale in scales ] def main() -> None: _, latest_bar = load_component_bundle(CACHE_PATH) period_specs: list[tuple[str, str, pd.Timestamp, pd.Timestamp]] = [] for days, label in WINDOWS: period_specs.append(("window", label, latest_bar - pd.Timedelta(days=days), latest_bar)) for label, start, end_exclusive in YEAR_PERIODS: period_specs.append(("year", label, start, min(latest_bar, end_exclusive - pd.Timedelta(seconds=1)))) period_specs.append(("year", "2026_YTD", YTD_START, latest_bar)) ctx = mp.get_context("fork") results: list[dict[str, object]] = [] candidates = _candidate_space() for idx, candidate in enumerate(candidates, start=1): print(f"[candidate] {idx}/{len(candidates)} {candidate.name}", flush=True) window_results: dict[str, dict[str, float]] = {} year_results: dict[str, dict[str, float]] = {} with ProcessPoolExecutor(max_workers=min(6, len(period_specs)), mp_context=ctx) as executor: future_map = { executor.submit( _exact_period_worker, CACHE_PATH, candidate_payload=asdict(candidate), kind=kind, label=label, start_text=str(start), end_text=str(end), ): (kind, label) for kind, label, start, end in period_specs } for future in as_completed(future_map): kind, label, metrics = future.result() if kind == "window": window_results[label] = metrics else: year_results[label] = metrics score, negative_years, mdd_violations = score_candidate( {label: window_results[label] for _, label in WINDOWS}, {k: year_results[k] for k, _, _ in YEAR_PERIODS}, ) results.append( { "candidate": asdict(candidate), "name": candidate.name, "score": score, "negative_years": negative_years, "mdd_violations": mdd_violations, "windows": {label: window_results[label] for _, label in WINDOWS}, "years": year_results, } ) results.sort(key=lambda item: float(item["score"]), reverse=True) output = { "analysis": "current_cash_blocked_scale_exact_sweep", "latest_bar": str(latest_bar), "results": results, "baseline_exact": json.loads(BASELINE_JSON.read_text(encoding="utf-8")) if BASELINE_JSON.exists() else None, } OUT_JSON.write_text(json.dumps(output, indent=2), encoding="utf-8") print(json.dumps(output["results"], indent=2)) print(f"[saved] {OUT_JSON}", flush=True) if __name__ == "__main__": main()