diff --git a/docs/diary/2026-06-12-improvements-sweep.md b/docs/diary/2026-06-12-improvements-sweep.md new file mode 100644 index 0000000..fb83715 --- /dev/null +++ b/docs/diary/2026-06-12-improvements-sweep.md @@ -0,0 +1,51 @@ +# 2026-06-12 — Sweep migliorie/strategie nuove (sera, post-swap 15m) + +Richiesta: cercare altre migliorie da implementare o strategie nuove. Tre +esperimenti + una miglioria di codice. + +## 1. INIT_LINEAGE — eredità capitale al cambio timeframe (IMPLEMENTATO) + +`StrategyWorker._inherit_lineage_capital`: al primo avvio (niente status.json) +il worker eredita `capital`/`real_capital` dal worker più recente di stessa +strategia+asset su altro tf (glob `{strategy}__{asset}__*`). MAI la posizione. +Nato dallo swap 1h→15m di oggi: i worker nuovi partivano dall'allocazione del +pool scartando il PnL del gemello (−16.8 di equity fantasma, riallineata a mano +col seed). Il prossimo swap non avrà bisogno di seed manuale. +Test: `tests/portfolio/test_capital_lineage.py` (eredita / no-sibling / resume). + +## 2. Gate 10m ADD selettivo (MR01/MR07) — BOCCIATO, watchlist chiusa + +Baseline aggiornata al PORT06 post-swap (fade 15m): ADD di 4 sleeve 10m +(MR01/MR07 × BTC/ETH, MR02 escluso perché fee-fragile) dà FULL Sh 8.13→8.33 e +DD 2.47→2.28, ma **OOS Sharpe 10.86→10.76** → fallisce il criterio standard. +Il 15m cattura già quasi tutto l'alpha veloce (corr 10m↔15m 0.53). Chiuso. + +## 3. XSEC breadth (universo 8 → 14/15) — direzione GIUSTA, venue SBAGLIATO + +La breadth è la leva classica delle strategie cross-sectional. Due banchi: + +- **Hyperliquid 15 coin** (dati REALI, ma profondità v2 limitata a ~207 giorni, + regime calmo recente): CORE-8 **−6.2%** (Sh −1.38, coerente col + dispersion-gate live che tiene XS01 fuori in questo regime) vs FULL-15 + **+9.4%** (Sh 1.58, WR 41→51%). La breadth trasforma un book perdente in + vincente — **sui prezzi veri**. +- **Deribit 14 coin** (storia piena 2022-10→2026-06, +AVAX/DOT/TRX/LINK/BCH/UNI, + parity del sim verificata ESATTA vs `xsec_sim`): FULL Sh 1.48→**1.22**, OOS + 4.66→**3.41**, fee 2x da −0.33 a **−1.45**. PEGGIO dell'8: le 6 gambe nuove + hanno chiusure flat al **91-99%** e il loro "momentum" è rumore stale che + corrompe il ranking cross-section (3ª conferma della lezione del giorno: + pairs-alt, XEX/DOGE, ora XSEC). + +**Conclusione:** l'espansione dell'universo XS01 è promettente ma bloccata dalla +qualità dati del venue. Sblocco strategico: **routing dati Hyperliquid** nel +runner (il client v2 già supporta `exchange="hyperliquid"`) + accumulo storia +HL in avanti. Rivalutare quando HL avrà ≥12-18 mesi di storia utile. + +## Direzioni aperte residue (non attaccate oggi) + +- **Put settimanale standing** (catastrofe-cap): unica struttura opzioni + eseguibile, da gateare coi premi reali cerbero-bite (~1%/mese il 10% OTM). + Harness `option_overlay_lab.py` pronto. +- **Hyperliquid come venue di esecuzione** (oltre che dati): aprirebbe fades su + alt liquidi con fill realistici; lavoro infrastrutturale grosso. +- 10m fade: chiuso oggi. 1m/2m/5m: chiusi oggi. Pairs nuove/PAXG/XEX: chiusi oggi. diff --git a/src/live/strategy_worker.py b/src/live/strategy_worker.py index 3e72f2b..26866de 100644 --- a/src/live/strategy_worker.py +++ b/src/live/strategy_worker.py @@ -114,10 +114,40 @@ class StrategyWorker: self._load_state() self._save_state() + def _inherit_lineage_capital(self): + """Al primo avvio (nessun status.json) eredita capital/real_capital dal + worker piu' recente di STESSA strategia+asset su altro timeframe (lineage). + Nato dallo swap fade 1h->15m (2026-06-12): i worker nuovi partivano + dall'allocazione del pool scartando il PnL accumulato dal gemello 1h + (-16.8 di equity fantasma, riallineata a mano). Eredita SOLO i ledger, + MAI la posizione (quella appartiene al worker vecchio).""" + try: + stem = f"{self.strategy.name}__{self.asset}__" + siblings = [d for d in self.work_dir.parent.glob(f"{stem}*") + if d.is_dir() and d.name != self.worker_id + and (d / "status.json").exists()] + if not siblings: + return + latest = max(siblings, key=lambda d: (d / "status.json").stat().st_mtime) + with open(latest / "status.json") as f: + old = json.load(f) + if old.get("capital") is not None: + self.capital = float(old["capital"]) + if old.get("real_capital") is not None: + self.real_capital = float(old["real_capital"]) + self._log("INIT_LINEAGE", {"da": latest.name, + "capital": round(self.capital, 2), + "real_capital": round(self.real_capital, 2)}) + except Exception as e: # mai bloccare il boot per il lineage + print(f" [{self.worker_id}] WARN lineage: {e}") + def _load_state(self): """Riprende stato da status.json se esiste.""" if not self.status_path.exists(): - self._log("INIT", {"capital": self.capital, "strategy": self.strategy.name, + self._inherit_lineage_capital() + self._log("INIT", {"capital": round(self.capital, 2), + "real_capital": round(self.real_capital, 2), + "strategy": self.strategy.name, "asset": self.asset, "tf": self.tf}) return diff --git a/tests/portfolio/test_capital_lineage.py b/tests/portfolio/test_capital_lineage.py new file mode 100644 index 0000000..90f07d1 --- /dev/null +++ b/tests/portfolio/test_capital_lineage.py @@ -0,0 +1,49 @@ +"""INIT_LINEAGE (2026-06-12): al primo avvio un worker eredita capital/real_capital +dal worker piu' recente di stessa strategia+asset su altro timeframe. Nato dallo +swap fade 1h->15m: i worker nuovi partivano dall'allocazione del pool scartando +il PnL del gemello (-16.8 di equity fantasma, riallineata a mano col seed).""" +import json +from types import SimpleNamespace + +from src.live.strategy_worker import StrategyWorker + + +def _mk(tmp_path, tf, capital=100.0): + w = StrategyWorker(strategy=SimpleNamespace(name="FAKE", fee_rt=0.001), + asset="BTC", tf=tf, capital=capital, position_size=0.5, + leverage=2.0, data_dir=tmp_path) + w._notify = lambda *a, **k: None + return w + + +def test_new_tf_inherits_capital_from_sibling(tmp_path): + old = _mk(tmp_path, "1h", capital=100.0) + old.capital = 181.19 + old.real_capital = 181.18 + old._save_state() + + new = _mk(tmp_path, "15m", capital=174.50) + assert new.capital == 181.19 + assert new.real_capital == 181.18 + # la posizione NON si eredita + assert new.in_position is False + events = [json.loads(l)["event"] for l in new.trades_path.read_text().splitlines()] + assert "INIT_LINEAGE" in events + + +def test_no_sibling_starts_from_allocation(tmp_path): + w = _mk(tmp_path, "15m", capital=174.50) + assert w.capital == 174.50 + events = [json.loads(l)["event"] for l in w.trades_path.read_text().splitlines()] + assert "INIT_LINEAGE" not in events + + +def test_resume_ignores_lineage(tmp_path): + old = _mk(tmp_path, "1h", capital=100.0) + old.capital = 999.0 + old._save_state() + new = _mk(tmp_path, "15m", capital=174.50) # eredita 999 + new.capital = 150.0 + new._save_state() + again = _mk(tmp_path, "15m", capital=174.50) # RESUME, non lineage + assert again.capital == 150.0