feat(live): portafoglio REALE-only — i 2000EUR ai soli 14 sleeve eseguiti, paper fuori dal pool
Decisione utente: il conto reale deve mostrare il risultato puro. I 2000 si dividono SOLO tra i 14 sleeve reali (6 fade + DIP01 + 5 pairs + SH01). I 3 multi-asset (TR01/ROT02/TSM01), non eseguibili in reale, escono da pool/pesi/ledger e girano solo per statistica. - portfolios.yml: paper_sleeves [TR01,ROT02,TSM01]. - runner: live_specs (pool/ledger) vs paper_specs (capitale nozionale fisso = total/N, data/portfolio_paper_stats/, ticcati ma MAI in update_equity). - hourly_report: sezione PAPER da portfolio_paper_stats, etichettata 'fuori dal conto reale'. Pesi reale-only (14, cap): non-cappati 0.087, PAIRS 0.33, SHAPE 0.0588. Costo misurato e accettato: FULL DD 3.96->5.34%, OOS DD 1.36->1.70% (perde i diversificatori PAPER, corr 0.07); Sharpe ~invariato. Test 93/93. Diario docs/diary/2026-06-08-real-only-portfolio.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+30
-4
@@ -232,12 +232,25 @@ def run(config_path: str = "portfolios.yml"):
|
||||
|
||||
def _supported(s):
|
||||
return s.kind in ("pairs",) + _MULTI_KINDS or s.name in _STRAT_MODULE
|
||||
live_specs = [s for s in p.sleeves if _supported(s)]
|
||||
supported = [s for s in p.sleeves if _supported(s)]
|
||||
skipped = [s.sid for s in p.sleeves if not _supported(s)]
|
||||
if skipped:
|
||||
print(f"[runner] sleeve saltati nel live (worker non disponibili): {skipped}")
|
||||
|
||||
# SLEEVE "PAPER" (solo statistica, 2026-06-08): NON entrano nel pool/pesi/ledger del
|
||||
# portafoglio — i €total_capital si dividono SOLO tra gli sleeve reali. I paper girano
|
||||
# con un capitale nozionale fisso (la fetta equal che avrebbero avuto) per raccogliere
|
||||
# statistica in vista di future implementazioni reali. Default: TR01/ROT02/TSM01
|
||||
# (multi-asset, esecuzione reale bloccata dal capitale).
|
||||
paper_codes = {str(c).upper() for c in (_ov.get("paper_sleeves") or [])}
|
||||
live_specs = [s for s in supported if s.name.upper() not in paper_codes]
|
||||
paper_specs = [s for s in supported if s.name.upper() in paper_codes]
|
||||
if paper_specs:
|
||||
print(f"[runner] sleeve PAPER (solo statistica, fuori dal pool): "
|
||||
f"{[s.sid for s in paper_specs]}")
|
||||
live_ids = [s.sid for s in live_specs]
|
||||
clusters = {s.sid: (s.cluster or s.sid) for s in live_specs}
|
||||
paper_notional = p.total_capital / max(len(supported), 1) # fetta equal nozionale
|
||||
|
||||
ledger = PortfolioLedger(p.code, total_capital=p.total_capital)
|
||||
client = CerberoClient()
|
||||
@@ -294,6 +307,14 @@ def run(config_path: str = "portfolios.yml"):
|
||||
if ps_family:
|
||||
print(f"[runner] position_size globale={position_size} override famiglia={ps_family}")
|
||||
|
||||
# worker PAPER (solo statistica): capitale nozionale fisso, NESSUNA esecuzione reale,
|
||||
# NON nel ledger del portafoglio. Salvano in data/portfolio_paper_stats/.
|
||||
paper_dir = DATA_DIR.parent / "portfolio_paper_stats"
|
||||
paper_workers = {s.sid: build_worker_for(s, paper_notional, p.leverage,
|
||||
data_dir=paper_dir,
|
||||
position_size=pos_for_spec(s.sid, position_size, ps_family))
|
||||
for s in paper_specs}
|
||||
|
||||
# bootstrap storia full per gli sleeve ML (SH01): parquet locale + feed live.
|
||||
# L'edge SH01 richiede train expanding full-history (sh01_trainwindow_validate);
|
||||
# il path live fitta solo l'ultimo blocco (last_block_only nei params SHAPE).
|
||||
@@ -374,11 +395,10 @@ def run(config_path: str = "portfolios.yml"):
|
||||
_check_stale_feed(asset, raw1h[asset], stale_alerted)
|
||||
|
||||
# tick di ogni worker col suo timeframe (resample dal 1h)
|
||||
for s in live_specs:
|
||||
w = workers[s.sid]
|
||||
def _tick(s, w):
|
||||
assets, tf = _spec_assets_tf(s)
|
||||
if any(a not in raw1h for a in assets):
|
||||
continue
|
||||
return
|
||||
res = {a: _resample(raw1h[a], tf) for a in assets}
|
||||
if s.kind == "pairs":
|
||||
w.tick(res[s.a], res[s.b])
|
||||
@@ -393,6 +413,12 @@ def run(config_path: str = "portfolios.yml"):
|
||||
# single (fade/dip): StrategyWorker su feed live.
|
||||
w.tick(res[s.asset])
|
||||
|
||||
for s in live_specs:
|
||||
_tick(s, workers[s.sid])
|
||||
# PAPER: ticcati per statistica, MAI nel ledger del portafoglio
|
||||
for s in paper_specs:
|
||||
_tick(s, paper_workers[s.sid])
|
||||
|
||||
ledger.update_equity({sid: _worker_equity(wk) for sid, wk in workers.items()})
|
||||
|
||||
today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
|
||||
|
||||
Reference in New Issue
Block a user