from src.portfolio.runner import build_worker_for from src.portfolio.base import SleeveSpec from src.live.strategy_worker import StrategyWorker from src.live.pairs_worker import PairsWorker def test_build_single_worker_capital_from_alloc(tmp_path): spec = SleeveSpec(kind="single", name="MR01", sid="MR01_BTC", asset="BTC", params={"bb_window": 50, "k": 2.5, "sl_atr": 2.0, "max_bars": 24}) w = build_worker_for(spec, alloc_capital=300.0, leverage=2.0, data_dir=tmp_path) assert isinstance(w, StrategyWorker) assert w.capital == 300.0 and w.leverage == 2.0 def test_build_pairs_worker(tmp_path): spec = SleeveSpec(kind="pairs", name="PR01", sid="PR_ETHBTC", a="ETH", b="BTC", params={"n": 50, "z_in": 2.0, "z_exit": 0.75, "max_bars": 72}) w = build_worker_for(spec, alloc_capital=200.0, leverage=2.0, data_dir=tmp_path) assert isinstance(w, PairsWorker) assert w.capital == 200.0 def test_build_ml_sh01_is_plain_strategyworker(tmp_path): """SH01 (kind=ml) deve costruirsi come StrategyWorker che esegue SH01_shape_ml — NON il vecchio MLWorkerWrapper (SignalEngine squeeze scartato, che ignorava la strategia ed usciva a hold_bars=3). Regressione del bug di wiring scoperto il 2026-06-01.""" spec = SleeveSpec(kind="ml", name="SH01", sid="SH_BTC", asset="BTC") w = build_worker_for(spec, alloc_capital=58.82, leverage=2.0, data_dir=tmp_path) assert isinstance(w, StrategyWorker) assert w.strategy.name == "SH01_shape_ml" # esegue davvero shape-ML assert not hasattr(w, "engine") # niente SignalEngine squeeze assert w.tf == "1h"