docs: aggiorna strategie_attive.html + CLAUDE/TODO a v1.1.20 (XS01, real-truth, 19 sleeve)

- make_strategy_doc: nuova famiglia XSEC — card XS01 (meccanismo, dispersion-gate
  disp_min=0.0313, gate PORT06 OOS 10.07->10.37) con grafico book+gate e tabella
  per anno (engine canonico ungated); pesi aggiornati a 19 sleeve (5.69/5.26/2.94%);
  header con real-truth ledger e 4 book paper; bullet metodologia real-truth;
  ripuliti i riferimenti 'oggi' (EXIT-16 su DIP01, fix punto-10 SH01)
- strategie_attive.html rigenerato: v1.1.20, backtest canonico FULL Sharpe 7.34 /
  DD 3.46% — OOS Sharpe 10.07 / DD 1.48% (19 sleeve, XS01 incluso)
- CLAUDE.md: sezione XS01 (famiglia XSEC, gate, dispersion-gate live-only, FC01
  scartata), copertura reale 15/19, varianti gioco options/session/grid in struttura
- TODO.md: stato esecuzione aggiornato (6 pairs, XS01 paper, real-truth)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Adriano Dal Pastro
2026-06-11 10:00:48 +00:00
parent 8d69a0cef5
commit ba5ec7bd6b
4 changed files with 155 additions and 46 deletions
+86 -15
View File
@@ -379,13 +379,43 @@ def chart_sh01():
return b64(fig)
def chart_xs01():
"""Book corrente (long perdenti/short vincenti) + dispersione cross-section col gate."""
from scripts.strategies.XS01_cross_sectional import aligned_panel, UNIVERSE, LB
M = aligned_panel()
logC = np.log(M.values)
ts = pd.to_datetime(M.index, unit="ms", utc=True)
dm = logC[-1] - logC[-1 - LB]
dm = dm - dm.mean()
w = -dm / np.sum(np.abs(dm))
fig, (ax, ax2) = plt.subplots(1, 2, figsize=(8.6, 3.4), width_ratios=[1, 1.5])
ax.bar(UNIVERSE, w * 100, color=[C_TP if x > 0 else C_SL for x in w])
ax.set_ylabel("peso book %")
ax.tick_params(axis="x", rotation=60)
ax.set_title("book: long i perdenti relativi,\nshort i vincenti (mom 48h demeaned)",
loc="left", fontweight="bold", fontsize=9)
D = logC[LB:] - logC[:-LB]
disp = pd.Series(D.std(axis=1), index=ts[LB:]).iloc[-24 * 180:]
ax2.plot(disp.index, disp.values, color="#333", lw=0.7)
ax2.axhline(0.0313, color=C_SL, ls="--", lw=1.2)
on = disp.values >= 0.0313
ax2.fill_between(disp.index, 0, disp.values.max(), where=on, alpha=0.10, color=C_TP)
ax2.annotate("disp_min 0.0313 (p50 TRAIN)\nentry solo sopra soglia",
(disp.index[-1], 0.0313), xytext=(-150, 10),
textcoords="offset points", color=C_SL, fontsize=8)
ax2.set_title("dispersion-gate: std cross-section del momentum",
loc="left", fontweight="bold", fontsize=9)
fig.tight_layout()
return b64(fig)
def chart_weights():
p = PORTFOLIOS["PORT06"]
ids = p.sleeve_ids
w = W.weight_vector("cap", ids, None, caps=p.caps)
fam = {i: W.family_of(i) for i in ids}
colors = {"FADE": "#1f6fd6", "HONEST": "#e08c1a", "PAIRS": "#9467bd",
"TSM": "#5ab4ac", "SHAPE": "#d64545"}
"TSM": "#5ab4ac", "SHAPE": "#d64545", "XSEC": "#8c564b"}
order = sorted(ids, key=lambda i: (fam[i], i))
fig, ax = plt.subplots(figsize=(8.6, 2.9))
ax.bar(order, [w[i] * 100 for i in order], color=[colors[fam[i]] for i in order])
@@ -547,6 +577,25 @@ def stats_multi():
yearly_table({"TSM01 (universo 8)": equity_yearly(tsm)}, note))
def stats_xs01():
"""XS01 per-anno dall'engine canonico (UNGATED, come il backtest di portafoglio)."""
from scripts.strategies.XS01_cross_sectional import xsec_sim
r = xsec_sim()
s = pd.Series(r["eq_v"], index=pd.to_datetime(r["eq_ts"], utc=True))
years = {}
for y in sorted(r["yearly"]):
g = s[s.index.year == y]
dd = float(((g.cummax() - g) / g.cummax()).max() * 100) if len(g) else 0.0
years[int(y)] = {"n": r["yearly_n"].get(y, 0), "pnl": r["yearly"][y], "dd": dd}
pk = s.cummax()
note = ("PnL = Σ rendimenti netti per trade del book (%, gross 1, fee 0.20% RT/book = "
"turnover 2×0.10%); DD dall'equity compounding (pos 0.15, leva 3x convenzione "
"test). Engine canonico SENZA dispersion-gate (il gate agisce solo sul path "
"live, come trend/hurst sulle fade → il live farà meglio del backtest).")
return yearly_table({"XS01 (universo 8)": (years, float(((pk - s) / pk).max() * 100))},
note)
def stats_sh01():
"""SH01 per-anno dal walk-forward EXPANDING (il regime validato e ora live)."""
from scripts.analysis.shape_ml_research import ml_wf_entries
@@ -580,8 +629,8 @@ box-shadow:0 1px 3px rgba(0,0,0,.05)}
.badge{display:inline-block;font-size:11px;font-weight:700;padding:2px 9px;border-radius:10px;
margin-right:6px;color:#fff}
.b-fade{background:#1f6fd6}.b-honest{background:#e08c1a}.b-pairs{background:#9467bd}
.b-tsm{background:#5ab4ac}.b-shape{background:#d64545}.b-real{background:#2e9e6b}
.b-sim{background:#8a8f98}
.b-tsm{background:#5ab4ac}.b-shape{background:#d64545}.b-xsec{background:#8c564b}
.b-real{background:#2e9e6b}.b-sim{background:#8a8f98}
img{max-width:100%;border:1px solid #eee;border-radius:6px;margin-top:10px}
table{border-collapse:collapse;width:100%;font-size:13px;margin-top:8px}
td,th{border:1px solid #e3e5e8;padding:5px 9px;text-align:left}
@@ -609,6 +658,7 @@ def main():
st_dip = stats_dip()
t_pairs = stats_pairs()
t_tr, t_rot, t_tsm = stats_multi()
t_xs = stats_xs01()
print(" SH01 walk-forward expanding (il piu' lento)...")
st_sh = stats_sh01()
@@ -631,6 +681,7 @@ def main():
g_pr = chart_pr01()
g_tsm = chart_tsm01(panel)
g_sh = chart_sh01()
g_xs = chart_xs01()
# metriche canoniche del default per l'intestazione (sempre aggiornate)
pr = PORTFOLIOS["PORT06"].backtest()
@@ -665,7 +716,7 @@ estremo in un'ora), fada il movimento. Exit in multipli di ATR. È la fade più
sono falsi negativi</b> — l'overshoot che buca lo stop e rientra è esattamente il movimento che
la fade sta comprando. Con EXIT-16 lo SL intrabar è disattivato: si esce solo se il <b>close</b>
della barra completata sfonda il livello di 0.5·ATR. Il TP intrabar resta. Impatto sul
portafoglio: OOS Sharpe 8.82→10.06. Esteso oggi anche a DIP01 (grid 36/36).</p>""", g_e16)
portafoglio: OOS Sharpe 8.82→10.06. Esteso anche a DIP01 (2026-06-07, grid 36/36).</p>""", g_e16)
c_dip = card("DIP01 — Dip Buy (BTC)", B("honest", "HONEST") + real, """
<p>Compra il <b>dip</b>: quando lo z-score del prezzo incrocia sotto 2.5 (sell-off rapido),
@@ -713,24 +764,41 @@ l'edge): la coda si gestisce dimezzando il peso della famiglia (cap 5.88%).</p>
<p class='sub'>Esecuzione reale single-leg su Deribit testnet: niente TP/SL, chiusura a orizzonte
H=12 (market reduce-only), disaster-bracket on-book come unica protezione di coda — è il
diversificatore più decorrelato del portafoglio.</p>
<p class='sub'>Fix di oggi (punto-10): il training live usa la storia COMPLETA dal parquet
<p class='sub'>Fix punto-10 (2026-06-07): il training live usa la storia COMPLETA dal parquet
locale (il regime corto a 365g non era robusto: trade-rate 22% vs 10% validato).</p>""", g_sh, yearly_table(st_sh))
c_xs = card("XS01 — Cross-Sectional Reversion (8 asset)", B("xsec", "XSEC") + sim, """
<p><b>Famiglia nuova (2026-06-09)</b>: ogni 12 ore classifica 8 crypto (BTC, ETH, LTC, ADA,
SOL, BNB, XRP, DOGE) per rendimento a 48 ore e va <b>long i perdenti relativi / short i
vincenti</b> (peso ∝ (ret media cross-section)), market-neutral gross 1. Cattura il
<i>fattore</i> reversione cross-sezionale — distinto dai pairs (pairwise) e dai fade
(single-asset): correlazione ~0 con entrambi. Gate PORT06: OOS Sharpe 9.66→10.07,
FULL DD 3.68→3.46. Plateau robusto (lb 12-72 × hold 6-24 tutte OOS+).</p>
<p class='sub'><b>Dispersion-gate (2026-06-10, v1.1.20)</b>: entra solo se la dispersione
cross-section del momentum ≥ 0.0313 (mediana TRAIN) — la reversione paga quando c'è
dispersione da far rientrare. Diagnostica monotona TRAIN e OOS, plateau p30-p70,
standalone Sharpe 2.50→3.46 (regge fee 2x), PORT06 OOS 10.07→<b>10.37</b> a DD pari.
Solo path live (backtest canonico non filtrato → il live farà meglio del backtest).</p>
<p class='sub'>8 gambe → niente esecuzione reale: gira PAPER come i book multi-asset
(il rumore di arrotondamento a €2k dominerebbe). Worker validato (replay == backtest esatto).</p>""",
g_xs, t_xs)
html = f"""<!doctype html><html lang="it"><head><meta charset="utf-8">
<title>PythagorasGoal — Strategie attive PORT06</title><style>{CSS}</style></head>
<body><div class="wrap">
<h1>PythagorasGoal — Strategie attive</h1>
<p class="sub">Portafoglio live <b>PORT06</b> — definizione {n_def} sleeve; <b>pool live real-only 15 sleeve</b>
(i 3 book multi-asset girano in statistica, fuori dal capitale-pool). Capitale pool €2.000, leva 2x ·
(i 4 book multi-asset TR01/ROT02/TSM01/XS01 girano in statistica, fuori dal capitale-pool).
Capitale pool €2.000, leva 2x, <b>real-truth ledger</b> ·
v{ver} · generato {now} · backtest canonico: {hdr}</p>
<div class="card">
<p>Tre famiglie principali quasi <b>scorrelate</b> fra loro (fade↔honest ~0.05, pairs ~0.02-0.09,
shape ~0.08): la diversificazione è la leva anti-drawdown. Pesi equal con tetti per famiglia,
ribilancio giornaliero su capitale pool condiviso. Fee Deribit 0.10% round-trip incluse ovunque;
ogni meccanismo live è passato da un gate out-of-sample a livello di portafoglio.</p>
<p>Famiglie quasi <b>scorrelate</b> fra loro (fade↔honest ~0.05, pairs ~0.02-0.09,
shape ~0.08, xsec ~0): la diversificazione è la leva anti-drawdown. Pesi equal con tetti
per famiglia, ribilancio giornaliero su capitale pool condiviso. Fee Deribit 0.10% round-trip
incluse ovunque; ogni meccanismo live è passato da un gate out-of-sample a livello di portafoglio.</p>
<img src="data:image/png;base64,{g_w}"></div>
<h2>FADE — mean-reversion intraday 1h <span class="sub">(6 sleeve × 6.47%)</span></h2>
<h2>FADE — mean-reversion intraday 1h <span class="sub">(6 sleeve × 5.69%)</span></h2>
<div class="note"><b>Tesi della famiglia:</b> sui perpetui crypto l'edge è la <i>reversione</i>:
i movimenti estremi rientrano (i breakout falliscono — l'intera famiglia squeeze-breakout è stata
scartata come artefatto di look-ahead). Le fade vendono l'eccesso e comprano il panico, con tre
@@ -741,14 +809,16 @@ non si fada un crollo/parabolica), <b>EXIT-16</b> (stop solo sul close confermat
{c_mr02}
{c_mr07}
{c_e16}
<h2>HONEST — long-only multi-regime <span class="sub">(3 sleeve × 6.47%)</span></h2>
<h2>HONEST — long-only multi-regime <span class="sub">(3 sleeve × 5.69%)</span></h2>
{c_dip}
{c_tr}
{c_rot}
<h2>PAIRS — spread reversion market-neutral <span class="sub">(6 sleeve: 5 coppie 1h + ETH/BTC 15m a mezza size, famiglia ≤33%)</span></h2>
<h2>PAIRS — spread reversion market-neutral <span class="sub">(6 sleeve × 5.26%: 5 coppie 1h + ETH/BTC 15m a mezza size, famiglia ≤33%)</span></h2>
{c_pr}
<h2>TSM — trend-following multi-orizzonte <span class="sub">(1 sleeve × 6.47%)</span></h2>
<h2>TSM — trend-following multi-orizzonte <span class="sub">(1 sleeve × 5.69%)</span></h2>
{c_tsm}
<h2>XSEC — reversione cross-sectional <span class="sub">(1 sleeve × 5.69%)</span></h2>
{c_xs}
<h2>SHAPE — ML morfologico <span class="sub">(2 sleeve × 2.94%, famiglia ≤5.88%)</span></h2>
{c_sh}
@@ -757,7 +827,8 @@ non si fada un crollo/parabolica), <b>EXIT-16</b> (stop solo sul close confermat
<li><b>Fee sempre incluse</b>: 0.10% round-trip taker Deribit (misurate reali = assunte). Molte operazioni = morte per fee: ogni strategia regge lo stress a fee doppie.</li>
<li><b>Niente look-ahead</b>: direzione e prezzo decisi solo con dati fino al close corrente; barre in formazione escluse (lezione EXIT-16). La famiglia squeeze (accuratezze 76-82%) è stata scartata proprio per questo artefatto.</li>
<li><b>Gate out-of-sample</b>: nessun meccanismo va in produzione senza migliorare (o non degradare) il portafoglio FULL e OOS — robusto individualmente ≠ migliora PORT06.</li>
<li><b>Esecuzione reale shadow (~83% del portafoglio)</b>: 15 sleeve eseguono ordini reali su Deribit testnet accanto al sim — i 6 fade + DIP01 (single-leg, TP limit al livello, disaster-stop 30% on-book), i 6 pairs incl. <b>ETH/BTC 15m flat-skip</b> (2 gambe, leg-risk unwind) e i 2 SH01 (single-leg, exit a orizzonte, niente TP/SL). Restano simulati solo i book multi-asset TR01/ROT02/TSM01 (bloccati dal capitale: serve ~€20k per il rumore di arrotondamento). Fee reali verificate dai trade; divergenze sim/reale ≥100bps alertate su Telegram.</li>
<li><b>Esecuzione reale shadow (15 sleeve su 19)</b>: i 6 fade + DIP01 (single-leg, TP limit al livello, disaster-stop 30% on-book), i 6 pairs incl. <b>ETH/BTC 15m flat-skip</b> (2 gambe, leg-risk unwind) e i 2 SH01 (single-leg, exit a orizzonte, niente TP/SL) eseguono ordini reali su Deribit testnet. Restano simulati solo i book multi-asset TR01/ROT02/TSM01/XS01 (bloccati dal capitale: serve ~€20k per il rumore di arrotondamento). Fee reali verificate dai trade; divergenze sim/reale ≥100bps alertate su Telegram.</li>
<li><b>Real-truth ledger (2026-06-10)</b>: il capitale dei worker eseguiti si aggiorna col PnL dei <b>fill reali</b> (fee reali incluse), non col sim — equity, pesi e notional derivano dai soldi veri sul conto; il sim resta diagnostica nel log (fallback dichiarato solo se il trade reale non è mai esistito). Le decisioni di entry/exit restano guidate dal feed.</li>
</ul></div>
<p class="sub">Generato da <code>scripts/analysis/make_strategy_doc.py</code> — grafici da episodi reali sui dati parquet locali.</p>
</div></body></html>"""