8c0b5f97ee
I dodici filoni della sesta ondata erano rimasti `_in corso_`: la sessione si e' chiusa dopo che gli agenti avevano scritto gli script e prima che i verdetti fossero consolidati, e i loro messaggi finali sono persi. I numeri qui NON vengono da quei messaggi: vengono dalla riesecuzione dei dodici script (07:38-08:06 UTC, sequenziale, log in logs/r0823b/ che e' gitignored). E' il motivo per cui l'ondata non e' andata persa: ogni script calcola il proprio verdetto a runtime. Cosa tocca decisioni gia' prese: - §67 il gate pre-registrato XSR01 del 23/10 legge un monitor tarato sul pavimento del venue sbagliato (C* $15-20k, non ~$3k) - §64 la raccomandazione di §51 (raccogliere la catena USDC) non e' giustificata dalla ragione che porta: le due superfici sono la stessa - §60 la politica MISTO scelta il 25/07 non e' piu' l'ottimo (oggi MISTO-A) - §63 domanda fiscale NUOVA, diversa da quella aperta il 07/08 Il risultato piu' grande e' di §58: l'obiettivo del progetto ha DUE definizioni operative in uso che danno 33,9% contro 0,33% sulla stessa domanda, e non era mai stato detto quale si stesse ottimizzando. r0823b_quasi_passati.py (§68) terminava con IndexError: Griglia.combo() indicizzava con self.idx (2720 giorni) un sottoinsieme di 958 righe. Corretto col parametro idx esplicito piu' un controllo di lunghezza; la rinormalizzazione e' riga per riga, quindi i valori sono quelli dell'intento dell'autore. La correzione e' del coordinatore, non dell'autore, ed e' dichiarata nel registro. Libro, pesi, cron, config INVARIATI. Nessun ordine. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
786 lines
43 KiB
Python
786 lines
43 KiB
Python
"""§67 REGIME-CAPITALE — cosa e' stato archiviato PER TAGLIA, e cosa sarebbe vivo a $100.000.
|
|
|
|
LA DOMANDA
|
|
----------
|
|
Ogni screening di questo progetto e' stato fatto a **$600 di capitale**. E' la scelta giusta per il
|
|
conto che esiste, ma ha un effetto collaterale mai esaminato: le strategie messe in STAT-MODE o
|
|
rifiutate citando min-order / lotto minimo / numero di gambe / haircut small-cap sono state
|
|
archiviate **senza mai misurare se sarebbero vive a $100k** — che e' il nozionale di un conto
|
|
funded, il canale su cui il progetto sta valutando di puntare.
|
|
|
|
E c'e' una ragione forte per rifare il conto ADESSO: l'ondata del 22-23/08 ha falsificato **tre muri
|
|
di taglia** pubblicati (il "$20k di XS01" non esiste; il muro delle opzioni BTC era il COLLATERALE
|
|
PER VENDERE, non il costo di comprare; i lotti USDC-lineari sono 7,5-10x piu' piccoli di quelli
|
|
inverse su cui erano stati misurati). Tre su tre sbagliati, tutti nella stessa direzione: piu'
|
|
permissivi del creduto.
|
|
|
|
ATTESE A PRIORI — scritte leggendo il mandato, PRIMA di qualunque misura
|
|
-----------------------------------------------------------------------
|
|
A1 Il censimento conterra' 10-15 voci e la MAGGIORANZA avra' anche un rifiuto sul lato EDGE:
|
|
la taglia sara' raramente l'unico motivo.
|
|
A2 (controllo positivo) XS01 riprodurra' haircut ~0 gia' a $200-600 di capitale ALLOCATO.
|
|
Se il mio codice non lo trova, il mio codice e' rotto e mi fermo.
|
|
A3 A $100.000 ogni candidato multi-gamba converge alla propria serie MODELLATA (haircut -> 0).
|
|
A4 Nessun archiviato passera' TUTTI i gate a $100k — e non perche' ricontrolli i gate, ma per una
|
|
ragione strutturale: i gate pubblicati sono stati calcolati sulla serie MODELLATA, che E' il
|
|
limite a capitale infinito. Se e' vero, la domanda del mandato era gia' risposta nei registri.
|
|
A5 Il corollario funded non aggiungera' NIENTE di nuovo: l'unico ampliamento reale dell'insieme
|
|
ammissibile e' XS01, e §1/§29 l'hanno gia' contato (+0,400 di J).
|
|
A6 L'haircut sara' MONOTONO decrescente nel capitale. <-- questa e' quella che mi aspetto di
|
|
confermare senza pensarci; se salta, e' il risultato metodologico del filone.
|
|
|
|
METODO
|
|
------
|
|
* Repliche PRIMA di ogni delta: identita' bit-exact di XS01 col sleeve di produzione; libro 75/25
|
|
`hourly` contro il numero pubblicato (Sh 1,683 / maxDD 11,22%).
|
|
* Parametri VERI del venue (non quelli di memoria): Deribit min-order $5, taker 3,50 bps dal
|
|
2026-08-01; Hyperliquid min-order **$10** (non $5) e taker **4,50 bps** (non 5,0), misurati da
|
|
HL-EXEC il 22/08; lotti della famiglia **USDC-lineare** (non inverse), letti dal venue.
|
|
* La LENTE del libro e' dichiarata: `hourly` (path live), Sh 1,683 / maxDD 11,22% — non la
|
|
canonica 1,800/9,56% ne' la mediana d'ancora 1,626/10,4%.
|
|
* Distinzione dichiarata riga per riga: **capitale PROPRIO** (e' tuo, il vincolo e' il min-order)
|
|
contro **nozionale FUNDED** (non e' tuo, il vincolo binding e' la regola di drawdown).
|
|
|
|
SICUREZZA: sola lettura. Nessun ordine. Nessun file di produzione toccato. Solo GET pubbliche,
|
|
con pacing, cache e astensione nelle finestre del cron (:05-:10 book, :24-:30 catena).
|
|
|
|
uv run python scripts/research/r0823b_regime_capitale.py
|
|
uv run python scripts/research/r0823b_regime_capitale.py --no-net # tutto da cache/costanti
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
import urllib.request
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
|
|
import numpy as np
|
|
import pandas as pd
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
for _p in (ROOT, ROOT / "scripts" / "research", ROOT / "scripts" / "research" / "alt"):
|
|
if str(_p) not in sys.path:
|
|
sys.path.insert(0, str(_p))
|
|
|
|
CACHE = ROOT / "data" / "cache_regime_capitale.json"
|
|
|
|
# --------------------------------------------------------------------------------------------
|
|
# PARAMETRI VENUE — dichiarati, con la FONTE. Nessuno di questi e' "di memoria".
|
|
# --------------------------------------------------------------------------------------------
|
|
DER_MIN_ORDER = 5.0 # config/live.json -> min_order_usd
|
|
DER_FEE_SIDE = 0.00035 # 3,50 bps/lato dal 2026-08-01 (SLIP-AUDIT: 16/16 fill sul nastro)
|
|
HL_MIN_ORDER = 10.0 # HL-EXEC 22/08: docs + 3.827 livelli, 0,0% sotto $10 (NON $5)
|
|
HL_FEE_SIDE = 0.00045 # HL-EXEC 22/08: 4,50 bps, due fonti indipendenti (NON 5,0)
|
|
SPLIT_FUNDED = 0.80 # r0725_hyro.SPLIT — quota trader del profitto su conto funded
|
|
BARRIERA_FUNDED = 0.06 # HyroTrader max loss STATICO 6% (r0725_hyro.EV_DD, regole vere)
|
|
EURUSD = 1.09
|
|
CAPITALI = (635.0, 5_000.0, 20_000.0, 100_000.0) # la griglia del mandato
|
|
GRIGLIA_DENSA = (95.0, 200.0, 400.0, 600.0, 635.0, 1_000.0, 1_500.0, 2_000.0, 3_000.0,
|
|
5_000.0, 7_500.0, 10_000.0, 15_000.0, 20_000.0, 50_000.0, 100_000.0, 200_000.0)
|
|
TOL_STAB = 0.10 # |Sh(C) - Sh(modellata)| <= 0,10 per DICHIARARE eseguibile
|
|
SEED = 823
|
|
|
|
_last_net = [0.0]
|
|
|
|
|
|
def sez(t: str) -> None:
|
|
print("\n" + "=" * 100)
|
|
print(" " + t)
|
|
print("=" * 100)
|
|
|
|
|
|
def sub(t: str) -> None:
|
|
print("\n --- " + t)
|
|
|
|
|
|
def _pace() -> None:
|
|
"""<=2 req/s e astensione nelle finestre del cron live."""
|
|
now = datetime.now(timezone.utc)
|
|
while now.minute in (5, 6, 7, 8, 9, 10, 24, 25, 26, 27, 28, 29, 30):
|
|
print(f" [pausa: minuto {now.minute} e' finestra del cron live]")
|
|
time.sleep(20)
|
|
now = datetime.now(timezone.utc)
|
|
dt = time.time() - _last_net[0]
|
|
if dt < 0.6:
|
|
time.sleep(0.6 - dt)
|
|
_last_net[0] = time.time()
|
|
|
|
|
|
def get_json(url: str, key: str, no_net: bool):
|
|
c = json.loads(CACHE.read_text()) if CACHE.exists() else {}
|
|
if no_net or (key in c and time.time() - c[key].get("_t", 0) < 12 * 3600):
|
|
return c.get(key, {}).get("v")
|
|
try:
|
|
_pace()
|
|
with urllib.request.urlopen(url, timeout=20) as r:
|
|
v = json.loads(r.read().decode())
|
|
except Exception as e: # noqa: BLE001
|
|
print(f" [rete KO su {key}: {type(e).__name__}] -> uso la cache se c'e'")
|
|
return c.get(key, {}).get("v")
|
|
c[key] = {"_t": time.time(), "v": v}
|
|
CACHE.write_text(json.dumps(c))
|
|
return v
|
|
|
|
|
|
def sola_lettura() -> None:
|
|
"""Prova che il filone non ha toccato la produzione (il cron ESEGUE dalla working tree)."""
|
|
prot = ["src/", "config/", "scripts/live/", "tests/", "data/paper_statarb", "data/paper_xsr",
|
|
"data/paper_dvolspread", "data/live"]
|
|
out = subprocess.run(["git", "status", "--porcelain", "--"] + prot,
|
|
cwd=ROOT, capture_output=True, text=True).stdout.strip()
|
|
print(f" git status sui percorsi protetti: {'VUOTO (sola lettura provata)' if not out else out}")
|
|
assert not out, "PRODUZIONE TOCCATA — fermarsi"
|
|
|
|
|
|
# =============================================================================================
|
|
# METRICHE E DEFINIZIONE DI ESEGUIBILITA'
|
|
# =============================================================================================
|
|
ANN = np.sqrt(365.25)
|
|
|
|
|
|
def met(net: np.ndarray, idx) -> dict:
|
|
x = np.asarray(net, float)
|
|
sd = x.std(ddof=1)
|
|
sh = float(x.mean() / sd * ANN) if sd > 0 else 0.0
|
|
eq = np.cumprod(1.0 + np.clip(x, -0.99, None))
|
|
dd = float((1.0 - eq / np.maximum.accumulate(eq)).max())
|
|
yrs = max((idx[-1] - idx[0]) / pd.Timedelta("365.25D"), 1e-9)
|
|
return dict(sharpe=sh, maxdd=100 * dd, cagr=100 * (eq[-1] ** (1 / yrs) - 1.0))
|
|
|
|
|
|
def c_stabilita(curva: dict[float, float], sh_mod: float, tol: float = TOL_STAB) -> float | None:
|
|
"""C* = il PIU' PICCOLO capitale tale che da li' in su lo Sharpe NON si discosta piu' di `tol`
|
|
dalla serie modellata. Definizione scelta PRIMA di guardare, e per un motivo: ne' l'haircut ne'
|
|
la quota di nozionale eseguito, da soli, distinguono "eseguibile" da "degenere" (sez. 3)."""
|
|
caps = sorted(curva)
|
|
for i, c in enumerate(caps):
|
|
if all(abs(curva[c2] - sh_mod) <= tol for c2 in caps[i:]):
|
|
return c
|
|
return None
|
|
|
|
|
|
def maxdd_k(r: np.ndarray, k: float) -> float:
|
|
eq = np.cumprod(1.0 + np.clip(k * np.asarray(r, float), -0.99, None))
|
|
return float((1.0 - eq / np.maximum.accumulate(eq)).max())
|
|
|
|
|
|
def cagr_k(r: np.ndarray, k: float, idx) -> float:
|
|
eq = np.cumprod(1.0 + np.clip(k * np.asarray(r, float), -0.99, None))
|
|
yrs = max((idx[-1] - idx[0]) / pd.Timedelta("365.25D"), 1e-9)
|
|
return float(eq[-1] ** (1 / yrs) - 1.0)
|
|
|
|
|
|
def k_barriera(r: np.ndarray, target: float = BARRIERA_FUNDED) -> float:
|
|
lo, hi = 1e-3, 20.0
|
|
if maxdd_k(r, hi) < target:
|
|
return hi
|
|
for _ in range(60):
|
|
mid = 0.5 * (lo + hi)
|
|
if maxdd_k(r, mid) < target:
|
|
lo = mid
|
|
else:
|
|
hi = mid
|
|
return lo
|
|
|
|
|
|
# =============================================================================================
|
|
# SEZ. 1 — IL CENSIMENTO
|
|
# =============================================================================================
|
|
# (voce, dove il muro di taglia e' dichiarato, muro come pubblicato, stato del muro OGGI,
|
|
# c'e' ANCHE un rifiuto sul lato EDGE?)
|
|
CENSIMENTO = [
|
|
("XS01 (19 gambe HL)", "CLAUDE.md 19/06",
|
|
"STAT-MODE, 'serve ~20k'", "FALSIFICATO 22/08 (haircut ~0 a $200-600)",
|
|
"no — e' nel book di ricerca; fuori dal LIVE per VENUE (14/19) e decisione venue"),
|
|
("XSR01 (50 gambe HL)", "CLAUDE.md 25/07",
|
|
"ticket $1,73 < $5 -> 'reale a ~$5k'", "RIDIMENSIONATO 22/08: soglia vera ~$3.000",
|
|
"SI — weights_tilt_null FAIL (book 5-sleeve), storia 2,6a monoregime; gate 23/10 APERTO"),
|
|
("STATARB-MULTI (50 coppie)", "CLAUDE.md 25/07",
|
|
"51 gambe = STAT-MODE", "MAI MISURATO -> misurato qui",
|
|
"SI — IC95 [-0,12; 1,72], t 1,31; uplift -0,11 vs null a priori"),
|
|
("CC01 cash-and-carry", "CLAUDE.md 26/06",
|
|
"4-38 gambe, 'rivedere a ~20k+'", "FALSIFICATO 22/08 (§34: lotti $10/$1; §39: linea USDC esiste)",
|
|
"SI — DSR 0,363 FAIL, implausible_sharpe 5/5, de-levering REFUTED, 2 gambe = Sharpe -0,42"),
|
|
("VRP01 (put credit spread)", "CLAUDE.md 30/07",
|
|
"lotto BTC $6.210 / ETH $1.832 -> 0 lotti al peso 12%",
|
|
"FALSIFICATO 22/08 (§50: famiglia USDC ETH $242 / BTC $772)",
|
|
"SI — regola 'niente short-vol da modello in deploy' + f=0,73 misurato"),
|
|
("Opzioni long (tail-hedge, gamma)", "CLAUDE.md 26/06 + 22/08",
|
|
"'opzione BTC min $5.968 >> $600'", "FALSIFICATO 23/08 (§46: comprare costa il PREMIO, $1,79-7,93)",
|
|
"SI — gamma scalp Sharpe -3..-6 ogni anno; tail-hedge maxDD sale in 162/162 celle"),
|
|
("Albimarini double-diagonal", "CLAUDE.md 02/07",
|
|
"(nessun muro di taglia)", "n/d — fee-negativa a QUALSIASI size",
|
|
"SI — fee 8 gambe = 194-221% del theta; celle 0-perdite = implausible_sharpe"),
|
|
("Tranching TP01 K=2/4", "CLAUDE.md 02/07",
|
|
"'delta ~$1-2 < min-order -> K=1'; rivalutare a >=5-10k",
|
|
"FALSIFICATO 23/08 (§47: build_book_order banda la posizione NETTA)",
|
|
"SI — dSharpe n.s.; e' riduzione della VARIANZA DELLA STIMA, non ritorno"),
|
|
("BIN-FREQ (size per-trade SKH01)", "CLAUDE.md 22/08",
|
|
"riapertura = SOGLIA DI CAPITALE $5.600 (cella) / $53.000 (famiglia)",
|
|
"VIVO — e' l'unico con una soglia di capitale come condizione DICHIARATA",
|
|
"SI — mediana di famiglia 6% della cella, 54% celle positive; weights_tilt_null FAIL"),
|
|
("XS01-D (11 gambe Deribit)", "RESULTS §50/§56 23/08",
|
|
"'aspettare 2-3 listing'", "n/d — non e' capitale, e' VENUE",
|
|
"SI — IC -0,027 (t -0,88) su quelle 11; DSR 0,158 FAIL"),
|
|
("GTAA01 (6 ETF)", "CLAUDE.md 25-26/07",
|
|
"GTAA_MIN_CAPITAL $3.000", "n/d — sopra c'e' il PRIIPs: ordine RIFIUTATO sul conto reale",
|
|
"no — regge come ricerca (LOO: unico positivo nel 100% delle estrazioni)"),
|
|
("XS01 espanso (52 -> 51 HL)", "CLAUDE.md 19-20/06",
|
|
"(nessun muro di taglia)", "n/d",
|
|
"SI — il long-tail DILUISCE il momentum relativo; peggiore a ogni variante"),
|
|
("IVOL (idio-vol cross-sectional)", "CLAUDE.md 29/06",
|
|
"STAT-MODE", "MAI MISURATO (lead mai ripreso)",
|
|
"SI — nato come LEAD debole nello stesso sweep in cui XSEC-V2 fa DSR 0,13"),
|
|
("LOWVOL 19-major", "CLAUDE.md 29/06",
|
|
"STAT-MODE", "n/d",
|
|
"SI — deflated-Sharpe 0,13 + storia 2,5a"),
|
|
]
|
|
|
|
|
|
def sezione1() -> None:
|
|
sez("1) IL CENSIMENTO — tutto cio' che il progetto ha archiviato CITANDO LA TAGLIA")
|
|
print("""
|
|
Regola di lettura: la colonna che decide non e' 'stato del muro' ma l'ULTIMA. Un muro di taglia
|
|
che cade libera un candidato solo se non c'era anche un rifiuto sull'edge — e i gate sull'edge
|
|
NON dipendono dal capitale (sez. 5).
|
|
""")
|
|
print(" " + "-" * 98)
|
|
n_edge = 0
|
|
for nome, fonte, muro, stato, edge in CENSIMENTO:
|
|
n_edge += edge.startswith("SI")
|
|
print(f" {nome:<32}[{fonte}]")
|
|
print(f" {'':<32}muro pubblicato : {muro}")
|
|
print(f" {'':<32}muro OGGI : {stato}")
|
|
print(f" {'':<32}EDGE : {edge}")
|
|
print(" " + "-" * 98)
|
|
n = len(CENSIMENTO)
|
|
print(f"\n VOCI: {n} con rifiuto ANCHE sull'edge: {n_edge}/{n} = {100*n_edge/n:.0f}%")
|
|
falsi = sum(1 for _, _, _, s, _ in CENSIMENTO if s.startswith("FALSIFICATO"))
|
|
mai = sum(1 for _, _, _, s, _ in CENSIMENTO if s.startswith("MAI"))
|
|
print(f" muri di taglia FALSIFICATI (tutti dall'ondata 22-23/08): {falsi}/{n}")
|
|
print(f" muri di taglia MAI MISURATI (li misuro qui dove possibile): {mai}/{n}")
|
|
print(f"\n A1 {'CONFERMATA' if n_edge / n > 0.5 else 'REFUTATA'}: la taglia e' l'unico motivo "
|
|
f"in {n - n_edge}/{n} voci.")
|
|
|
|
|
|
# =============================================================================================
|
|
# SEZ. 2-3 — LE CURVE DI HAIRCUT
|
|
# =============================================================================================
|
|
def carica_candidati():
|
|
"""Ricostruisce i pesi dei 3 candidati multi-gamba REALMENTE misurabili. Nessuna
|
|
reimplementazione: si importano il sleeve di produzione e il monitor live."""
|
|
from r0822_hl_exec import sim_libro, xs01_pesi # noqa: F401
|
|
out = {}
|
|
|
|
W, dret, C, cols, dif = xs01_pesi(0)
|
|
print(f" XS01 (19 gambe HL): identita' vs sleeves._xsec_returns max|dif| = {dif}")
|
|
assert dif == 0.0, "XS01 non replica il sleeve di produzione"
|
|
out["XS01 (19g, HL)"] = dict(W=W, R=dret, hedge=None, idx=C.index,
|
|
fee=HL_FEE_SIDE, floor=HL_MIN_ORDER, legs=19, venue="HL")
|
|
|
|
from scripts.live.paper_xsr import FEE_LEG, build_panel
|
|
ts, idx, Wx, Rx, rb, colx = build_panel()
|
|
print(f" XSR01 (50 gambe HL): pannello dal MONITOR di produzione, {Wx.shape[1]} gambe, "
|
|
f"fee monitor {FEE_LEG*1e4:.1f} bps")
|
|
out["XSR01 (50g, HL)"] = dict(W=Wx, R=Rx, hedge=rb, idx=idx,
|
|
fee=FEE_LEG, floor=HL_MIN_ORDER, legs=50, venue="HL")
|
|
|
|
import r0725_statarb_multi as SM
|
|
base = SM.load_hl(SM.BASE)
|
|
pc, rc = {}, {}
|
|
for sym in SM.universe():
|
|
try:
|
|
tgt = SM.load_hl(sym)
|
|
except FileNotFoundError:
|
|
continue
|
|
ix = base.index.intersection(tgt.index)
|
|
if len(ix) < SM.MIN_BARS:
|
|
continue
|
|
p, _ = SM.signal(base[ix], tgt[ix])
|
|
pc[sym] = pd.Series(p, index=ix)
|
|
rc[sym] = tgt[ix].pct_change().fillna(0.0)
|
|
P = pd.concat(pc, axis=1, sort=True).sort_index().fillna(0.0)
|
|
Rr = pd.concat(rc, axis=1, sort=True).sort_index().reindex(P.index).fillna(0.0)[P.columns]
|
|
k = P.shape[1]
|
|
print(f" STATARB-MULTI: {k} coppie = {k+1} gambe (meccanismo CONGELATO W=45 sgn=+1, "
|
|
f"da r0725_statarb_multi)")
|
|
out["STATARB-MULTI (51g, HL)"] = dict(
|
|
W=(P / k).to_numpy(float), R=Rr.to_numpy(float),
|
|
hedge=base.pct_change().fillna(0.0).reindex(P.index).fillna(0.0).to_numpy(float),
|
|
idx=P.index, fee=SM.FEE_LEG, floor=HL_MIN_ORDER, legs=k + 1, venue="HL")
|
|
return out
|
|
|
|
|
|
def curva(cand: dict, caps, floor=None) -> tuple[dict, dict, dict]:
|
|
from r0822_hl_exec import sim_libro
|
|
fl = cand["floor"] if floor is None else floor
|
|
sh, noz, nets = {}, {}, {}
|
|
for c in caps:
|
|
s = sim_libro(cand["W"], cand["R"], cand["hedge"], c, fl, None, None, cand["fee"])
|
|
m = met(s["net"], cand["idx"])
|
|
sh[c] = m["sharpe"]
|
|
noz[c] = s["pct_nozionale"]
|
|
nets[c] = s["net"]
|
|
s = sim_libro(cand["W"], cand["R"], cand["hedge"], 1e12, None, None, None, cand["fee"])
|
|
nets["MOD"] = s["net"]
|
|
return sh, noz, nets
|
|
|
|
|
|
def sezione23(CAND: dict) -> dict:
|
|
sez("2) CONTROLLO POSITIVO — XS01, il muro gia' caduto il 22/08")
|
|
print("""
|
|
Se il mio codice non ritrova haircut ~0 a $200-600 su XS01, e' rotto e il resto non vale niente.
|
|
(HL-EXEC 22/08: "haircut ~0 gia' a $200-600 allocati; a $600 lo Sharpe passa 1,33 -> 1,30".)""")
|
|
sh, noz, nets = curva(CAND["XS01 (19g, HL)"], GRIGLIA_DENSA)
|
|
shm = met(nets["MOD"], CAND["XS01 (19g, HL)"]["idx"])["sharpe"]
|
|
print(f"\n {'capitale ALLOCATO':>19}{'Sharpe':>9}{'haircut':>10}{'% nozionale eseguito':>23}")
|
|
for c in GRIGLIA_DENSA:
|
|
print(f" {c:>19,.0f}{sh[c]:>9.3f}{shm - sh[c]:>10.3f}{noz[c]:>22.1f}%")
|
|
print(f" {'MODELLATA':>19}{shm:>9.3f}{0.0:>10.3f}{100.0:>22.1f}%")
|
|
ok = abs(sh[200.0] - shm) <= 0.05 and abs(sh[635.0] - shm) <= 0.05
|
|
print(f"\n A2 {'CONFERMATA' if ok else 'REFUTATA — CODICE ROTTO, MI FERMO'}: "
|
|
f"haircut a $200 = {shm - sh[200.0]:+.3f}, a $635 = {shm - sh[635.0]:+.3f}.")
|
|
assert ok, "controllo positivo fallito"
|
|
for tol in (0.10, 0.05):
|
|
cs = c_stabilita(sh, shm, tol)
|
|
print(f" C* a tolleranza {tol:.2f} di Sharpe = "
|
|
f"{('$%s' % f'{cs:,.0f}') if cs else '>200k'} di capitale ALLOCATO.")
|
|
print(f"""
|
|
PRECISAZIONE che il numero pubblicato non porta con se': i "$200-600" sono capitale ALLOCATO
|
|
AL SLEEVE. A peso di libro 15% servono ${200/0.15:,.0f}-${635/0.15:,.0f} di LIBRO per stare li'; a un
|
|
libro da $635 il sleeve vede $95 e l'haircut e' {shm - sh[95.0]:+.3f} — non zero.""")
|
|
|
|
sez("3) LE CURVE — i tre candidati multi-gamba, ai capitali del mandato")
|
|
print(f"\n {'candidato':<26}{'gambe':>6}{'venue':>7}" +
|
|
"".join(f"{('$%s' % f'{c:,.0f}'):>12}" for c in CAPITALI) +
|
|
f"{'MODELL.':>10}{'C*':>12}")
|
|
print(" " + "-" * 110)
|
|
res = {}
|
|
for nome, cand in CAND.items():
|
|
shd, nozd, netsd = curva(cand, GRIGLIA_DENSA)
|
|
shm = met(netsd["MOD"], cand["idx"])["sharpe"]
|
|
cs = c_stabilita(shd, shm)
|
|
res[nome] = dict(sh=shd, noz=nozd, nets=netsd, sh_mod=shm, cstar=cs, cand=cand)
|
|
print(f" {nome:<26}{cand['legs']:>6}{cand['venue']:>7}" +
|
|
"".join(f"{shd[c]:>12.3f}" for c in CAPITALI) +
|
|
f"{shm:>10.3f}" + (f"{cs:>12,.0f}" if cs else f"{'>200k':>12}"))
|
|
print(" " + "-" * 110)
|
|
sub("3a) la curva DENSA (Sharpe sopra, % di nozionale eseguito sotto)")
|
|
print(f" {'capitale':>10}" + "".join(f"{nm.split(' (')[0]:>13}" for nm in res))
|
|
for c in GRIGLIA_DENSA:
|
|
print(f" {c:>10,.0f}" + "".join(f"{res[nm]['sh'][c]:>13.3f}" for nm in res))
|
|
print(f" {'':>10}" + "".join(f"{res[nm]['noz'][c]:>12.1f}%" for nm in res))
|
|
print(f" {'MODELLATA':>10}" + "".join(f"{res[nm]['sh_mod']:>13.3f}" for nm in res))
|
|
print(f"\n C* per tolleranza:")
|
|
for tol in (0.10, 0.05):
|
|
row = []
|
|
for nm in res:
|
|
cs = c_stabilita(res[nm]["sh"], res[nm]["sh_mod"], tol)
|
|
row.append(("$%s" % f"{cs:,.0f}") if cs else ">200k")
|
|
print(f" tol {tol:.2f}: " + " ".join(f"{nm.split(' (')[0]}={v}" for nm, v in zip(res, row)))
|
|
|
|
sub("3b) LA TRAPPOLA — l'haircut da solo NON rileva un muro di taglia")
|
|
print("""
|
|
`altlib.eval_weights_smallcap` riporta `sharpe_haircut = modellata - reale`. Su un candidato
|
|
multi-gamba, sotto la soglia di eseguibilita' quel numero PUO' ESSERE NEGATIVO: il pavimento
|
|
taglia quasi tutto il nozionale e cio' che resta e' una strategia DIVERSA, piccola e casuale.
|
|
Letto da solo, un haircut negativo dice "eseguibile, anzi meglio". E' esattamente al rovescio.""")
|
|
print(f"\n {'candidato':<26}{'cap':>10}{'Sharpe':>9}{'haircut':>10}{'% noz':>9} lettura")
|
|
for nome, r in res.items():
|
|
for c in (635.0, 5_000.0):
|
|
h = r["sh_mod"] - r["sh"][c]
|
|
deg = r["noz"][c] < 50.0
|
|
print(f" {nome:<26}{c:>10,.0f}{r['sh'][c]:>9.3f}{h:>10.3f}{r['noz'][c]:>8.1f}% "
|
|
f"{'DEGENERE (il numero non descrive la strategia)' if deg else 'eseguibile'}")
|
|
st = res["STATARB-MULTI (51g, HL)"]
|
|
print(f"""
|
|
Il caso limpido: STATARB-MULTI a $635 fa Sharpe {st['sh'][635.0]:.3f} contro
|
|
{st['sh_mod']:.3f} modellata -> haircut {st['sh_mod'] - st['sh'][635.0]:+.3f}, cioe' NEGATIVO,
|
|
con il {st['noz'][635.0]:.1f}% del nozionale eseguito. Un gate scritto su `sharpe_haircut <= X`
|
|
lo avrebbe promosso.""")
|
|
|
|
sub("3c) A6 — monotonia dell'haircut nel capitale")
|
|
for nome, r in res.items():
|
|
seq = [r["sh"][c] for c in GRIGLIA_DENSA]
|
|
d = np.diff(seq)
|
|
mono = bool((d >= -1e-9).all())
|
|
print(f" {nome:<26} monotono crescente in C? {'SI' if mono else 'NO'} "
|
|
f"(peggior passo indietro {d.min():+.3f}; max sopra la modellata "
|
|
f"{max(seq) - r['sh_mod']:+.3f})")
|
|
print("""
|
|
A6 REFUTATA su 3/3. Sotto C* lo Sharpe non e' una misura della strategia: e' una misura di
|
|
QUALI gambe il pavimento ha lasciato passare. Il segno del passo e' rumore.""")
|
|
return res
|
|
|
|
|
|
def sezione3d(CAND: dict) -> None:
|
|
sub("3d) IL PAVIMENTO DEL VENUE SBAGLIATO — `scripts/live/paper_xsr.MIN_ORDER = 5.0`")
|
|
print("""
|
|
XSR01 gira su HYPERLIQUID (50 alt). Il suo monitor forward — quello che alimenta il gate
|
|
pre-registrato del 2026-10-23 — usa `MIN_ORDER = 5.0`, che e' il pavimento di **DERIBIT**.
|
|
Il pavimento di Hyperliquid e' **$10** (HL-EXEC 22/08: 3.827 livelli, 0,0% sotto $10).
|
|
I due libri REAL del monitor sono $600 e $5.000: entrambi nella regione in cui il pavimento morde.""")
|
|
c = CAND["XSR01 (50g, HL)"]
|
|
sh5, noz5, _ = curva(c, GRIGLIA_DENSA, floor=5.0)
|
|
sh10, noz10, nets = curva(c, GRIGLIA_DENSA, floor=10.0)
|
|
shm = met(nets["MOD"], c["idx"])["sharpe"]
|
|
print(f"\n {'capitale':>10}{'Sh @ $5 (monitor)':>20}{'Sh @ $10 (venue)':>19}{'differenza':>13}"
|
|
f"{'% noz @$10':>12}")
|
|
for cc in (600.0, 635.0, 2_000.0, 3_000.0, 5_000.0, 20_000.0, 100_000.0):
|
|
print(f" {cc:>10,.0f}{sh5[cc]:>20.3f}{sh10[cc]:>19.3f}{sh5[cc]-sh10[cc]:>13.3f}"
|
|
f"{noz10[cc]:>11.1f}%")
|
|
d = np.array([sh5[cc] - sh10[cc] for cc in GRIGLIA_DENSA if cc <= 5_000.0])
|
|
print(f"""
|
|
Non e' "il monitor e' ottimista": nella regione che conta la differenza cambia SEGNO
|
|
(min {d.min():+.3f}, max {d.max():+.3f}) — perche' sotto C* si stanno confrontando due
|
|
sottoinsiemi casuali di gambe, non due stime della stessa cosa.
|
|
C* col pavimento vero = ${c_stabilita(sh10, shm) or float('nan'):,.0f}; col pavimento del monitor
|
|
= ${c_stabilita(sh5, shm) or float('nan'):,.0f}.
|
|
Effetto sul gate 23/10: la sua gamba di eseguibilita' e' "haircut a $5.000 <= 40%", e a $5.000 il
|
|
monitor misura con il pavimento sbagliato. NON anticipo il gate: registro che l'ingresso e' storto.
|
|
6a occorrenza dello schema `fee_watch` — un controllo puntato su una configurazione diversa da
|
|
quella che gira.""")
|
|
|
|
|
|
# =============================================================================================
|
|
# SEZ. 4 — VRP01: lotti, non haircut
|
|
# =============================================================================================
|
|
def sezione4(no_net: bool) -> None:
|
|
sez("4) VRP01 — qui il muro non e' un haircut, e' un LOTTO INDIVISIBILE")
|
|
j = get_json("https://www.deribit.com/api/v2/public/get_instruments"
|
|
"?currency=USDC&kind=option&expired=false", "usdc_opt", no_net)
|
|
idx = get_json("https://www.deribit.com/api/v2/public/get_index_price?index_name=btc_usd",
|
|
"px_btc", no_net)
|
|
idxe = get_json("https://www.deribit.com/api/v2/public/get_index_price?index_name=eth_usd",
|
|
"px_eth", no_net)
|
|
px = {"BTC": (idx or {}).get("result", {}).get("index_price"),
|
|
"ETH": (idxe or {}).get("result", {}).get("index_price")}
|
|
mins, nstr = {}, {}
|
|
for it in ((j or {}).get("result") or []):
|
|
nm = it.get("instrument_name", "")
|
|
for a in ("BTC", "ETH"):
|
|
if nm.startswith(f"{a}_USDC-") and it.get("option_type") == "put":
|
|
mins[a] = min(mins.get(a, 9e9), float(it.get("min_trade_amount", 9e9)))
|
|
nstr[a] = nstr.get(a, 0) + 1
|
|
if mins and all(px.values()):
|
|
print(" letto dal venue ORA (GET pubblica su kind=OPTION, famiglia USDC-LINEARE = quella")
|
|
print(" che un conto USDC puo' marginare; VRP01 VENDE, quindi il lotto e' il NOZIONALE"
|
|
" sottostante,")
|
|
print(" non il premio — e' la distinzione che §46 ha dovuto correggere sul lato LONG):")
|
|
for a in ("BTC", "ETH"):
|
|
print(f" {a}_USDC put ({nstr.get(a,0)} strumenti) min_trade_amount = "
|
|
f"{mins.get(a)} indice ${px[a]:,.0f} -> lotto = ${mins.get(a, 0) * px[a]:,.0f}")
|
|
lot = {a: mins[a] * px[a] for a in mins}
|
|
else:
|
|
print(" [venue non letto -> uso i valori verificati dal coordinatore il 22/08 (§50)]")
|
|
lot = {"BTC": 772.0, "ETH": 242.0}
|
|
print(f"""
|
|
Il sleeve VRP01 e' 50/50 BTC+ETH al peso di libro 12%. Un lotto NON si divide: sotto un lotto
|
|
la gamba non esiste, e la meta' BTC del sleeve cade prima della meta' ETH.""")
|
|
print(f" (memoria: $242 ETH / $772 BTC, §50 22/08 — e $1.832/$6.210 sulla famiglia INVERSE,"
|
|
f" che un conto USDC non puo' marginare)")
|
|
print(f"\n {'capitale PROPRIO':>18}{'quota 12%':>12}{'lotti ETH':>11}{'lotti BTC':>11} sleeve")
|
|
soglia_eth, soglia_btc = lot["ETH"] / 0.06, lot["BTC"] / 0.06
|
|
for c in (635.0, 2_000.0, soglia_eth, 5_000.0, 6_440.0, soglia_btc, 20_000.0, 100_000.0):
|
|
q = 0.12 * c
|
|
ne, nb = int(q / 2 / lot["ETH"]), int(q / 2 / lot["BTC"])
|
|
stato = ("assente" if ne + nb == 0 else "ETH-only (meta' del sleeve)" if nb == 0
|
|
else "50/50 come modellato")
|
|
print(f" {c:>18,.0f}{q:>12,.0f}{ne:>11}{nb:>11} {stato}")
|
|
print(f"""
|
|
A $100.000 il sleeve e' 50/50 con {int(0.06*100000/lot['ETH'])} lotti ETH e
|
|
{int(0.06*100000/lot['BTC'])} BTC: il muro di taglia di VRP01 CADE del tutto.
|
|
|
|
CORREZIONE A UN NUMERO PUBBLICATO IERI (§50 / CLAUDE.md): "1 lotto ETH copre il peso di book 12%
|
|
gia' da ~$2.000, e il BTC da ~$6.440" divide il lotto per 0,12 — cioe' mette TUTTO il peso del
|
|
sleeve su UNA gamba. VRP01 e' 50/50: la gamba ETH vede il 6% del libro, non il 12%.
|
|
Soglie vere: prima gamba (ETH) da ${soglia_eth:,.0f}; sleeve 50/50 completo da ${soglia_btc:,.0f}
|
|
— cioe' il DOPPIO di quanto pubblicato, su entrambe. Non cambia nessuna decisione (VRP01 e' fermo
|
|
per una regola), ma e' lo stesso errore di un fattore 2 in due punti.
|
|
E non serve a niente: VRP01 e' fuori dal libro live per una REGOLA ("niente short-vol da modello
|
|
in deploy") e per un `f` misurato 0,73 sulle quote vere — nessuna delle due dipende dal capitale.
|
|
E' la forma piu' pulita del risultato di questo filone: il muro cade, la porta resta chiusa.""")
|
|
|
|
|
|
# =============================================================================================
|
|
# SEZ. 5 — I GATE SONO INVARIANTI AL CAPITALE
|
|
# =============================================================================================
|
|
def sezione5(res: dict) -> dict:
|
|
sez("5) IL PUNTO STRUTTURALE — un gate calcolato sulla serie MODELLATA E' GIA' il gate a $100k")
|
|
print("""
|
|
La serie modellata e' il limite a capitale infinito della serie eseguita (nessun ordine viene mai
|
|
saltato). Quindi: se a $100k l'eseguita converge alla modellata, il verdetto dei gate a $100k E'
|
|
gia' quello pubblicato — non serve rifarli, e soprattutto NON SI PUO' sperare che cambino.
|
|
Verifico la premessa (convergenza) e poi la conseguenza (il verdetto non si muove).""")
|
|
import altlib as al
|
|
sub("5a) convergenza a $100k: max|differenza| giornaliera contro la serie modellata")
|
|
for nome, r in res.items():
|
|
d = np.abs(r["nets"][100_000.0] - r["nets"]["MOD"]).max()
|
|
print(f" {nome:<26} max|dif| = {d:.3e} dSharpe = "
|
|
f"{r['sh'][100_000.0] - r['sh_mod']:+.4f} % noz = {r['noz'][100_000.0]:.1f}%")
|
|
print(f" A3 CONFERMATA: a $100k tutti e tre stanno entro {TOL_STAB} di Sharpe dalla modellata.")
|
|
|
|
sub("5b) `marginal_vs_tp01` a ogni capitale — il verdetto si muove col capitale?")
|
|
print(f"\n {'candidato':<26}" + "".join(f"{('$%s' % f'{c:,.0f}'):>13}" for c in CAPITALI)
|
|
+ f"{'MODELLATA':>13}")
|
|
ver = {}
|
|
for nome, r in res.items():
|
|
row, vv = [], []
|
|
for c in list(CAPITALI) + ["MOD"]:
|
|
s = pd.Series(r["nets"][c], index=r["cand"]["idx"])
|
|
m = al.marginal_vs_tp01(s)
|
|
v = m.get("marginal_verdict", "?")
|
|
row.append(v)
|
|
vv.append(v)
|
|
ver[nome] = vv
|
|
print(f" {nome:<26}" + "".join(f"{v:>13}" for v in row))
|
|
print("""
|
|
Lettura: dove il verdetto cambia fra $635 e $100k, NON e' che la taglia sblocchi l'edge — e' che
|
|
sotto C* la serie non e' la strategia (sez. 3b). Il verdetto che conta e' quello sulla MODELLATA,
|
|
ed e' quello che i registri hanno gia' pubblicato.""")
|
|
|
|
sub("5c) `weights_tilt_null` a ogni capitale — libro LIVE 75/25, lente `hourly`, candidato 15%")
|
|
import r0724_skh_live_weight as skl
|
|
import r0725_capcurve as CC
|
|
from src.portfolio.portfolio import combine_outer, metrics, weights_tilt_null
|
|
skh = skl.skh_book(0, "hourly")
|
|
if skh.index.tz is None:
|
|
skh.index = skh.index.tz_localize("UTC")
|
|
lo = pd.Timestamp("2019-03-14", tz="UTC")
|
|
tp = CC.tp01_realistic(635.0 * 0.75)
|
|
base = combine_outer({"TP01": tp, "SKH01": skh}, {"TP01": 0.75, "SKH01": 0.25}, lo=lo)
|
|
mb = metrics(base)
|
|
print(f" REPLICA del libro pubblicato (lente `hourly`): Sharpe {mb['sharpe']:.3f} "
|
|
f"(pubblicato 1,683) maxDD {100*mb['maxdd']:.2f}% (pubblicato 11,22%) "
|
|
f"-> {'OK' if abs(mb['sharpe']-1.683) < 0.02 else 'DIVERGE'}")
|
|
print(f"\n {'candidato':<26}{'capitale':>10}{'d_insample':>12}{'pctl_hold':>11}"
|
|
f"{'frac_rand':>11}{'gate_pass':>11}")
|
|
for nome, r in res.items():
|
|
for c in CAPITALI:
|
|
s = pd.Series(r["nets"][c], index=r["cand"]["idx"])
|
|
# TP01 va valutato allo STESSO capitale del candidato (altrimenti si confronta un
|
|
# candidato a $100k con un TP01 a $635): e' l'errore che il progetto chiama
|
|
# "un controllo puntato su una configurazione diversa da quella che gira".
|
|
tpc = CC.tp01_realistic(c * 0.6375)
|
|
g = weights_tilt_null({"TP01": tpc, "SKH01": skh, "C": s},
|
|
{"TP01": 0.75, "SKH01": 0.25, "C": 0.0},
|
|
{"TP01": 0.6375, "SKH01": 0.2125, "C": 0.15},
|
|
n=300, seed=SEED)
|
|
print(f" {nome:<26}{c:>10,.0f}{g['delta_insample']:>12.4f}{g['pctl_hold']:>11.1f}"
|
|
f"{g['frac_random_beat_hold']:>11.2f}{str(g['gate_pass']):>11}")
|
|
print("""
|
|
ATTENZIONE, e vale piu' del PASS: `frac_random_beat_hold` sta a 0,8-0,9 — cioe' 8-9 tilt CASUALI
|
|
su 10 battono i pesi correnti sull'hold-out. Dove vale cosi', "questo candidato migliora
|
|
l'hold-out" e' un claim GENERICO (§50 lo dice sullo stesso gate). E il libro di riferimento non e'
|
|
quello su cui XSR01 fu bocciato: CLAUDE.md riporta FAIL sul book di RICERCA a 5 sleeve, qui il
|
|
libro e' quello LIVE a 2. Due libri, due verdetti — si dichiara quale.""")
|
|
return ver
|
|
|
|
|
|
# =============================================================================================
|
|
# SEZ. 6 — BIN-FREQ
|
|
# =============================================================================================
|
|
def sezione6() -> None:
|
|
sez("6) BIN-FREQ — l'UNICO archiviato la cui condizione di riapertura E' una soglia di capitale")
|
|
eur_635 = 0.0113 # RESULTS §25: taglia misurata a $635
|
|
rapporto_fam = 0.007 / 0.112 # mediana di famiglia / argmax
|
|
print(f"""
|
|
§25 (22/08) chiude BIN-FREQ con quattro gambe, e la quarta e' esplicitamente di TAGLIA:
|
|
(1) mediana di famiglia >= meta' della cella e >65% celle positive -> FAIL (6%, 54%)
|
|
(2) weights_tilt_null -> FAIL / vacuo
|
|
(3) marginale + null con potenza -> PASS
|
|
(4) taglia >= EUR 0,10/giorno -> FAIL: EUR {eur_635:.4f}/g a $635
|
|
La (4) e' lineare nel capitale. Le altre tre NON lo sono: sono statistiche sulla serie.""")
|
|
print(f"\n {'capitale':>10}{'EUR/g (cella)':>16}{'EUR/g (famiglia)':>19}{'gate (4)?':>12}")
|
|
for c in (635.0, 5_600.0, 20_000.0, 53_000.0, 100_000.0, 200_000.0):
|
|
ec = eur_635 * c / 635.0
|
|
ef = ec * rapporto_fam
|
|
print(f" {c:>10,.0f}{ec:>16.4f}{ef:>19.4f}{('PASS' if ef >= 0.10 else 'PASS(cella)' if ec >= 0.10 else 'FAIL'):>12}")
|
|
print(f"""
|
|
A $100.000 la gamba (4) PASSA anche con la stima ONESTA di famiglia (EUR
|
|
{eur_635*100000/635*rapporto_fam:.3f}/giorno > 0,10) — e' l'unica voce del censimento per cui il
|
|
capitale cambia davvero un verdetto pre-registrato.
|
|
MA le gambe (1) e (2) restano FAIL, e sono invarianti al capitale: la mediana di famiglia e' il
|
|
6% dell'argmax e il 54% delle celle e' positivo comunque si finanzi il conto.
|
|
-> BIN-FREQ a $100k: 1 gate su 4 si sblocca, 2 restano chiusi. RESTA UN LEAD.""")
|
|
|
|
|
|
# =============================================================================================
|
|
# SEZ. 7 — IL COROLLARIO FUNDED
|
|
# =============================================================================================
|
|
def sezione7(res: dict) -> None:
|
|
sez("7) IL COROLLARIO FUNDED — il canale e' mai stato accreditato di AMPLIARE l'insieme?")
|
|
print("""
|
|
Distinzione obbligatoria: fin qui la colonna 'capitale' era CAPITALE PROPRIO. Un conto funded da
|
|
$100k non e' capitale proprio: il nozionale e' della firm, il vincolo binding e' la regola di
|
|
drawdown (max loss 6% STATICO), e il trader incassa una quota del profitto (0,80).
|
|
Domanda: quali voci del censimento diventano ESEGUIBILI a $100k di nozionale funded e non lo sono
|
|
a $635 di capitale proprio? E quanto vale, in Sharpe di libro e in EUR/giorno?""")
|
|
import r0724_skh_live_weight as skl
|
|
import r0725_capcurve as CC
|
|
from src.portfolio.portfolio import combine_outer, metrics
|
|
skh = skl.skh_book(0, "hourly")
|
|
if skh.index.tz is None:
|
|
skh.index = skh.index.tz_localize("UTC")
|
|
lo = pd.Timestamp("2019-03-14", tz="UTC")
|
|
tp = CC.tp01_realistic(100_000.0 * 0.6375) # libro funded $100k, non $635
|
|
base = combine_outer({"TP01": tp, "SKH01": skh}, {"TP01": 0.75, "SKH01": 0.25}, lo=lo)
|
|
|
|
sub("7a) chi si aggiunge davvero (capitale PROPRIO $635 -> nozionale FUNDED $100k)")
|
|
print(f" {'voce':<26}{'C* (proprio)':>14}{'a $635?':>10}{'a $100k?':>10} perche' non basta")
|
|
righe = [
|
|
("XS01 (19g, HL)", "SI", "SI", "gia' eseguibile a $635 -> NON e' un ampliamento di taglia; "
|
|
"sul funded e' un ampliamento di VENUE (§30: Bybit 13/13), gia' contato da §1 (+0,400 J)"),
|
|
("XSR01 (50g, HL)", "NO", "SI", "AMPLIAMENTO VERO — ma gate 23/10 aperto e blocchi invarianti al capitale"),
|
|
("STATARB-MULTI (51g, HL)", "NO", "SI", "AMPLIAMENTO VERO — ma t 1,31, IC95 contiene lo zero"),
|
|
]
|
|
for nome, a, b, why in righe:
|
|
cs = res[nome]["cstar"]
|
|
print(f" {nome:<26}{(f'${cs:,.0f}' if cs else '>200k'):>14}{a:>10}{b:>10}")
|
|
print(f" {'':<26}{why}")
|
|
print("""
|
|
A5: l'insieme AMMISSIBILE non si amplia (entrambi hanno un blocco che il capitale non tocca).
|
|
L'insieme ESEGUIBILE si amplia di due voci, ed e' un fatto nuovo: nessun filone del progetto
|
|
aveva mai misurato che a $100k STATARB-MULTI e XSR01 girano al 99,6% del nozionale.""")
|
|
|
|
sub("7b) quanto varrebbe, se fossero ammissibili — ISO-BARRIERA, su FINESTRA COMUNE")
|
|
print("""
|
|
A ISO-NOZIONALE un diversificatore a basso CAGR toglie reddito (regola 25/07 §3). Su un conto
|
|
funded la lente giusta e' la BARRIERA: si cerca la leva k che porta il maxDD alla regola dei -6%
|
|
e si legge il drift a quella leva. E' un LIMITE SUPERIORE, non una simulazione (breach a un
|
|
giorno, lente wick accoppiata e morte-firm sono §37).
|
|
FINESTRA COMUNE 2024+ obbligatoria: i candidati esistono solo da li'. Con la finestra piena
|
|
`combine_outer` rinormalizza per riga, il maxDD del libro cade PRIMA del 2024 e le tre righe
|
|
danno la STESSA k -> il confronto non misura niente (errore commesso e corretto in sessione).""")
|
|
lo2 = pd.Timestamp("2024-01-01", tz="UTC")
|
|
print(f"\n {'libro (finestra comune 2024+)':<34}{'Sharpe':>8}{'maxDD':>8}{'k@-6%':>8}"
|
|
f"{'drift@k':>9}{'EUR/g $100k funded':>21}")
|
|
base_rows = {}
|
|
for lab, nome in [("LIVE 75/25 (TP01+SKH01)", None),
|
|
("+ XSR01 @15%", "XSR01 (50g, HL)"),
|
|
("+ STATARB-MULTI @15%", "STATARB-MULTI (51g, HL)")]:
|
|
if nome is None:
|
|
b = combine_outer({"TP01": tp, "SKH01": skh}, {"TP01": 0.75, "SKH01": 0.25}, lo=lo2)
|
|
else:
|
|
s_ = pd.Series(res[nome]["nets"][100_000.0], index=res[nome]["cand"]["idx"])
|
|
b = combine_outer({"TP01": tp, "SKH01": skh, "C": s_},
|
|
{"TP01": 0.6375, "SKH01": 0.2125, "C": 0.15}, lo=lo2)
|
|
b = b[b.index >= lo2]
|
|
r = b.values
|
|
m = metrics(b)
|
|
k = k_barriera(r)
|
|
g = cagr_k(r, k, b.index)
|
|
eur = SPLIT_FUNDED * g * 100_000.0 / 365.0 / EURUSD
|
|
base_rows[lab] = eur
|
|
print(f" {lab:<34}{m['sharpe']:>8.3f}{100*m['maxdd']:>7.1f}%{k:>8.2f}{100*g:>8.1f}%"
|
|
f"{eur:>21.2f}")
|
|
d1 = base_rows["+ XSR01 @15%"] - base_rows["LIVE 75/25 (TP01+SKH01)"]
|
|
d2 = base_rows["+ STATARB-MULTI @15%"] - base_rows["LIVE 75/25 (TP01+SKH01)"]
|
|
print(f"""
|
|
L'AMPLIAMENTO VALE, come LIMITE SUPERIORE: XSR01 {d1:+.2f} EUR/giorno, STATARB-MULTI {d2:+.2f}
|
|
EUR/giorno su $100.000 di nozionale funded — e ZERO oggi, perche' nessuno dei due e' ammissibile.
|
|
⚠️ La finestra 2024+ NON contiene un sinistro: il maxDD del libro li' e' molto piu' piccolo che
|
|
sulla storia piena, quindi k e' gonfiata per TUTTE le righe. Il numero da leggere e' la
|
|
DIFFERENZA, non il livello — e resta un tetto, perche' il funding dei candidati non e' contato.
|
|
|
|
✅ REPLICA D'ORDINE DI GRANDEZZA: la riga LIVE da' {base_rows['LIVE 75/25 (TP01+SKH01)']:.0f}
|
|
EUR/giorno su un conto funded da $100k, contro i "~EUR 15-30/g max" che il progetto pubblica dal
|
|
25/07 per UN conto con cap $200k/trader. Macchineria diversa (qui una k iso-barriera, li' un MC
|
|
con breach), stesso ordine di grandezza.
|
|
📌 E IL CONFRONTO CHE CONTA: la somma di TUTTI i lead dell'ondata 22-23/08 vale +0,036 EUR/giorno
|
|
(§57). Il solo AMPLIAMENTO DELL'INSIEME su un conto funded vale, come tetto,
|
|
{d1:+.2f} / {d2:+.2f} EUR/giorno — cioe' 40-100x. Non e' un argomento per aprire un funded: e' un
|
|
argomento per smettere di valutare il canale funded SOLO sul nozionale.""")
|
|
|
|
|
|
# =============================================================================================
|
|
# SEZ. 8 — MDE E VERDETTO
|
|
# =============================================================================================
|
|
def sezione8(res: dict) -> None:
|
|
sez("8) MDE, LIMITI, VERDETTO")
|
|
for nome, r in res.items():
|
|
idx = r["cand"]["idx"]
|
|
yrs = (idx[-1] - idx[0]) / pd.Timedelta("365.25D")
|
|
print(f" {nome:<26} {yrs:.2f} anni -> SE(Sharpe) ~ {1/np.sqrt(yrs):.2f}, "
|
|
f"MDE(95%) ~ {1.96/np.sqrt(yrs):.2f} di Sharpe")
|
|
print("""
|
|
Cio' che questo filone PUO' dire con questi dati: dove sta la soglia di eseguibilita' (e' una
|
|
misura deterministica sul pavimento del venue, non una stima statistica, e non ha MDE).
|
|
Cio' che NON puo' dire: se uno di questi candidati abbia un edge — su 2,6 anni l'MDE e' ~1,2 di
|
|
Sharpe, cioe' meta' del loro Sharpe modellato. Non lo dico: lo dicono i gate gia' pubblicati.
|
|
|
|
LIMITI DICHIARATI
|
|
- I 3 candidati misurati vivono su HYPERLIQUID. Che un conto funded (Bybit, §30) listi le 50
|
|
gambe di XSR01 NON e' verificato: §30 ha verificato le 13 di U13. E' una GET pubblica: costa
|
|
zero e va fatta prima di credere alla sez. 7.
|
|
- `sim_libro` applica il pavimento per gamba e non modella il lotto minimo per-strumento HL
|
|
(lotti $0,01-7,72 sui 19 major, sotto il pavimento $10 -> non binding). Sui 50 alt della coda
|
|
non e' verificato strumento per strumento.
|
|
- Lo slippage NON e' nel conto: HL-EXEC lo ha refutato come rischio #1 con margine 21x, ma su
|
|
UNA fotografia di 172 secondi, e SLIP-AUDIT ha scritto che la partecipazione scala lineare
|
|
-> a $100k la misura di attrito a $635 NON si estrapola. La sez. 7 va riletta a quella taglia.
|
|
- Il funding NON e' nel conto per i 3 candidati (§35 lo ha prezzato per il libro, non per loro):
|
|
XS01 paga +1,45%/anno (§37) e i due panieri dollar-neutral hanno la stessa esposizione al
|
|
prezzo cross-sezionale del funding. Ogni EUR/giorno della sez. 7 e' quindi OTTIMISTA.""")
|
|
|
|
|
|
def main() -> None:
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument("--no-net", action="store_true")
|
|
a = ap.parse_args()
|
|
t0 = time.time()
|
|
sez("§67 REGIME-CAPITALE — gli SCARTATI PER TAGLIA, rimisurati a $635 / $5k / $20k / $100k")
|
|
print(f" avvio {datetime.now(timezone.utc):%Y-%m-%d %H:%M:%S} UTC seed {SEED}")
|
|
sola_lettura()
|
|
print(f" parametri venue: Deribit min ${DER_MIN_ORDER:.0f} / {DER_FEE_SIDE*1e4:.2f} bps ; "
|
|
f"Hyperliquid min ${HL_MIN_ORDER:.0f} / {HL_FEE_SIDE*1e4:.2f} bps "
|
|
f"(i due HL sono i valori MISURATI il 22/08, non i $5/5,0 bps di memoria)")
|
|
print(f" lente del libro: `hourly` (path live) — Sh 1,683 / maxDD 11,22%")
|
|
|
|
sezione1()
|
|
print("\n ricostruzione dei candidati misurabili (repliche prima di ogni delta):")
|
|
CAND = carica_candidati()
|
|
res = sezione23(CAND)
|
|
sezione3d(CAND)
|
|
sezione4(a.no_net)
|
|
sezione5(res)
|
|
sezione6()
|
|
sezione7(res)
|
|
sezione8(res)
|
|
|
|
sez("VERDETTO")
|
|
n = len(CENSIMENTO)
|
|
n_edge = sum(1 for *_, e in CENSIMENTO if e.startswith("SI"))
|
|
nf = sum(1 for _, _, _, st, _ in CENSIMENTO if st.startswith("FALSIFICATO"))
|
|
print(f"""
|
|
`A $100.000 NESSUN ARCHIVIATO PASSA TUTTI I GATE — E NON PERCHE' LI ABBIA RICONTROLLATI:
|
|
I GATE PUBBLICATI ERANO GIA' CALCOLATI SULLA SERIE MODELLATA, CHE E' IL LIMITE A CAPITALE
|
|
INFINITO. IL MURO DI TAGLIA NON NASCONDEVA NIENTE.`
|
|
|
|
Il censimento ha {n} voci; {n_edge} hanno ANCHE un rifiuto sull'edge. Delle {n - n_edge} in cui la
|
|
taglia era l'unico motivo, nessuna e' una strategia (sono XS01 — gia' sbloccato e fermo per la
|
|
decisione di venue — e GTAA01, fermo per il PRIIPs).
|
|
Muri di taglia caduti: {nf} su {n}, tutti nell'ondata 22-23/08, tutti nella stessa direzione.
|
|
Muri di taglia MISURATI QUI per la prima volta: STATARB-MULTI (C* $2.000-$7.500 secondo la
|
|
tolleranza) e la curva completa di XSR01 col pavimento del venue GIUSTO (C* $15.000-$20.000,
|
|
contro i ~$3.000 che HL-EXEC riportava con un criterio piu' permissivo).
|
|
|
|
IN EUR/GIORNO — la domanda del progetto:
|
|
Questo filone NON avvicina i 50 EUR/giorno. Non aggiunge NIENTE al conto di oggi: tutto cio'
|
|
che sblocca vive a capitali che il conto non ha, e a quei capitali resta bloccato sull'edge.
|
|
Il suo contributo e' +0,000 EUR/giorno.
|
|
Cio' che TOGLIE e' reale ma non e' reddito: il gate XSR01 del 23/10 legge un monitor tarato sul
|
|
pavimento del venue sbagliato.
|
|
Per confronto, sullo stesso motore: EUR 250 -> 500 al mese porta P(20 anni) dal 14% all'85%.
|
|
|
|
Libro, pesi, cron, config INVARIATI. Nessun ordine.
|
|
durata {time.time()-t0:.0f} s""")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|