docs: aggiorna CLAUDE.md + strategie_attive.html col BLEND ETH/BTC 15m

- CLAUDE.md: default PORT06 (FULL 7.20/OOS 9.66, 18 sleeve), paragrafo BLEND 15m
  flat-skip (origine gioco Blind Traders, gate, validazione, caveat slippage),
  copertura reale ~83% (6 pairs), scripts/games/ + pairs15m_* nella struttura.
- make_strategy_doc.py: header dinamico dal backtest, colonna ETH/BTC·15m nella
  tabella pairs, card PR01 col blend, conteggi sleeve aggiornati -> rigenerato HTML.
- pairs_sim_flat: ritorna yearly_n (parita' con pairs_sim, usato dal doc).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Adriano Dal Pastro
2026-06-09 11:59:36 +00:00
parent be72b157cf
commit 90c4726a31
4 changed files with 50 additions and 22 deletions
+25 -8
View File
@@ -495,9 +495,14 @@ def stats_pairs():
from scripts.analysis.pairs_research import pairs_sim
from scripts.strategies.PR01_pairs_reversion import PAIRS as PAIRS_CFG
cols, data = [], {}
for a, b, p in PAIRS_CFG:
r = pairs_sim(a, b, **p)
tag = f"{a}/{b}"
from scripts.analysis.pairs_research import pairs_sim_flat
# le 5 coppie 1h universali + il BLEND ETH/BTC 15m flat-skip (mezza size = sleeve live)
runs = [(f"{a}/{b}", lambda a=a, b=b, p=p: pairs_sim(a, b, **p)) for a, b, p in PAIRS_CFG]
runs.append(("ETH/BTC·15m", lambda: pairs_sim_flat(
"ETH", "BTC", tf="15m", n=66, z_in=1.674, z_exit=1.0, max_bars=35,
flat_skip=True, pos=0.075)))
for tag, fn in runs:
r = fn()
cols.append(tag)
s = pd.Series(r["eq_v"], index=pd.to_datetime(r["eq_ts"], utc=True))
ydd = {int(y): float(((g.cummax() - g) / g.cummax()).max() * 100)
@@ -627,6 +632,12 @@ def main():
g_tsm = chart_tsm01(panel)
g_sh = chart_sh01()
# metriche canoniche del default per l'intestazione (sempre aggiornate)
pr = PORTFOLIOS["PORT06"].backtest()
n_def = len(PORTFOLIOS["PORT06"].sleeve_ids)
hdr = (f"FULL Sharpe {pr.full['sharpe']:.2f} / DD {pr.full['dd']:.2f}% — "
f"OOS Sharpe {pr.oos['sharpe']:.2f} / DD {pr.oos['dd']:.2f}%")
B = lambda f, t: f'<span class="badge b-{f}">{t}</span>'
real = B("real", "ESECUZIONE REALE (testnet)")
sim = B("sim", "SIMULATO")
@@ -671,13 +682,19 @@ le fade per costruzione non prendono. Valuta solo barre 4h COMPLETE.</p>""", g_t
(solo se positive), gross 0.45. Gate di regime: tutto cash se BTC&lt;SMA100. Diversificare su 3
asset invece di 2 ha quasi dimezzato il DD (40%→26%) alzando il ritorno.</p>""", g_rot, t_rot)
c_pr = card("PR01 — Pairs Reversion (ETH/BTC, LTC/ETH, ADA/ETH, BTC/LTC, ETH/SOL)",
c_pr = card("PR01 — Pairs Reversion (ETH/BTC, LTC/ETH, ADA/ETH, BTC/LTC, ETH/SOL + ETH/BTC 15m)",
B("pairs", "PAIRS") + real, """
<p><b>Market-neutral</b>: quando il rapporto fra due asset si allontana troppo dalla sua media
(|z| del log-ratio ≥ 2), compra la gamba debole e shorta la forte; chiude quando il rapporto
rientra (|z| ≤ 0.75) o dopo 72 barre. Config <b>universale</b> per tutte le coppie (niente tuning
per-coppia = anti-overfit). Correlazione col mercato ~0.05: rende anche quando il mercato è fermo.
Fee su 2 gambe. Senza stop per design → position size ridotto a 0.20 (esposizione ≈ validato).</p>
<p class='sub'><b>BLEND timeframe (nuovo, 2026-06-09)</b>: ETH/BTC gira anche a <b>15m</b> accanto al 1h
(config n=66, |z|≥1.67, exit |z|≤1.0 o 35 barre). Origine: gioco "Blind Traders" (100 agenti ciechi
su dati anonimi); testato col gate PORT06 — <b>decorrelato dal 1h (corr 0.37)</b>, robusto (16/16),
e l'edge regge anche filtrando le candele flat ETH 15m (<b>flat-skip</b>: niente ingresso/uscita su
barre stale O=H=L=C). Worker validato (replay == backtest). A <b>mezza size</b> (blend-tilt prudente
sul caveat slippage): porta il PORT06 a FULL Sharpe ~7.2 / OOS ~9.7.</p>
<p class='sub'>Esecuzione reale a 2 gambe su Deribit testnet (<code>PairsExecutionClient</code>):
open/close long A / short B, leg-risk unwind, mai <code>close_position</code>.</p>""", g_pr, t_pairs)
@@ -703,9 +720,9 @@ locale (il regime corto a 365g non era robusto: trade-rate 22% vs 10% validato).
<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 17 sleeve; <b>pool live real-only 14 sleeve</b>
<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 ·
v{ver} · generato {now} · backtest canonico: FULL Sharpe 6.61 / DD 3.58% — OOS Sharpe 8.77 / DD 1.34%</p>
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,
@@ -728,7 +745,7 @@ non si fada un crollo/parabolica), <b>EXIT-16</b> (stop solo sul close confermat
{c_dip}
{c_tr}
{c_rot}
<h2>PAIRS — spread reversion market-neutral <span class="sub">(5 coppie × 5.88%, famiglia ≤33%)</span></h2>
<h2>PAIRS — spread reversion market-neutral <span class="sub">(6 sleeve: 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>
{c_tsm}
@@ -740,7 +757,7 @@ 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 (~81% del portafoglio)</b>: 14 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 5 pairs (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 (~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>
</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>"""
+3 -2
View File
@@ -141,7 +141,7 @@ def pairs_sim_flat(a, b, tf="1h", n=50, z_in=2.0, z_exit=0.5, max_bars=72,
split = int(N * split_frac)
fee = 2 * fee_rt * lev
cap = peak = 1000.0; dd = 0.0; last = -1
trades = wins = 0; rets = []; yearly = {}
trades = wins = 0; rets = []; yearly = {}; yearly_n = {}
eq_ts, eq_v = [], []
n_skip_entry = 0
kmax = max_bars + (scan_buffer if flat_skip else 0)
@@ -176,6 +176,7 @@ def pairs_sim_flat(a, b, tf="1h", n=50, z_in=2.0, z_exit=0.5, max_bars=72,
trades += 1; wins += ret > 0; rets.append(ret * pos); last = j
eq_ts.append(ts.iloc[j]); eq_v.append(cap)
yearly[ts.iloc[i].year] = yearly.get(ts.iloc[i].year, 0.0) + ret * 100
yearly_n[ts.iloc[i].year] = yearly_n.get(ts.iloc[i].year, 0) + 1
yrs_span = (ts.iloc[-1] - ts.iloc[max(split, 0)]).days / 365.25 or 1
sharpe = 0.0
if len(rets) > 1 and np.std(rets) > 0:
@@ -183,7 +184,7 @@ def pairs_sim_flat(a, b, tf="1h", n=50, z_in=2.0, z_exit=0.5, max_bars=72,
ret_tot = (cap / 1000 - 1) * 100
cagr = ((cap / 1000) ** (1 / yrs_span) - 1) * 100 if cap > 0 else -100
return dict(trades=trades, win=wins / trades * 100 if trades else 0, ret=ret_tot,
cagr=cagr, dd=dd * 100, sharpe=sharpe, yearly=yearly,
cagr=cagr, dd=dd * 100, sharpe=sharpe, yearly=yearly, yearly_n=yearly_n,
eq_ts=eq_ts, eq_v=eq_v, n_skip_entry=n_skip_entry)