crolli: il libro perde nel giorno e chiude > 0 in 8/8 finestre dal 2022 (SKH01 short); crollo catturato a vol bassa, XRP diluisce come SOL
Quattro misure (r0909_*), nessun cambio a libro/pesi/config: - libro nei crolli: −0,48%/g nei 160 giorni ≤ −5%, positivo per finestra dal 2022 per la gamba short di SKH01 (108% dei guadagni); beta 0,0769 riprodotto (§46); il peso di SKH01 resta chiuso dal gate (in-sample). - crollo catturato 1-5/06/2026 a quote vere: f_net 0,74 = rally; put δ−0,10 1,92× il modello; a vol bassa e fuori dal gate di VRP01 → §3 non si riapre. - XRP terza gamba (harness r0822_sol_leg): hold-out −0,169 in 0/24, un anno buono. - universo Deribit: liquidi solo BTC/ETH/XRP/SOL; XS01 13/19; BTCDVOL future non negoziabile; PAXG non misurato. Revisione fable: due conclusioni smontate (MTM 2,6× era un artefatto di quote; «8/12 guadagna» era l'ordine delle classi). Debito §5.18: cblib.spot_series + asof guarda un'ora avanti (feed 1h etichettato all'apertura). Test Opus: 92 nuovi, suite 1008/1008; due difetti del verdetto XRP corretti. Docs: diario, RESULTS §74-77, CLAUDE.md, memoria 20/50, README; journal 07-08/09. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017zJjHUS7mf4pnGE6pq9RTt
This commit is contained in:
@@ -0,0 +1,338 @@
|
||||
"""r0909 — IL LIBRO NEI CROLLI: revisione dei sistemi adottati, misurata invece che ricordata.
|
||||
|
||||
DOMANDA DELL'OPERATORE (2026-09-09): "trova una soluzione per guadagnare anche nei crolli".
|
||||
Prima di cercare un meccanismo nuovo, questo script misura cosa fanno OGGI i due sleeve live
|
||||
(TP01 long-flat 75% + SKH01 long/short 25%) nei crolli — giorno per giorno e per episodio —
|
||||
e da dove viene, se c'e', il guadagno. La memoria dice tre cose che qui vengono RIPRODOTTE
|
||||
(M23: far riprodurre alla macchina il numero vecchio prima di pubblicarne uno nuovo):
|
||||
(a) beta del libro al sottostante +0,076 (§46) → "non si assicura un libro gia' piatto";
|
||||
(b) nel sinistro TP01 va flat e SKH01 si gira short e guadagna (§26);
|
||||
(c) il peggior giorno del libro: §33 lo trova in uno short squeeze di SKH01 (−15,77% di sleeve,
|
||||
2020-03-13) sulla lente HOURLY — su questa lente (canonica, sleeve giornalieri) NON si
|
||||
riproduce: §33 stesso nota che la canonica da' il 2025-10-10. Qui si stampa e si dichiara.
|
||||
|
||||
LENTE, dichiarata: sleeve di ricerca `sleeves._tp01_returns` / `_skyhook_returns` (ancora
|
||||
canonica 00:00 UTC, fee 0,10% RT, funding NON modellato, come ogni numero del progetto).
|
||||
⚠️ SKH01 e' MARCATO ALL'USCITA del trade (`harness.backtest_signals` muove l'equity alla
|
||||
chiusura): la serie giornaliera e' zero nell'87,7% dei giorni, e uno short aperto in una finestra
|
||||
e chiuso fuori e' accreditato FUORI. E' il motivo per cui la tabella per GIORNO mostra SKH01 ≈ 0
|
||||
e quella per FINESTRA mostra il guadagno. NON e' la serie del libro live (armato dal 2026-06-20): i crolli del 2026 (5 febbraio
|
||||
−14%/−15%, 1-5 giugno −25%/−30%) sono PRIMA dell'arming, quindi qui sono backtest, non fill.
|
||||
|
||||
DEFINIZIONI, scritte prima di guardare i numeri:
|
||||
- indice = 50/50 BTC/ETH, ribilanciato ogni giorno (la stessa geometria del libro);
|
||||
- giorno di crollo = indice ≤ −5% (severo: ≤ −10%);
|
||||
- episodio = le 12 peggiori finestre di 20 giorni NON sovrapposte dell'indice, piu' un elenco
|
||||
NOMINATO di episodi noti (COVID, maggio 2021, LUNA, FTX, agosto 2024, febbraio e giugno 2026);
|
||||
- classi ESCLUSIVE, in quest'ordine: "immune" = |libro| < 1/10 dell'indice (col segno stampato);
|
||||
altrimenti "POSITIVO" se libro > 0, "PERDE" se < 0. Si stampa anche il conteggio nudo libro > 0.
|
||||
(Prima stesura: POSITIVO provato prima di immune → 8/12; con l'ordine dichiarato qui 4/12 + 6
|
||||
immune. L'ordine delle regole ERA il verdetto: corretto in revisione, si stampano entrambi.)
|
||||
- beta al ribasso = pendenza OLS del libro sull'indice nei soli giorni con indice < 0.
|
||||
|
||||
PESI: 75/25 e' decisione vincolante (confermata 3 volte, §3). Il 37,5/62,5 di §26 compare qui
|
||||
SOLO come riga informativa negli stessi episodi — non e' una proposta: la riapre solo
|
||||
`weights_tilt_null`, che finora fallisce.
|
||||
|
||||
Nessun file di produzione toccato. Nessun ordine.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from src.backtest.harness import backtest_signals # noqa: E402
|
||||
from src.data.downloader import load_data # noqa: E402
|
||||
from src.live.book import W_SKH, W_TP01 # noqa: E402 (i pesi si IMPORTANO, non si ridichiarano: P1)
|
||||
from src.portfolio.sleeves import _skyhook_returns, _tp01_returns # noqa: E402
|
||||
from src.strategies.skyhook import SKH01_V2_DD, build_frames, skyhook_entries # noqa: E402
|
||||
from src.strategies.trend_portfolio import CANONICAL, TrendPortfolio, resample_1d # noqa: E402
|
||||
|
||||
ASSETS = ("BTC", "ETH")
|
||||
SOGLIA_CROLLO = -0.05
|
||||
SOGLIA_SEVERO = -0.10
|
||||
FINESTRA = 20
|
||||
N_EPISODI = 12
|
||||
BETA_MEMORIA = 0.076 # §46, da riprodurre SULLA SUA FINESTRA (DVOL: 2021-03-24+)
|
||||
FINESTRA_46 = pd.Timestamp("2021-03-24", tz="UTC")
|
||||
TOL_BETA = 0.01
|
||||
W_INFO = (0.375, 0.625) # §26: riga informativa, NON proposta
|
||||
|
||||
EPISODI_NOMINATI = {
|
||||
"COVID 2020-03": ("2020-03-01", "2020-03-20"),
|
||||
"maggio 2021": ("2021-05-08", "2021-05-24"),
|
||||
"LUNA 2022-05": ("2022-05-04", "2022-05-14"),
|
||||
"giugno 2022": ("2022-06-06", "2022-06-19"),
|
||||
"FTX 2022-11": ("2022-11-06", "2022-11-22"),
|
||||
"agosto 2024": ("2024-07-28", "2024-08-06"),
|
||||
"febbraio 2025": ("2025-01-31", "2025-03-11"),
|
||||
"5 febbraio 2026": ("2026-01-28", "2026-02-07"),
|
||||
"1-5 giugno 2026": ("2026-05-28", "2026-06-06"),
|
||||
}
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ serie
|
||||
|
||||
def indice_5050() -> pd.Series:
|
||||
r = {}
|
||||
for a in ASSETS:
|
||||
d = resample_1d(load_data(a, "1h"))
|
||||
s = pd.Series(d["close"].values.astype(float), index=pd.to_datetime(d["datetime"], utc=True))
|
||||
r[a] = s.pct_change()
|
||||
J = pd.concat(r, axis=1, join="inner").dropna()
|
||||
return 0.5 * J["BTC"] + 0.5 * J["ETH"]
|
||||
|
||||
|
||||
def tp01_esposizione() -> pd.Series:
|
||||
"""Esposizione lorda media (BTC+ETH)/2 tenuta nel giorno (decisa a close[i-1])."""
|
||||
tp = TrendPortfolio(**CANONICAL)
|
||||
e = {}
|
||||
for a in ASSETS:
|
||||
d = resample_1d(load_data(a, "1h"))
|
||||
tgt = tp.target_series(d)
|
||||
held = np.zeros(len(tgt)); held[1:] = tgt[:-1]
|
||||
e[a] = pd.Series(held, index=pd.to_datetime(d["datetime"], utc=True))
|
||||
J = pd.concat(e, axis=1, join="inner")
|
||||
return 0.5 * J["BTC"] + 0.5 * J["ETH"]
|
||||
|
||||
|
||||
def skh_per_direzione() -> dict[str, pd.Series]:
|
||||
"""SKH01-V2-DD 50/50 su griglia giornaliera, in tre versioni: intera, SOLO long, SOLO short.
|
||||
|
||||
Le versioni per direzione filtrano gli entry-dict prima del backtest: stesso motore, stessi
|
||||
stop. In principio filtrare potrebbe cambiare il non-overlap; MISURATO (revisione 09/09):
|
||||
long + short == intera su 2735/2735 giorni (max|diff| 3,5e-18) — la scomposizione e' ESATTA,
|
||||
e `misura()` lo verifica con un assert (P1: la versione "tutto" e' confrontata bit-exact con
|
||||
`sleeves._skyhook_returns`).
|
||||
"""
|
||||
out = {k: {} for k in ("tutto", "long", "short")}
|
||||
for a in ASSETS:
|
||||
ltf, htf = build_frames(load_data(a, "5m"))
|
||||
ent = skyhook_entries(ltf, htf, SKH01_V2_DD)
|
||||
for k, keep in (("tutto", (1, -1)), ("long", (1,)), ("short", (-1,))):
|
||||
e2 = [x if (x is not None and x["dir"] in keep) else None for x in ent]
|
||||
m = backtest_signals(ltf, e2, fee_rt=0.001, leverage=1.0, asset=a, tf="230m")
|
||||
s = pd.Series(m.equity, index=pd.DatetimeIndex(pd.to_datetime(m.eq_index, utc=True)))
|
||||
out[k][a] = s.resample("1D").last().ffill().pct_change().dropna()
|
||||
res = {}
|
||||
for k in out:
|
||||
J = pd.concat(out[k], axis=1, join="inner").fillna(0.0)
|
||||
res[k] = 0.5 * J["BTC"] + 0.5 * J["ETH"]
|
||||
return res
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ misure
|
||||
|
||||
def sh(s: pd.Series) -> float:
|
||||
s = s.dropna()
|
||||
return float(s.mean() / s.std() * np.sqrt(365.0)) if len(s) > 5 and s.std() > 0 else float("nan")
|
||||
|
||||
|
||||
def maxdd(s: pd.Series) -> float:
|
||||
eq = (1 + s.fillna(0)).cumprod(); return float((eq / eq.cummax() - 1).min())
|
||||
|
||||
|
||||
def cum(s: pd.Series) -> float:
|
||||
return float((1 + s.fillna(0)).prod() - 1)
|
||||
|
||||
|
||||
def finestre_peggiori(idx: pd.Series, n: int = N_EPISODI, w: int = FINESTRA) -> list[tuple]:
|
||||
"""Le n peggiori finestre di w giorni NON sovrapposte dell'indice (greedy dal peggiore)."""
|
||||
roll = (1 + idx).rolling(w).apply(np.prod, raw=True) - 1
|
||||
roll = roll.dropna().sort_values()
|
||||
prese, occupati = [], []
|
||||
for end, v in roll.items():
|
||||
start = end - pd.Timedelta(days=w - 1)
|
||||
if any(not (end < s or start > e) for s, e in occupati):
|
||||
continue
|
||||
prese.append((start, end, float(v))); occupati.append((start, end))
|
||||
if len(prese) >= n:
|
||||
break
|
||||
return sorted(prese)
|
||||
|
||||
|
||||
def classifica(libro: float, indice: float) -> str:
|
||||
"""Classi esclusive nell'ordine dichiarato: immune (col segno) → POSITIVO → PERDE."""
|
||||
if abs(libro) < abs(indice) / 10:
|
||||
return "immune+" if libro > 0 else "immune-"
|
||||
return "POSITIVO" if libro > 0 else "PERDE"
|
||||
|
||||
|
||||
def beta_ribasso(libro: pd.Series, idx: pd.Series, soglia: float = 0.0) -> float:
|
||||
J = pd.concat({"b": libro, "i": idx}, axis=1, join="inner").dropna()
|
||||
J = J[J["i"] < soglia]
|
||||
if len(J) < 20:
|
||||
return float("nan")
|
||||
x, y = J["i"].values, J["b"].values
|
||||
return float(np.cov(x, y)[0, 1] / np.var(x, ddof=1))
|
||||
|
||||
|
||||
def beta_pieno(libro: pd.Series, idx: pd.Series) -> float:
|
||||
J = pd.concat({"b": libro, "i": idx}, axis=1, join="inner").dropna()
|
||||
x, y = J["i"].values, J["b"].values
|
||||
return float(np.cov(x, y)[0, 1] / np.var(x, ddof=1))
|
||||
|
||||
|
||||
def misura() -> dict:
|
||||
idx = indice_5050()
|
||||
tp = _tp01_returns()
|
||||
if tp.index.tz is None:
|
||||
tp.index = tp.index.tz_localize("UTC")
|
||||
skh = skh_per_direzione()
|
||||
esp = tp01_esposizione()
|
||||
J = pd.concat({"idx": idx, "tp": tp, "skh": skh["tutto"], "skl": skh["long"],
|
||||
"sks": skh["short"], "esp": esp}, axis=1, join="inner").dropna()
|
||||
ref = _skyhook_returns()
|
||||
if ref.index.tz is None:
|
||||
ref.index = ref.index.tz_localize("UTC")
|
||||
comune = J.index.intersection(ref.index)
|
||||
assert float((J.loc[comune, "skh"] - ref.loc[comune]).abs().max()) == 0.0, "SKH01 'tutto' non e' bit-exact col sleeve"
|
||||
assert float((J["skl"] + J["sks"] - J["skh"]).abs().max()) < 1e-12, "long + short != intera: la scomposizione non e' esatta"
|
||||
J["libro"] = W_TP01 * J["tp"] + W_SKH * J["skh"]
|
||||
J["info"] = W_INFO[0] * J["tp"] + W_INFO[1] * J["skh"]
|
||||
J["c_tp"] = W_TP01 * J["tp"]
|
||||
J["c_skh"] = W_SKH * J["skh"]
|
||||
J["c_sks"] = W_SKH * J["sks"]
|
||||
|
||||
# giorni di crollo
|
||||
giorni = {}
|
||||
for nome, soglia in (("≤ −5%", SOGLIA_CROLLO), ("≤ −10%", SOGLIA_SEVERO)):
|
||||
g = J[J["idx"] <= soglia]
|
||||
giorni[nome] = dict(n=int(len(g)), idx=float(g["idx"].mean()), libro=float(g["libro"].mean()),
|
||||
tp=float(g["c_tp"].mean()), skh=float(g["c_skh"].mean()),
|
||||
sks=float(g["c_sks"].mean()), esp=float(g["esp"].mean()),
|
||||
libro_pos=float((g["libro"] > 0).mean()),
|
||||
per_anno={int(y): dict(n=int(len(gg)), idx=float(gg["idx"].sum()),
|
||||
libro=float(gg["libro"].sum()),
|
||||
sks=float(gg["c_sks"].sum()))
|
||||
for y, gg in g.groupby(g.index.year)})
|
||||
|
||||
# episodi: finestre peggiori + nominati
|
||||
def riga_ep(nome, s, e):
|
||||
w = J.loc[s:e]
|
||||
if len(w) < 3:
|
||||
return None
|
||||
return dict(nome=nome, da=str(w.index[0].date()), a=str(w.index[-1].date()), n=int(len(w)),
|
||||
idx=cum(w["idx"]), libro=cum(w["libro"]), tp=cum(w["c_tp"]), skh=cum(w["c_skh"]),
|
||||
sks=cum(w["c_sks"]), skl=cum(W_SKH * w["skl"]), esp=float(w["esp"].mean()),
|
||||
info=cum(w["info"]), classe=classifica(cum(w["libro"]), cum(w["idx"])))
|
||||
ep_auto = [r for r in (riga_ep(f"peggiore #{i+1}", s, e)
|
||||
for i, (s, e, _) in enumerate(finestre_peggiori(J["idx"]))) if r]
|
||||
ep_nom = [r for r in (riga_ep(k, pd.Timestamp(a, tz="UTC"), pd.Timestamp(b, tz="UTC"))
|
||||
for k, (a, b) in EPISODI_NOMINATI.items()) if r]
|
||||
|
||||
# peggior giorno del libro (riproduce §33: e' uno squeeze, non un crollo?)
|
||||
peggiori = J.nsmallest(5, "libro")[["idx", "libro", "c_tp", "c_skh"]]
|
||||
|
||||
n_auto = len(ep_auto)
|
||||
guad = sum(r["classe"] == "POSITIVO" for r in ep_auto)
|
||||
imm = sum(r["classe"].startswith("immune") for r in ep_auto)
|
||||
pos = sum(r["libro"] > 0 for r in ep_auto)
|
||||
dal22 = [r for r in ep_auto if r["da"] >= "2022"]
|
||||
J46 = J[J.index >= FINESTRA_46]
|
||||
quota_short = (sum(r["sks"] for r in ep_auto if r["libro"] > 0)
|
||||
/ max(1e-12, sum(r["libro"] for r in ep_auto if r["libro"] > 0)))
|
||||
return dict(
|
||||
finestra=(str(J.index[0].date()), str(J.index[-1].date()), int(len(J))),
|
||||
beta=beta_pieno(J["libro"], J["idx"]), beta_46=beta_pieno(J46["libro"], J46["idx"]),
|
||||
beta_giu=beta_ribasso(J["libro"], J["idx"]),
|
||||
beta_giu3=beta_ribasso(J["libro"], J["idx"], -0.03),
|
||||
sharpe=dict(libro=sh(J["libro"]), info=sh(J["info"]), tp=sh(J["tp"]), skh=sh(J["skh"]),
|
||||
sks=sh(J["sks"]), skl=sh(J["skl"])),
|
||||
maxdd=dict(libro=maxdd(J["libro"]), info=maxdd(J["info"]), idx=maxdd(J["idx"])),
|
||||
giorni=giorni, episodi=ep_auto, nominati=ep_nom,
|
||||
peggiori=[(str(t.date()), float(r.idx), float(r.libro), float(r.c_tp), float(r.c_skh))
|
||||
for t, r in peggiori.iterrows()],
|
||||
sks_anno={int(y): cum(g) for y, g in J["c_sks"].groupby(J.index.year)},
|
||||
sintesi=dict(n_episodi=n_auto, guadagna=guad, immune=imm, perde=n_auto - guad - imm, positivi=pos,
|
||||
dal22=(sum(r["libro"] > 0 for r in dal22), len(dal22)),
|
||||
giorni_pos=giorni["≤ −5%"]["libro_pos"], giorni_libro=giorni["≤ −5%"]["libro"],
|
||||
quota_short_nei_guadagni=float(quota_short),
|
||||
info_batte_live=sum(r["info"] > r["libro"] for r in ep_auto)),
|
||||
)
|
||||
|
||||
|
||||
def verdetto(m: dict) -> str:
|
||||
"""Tre affermazioni separate, perche' rispondono a tre domande diverse:
|
||||
(1) nel GIORNO del crollo (media sui giorni ≤ −5%); (2) per FINESTRA (classi esclusive);
|
||||
(3) da dove viene il guadagno. Un solo aggettivo ("guadagna"/"immune") sarebbe l'ordine delle regole."""
|
||||
s = m["sintesi"]
|
||||
beta_ok = abs(m["beta_46"] - BETA_MEMORIA) < TOL_BETA
|
||||
giorno = ("PERDE" if s["giorni_libro"] < 0 else "GUADAGNA")
|
||||
if s["guadagna"] >= 2 * s["n_episodi"] / 3:
|
||||
finestra = "POSITIVO per finestra"
|
||||
elif s["guadagna"] + s["immune"] >= 2 * s["n_episodi"] / 3:
|
||||
finestra = "IMMUNE-O-POSITIVO per finestra"
|
||||
else:
|
||||
finestra = "PERDE per finestra"
|
||||
return (f"NEL GIORNO DEL CROLLO IL LIBRO {giorno} ({s['giorni_libro']*100:+.2f}%/g, positivo nel {s['giorni_pos']*100:.0f}% dei giorni ≤ −5%); "
|
||||
f"{finestra}: {s['guadagna']} POSITIVO / {s['immune']} immune / {s['perde']} PERDE su {s['n_episodi']} (libro > 0 in {s['positivi']}, "
|
||||
f"dal 2022 in {s['dal22'][0]}/{s['dal22'][1]}); beta {m['beta_46']:+.4f} sulla finestra di §46 "
|
||||
f"({'riproduce' if beta_ok else 'NON riproduce'} il +{BETA_MEMORIA:.3f}); "
|
||||
f"la gamba short di SKH01 (marcata all'uscita) fa il {s['quota_short_nei_guadagni']*100:.0f}% dei guadagni negli episodi con libro > 0")
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ stampa
|
||||
|
||||
def pct(x: float) -> str:
|
||||
return f"{x*100:+.2f}%"
|
||||
|
||||
|
||||
def main() -> None:
|
||||
print("=" * 100)
|
||||
print(" r0909 — IL LIBRO NEI CROLLI (TP01 75 / SKH01 25, lente di ricerca, ancora canonica)")
|
||||
print("=" * 100)
|
||||
m = misura()
|
||||
a, b, n = m["finestra"]
|
||||
print(f" finestra {a} → {b} ({n} giorni) pesi importati da src/live/book: TP01 {W_TP01} / SKH01 {W_SKH}")
|
||||
|
||||
print("\n [1] BETA DEL LIBRO ALL'INDICE 50/50")
|
||||
print(f" pieno {m['beta']:+.3f} · sulla finestra di §46 (2021-03-24+) {m['beta_46']:+.4f} (memoria: +{BETA_MEMORIA:.3f}) "
|
||||
f"al ribasso (idx<0) {m['beta_giu']:+.3f} nei giorni ≤ −3% {m['beta_giu3']:+.3f}")
|
||||
|
||||
print("\n [2] GIORNI DI CROLLO — media per giorno")
|
||||
print(f" {'soglia':<8}{'n':>5}{'indice':>9}{'libro':>9}{'TP01':>9}{'SKH01':>9}{'SKH short':>11}{'esp TP01':>10}{'libro>0':>9}")
|
||||
for k, g in m["giorni"].items():
|
||||
print(f" {k:<8}{g['n']:>5}{pct(g['idx']):>9}{pct(g['libro']):>9}{pct(g['tp']):>9}"
|
||||
f"{pct(g['skh']):>9}{pct(g['sks']):>11}{g['esp']:>10.3f}{g['libro_pos']*100:>8.0f}%")
|
||||
print("\n per anno (giorni ≤ −5%, SOMMA nel gruppo):")
|
||||
print(f" {'anno':<6}{'n':>4}{'indice':>10}{'libro':>10}{'SKH short':>11}")
|
||||
for y, g in m["giorni"]["≤ −5%"]["per_anno"].items():
|
||||
print(f" {y:<6}{g['n']:>4}{pct(g['idx']):>10}{pct(g['libro']):>10}{pct(g['sks']):>11}")
|
||||
|
||||
def tab(rows, titolo):
|
||||
print(f"\n {titolo}")
|
||||
print(f" {'episodio':<18}{'da':>11}{'a':>11}{'indice':>9}{'libro':>9}{'TP01':>8}{'SKH01':>8}"
|
||||
f"{'SKH-S':>8}{'SKH-L':>8}{'esp':>6}{'37/63':>8} classe")
|
||||
for r in rows:
|
||||
print(f" {r['nome']:<18}{r['da']:>11}{r['a']:>11}{pct(r['idx']):>9}{pct(r['libro']):>9}"
|
||||
f"{pct(r['tp']):>8}{pct(r['skh']):>8}{pct(r['sks']):>8}{pct(r['skl']):>8}{r['esp']:>6.2f}"
|
||||
f"{pct(r['info']):>8} {r['classe']}")
|
||||
tab(m["episodi"], f"[3] LE {N_EPISODI} PEGGIORI FINESTRE DI {FINESTRA} GIORNI DELL'INDICE (non sovrapposte)")
|
||||
tab(m["nominati"], "[4] EPISODI NOMINATI (elenco scritto prima di misurare)")
|
||||
|
||||
print("\n [5] I 5 PEGGIORI GIORNI DEL LIBRO (§33 su lente hourly: squeeze SKH01 2020-03-13; qui lente canonica: NON si riproduce, e' un crollo con TP01 mezzo long)")
|
||||
print(f" {'giorno':<12}{'indice':>9}{'libro':>9}{'TP01':>9}{'SKH01':>9}")
|
||||
for t, i, l, tpv, sk in m["peggiori"]:
|
||||
print(f" {t:<12}{pct(i):>9}{pct(l):>9}{pct(tpv):>9}{pct(sk):>9}")
|
||||
|
||||
print("\n [6] LA GAMBA SHORT DI SKH01 (peso 0,25 gia' applicato) — contributo per anno al libro")
|
||||
print(" " + " ".join(f"{y} {pct(v)}" for y, v in m["sks_anno"].items()))
|
||||
s = m["sharpe"]; d = m["maxdd"]
|
||||
print(f"\n Sharpe: libro {s['libro']:.2f} · TP01 {s['tp']:.2f} · SKH01 {s['skh']:.2f} "
|
||||
f"(solo long {s['skl']:.2f} / solo short {s['sks']:.2f}) maxDD libro {d['libro']*100:.1f}% · indice {d['idx']*100:.1f}%")
|
||||
print(f" riga INFORMATIVA 37,5/62,5 (§26, NON proposta): Sharpe {s['info']:.2f}, maxDD {d['info']*100:.1f}%, "
|
||||
f"batte il 75/25 in {m['sintesi']['info_batte_live']}/{m['sintesi']['n_episodi']} episodi")
|
||||
|
||||
print("\n VERDETTO: " + verdetto(m))
|
||||
print("=" * 100)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user