Files
Adriano Dal Pastro 76120b59c2 research(wave-0702bis): ondata video-claims — Elliott 3/3 e Albimarini 2/2 scartati, mappa capital scaling 2-5k
Sei agenti, nessun sopravvissuto:
- ELL-A range-cycle: rumore (0/24 Bonferroni; nessuna cella weekly regge
  a tutte le 7 ancore). Lezione pandas: resample("7D", origin) IGNORA
  origin -> usare "168h" per le bande d'ancora weekly.
- ELL-B Fibonacci: l'edge apparente e' la POSIZIONE dei livelli, non i
  numeri (null location-matched: pctl 0.39-0.68); confluenza FAIL 4/4.
- ELL-C canale: Donchian travestito (non batte il Donchian equivalente,
  DSR 0.685, IS 1.40 -> HOLD -0.87; target 1.618 = caso; anchor-luck 4h).
- ALB-A diagonale: il condor stessa-scadenza la batte a ogni f; senza
  gate IV-rank tutte le strutture perdono (3a conferma: l'alpha del VRP
  e' il gate); fee-negativa su Deribit a qualsiasi size; 2o caso
  "0-perdite = Sharpe implausibile" dopo CC01.
- ALB-B claims: 82%/PF 5.16/"420%" consistente con zero skill (P=20-45%,
  78.6% delle finestre 6-mesi lo produce); replay con code reali =
  rovina 1998/2002/2020; la diagonale passa il 12-40% della perdita naked.
- Capital scaling 600->2-5k: unico vincolo binding = cap $300/asset
  (a 5k book al 49% del target) -> AL DEPOSITO alzare a equity/2;
  min_order $5 lasciare; XS01 ~20k confermata; aspettativa onesta
  de-luckata 2k ~EUR 0.6-0.8/g, 5k ~EUR 1.4-2/g.

Nessun nuovo sleeve, book live invariato. 168 test verdi.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 23:31:38 +00:00

663 lines
34 KiB
Python

"""r0702_alb_claims — Audit STATISTICO delle claim "strategia Albimarini" (video didattici).
CLAIM DICHIARATE (input, dal video):
- Struttura: double diagonal DEEP OTM su SPX/SPY (short strangle ~6DTE + long strangle
a scadenza successiva, strike piu' lontani), durata trade 3-6 giorni di calendario.
- Track record: n=28 trade, win-rate 82% (23W/5L), P&L medio +$113/trade,
perdite totali $765 (loss medio $153), profit factor 5.16, capitale ~$10k,
estrapolazione "420% annuo", sizing a compounding 1->2->3->4 contratti.
Derivati: totale +$3.164; gross win $3.929 (avg win $171); PF = 3929/765 = 5.14 ok.
OBIETTIVO: QUANTIFICARE la critica (win-rate strutturale, coda non campionata, PF non
significativo su n=28), non riformularla. 6 test, ognuno con numeri.
DATI: SOLO locali gia' certificati/presenti nel progetto:
- data/raw/eq_spy_1d.parquet (SPY daily 1996-2026, fetch IB del filone GTAA)
- BTC/ETH 1d via altlib (Deribit mainnet certificato)
Nessuna rete. Nessun file scritto fuori dallo scratchpad. Nessun DatetimeIndex.view.
Convenzioni oneste:
- 6 giorni di calendario ~= 4 trading day (h=4 primario; h=2..5 come robustezza).
- Le probabilita' empiriche usano finestre rolling OVERLAPPING (stima della prob.
per-finestra); i test di significativita' usano le 28 finestre NON sovrapposte
del track record e cicli non sovrapposti nel replay.
- Il replay del Test 3 e' CLAIM-ANCHORED: usa l'economia per-trade dichiarata dal
video (massimo beneficio al claim); l'unico parametro nostro e' la severita'
della coda, calibrata in due modi (cap BS della diagonale / EV=0).
- BS senza skew nei Test 4-5: sottostima il premio degli OTM (a favore del claim).
Uso: uv run python scripts/research/r0702_alb_claims.py
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
import numpy as np
import pandas as pd
from scipy.stats import norm, t as student_t
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "research" / "alt"))
import altlib as al # noqa: E402
RNG = np.random.default_rng(20260702)
ROOT = Path(__file__).resolve().parents[2]
# ---- claim dichiarate ------------------------------------------------------
N_TRADES = 28
WINRATE_CLAIM = 23 / 28 # 0.821
AVG_PNL = 113.0 # $/trade
TOT_LOSS = 765.0 # $ totali (5 loss -> avg $153)
AVG_WIN = (N_TRADES * AVG_PNL + TOT_LOSS) / 23 # $171
AVG_LOSS = TOT_LOSS / 5 # $153
PF_CLAIM = (23 * AVG_WIN) / TOT_LOSS # 5.14
CAPITAL = 10_000.0
H_TD = 4 # 6 giorni calendario ~ 4 trading day
OUT: dict = {}
def p(msg=""):
print(msg, flush=True)
# ===========================================================================
# BS helpers (no dividendi/tassi — orizzonte 6-13 giorni)
# ===========================================================================
def bs_put(S, K, T, sigma):
T = max(T, 1e-9)
d1 = (np.log(S / K) + 0.5 * sigma**2 * T) / (sigma * np.sqrt(T))
d2 = d1 - sigma * np.sqrt(T)
return K * norm.cdf(-d2) - S * norm.cdf(-d1)
def bs_call(S, K, T, sigma):
T = max(T, 1e-9)
d1 = (np.log(S / K) + 0.5 * sigma**2 * T) / (sigma * np.sqrt(T))
d2 = d1 - sigma * np.sqrt(T)
return S * norm.cdf(d1) - K * norm.cdf(d2)
# ===========================================================================
# DATA
# ===========================================================================
def load_spy() -> pd.DataFrame:
df = pd.read_parquet(ROOT / "data" / "raw" / "eq_spy_1d.parquet")
df = df.sort_values("timestamp").reset_index(drop=True)
df["datetime"] = pd.to_datetime(df["timestamp"].astype("int64"), unit="ms", utc=True)
return df
def hmoves(close: np.ndarray, h: int) -> np.ndarray:
return close[h:] / close[:-h] - 1.0
def cal_moves(df: pd.DataFrame, days: int = 6) -> np.ndarray:
"""Move su ESATTAMENTE `days` giorni di calendario (ultimo close <= t+days).
Epoca ms esplicita (mai .view su DatetimeIndex)."""
ts = df["timestamp"].to_numpy(dtype="int64") # ms
c = df["close"].to_numpy(dtype=float)
tgt = ts + days * 86_400_000
j = np.searchsorted(ts, tgt, side="right") - 1 # ultimo close <= t+days
ok = j > np.arange(len(ts))
return c[j[ok]] / c[ok] - 1.0
# ===========================================================================
# TEST 1 — WIN-RATE STRUTTURALE
# ===========================================================================
def test1():
p("=" * 88)
p("TEST 1 — WIN-RATE STRUTTURALE: P(move >9% in ~6 giorni) e strike impliciti dall'82%")
p("=" * 88)
spy = load_spy()
c, lo, hi = spy["close"].values, spy["low"].values, spy["high"].values
r1 = np.diff(np.log(c))
vol_ann = r1.std() * np.sqrt(252)
res = {}
for h in (2, 3, 4, 5):
m = hmoves(c, h)
res[h] = dict(p_abs9=float(np.mean(np.abs(m) > 0.09)),
p_dn9=float(np.mean(m < -0.09)),
sigma=float(m.std()),
p_2sig=float(np.mean(np.abs(m) > 2 * m.std())))
m6c = cal_moves(spy, 6)
m4 = hmoves(c, H_TD)
# touch (management intra-finestra): min low / max high nei prossimi h giorni
n = len(c) - H_TD
lo_w = np.array([lo[i + 1:i + 1 + H_TD].min() for i in range(n)])
hi_w = np.array([hi[i + 1:i + 1 + H_TD].max() for i in range(n)])
p_touch9 = float(np.mean((lo_w < c[:n] * 0.91) | (hi_w > c[:n] * 1.09)))
# analitico: normale a vol 16% e alla vol campione; t-Student fittata sui move 4td
sig6_16 = 0.16 * np.sqrt(6 / 365)
p_norm16 = 2 * (1 - norm.cdf(0.09 / sig6_16))
sig4 = res[4]["sigma"]
p_norm_emp = 2 * (1 - norm.cdf(0.09 / sig4))
tdf, tloc, tscale = student_t.fit(m4)
p_t = float(2 * student_t.sf((0.09 - tloc) / tscale, tdf))
# strike implicito dall'82% di win (quantile 82% di |move|) e win-rate a 9% OTM
q82_4 = float(np.quantile(np.abs(m4), WINRATE_CLAIM))
q82_6c = float(np.quantile(np.abs(m6c), WINRATE_CLAIM))
win_at_9 = 1 - res[4]["p_abs9"]
p(f"SPY 1996-2026 ({len(spy)} barre, vol ann {vol_ann:.1%})")
for h in (2, 3, 4, 5):
r = res[h]
p(f" h={h}td: P(|m|>9%)={r['p_abs9']:.4%} P(m<-9%)={r['p_dn9']:.4%} "
f"sigma={r['sigma']:.2%} P(|m|>2sig)={r['p_2sig']:.2%}")
p(f" 6 giorni CALENDARIO esatti: P(|m|>9%)={np.mean(np.abs(m6c) > 0.09):.4%} "
f"sigma={m6c.std():.2%}")
p(f" TOUCH intra-finestra (h=4td, strike a +-9%): P={p_touch9:.4%}")
p(f" Analitico: normale vol16% -> {p_norm16:.2e} | normale vol camp. -> {p_norm_emp:.2e} | "
f"t-Student fit (df={tdf:.2f}) -> {p_t:.4%} | empirico {res[4]['p_abs9']:.4%}")
p(f" -> fat-tail factor empirico/normale = {res[4]['p_abs9'] / p_norm_emp:,.0f}x")
p()
p(f" INCONSISTENZA QUANTIFICATA delle claim:")
p(f" (a) se gli strike sono DAVVERO ~9% OTM: win-rate strutturale a scadenza = "
f"{win_at_9:.2%} (>=99%), non 82% -> le 5 perdite non sono breach, sono gestione;")
p(f" (b) se il win-rate 82% e' letterale (breach a scadenza): gli strike distano "
f"q82(|m4|) = {q82_4:.2%} (6gg cal: {q82_6c:.2%}) ~= 1.2 sigma, NON 'deep OTM'.")
p(f" In entrambi i casi il win-rate e' il quantile della distribuzione (il delta "
f"venduto), non skill: qualunque venditore agli stessi strike ottiene lo stesso numero.")
# BTC/ETH dai NOSTRI dati certificati
crypto = {}
for a in ("BTC", "ETH"):
d = al.get(a, "1d")
cc = d["close"].values
m6 = hmoves(cc, 6)
crypto[a] = dict(p_abs9=float(np.mean(np.abs(m6) > 0.09)),
sigma=float(m6.std()),
p_2sig=float(np.mean(np.abs(m6) > 2 * m6.std())),
q82=float(np.quantile(np.abs(m6), WINRATE_CLAIM)),
span=f"{d['datetime'].iloc[0].date()}->{d['datetime'].iloc[-1].date()}",
last=float(cc[-1]))
p(f" {a} (certificato, {crypto[a]['span']}): P(|m6d|>9%)={crypto[a]['p_abs9']:.1%} "
f"sigma6d={crypto[a]['sigma']:.1%} P(>2sig)={crypto[a]['p_2sig']:.1%} "
f"strike per win82% = {crypto[a]['q82']:.1%} OTM")
p(f" -> la stessa struttura trasposta su crypto: strike a 9% OTM = win-rate "
f"{1 - crypto['BTC']['p_abs9']:.0%} (BTC) / {1 - crypto['ETH']['p_abs9']:.0%} (ETH), "
f"cioe' il 9% che su SPY e' 'deep' su crypto e' ~1 sigma.")
OUT["test1"] = dict(spy=res, spy_6cal_p9=float(np.mean(np.abs(m6c) > 0.09)),
p_touch9=p_touch9, p_norm16=float(p_norm16),
p_norm_emp=float(p_norm_emp), p_t=p_t, t_df=float(tdf),
q82_4td=q82_4, q82_6cal=q82_6c, win_at_9otm=float(win_at_9),
crypto=crypto)
return spy, m4, q82_4
# ===========================================================================
# TEST 2 — SIGNIFICATIVITA' DEL PF 5.16 SU n=28 (null EV=0)
# ===========================================================================
def test2(spy: pd.DataFrame, q82: float):
p("\n" + "=" * 88)
p("TEST 2 — PF 5.16 su n=28: distribuzione sotto il null 'vendita premio a EV=0 netto'")
p("=" * 88)
c = spy["close"].values
m4 = hmoves(c, H_TD)
NSIM = 200_000
W = 126 # 6 mesi di trading day
def branch(name: str, thr: float) -> dict:
"""Null EV=0: win +$171 (p=0.821), small loss -$153, tail loss -L con
p_tail = P(|m4|>thr) empirica; L calibrata perche' EV=0 netto."""
p_loss = 1 - WINRATE_CLAIM
p_tail = float(np.mean(np.abs(m4) > thr))
p_small = max(p_loss - p_tail, 0.0)
L_tail = (WINRATE_CLAIM * AVG_WIN - p_small * AVG_LOSS) / p_tail
wins = RNG.normal(AVG_WIN, 60, (NSIM, N_TRADES)).clip(20, None)
smalls = RNG.normal(AVG_LOSS, 60, (NSIM, N_TRADES)).clip(30, None)
u = RNG.random((NSIM, N_TRADES))
is_win = u < min(WINRATE_CLAIM, 1 - p_tail)
is_tail = u > 1 - p_tail
pnl = np.where(is_win, wins, np.where(is_tail, -L_tail, -smalls))
gp = np.where(pnl > 0, pnl, 0).sum(1)
gl = -np.where(pnl < 0, pnl, 0).sum(1)
pf = gp / np.maximum(gl, 1e-9)
wr = is_win.mean(1)
p_joint = float(np.mean((pf >= PF_CLAIM) & (wr >= WINRATE_CLAIM - 1e-9)))
p_notail = float(np.mean(~is_tail.any(1)))
tail_day = np.abs(m4) > thr
frac_clean = float(np.mean([not tail_day[i:i + W].any()
for i in range(0, len(tail_day) - W)]))
r = dict(name=name, thr=thr, p_tail=p_tail, p_small=p_small,
L_tail=float(L_tail), ev_check=float(pnl.mean()),
pf_median=float(np.median(pf)),
p_pf_ge=float(np.mean(pf >= PF_CLAIM)), p_joint=p_joint,
p_no_tail_28=p_notail,
p_no_tail_analytic=float((1 - p_tail) ** N_TRADES),
frac_6m_clean=frac_clean)
p(f" Branch {name}:")
p(f" tail = |m4| > {thr:.2%}: P(tail/trade)={p_tail:.2%} -> L_tail per EV=0 = "
f"${L_tail:,.0f} ({L_tail / CAPITAL:.0%} del capitale per unita' di size); "
f"EV MC {pnl.mean():+.1f}$/trade")
p(f" PF su 28 trade: mediana {r['pf_median']:.2f} | P(PF>=5.16)={r['p_pf_ge']:.1%} | "
f"P(PF>=5.16 E win>=82%)={p_joint:.1%}")
p(f" P(zero tail in 28 trade)={p_notail:.1%} (analitico {r['p_no_tail_analytic']:.1%}); "
f"quota storica finestre 6 mesi SENZA tail = {frac_clean:.1%}")
return r
p(f"Null 'venditore di premio a EV=0 netto' (tanti +$171, -$153 gestite, code rare):")
b_obs = branch(f"OBS (strike {q82:.2%} OTM, breach oltre il long +2%)", q82 + 0.02)
b_deep = branch("DEEP (strike 9% OTM come dichiarato)", 0.09)
p(f" -> Il track record dichiarato (PF 5.16, win 82%, zero code) e' un esito da "
f"P={b_obs['p_joint']:.0%} (lettura OBS) a P={b_deep['p_joint']:.0%} (lettura DEEP) "
f"sotto ZERO skill: tra 'comune' e 'esito mediano'.")
p(f" Il PF senza coda campionata misura solo 23/5 * avg_win/avg_smallloss = "
f"{(23 * AVG_WIN) / (5 * AVG_LOSS):.2f}: e' un rapporto STRUTTURALE, non una statistica "
f"di edge (n=28 non tocca mai la coda che paga tutto).")
OUT["test2"] = dict(obs=b_obs, deep=b_deep)
return b_obs["L_tail"]
# ===========================================================================
# TEST 3 — IL "420% ANNUO" ATTRAVERSO UNA CODA REALE (replay claim-anchored)
# ===========================================================================
def _replay(dates, moves, q82: float, width: float, L_tail: float,
win_amt: float, small_amt: float,
sizing: str = "video", start: float = CAPITAL, label: str = ""):
"""Replay CLAIM-ANCHORED sulla sequenza REALE di move (cicli non sovrapposti).
PER CONTRATTO:
|m| <= q82 -> +win_amt (win, calibrato sui numeri del video)
q82 < |m| <= q82+w -> -small_amt (perdita gestita, come le 5 del video)
|m| > q82+w -> -L_tail (breach oltre il long: coda)
sizing: 'video' = +1 contratto ogni +$1000 di profitto (1->2->3->4 come
dichiarato), cap margine equity/$2500; 'prop' = leva costante iniziale
(n = equity//10k); 'fixed' = 1 contratto. Ruin se equity <= 0."""
eq = start
path, pnl_hist, npos = [], [], []
ruined = None
for i, m in enumerate(moves):
if abs(m) <= q82:
out = win_amt
elif abs(m) <= q82 + width:
out = -small_amt
else:
out = -L_tail
if sizing == "video":
n = int(np.clip(1 + max(eq - start, 0) // 1000, 1, max(1, eq // 2500)))
elif sizing == "prop":
n = int(max(1, eq // 10_000))
else:
n = 1
pnl = n * out
eq += pnl
pnl_hist.append(pnl)
npos.append(n)
path.append(eq)
if eq <= 0:
ruined = dates[i]
break
path = np.asarray(path, dtype=float)
peak = np.maximum.accumulate(np.maximum(path, 1e-9))
maxdd = float((path / peak - 1.0).min()) if len(path) else 0.0
last_i = len(path) - 1
yrs = max(1e-9, (pd.Timestamp(dates[last_i]) - pd.Timestamp(dates[0])).days / 365.25)
cagr = float((max(path[-1], 1e-9) / start) ** (1 / yrs) - 1) if len(path) else 0.0
wi = int(np.argmin(pnl_hist)) if pnl_hist else 0
return dict(label=label, final=float(path[-1]) if len(path) else start,
cagr=cagr, maxdd=maxdd,
ruined=str(pd.Timestamp(ruined).date()) if ruined is not None else None,
worst_cycle=float(min(pnl_hist)) if pnl_hist else 0.0,
worst_date=str(pd.Timestamp(dates[wi]).date()) if pnl_hist else None,
n_at_worst=int(npos[wi]) if npos else 0,
n_cycles=len(pnl_hist),
n_tails=int(np.sum(np.abs(np.asarray(moves[:len(pnl_hist)])) > q82 + width)),
avg_pnl_cycle=float(np.mean(pnl_hist)) if pnl_hist else 0.0,
winrate=float(np.mean(np.asarray(pnl_hist) > 0)) if pnl_hist else 0.0,
years=float(yrs))
def _cycles(df: pd.DataFrame, h: int):
c = df["close"].values
idx = np.arange(0, len(c) - h, h)
moves = c[idx + h] / c[idx] - 1.0
dates = df["datetime"].values[idx + h]
return dates, moves
def _calib_win_per_contract() -> float:
"""Calibra il win PER CONTRATTO cosi' che 28 cicli TUTTI win con il sizing
video (+1 contratto/$1000, 1->2->3->4) riproducano il totale dichiarato
(+$3.164): i +$171/+$113 del video sono medie a livello CONTO su 1-4
contratti, non per contratto."""
lo, hi = 10.0, 171.0
for _ in range(60):
w = 0.5 * (lo + hi)
eq, tot = CAPITAL, 0.0
for _k in range(N_TRADES):
n = int(np.clip(1 + max(eq - CAPITAL, 0) // 1000, 1, max(1, eq // 2500)))
eq += n * w
tot += n * w
if tot > N_TRADES * AVG_PNL:
hi = w
else:
lo = w
return round(0.5 * (lo + hi), 1)
def test3(spy: pd.DataFrame, q82: float):
p("\n" + "=" * 88)
p("TEST 3 — '420% ANNUO': la stessa strategia attraverso le code reali, con compounding")
p("=" * 88)
# Replay CLAIM-ANCHORED: economia PER CONTRATTO calibrata sui numeri del video,
# guidata dalla sequenza REALE dei move ~6gg. Unico parametro nostro: la
# severita' della coda per contratto, boxata da due letture convergenti:
# cap fisico = width 2% x notional $60k = $1.200 (cap della diagonale, Test 4)
# EV=0 = la severita' che rende il gioco fair alle p empiriche
w_c = _calib_win_per_contract()
s_c = round(w_c * AVG_LOSS / AVG_WIN, 1)
m4 = hmoves(spy["close"].values, H_TD)
p_tail = float(np.mean(np.abs(m4) > q82 + 0.02))
p_small = max(1 - WINRATE_CLAIM - p_tail, 0.0)
L_ev0 = round((WINRATE_CLAIM * w_c - p_small * s_c) / p_tail, 0)
L_bs = 1200.0
p(f"Replay claim-anchored (cicli 4td non sovrapposti, strike q82={q82:.2%}, long +2%):")
p(f"PER CONTRATTO: win +${w_c} (|m|<=q82), gestita -${s_c} (<=q82+2%), coda -$L")
p(f"(|m|>q82+2%). Calibrazione: 28 cicli senza coda col sizing video 1->4 = +$3.164")
p(f"(riproduce il video PER COSTRUZIONE: win 82%, PF 5.1). Severita' coda boxata:")
p(f"cap fisico width2%x$60k = ${L_bs:,.0f} | EV=0 = ${L_ev0:,.0f} (convergenti).")
p(f"Sizing 'video' = +1 contratto/$1000 profitto (cap eq/$2500); 'prop' = eq//10k.")
windows = [("FULL 1996-2026", None, None),
("2019-2026 (era book)", "2019-01-01", None),
("H2-2023 (finestra tipo video)", "2023-07-01", "2023-12-31"),
("2020 (COVID)", "2020-01-01", "2020-12-31"),
("2022 (bear)", "2022-01-01", "2022-12-31"),
("2008 (GFC)", "2008-01-01", "2008-12-31")]
rows = []
for sizing, L, tag in (("video", L_bs, "video, cap fisico $1.200"),
("video", L_ev0, f"video, EV=0 ${L_ev0:,.0f}"),
("prop", L_bs, "leva costante, cap fisico")):
p(f"\n -- sizing {tag} --")
for label, a, b in windows:
d = spy
if a:
d = d[d["datetime"] >= pd.Timestamp(a, tz="UTC")]
if b:
d = d[d["datetime"] <= pd.Timestamp(b, tz="UTC")]
dates, moves = _cycles(d.reset_index(drop=True), H_TD)
out = _replay(dates, moves, q82, 0.02, L, w_c, s_c, sizing=sizing,
label=f"{label} [{tag}]")
rows.append(out)
ann = f"{out['cagr']:+.0%}" if not out["ruined"] else "RUIN"
p(f" {label:30s} finale ${out['final']:>8,.0f} CAGR {ann:>7s} "
f"maxDD {out['maxdd']:>5.0%} win {out['winrate']:.0%} "
f"code {out['n_tails']:>2d} worst ${out['worst_cycle']:>7,.0f} "
f"({out['worst_date']}, {out['n_at_worst']}c)"
+ (f" ** ROVINA {out['ruined']} **" if out["ruined"] else ""))
# controllo: size FISSA 1 contratto, e scenario 'premio generoso' (+20% sui win)
dates, moves = _cycles(spy, H_TD)
fx = _replay(dates, moves, q82, 0.02, L_ev0, w_c, s_c, sizing="fixed",
label="FULL fixed-1c EV=0")
gen_fx = _replay(dates, moves, q82, 0.02, L_ev0, w_c * 1.2, s_c, sizing="fixed",
label="FULL fixed-1c premio+20%")
gen = _replay(dates, moves, q82, 0.02, L_ev0, w_c * 1.2, s_c, sizing="video",
label="FULL video premio+20%")
p(f"\n Controlli (FULL 1996-2026, coda EV=0):")
p(f" - size FISSA 1 contratto: finale ${fx['final']:>8,.0f} CAGR {fx['cagr']:+.1%} "
f"maxDD {fx['maxdd']:.0%}" + (f" ROVINA {fx['ruined']}" if fx['ruined'] else ""))
p(f" - fixed-1c, premio +20%: finale ${gen_fx['final']:>8,.0f} CAGR "
f"{gen_fx['cagr']:+.1%} maxDD {gen_fx['maxdd']:.0%}"
+ (f" ROVINA {gen_fx['ruined']}" if gen_fx['ruined'] else ""))
p(f" - sizing video, premio +20%: finale ${gen['final']:>8,.0f} CAGR "
f"{gen['cagr']:+.1%} maxDD {gen['maxdd']:.0%}"
+ (f" ROVINA {gen['ruined']}" if gen['ruined'] else ""))
p(f" -> anche regalando un VRP persistente del +20%, il CAGR onesto multi-anno e'")
p(f" a una cifra, NON 420%; e il sizing del video lo converte comunque in rovina")
p(f" (scala dopo i win -> la coda colpisce sempre vicino alla size massima).")
rows += [fx, gen_fx, gen]
# BTC/ETH dai NOSTRI dati: stesso 82% strutturale (q82 proprio), width vol-scalata,
# severita' EV=0 ricalibrata sulla p_tail dell'asset.
p()
for aname in ("BTC", "ETH"):
d = al.get(aname, "1d")
m6 = hmoves(d["close"].values, 6)
q82a = float(np.quantile(np.abs(m6), WINRATE_CLAIM))
wa = float(0.9 * m6.std())
p_tail_a = float(np.mean(np.abs(m6) > q82a + wa))
La = round((WINRATE_CLAIM * w_c
- max(1 - WINRATE_CLAIM - p_tail_a, 0) * s_c) / p_tail_a, 0)
dates_a, moves_a = _cycles(d, 6)
out = _replay(dates_a, moves_a, q82a, wa, La, w_c, s_c, sizing="video",
label=f"{aname} strike {q82a:.0%} width {wa:.0%} EV=0")
rows.append(out)
ann = f"{out['cagr']:+.0%}" if not out["ruined"] else "RUIN"
p(f" {out['label']:38s} P(coda)={p_tail_a:.1%} L=${La:,.0f} -> finale "
f"${out['final']:>8,.0f} CAGR {ann:>6s} maxDD {out['maxdd']:>5.0%} "
f"code {out['n_tails']}"
+ (f" ** ROVINA {out['ruined']} **" if out["ruined"] else ""))
p()
p(" -> Il '420%' e' l'annualizzazione della finestra senza coda con il sizing gia'")
p(" scalato. Sulla storia intera, con la STESSA economia dichiarata dal video e")
p(" una coda fair, il compounding del video incontra la coda alla size massima.")
OUT["test3"] = dict(win_per_contract=w_c, small_per_contract=s_c,
L_bs=L_bs, L_ev0=L_ev0, p_tail=p_tail, rows=rows)
# ===========================================================================
# TEST 4 — QUANTO COPRE LA DIAGONALE IL GIORNO DEL CRASH
# ===========================================================================
def test4(q82: float):
p("\n" + "=" * 88)
p("TEST 4 — DIAGONALE COME PROTEZIONE: loss_con_diagonale / loss_naked (BS, gap+vol spike)")
p("=" * 88)
S0 = 100.0
iv0 = 0.16
def scenario(d_short, width, gap, iv1):
"""Ritorna (loss_naked, loss_diag, ratio) in frazione di S0 (positivi=perdita)."""
Kp_s = S0 * (1 - d_short)
Kp_l = S0 * (1 - d_short - width)
v_s0 = bs_put(S0, Kp_s, 6 / 365, iv0)
v_l0 = bs_put(S0, Kp_l, 13 / 365, iv0)
S1 = S0 * (1 + gap)
v_s1 = bs_put(S1, Kp_s, 5 / 365, iv1)
v_l1 = bs_put(S1, Kp_l, 12 / 365, iv1)
loss_naked = float(v_s1 - v_s0) # mark contro lo short
loss_diag = float((v_s1 - v_s0) - (v_l1 - v_l0)) # long compensa
ratio = loss_diag / loss_naked if loss_naked > 1e-9 else np.nan
return loss_naked, loss_diag, ratio
rows = []
p(f"Struttura put-side branch OBS: short K={100 * (1 - q82):.1f} T=6g, long T=13g piu' OTM.")
p(f"Scenario: gap overnight (resta 1g di vita in meno), IV 16% -> IV_crash. Perdite % notional.")
p(f" {'gap':>5s} {'IV->':>5s} {'width':>6s} | {'naked':>7s} {'diag':>7s} {'ratio':>6s} "
f"{'equity hit @6x':>14s}")
for width in (0.02, 0.03):
for gap in (-0.05, -0.10, -0.15):
for iv1 in (0.16, 0.40, 0.50, 0.70):
ln, ld, ratio = scenario(q82, width, gap, iv1)
rows.append(dict(d=q82, width=width, gap=gap, iv1=iv1,
naked=ln, diag=ld, ratio=ratio))
p(f" {gap:>5.0%} {iv1:>5.0%} {width:>6.0%} | {ln / S0:>7.2%} "
f"{ld / S0:>7.2%} {ratio:>6.0%} {6 * ld / S0:>13.1%}")
# branch DEEP (9% OTM come dichiarato), width 3%
deep = []
for gap in (-0.10, -0.15):
for iv1 in (0.40, 0.50, 0.70):
ln, ld, ratio = scenario(0.09, 0.03, gap, iv1)
deep.append(dict(d=0.09, width=0.03, gap=gap, iv1=iv1,
naked=ln, diag=ld, ratio=ratio))
p()
p(" Branch DEEP (short 9% OTM, long 12% OTM, width 3%):")
for r in deep:
p(f" {r['gap']:>5.0%} IV->{r['iv1']:.0%} | naked {r['naked'] / S0:>6.2%} "
f"diag {r['diag'] / S0:>6.2%} ratio {r['ratio']:>4.0%} "
f"equity hit @6x {6 * r['diag'] / S0:>6.1%}")
r10 = [r for r in rows if r["gap"] == -0.10 and r["iv1"] == 0.50]
r10_novol = [r for r in rows if r["gap"] == -0.10 and r["iv1"] == 0.16
and r["width"] == 0.02][0]
p()
p(f" -> La diagonale NON e' un hedge pieno e NON e' gratis: a gap -10%/IV 50 lascia")
p(f" passare il {r10[0]['ratio']:.0%} (width 2%) / {r10[1]['ratio']:.0%} (width 3%) "
f"della perdita naked; SENZA vol spike (IV ferma) il {r10_novol['ratio']:.0%}.")
p(f" A 6x notional un -10% costa {6 * r10[0]['diag'] / S0:.0%}-"
f"{6 * r10_novol['diag'] / S0:.0%} di equity PER UNITA' (x4 unita' post-scaling = "
f"{24 * r10[0]['diag'] / S0:.0%}-{24 * r10_novol['diag'] / S0:.0%}).")
p(f" La protezione dipende dal vol-spike che gonfia il long (vega del T+7g): e'")
p(f" un cap alla ROVINA, non alla perdita — e il costo del cap (long ricomprato")
p(f" ogni ciclo) e' il motivo per cui l'EV netto resta ~0 (Test 2/3).")
OUT["test4"] = dict(obs=rows, deep=deep)
# ===========================================================================
# TEST 5 — ESEGUIBILITA' PER NOI (Deribit BTC/ETH, $600)
# ===========================================================================
def test5():
p("\n" + "=" * 88)
p("TEST 5 — ESEGUIBILITA' su Deribit BTC/ETH al NOSTRO capitale ($600)")
p("=" * 88)
res = {}
for a, minsz in (("BTC", 0.1), ("ETH", 0.1)):
d = al.get(a, "1d")
S = float(d["close"].values[-1])
m6 = hmoves(d["close"].values, 6)
q82a = float(np.quantile(np.abs(m6), WINRATE_CLAIM))
leg_notional = minsz * S
sig = 0.55 if a == "BTC" else 0.70
w = float(0.9 * m6.std()) # width vol-scalata (come il 2% su SPY)
short0 = bs_put(S, S * (1 - q82a), 6 / 365, sig) + bs_call(S, S * (1 + q82a), 6 / 365, sig)
long0 = (bs_put(S, S * (1 - q82a - w), 13 / 365, sig)
+ bs_call(S, S * (1 + q82a + w), 13 / 365, sig))
long7 = (bs_put(S, S * (1 - q82a - w), 7 / 365, sig)
+ bs_call(S, S * (1 + q82a + w), 7 / 365, sig))
# raccolto theta atteso per ciclo a mercato FERMO (spread 5%/gamba)
harvest = (0.95 * short0 - 1.05 * long0 + 0.95 * long7) / S * leg_notional
# fee Deribit opzioni: min(0.0003*S, 12.5% premio) per gamba; 8 esecuzioni/ciclo
avg_leg = (short0 / 2 + long0 / 2) / 2 / S * leg_notional
fee = 8 * min(0.0003 * S * minsz, 0.125 * max(avg_leg, 1e-9))
# margine short (standard margin, senza netting sulle diagonali):
# ~max(0.15-OTM, 0.10)*S*size + mark, x2 gambe short
margin = 2 * (max(0.15 - q82a, 0.10) * S * minsz + short0 / 2 * minsz)
# tail netta: full-breach del cap (gap = -(q82+w), IV x1.5), lato put
gap_full = -(q82a + w)
Sc = S * (1 + gap_full)
vs = bs_put(Sc, S * (1 - q82a), 1 / 365, 1.5 * sig)
vl = bs_put(Sc, S * (1 - q82a - w), 8 / 365, 1.5 * sig)
vs0 = bs_put(S, S * (1 - q82a), 6 / 365, sig)
vl0 = bs_put(S, S * (1 - q82a - w), 13 / 365, sig)
tail = float(((vs - vs0) - (vl - vl0)) / S * leg_notional)
net_cycle = harvest - fee
fee_pct = f"{fee / harvest:.0%} del raccolto" if harvest > 0 else "raccolto <=0!"
res[a] = dict(S=S, q82=q82a, width=w, leg_notional=leg_notional,
harvest_cycle=float(harvest), fee_rt=float(fee),
net_cycle=float(net_cycle), margin=float(margin), tail_net=tail,
gap_full=float(gap_full))
p(f" {a} (close {S:,.0f}, strike {q82a:.0%} OTM per win 82%, width {w:.0%}): "
f"min {minsz}/gamba -> {leg_notional:,.0f}$/gamba, 4 gambe = "
f"{4 * leg_notional:,.0f}$ notional")
p(f" raccolto theta/ciclo (mercato fermo, netto spread 5%) ~${harvest:.2f}, "
f"fee Deribit 8 gambe ~${fee:.2f} ({fee_pct}) -> NETTO ${net_cycle:+.2f}/ciclo;")
p(f" margine short ~${margin:,.0f}; tail netta (full-breach {gap_full:.0%} 6d, "
f"IV x1.5) ~${tail:,.0f}")
p()
b, e = res["BTC"], res["ETH"]
p(f" Fee strutturale: le fee Deribit sono PROPORZIONALI alla size -> il rapporto")
p(f" fee/raccolto ({b['fee_rt'] / max(b['harvest_cycle'], 1e-9):.0%} BTC, "
f"{e['fee_rt'] / max(e['harvest_cycle'], 1e-9):.0%} ETH) NON migliora scalando:")
p(f" a questi strike la DOUBLE DIAGONAL e' fee-negativa su Deribit A QUALSIASI size")
p(f" (netto {b['net_cycle']:+.2f}$/ciclo BTC, {e['net_cycle']:+.2f}$/ciclo ETH per "
f"unita' minima). Il capitale sposta il muro di margine, non questo.")
p()
# ---- soglie di capitale: $600 oggi, 2k/3.5k/5k prospettici --------------
# Granularita' sensata = >=3 posizioni concorrenti + scalabile 1->2 contratti
# (>=6 slot di margine), budget margine 25% equity, rischio/posizione <=5%.
# ETH CREDIT SPREAD: numeri del filone gemello ALB-A (margine ~$107/spread =
# max-loss defined-risk, credito ~$2/spread) + fee 2 gambe.
sp_margin, sp_credit = 107.0, 2.0
sp_fee = 2 * min(0.0003 * e["S"] * 0.1, 0.125 * sp_credit) # entry 2 gambe, 0.1 ETH
p(f" SOGLIE DI CAPITALE (budget margine 25%, rischio/pos <=5%, granularita' sensata")
p(f" = 3 posizioni concorrenti scalabili 1->2 = 6 slot):")
p(f" {'conto':>7s} | {'DD BTC (m.$' + format(b['margin'], ',.0f') + ')':>22s} | "
f"{'DD ETH min (m.$' + format(e['margin'], ',.0f') + ')':>22s} | "
f"{'spread ETH (m.$107)':>20s}")
tiers = {}
for cap in (600.0, 2000.0, 3500.0, 5000.0):
mbud = 0.25 * cap
n_btc = int(mbud // b["margin"])
n_eth = int(mbud // e["margin"])
n_sp = int(mbud // sp_margin)
risk_ok_sp = sp_margin <= 0.05 * cap # max-loss spread <= 5% equity
risk_ok_eth = e["tail_net"] <= 0.05 * cap
tiers[int(cap)] = dict(margin_budget=mbud, n_btc=n_btc, n_eth_dd=n_eth,
n_spread=n_sp, spread_risk_ok=bool(risk_ok_sp),
eth_dd_risk_ok=bool(risk_ok_eth),
spread_pct_per_pos=sp_margin / cap,
spread_income_yr=n_sp * (sp_credit - sp_fee) * 52)
p(f" {cap:>7,.0f} | {n_btc:>4d} unita' ({b['margin'] / cap:>4.0%}/u) | "
f"{n_eth:>4d} unita' ({e['margin'] / cap:>4.0%}/u) | "
f"{n_sp:>3d} spread ({sp_margin / cap:>4.0%}/u, risk5% "
f"{'OK' if risk_ok_sp else 'NO'})")
p()
p(f" Lettura (con upgrade conto 2-5k):")
p(f" - DOUBLE DIAGONAL BTC: margine ${b['margin']:,.0f}/unita' -> 1a unita' dentro il")
p(f" budget 25% solo da ~${4 * b['margin']:,.0f}; 'granularita' sensata' (6 slot) da "
f"~${24 * b['margin'] / 1000:,.0f}k. Nemmeno 5k basta: resta il muro gamma-scalp")
p(f" (diario 2026-06-26: 'opzione BTC min $5.968 >> $600'), che si sposta a ~10-30k.")
p(f" - DOUBLE DIAGONAL ETH min 0.1: gia' a 2k il margine non e' il vincolo "
f"({int(0.25 * 2000 / e['margin'])} unita' nel budget), ma resta FEE-NEGATIVA "
f"({e['net_cycle']:+.2f}$/ciclo): eseguibile != conveniente, a ogni soglia.")
p(f" - CREDIT SPREAD ETH (ALB-A): il PRIMO oggetto con soglia reale. A $600: 1 spread")
p(f" = {sp_margin / 600:.0%} del conto (max-loss {sp_margin / 600:.0%} > 5% -> NO). "
f"A 2k: {int(0.25 * 2000 // sp_margin)} spread nel budget, max-loss {sp_margin / 2000:.1%}")
p(f" ~ok ma niente scalata 1->2 su 3 posizioni; a 3.5k: {int(0.25 * 3500 // sp_margin)} slot "
f"(3 pos x2 non ancora); a 5k: {int(0.25 * 5000 // sp_margin)} slot -> granularita' OK")
p(f" (3 concorrenti scalabili 1->2, {6 * sp_margin / 5000:.0%} del conto impegnato).")
p(f" SOGLIA ~${int(np.ceil(6 * sp_margin / 0.25 / 100) * 100):,} per il set completo; "
f"~$2.1k per 5 slot senza scalata. Income lordo a 5k: "
f"{tiers[5000]['spread_income_yr']:.0f}$/anno ({tiers[5000]['spread_income_yr'] / 5000:.1%}) "
f"PRIMA delle code (Test 2-3: EV~0).")
p(f" - Confronto scala video: 10k su SPX = 1 contratto SPY (~$60k notional, 6x leva).")
p(f" L'equivalente Deribit a parita' di prudenza (tail 5%) e' ~${20 * e['tail_net']:,.0f}+")
p(f" su ETH e ~${20 * b['tail_net']:,.0f}+ su BTC: anche a 5k il conto e' SOTTO la")
p(f" scala minima del gioco del video su BTC, e ci sta su ETH solo in versione")
p(f" credit-spread (che e' VRP01, non 'Albimarini').")
OUT["test5"] = dict(assets=res, tiers=tiers,
spread=dict(margin=sp_margin, credit=sp_credit, fee=float(sp_fee)))
# ===========================================================================
# TEST 6 — CONFRONTO CON VRP01 (perche' un theta-harvest sopravvive)
# ===========================================================================
def test6():
p("\n" + "=" * 88)
p("TEST 6 — Che cosa distingue un theta-harvest che sopravvive (VRP01) da uno che no")
p("=" * 88)
p(" VRP01 (audit superato, diario 2026-06-20): (1) DEFINED-RISK come struttura, non")
p(" come accessorio: il long wing cappa worst-week -16.6% -> -7.4% e DD 33% -> 21%;")
p(" (2) GATE di regime IV-rank>0.30 (vende solo vol RICCA): ribalta l'HOLD-OUT da")
p(" -0.25 a +0.28 — l'alpha e' il filtro, non il theta; (3) SIZING: sleeve al 12%")
p(" del book, niente compounding del numero di contratti; positivo/flat anche 2022.")
p(" La 'Albimarini' e' l'opposto quantificato: vende SEMPRE (nessun gate: incassa il")
p(" premio anche quando IV<RV), il rischio e' cappato solo dalla width della diagonale")
p(" (a 6x notional un gap -10% = ~-5/-12% equity PER UNITA', x4 post-scaling, Test 4),")
p(" e il sizing SCALA sui profitti proprio mentre la coda si avvicina (Test 3).")
p(" Win-rate 82% e PF 5.16 su 28 trade sono la FIRMA STRUTTURALE del venditore di")
p(" quantile a EV=0 in una finestra senza code (P 20-45%, Test 2), non un edge.")
# ===========================================================================
def main():
spy, m4, q82 = test1()
test2(spy, q82)
test3(spy, q82)
test4(q82)
test5()
test6()
p("\n" + "=" * 88)
p("JSON")
p("=" * 88)
p(json.dumps(OUT, indent=1, default=float))
if __name__ == "__main__":
main()