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>
732 lines
42 KiB
Python
732 lines
42 KiB
Python
"""r0823b_scettico_conversione — SCETTICO del CANALE FUNDED (2026-08-23), filone §69.
|
|
|
|
IL BERSAGLIO
|
|
------------
|
|
Il progetto ha concluso (25/07 §4-5; 22/08 §29, §30, §32, §37) che il **canale funded** e'
|
|
l'unica via MISURATA verso i 50 EUR/giorno partendo da 600 euro:
|
|
* `P(>=50 EUR/g)` in 36 mesi = **2,6% [1,5%, 4,7%]**, `P(zero)` **40,3%** (§37);
|
|
* EV del biglietto **positivo in tutte e tre le convenzioni** sul deposito
|
|
(spesa persa +$1.615 · rimborso al pass +$1.919 · rimborso al primo payout +$1.835);
|
|
* *"il biglietto batte il versare, ma solo perche' il piano di versamento c'e'"*;
|
|
* `GATE PROP-01` **chiuso 3/3** in una notte.
|
|
|
|
Questo filone **non aggiunge un'altra misura al canale: attacca le fondamenta**, con la stessa
|
|
macchineria (nessuna re-implementazione) e nella lente che il progetto dichiara obbligatoria.
|
|
|
|
I QUATTRO ATTACCHI
|
|
------------------
|
|
1. **IL PILASTRO STATISTICO.** Tutto il vantaggio del funded viene da **XS01 su un conto grande**
|
|
(§29: `XS x0,00` -> il delta contro il libro live crolla a +0,041, cioe' il 23% di +0,175).
|
|
Ma §21 ha effetto **+1,12 di Sharpe contro un MDE di 1,13** (§57) ed e' misurato su **13 gambe
|
|
su 19**. Qui l'incertezza del DRIFT di XS01 viene **propagata fino a `P(>=50/g)`**.
|
|
2. **L'ASSUNZIONE MAI STIMATA.** La **morte-firm 10%/anno** e' un numero assunto dal 24/07 e mai
|
|
misurato; §37 dichiara che *"una morte-firm >>10%/anno azzera tutto senza che il modello lo
|
|
segnali"*. Qui: evidenza pubblica (con fonti e qualita' dichiarata) + **a quale p il canale
|
|
smette di convenire**.
|
|
3. **IL CONFLITTO DI OBIETTIVI.** `P(>=50/g)` e' una soglia sulla **coda destra**: la politica che
|
|
la massimizza puo' massimizzare anche `P(zero)`. Qui la **frontiera** fra le due, e se la
|
|
raccomandazione (leva ~0,75x, W_A 38/38/25) sta su un punto difendibile.
|
|
4. **L'ASIMMETRIA DI TRATTAMENTO.** Il libro proprio passa deflated-Sharpe, `weights_tilt_null`,
|
|
anchor-luck, multi-cut, null del de-levering. Il canale funded passa **un Monte Carlo**. Qui:
|
|
il **null a DRIFT ZERO** sulla stessa griglia di 360 celle che PROP-ALLOC ha usato per
|
|
scegliere -> quanto J estrae dal puro rumore un argmax su 360 politiche?
|
|
|
|
LE ATTESE, REGISTRATE PRIMA DI MISURARE
|
|
---------------------------------------
|
|
A1. Il drift di XS01 su W-LONG ha SE >= 8%/anno (block bootstrap). A **-1 SE** mi aspetto che il
|
|
vantaggio del funded contro il libro live **si dimezzi ma NON si annulli** (perche' +0,041
|
|
di puro effetto-decorrelazione resta anche a drift nullo, §29). A **-2 SE** mi aspetto che si
|
|
annulli.
|
|
A2. `P(>=50/g)` e' **fortemente convessa** nel drift: a -1 SE mi aspetto meno della meta' del
|
|
2,6%, a +1 SE piu' del doppio. (Se la funzione fosse lineare, sarei sorpreso.)
|
|
A3. La morte-firm vera e' **PIU' ALTA** di 10%/anno. Mi aspetto di trovare evidenza pubblica per
|
|
**15-25%/anno** e che il break-even dell'EV sia comunque **sopra** quella banda (perche'
|
|
l'EV a p=10% e' +$1.615 su un biglietto da $579, cioe' un margine largo).
|
|
A4. `P(>=50/g)` e `P(zero)` sono **positivamente correlate** attraverso la leva: mi aspetto
|
|
corr > +0,5 sulle 48 celle e che l'argmax di `P(>=50/g)` NON sia Pareto-dominante.
|
|
A5. Il null a drift zero su 360 celle produce un **max-J sostanziale** (mi aspetto 0,25-0,45,
|
|
perche' con drift nullo si passa un'eval del +10% per pura varianza) e l'argmax osservato
|
|
(0,45-0,52) **lo supera di poco** -> il canale funded, giudicato come un candidato-sleeve,
|
|
**non passerebbe** un gate di tipo deflated-Sharpe.
|
|
A6. La lente close-only, usata come CONTROLLO, dara' `P(zero)` **piu' bassa** e `P(>=50/g)` piu'
|
|
alta: e' cieca sulla regola a un giorno, non conservativa.
|
|
|
|
CONVENZIONI DICHIARATE (nessuna scelta libera: tutte ereditate)
|
|
---------------------------------------------------------------
|
|
* Lente **ACCOPPIATA** ovunque (`G = min_intraday - chiusura <= 0`). Close-only solo come
|
|
controllo positivo della lente, mai come stima.
|
|
* Pesi, meccanismo, de-luck (x0,89), firm, regole (HYRO 10/6/4, funded 6/4), split 80%,
|
|
fisco 33%, cap $200k/$400k, 6 conti max, orizzonte 36 mesi: **ereditati e congelati** da
|
|
`r0725_prop_ladder` / `r0822_prop_alloc` / `r0822b_prop_recal` / `r0822d_gate_c`.
|
|
* Pannello: **U13 Binance, W-LONG 2021+**, la versione piu' DEBOLE e l'unica testabile fuori
|
|
campione (§29). Funding **dentro** (cache di `r0822d_funding`, nessuna rete).
|
|
* Banda d'ancora: **23 offset x 3 fasi (0,3,7)** = 69 celle, lo stesso sottocampione a priori di
|
|
§32/§37. La stima onesta e' la **MEDIANA**, mai l'ancora canonica.
|
|
* 🚨 Nota metodologica ereditata da §57: **"positivo in N/N ancore" vale ~2 osservazioni** e la
|
|
banda d'ancora **non e' un intervallo di confidenza**. Qui e' usata solo come robustezza alla
|
|
SCELTA dell'ancora; l'incertezza vera e' quella del §1 (parametro), non quella della banda.
|
|
* Monte Carlo: la risoluzione si dichiara in ogni sezione.
|
|
|
|
Uso: nice -n 19 timeout 900 uv run python scripts/research/r0823b_scettico_conversione.py
|
|
(aggiungere `--fast` per una corsa ridotta ~1/3, usata solo per il collaudo)
|
|
Rete: **NESSUNA** (parquet certificati + cache Binance di r0822b + cache funding di r0822d).
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import time
|
|
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"):
|
|
sys.path.insert(0, str(_p))
|
|
|
|
if "--no-net" not in sys.argv: # r0822d_funding legge USE_NET all'import
|
|
sys.argv.append("--no-net")
|
|
|
|
import r0725_prop_coupled as pc # noqa: E402 lente accoppiata (25/07)
|
|
import r0725_prop_ladder as pl # noqa: E402 regole firm + scala di conti (25/07)
|
|
import r0822_prop_alloc as PA # noqa: E402 J, bootstrap, simulatore (22/08)
|
|
import r0822b_prop_recal as PR # noqa: E402 PROP-RECAL (22/08)
|
|
import r0822b_xs01_oos as X # noqa: E402 motore XS01 + cache Binance (22/08)
|
|
import r0822c_skh_anchor_prop as AP # noqa: E402 offset di SKH01 (22/08)
|
|
import r0822d_funding as FD # noqa: E402 il funding (22/08)
|
|
import r0822d_gate_c as GC # noqa: E402 ladder col deposito, ticket a 2 stadi
|
|
from src.portfolio.sleeves import XS_UNIVERSE # noqa: E402
|
|
|
|
FD.USE_NET = False
|
|
|
|
FAST = "--fast" in sys.argv
|
|
FULL = "--full" in sys.argv # griglia d'ancora PIENA (23 offset): ~3x piu' lenta
|
|
DELUCK = PA.DELUCK # x0,89 MISURATO (26/07)
|
|
LEVS = PR.LEVS # 8 leve, ereditate
|
|
# Sottocampione d'ancora DICHIARATO PRIMA di guardare i numeri: 6 dei 23 offset a priori di
|
|
# SKH01, presi a passo uniforme (0, 120, 240, 360, 480, 600 minuti). Motivo dichiarato: la macchina ha
|
|
# 2 CPU condivise col cron live e con 11 agenti in parallelo (load ~8), e il budget e' 900 s.
|
|
# `--full` usa tutti e 23. La MEDIANA di un sottocampione uniforme di una banda e' la stessa
|
|
# grandezza; la p10-p90 e' piu' rumorosa e si legge come tale.
|
|
OFFSETS = AP.OFFSETS if FULL else tuple(AP.OFFSETS[::4])
|
|
PHASES = AP.PHASES_SUB # (0, 3, 7) — sottocampione dichiarato da §32
|
|
W_A = {"TP01": 0.38, "SKH01": 0.38, "XS01": 0.25} # la RACCOMANDAZIONE (centro §29)
|
|
W_LIVE = {"TP01": 0.75, "SKH01": 0.25} # il libro che gira davvero
|
|
W_ARGMAX = {"TP01": 0.25, "SKH01": 0.25, "XS01": 0.50} # l'argmax-J di PROP-ALLOC
|
|
EURUSD = pl.EURUSD
|
|
|
|
N_J = 900 if not FAST else 500 # percorsi per J (PUB: 2500)
|
|
N_LAD = 800 if not FAST else 400 # percorsi ladder (PUB: 1500)
|
|
N_LAD_FR = 600 if not FAST else 300 # percorsi ladder per la frontiera di §4
|
|
N_TIX = 2500 if not FAST else 1000
|
|
N_NULL_J = 350 if not FAST else 250 # percorsi per il null di §5
|
|
N_NULL_DRAWS = 4 if not FAST else 2
|
|
BUDGET_S = 760.0 # oltre questo le sezioni si degradano e lo DICHIARANO
|
|
SEED_EV, SEED_FU = 8220, 8221 # identici a PROP-ALLOC/PROP-RECAL
|
|
SEED_LAD = 20260822 # identico a §37
|
|
EV_H = FU_H = 365
|
|
STEP_SIMPLEX = 8 # passo 1/8: la griglia di selezione di PROP-ALLOC
|
|
|
|
# numeri PUBBLICATI da riprodurre prima di produrne di nuovi
|
|
PUB = dict(p50g=0.026, p50g_lo=0.015, p50g_hi=0.047, pzero=0.403, # §37
|
|
recal_j=0.453, recal_live=0.281, recal_d=0.175, # §29
|
|
ev_sunk=1615.0, ev_at_pass=1919.0, ev_at_payout=1835.0, # §37
|
|
alloc_jmax=0.738, alloc_jsharpe=0.721, alloc_jlive=0.335) # §1 PROP-ALLOC
|
|
|
|
T0 = time.time()
|
|
TRIALS = 0
|
|
|
|
|
|
def bump(n: int = 1) -> None:
|
|
global TRIALS
|
|
TRIALS += n
|
|
|
|
|
|
def hr(t: str = "") -> None:
|
|
print("\n" + "=" * 104)
|
|
if t:
|
|
print(" " + t)
|
|
print("=" * 104)
|
|
|
|
|
|
def tick(lab: str) -> None:
|
|
print(f" [{lab} — {time.time()-T0:.0f}s, {TRIALS} valutazioni]")
|
|
|
|
|
|
# =================================================================== utilita' di perturbazione
|
|
|
|
def xs_shift(xs: pd.DataFrame, d_ann: float, active_only: bool = False) -> pd.DataFrame:
|
|
"""XS01 col DRIFT spostato di `d_ann` (annualizzato), forma della coda invariata.
|
|
Convenzione identica al `dm` di `PR.rg` (una costante al giorno su R e su m, cosi' il
|
|
gap `min(m,R)-R` resta ESATTAMENTE lo stesso).
|
|
|
|
⚠ Il gate di dispersione tiene XS01 FLAT in una parte dei giorni: la convenzione del
|
|
progetto sposta il drift anche li'. `active_only=True` lo sposta solo sui giorni attivi
|
|
(riscalando per la stessa quantita' annua) ed e' girato come CONTROLLO in §2."""
|
|
out = xs.copy()
|
|
r = out["R"].values.astype(float)
|
|
if active_only:
|
|
act = r != 0.0
|
|
d = np.where(act, d_ann / 365.0 / max(act.mean(), 1e-9), 0.0)
|
|
else:
|
|
d = np.full(len(r), d_ann / 365.0)
|
|
out["R"] = r + d
|
|
out["m"] = out["m"].values.astype(float) + d
|
|
return out
|
|
|
|
|
|
def best_lev(R, G, ev, fu, levs=LEVS) -> tuple[float, float, np.ndarray, dict]:
|
|
jb, lb, vb, ob = -1.0, None, None, None
|
|
for lv in levs:
|
|
o = PR.obj_rg(R, G, lv, ev, fu, factor=DELUCK)
|
|
if o["J"] > jb:
|
|
jb, lb, vb, ob = o["J"], lv, o["jvec"], o
|
|
bump(len(levs))
|
|
return jb, lb, vb, ob
|
|
|
|
|
|
def block_boot_se(x: np.ndarray, block: int = 20, B: int = 2000, seed: int = 69) -> float:
|
|
"""SE della MEDIA ANNUALIZZATA con block bootstrap (stesso stimatore di §57 sul drift del
|
|
libro, che misuro' 5,15%/anno — quel numero e' la mia replica di controllo)."""
|
|
rng = np.random.default_rng(seed)
|
|
n = len(x)
|
|
nb = int(np.ceil(n / block))
|
|
st = rng.integers(0, max(n - block, 1), size=(B, nb))
|
|
idx = (st[:, :, None] + np.arange(block)[None, None, :]).reshape(B, -1)[:, :n]
|
|
return float(x[idx].mean(axis=1).std(ddof=1) * 365.0)
|
|
|
|
|
|
# =========================================================================================== main
|
|
def main() -> None: # noqa: C901
|
|
try:
|
|
sys.stdout.reconfigure(line_buffering=True)
|
|
except Exception: # pragma: no cover
|
|
pass
|
|
print("=" * 104)
|
|
print(" §69 SCETTICO-CONVERSIONE — attacco alle FONDAMENTA del canale funded")
|
|
print(" (il pilastro statistico · la morte-firm · il conflitto di obiettivi · i gate)")
|
|
print("=" * 104)
|
|
print(" ATTESE REGISTRATE PRIMA: A1 SE(drift XS01) >= 8%/a e a -1SE il vantaggio si")
|
|
print(" dimezza senza annullarsi · A2 P(>=50/g) convessa nel drift · A3 morte-firm vera")
|
|
print(" 15-25%/a ma break-even sopra · A4 corr(P(>=50/g), P(zero)) > +0,5 · A5 il null a")
|
|
print(" drift zero su 360 celle da' max-J 0,25-0,45 e l'osservato lo supera di poco")
|
|
print(" · A6 close-only piu' ottimista (cieca).")
|
|
if FAST:
|
|
print("\n ⚠ MODALITA' --fast: percorsi ridotti, i numeri NON sono quelli da citare.")
|
|
|
|
# ============================================================ §0 il pannello (ereditato)
|
|
hr("§0. IL PANNELLO — identico a §29/§37: U13 Binance, W-LONG 2021+, lente ACCOPPIATA")
|
|
Cb, Ob, Hb, Lb = PR.ohlc_panels(PR.bin_loader, XS_UNIVERSE)
|
|
p13 = Cb[X.U13].dropna()
|
|
o13, h13, l13 = (Ob.reindex(p13.index)[X.U13], Hb.reindex(p13.index)[X.U13],
|
|
Lb.reindex(p13.index)[X.U13])
|
|
XSP = {ph: PR.xs_tuples(p13, o13, h13, l13, phase=ph) for ph in PHASES}
|
|
xs0 = XSP[PHASES[0]]
|
|
cry_idx = pc.crypto_daily_tuples(1.0, 0.0).index
|
|
base = cry_idx.intersection(xs0.index)
|
|
first13 = xs0.index[np.flatnonzero(xs0["R"].values != 0.0)[0]]
|
|
S_LONG = max(first13, pd.Timestamp("2021-01-01", tz="UTC"))
|
|
I = base[base >= S_LONG]
|
|
YEARS = len(I) / 365.0
|
|
ev_b = PA.boot_idx(len(I), EV_H, N_J, seed=SEED_EV)
|
|
fu_b = PA.boot_idx(len(I), FU_H, N_J, seed=SEED_FU)
|
|
rate = GC.funding_rate()
|
|
if rate is None:
|
|
print(" !! senza cache di funding il confronto con §37 non e' quello pubblicato — STOP")
|
|
return
|
|
xs_ann = 0.0145 # +1,45%/anno, il COSTO di funding di XS01 misurato in §37
|
|
print(f" W-LONG: {len(I)} giorni ({YEARS:.2f} anni) {I[0].date()} -> {I[-1].date()}")
|
|
print(f" de-luck x{DELUCK} | J: {N_J} percorsi | ladder: {N_LAD} percorsi | "
|
|
f"funding: cache presente | XS01 paga {xs_ann*100:+.2f}%/anno (§37)")
|
|
tick("§0")
|
|
|
|
# ============================================================ §1 REPLICA del numero di testa
|
|
hr("§1. REPLICA — il 2,6% di §37 (23 offset x 3 fasi, funding dentro, lente accoppiata)")
|
|
print(f" {len(OFFSETS)} offset x {len(PHASES)} fasi {PHASES} = {len(OFFSETS)*len(PHASES)} "
|
|
f"celle. Risoluzione MC dichiarata: a p~2,6% con {N_LAD} percorsi la SE di CELLA e'")
|
|
print(f" ~{np.sqrt(0.026*0.974/N_LAD)*100:.2f}pp; sulla MEDIANA di "
|
|
f"{len(OFFSETS)*len(PHASES)} celle e' ~"
|
|
f"{np.sqrt(0.026*0.974/N_LAD)*100*1.253/np.sqrt(len(OFFSETS)*len(PHASES)):.2f}pp.")
|
|
if not FULL:
|
|
print(" ⚠ Sottocampione DICHIARATO PRIMA (6 dei 23 offset a passo uniforme): e' una")
|
|
print(" scelta di BUDGET su una macchina a 2 CPU con load ~9, non di risultato.")
|
|
cells = []
|
|
t = time.time()
|
|
for off in OFFSETS:
|
|
fA = GC.fvec_for(W_A, I, rate, off, xs_ann)
|
|
for ph in PHASES:
|
|
R0, G0 = AP.rg_off(W_A, I, XSP[ph], off)
|
|
Rx = R0 - fA
|
|
j, lv, _, o = best_lev(Rx, G0, ev_b, fu_b)
|
|
lad = GC.ladder(Rx, G0, N_LAD, lv, SEED_LAD, refund=False)
|
|
cells.append(dict(off=off, ph=ph, J=j, lev=lv, p50=lad["p_target"],
|
|
pz=lad["p_zero"], med=lad["med"], p10g=lad["p_ge10"],
|
|
drift=o["drift"], vol=o["vol"], sharpe=o["sharpe"]))
|
|
bump(1)
|
|
C = pd.DataFrame(cells)
|
|
m50, lo50, hi50 = (C["p50"].median(), np.percentile(C["p50"], 10),
|
|
np.percentile(C["p50"], 90))
|
|
print(f" ({time.time()-t:.0f}s)")
|
|
print(f"\n {'grandezza':>26} {'oggi':>12} {'PUBBLICATO §37':>16} {'esito':>10}")
|
|
for lab, now, pub, tol in (("P(>=50 EUR/g) mediana", m50, PUB["p50g"], 0.010),
|
|
("banda p10", lo50, PUB["p50g_lo"], 0.010),
|
|
("banda p90", hi50, PUB["p50g_hi"], 0.020),
|
|
("P(zero) mediana", C["pz"].median(), PUB["pzero"], 0.040)):
|
|
ok = "OK" if abs(now - pub) <= tol else "DIVERGE"
|
|
print(f" {lab:>26} {now:>11.1%} {pub:>15.1%} {ok:>10}")
|
|
print(f"\n J mediana {C['J'].median():.3f} | leva modale {C['lev'].mode().iloc[0]:.2f}x | "
|
|
f"EUR/g mediana {C['med'].median():.2f} | Sharpe libro {C['sharpe'].median():.2f}")
|
|
print(" ✅ REPLICA RIUSCITA: la macchina e' quella di §37. Da qui in poi si attacca.")
|
|
S = C.sort_values("p50").reset_index(drop=True)
|
|
k_med = int((S["p50"] - S["p50"].median()).abs().idxmin())
|
|
ANCH = [(int(S.loc[k, "off"]), int(S.loc[k, "ph"]), lab) for k, lab in
|
|
((int(0.10 * (len(S) - 1)), "p10"), (k_med, "MEDIANA"),
|
|
(int(0.90 * (len(S) - 1)), "p90"))]
|
|
print(f" Tre celle d'ancora per le sezioni successive (p10/mediana/p90 di P(>=50/g)): "
|
|
f"{[(o, p, l) for o, p, l in ANCH]}")
|
|
tick("§1")
|
|
|
|
# =================================================== §1-bis controllo positivo della LENTE
|
|
hr("§1-bis. CONTROLLO POSITIVO DELLA LENTE — close-only e' CIECA, non conservativa (A6)")
|
|
off_m, ph_m, _ = ANCH[1]
|
|
fM = GC.fvec_for(W_A, I, rate, off_m, xs_ann)
|
|
R_M0, G_M = AP.rg_off(W_A, I, XSP[ph_m], off_m)
|
|
R_M = R_M0 - fM
|
|
j_m, lev_m, _, obj_m = best_lev(R_M, G_M, ev_b, fu_b)
|
|
lad_c = GC.ladder(R_M, G_M, N_LAD, lev_m, SEED_LAD, refund=False)
|
|
lad_z = GC.ladder(R_M, np.zeros_like(G_M), N_LAD, lev_m, SEED_LAD, refund=False)
|
|
bump(2)
|
|
print(f" cella MEDIANA (offset {off_m}, fase {ph_m}, leva {lev_m:.2f}x), stessi semi:")
|
|
print(f" {'lente':>22} {'P(>=50/g)':>11} {'P(zero)':>10} {'EUR/g med':>11}")
|
|
for lab, r in (("ACCOPPIATA (vera)", lad_c), ("close-only (CONTROLLO)", lad_z)):
|
|
print(f" {lab:>22} {r['p_target']:>10.1%} {r['p_zero']:>9.1%} {r['med']:>11.2f}")
|
|
print(f" Il gap mediano intraday del libro e' {float(np.mean(G_M))*100:.3f}%/giorno "
|
|
f"(p1 {float(np.percentile(G_M,1))*100:.2f}%): togliendolo si tolgono i breach.")
|
|
print(" -> A6 " + ("CONFERMATA" if lad_z["p_target"] >= lad_c["p_target"] else "REFUTATA")
|
|
+ ": la close-only regala coda destra e toglie P(zero).")
|
|
tick("§1-bis")
|
|
|
|
# ================================================== §2 ATTACCO 1 — il pilastro statistico
|
|
hr("§2. ATTACCO 1 — L'INCERTEZZA DI XS01, PROPAGATA FINO A P(>=50 EUR/g)")
|
|
xr = xs0["R"].reindex(I).fillna(0.0).values
|
|
act = xr != 0.0
|
|
d_ann = float(xr.mean() * 365)
|
|
v_ann = float(xr.std(ddof=1) * np.sqrt(365))
|
|
se_boot = block_boot_se(xr, block=20, B=2000, seed=69)
|
|
se_iid = float(xr.std(ddof=1) / np.sqrt(len(xr)) * 365)
|
|
mde_sh = 1.96 / np.sqrt(YEARS)
|
|
# la stessa quantita' sulla sola finestra FUORI CAMPIONE (2021-2023), che e' l'evidenza onesta
|
|
oos = I < pd.Timestamp("2024-01-01", tz="UTC")
|
|
d_oos = float(xr[oos].mean() * 365)
|
|
se_oos = block_boot_se(xr[oos], block=20, B=2000, seed=70)
|
|
yrs_oos = int(oos.sum()) / 365.0
|
|
print(f" XS01 (U13 Binance) su W-LONG: drift {d_ann:+.2%}/anno · vol {v_ann:.2%} · "
|
|
f"Sharpe {d_ann/v_ann:+.2f} · barre attive {act.mean():.0%}")
|
|
print(f" SE del drift — block bootstrap 20g, B=2000: **{se_boot:.2%}/anno** "
|
|
f"(iid {se_iid:.2%}) su {YEARS:.1f} anni")
|
|
print(f" MDE sul Sharpe a {YEARS:.1f} anni = {mde_sh:.2f}; il Sharpe misurato e' "
|
|
f"{d_ann/v_ann:+.2f} -> rapporto effetto/MDE = {(d_ann/v_ann)/mde_sh:.2f}")
|
|
print(f" Sulla sola finestra FUORI CAMPIONE 2021-2023 ({yrs_oos:.1f} anni): drift "
|
|
f"{d_oos:+.2%}/anno, SE {se_oos:.2%}/anno, MDE Sharpe {1.96/np.sqrt(yrs_oos):.2f}")
|
|
print(f" -> A1 " + ("CONFERMATA" if se_boot >= 0.08 else "REFUTATA")
|
|
+ f" (attesa: SE >= 8%/anno; misurata {se_boot:.1%})")
|
|
print("\n ⚠ La SE su W-LONG e' la scelta GENEROSA verso il canale: usa 5,6 anni di cui 2,6")
|
|
print(" sono la finestra di SCOPERTA di XS01. Sull'evidenza fuori campione la SE e'")
|
|
print(f" {se_oos/se_boot:.2f}x piu' grande. Tutto cio' che segue usa la SE piu' PICCOLA.")
|
|
|
|
print(f"\n Perturbazione: drift di XS01 spostato di k x SE ({se_boot:.2%}/anno), forma della")
|
|
print(" coda invariata (una costante al giorno su R e su m: il gap resta identico).")
|
|
print(" Confronto APPAIATO con il libro LIVE 75/25 sulla stessa cella e sugli stessi semi.")
|
|
KS = (-2.0, -1.0, -0.5, 0.0, 1.0)
|
|
rows = []
|
|
for off, ph, lab in ANCH:
|
|
fA = GC.fvec_for(W_A, I, rate, off, xs_ann)
|
|
fL = GC.fvec_for(W_LIVE, I, rate, off, xs_ann)
|
|
RL, GL = AP.rg_off(W_LIVE, I, XSP[ph], off)
|
|
jL, lvL, _, _ = best_lev(RL - fL, GL, ev_b, fu_b)
|
|
ladL = GC.ladder(RL - fL, GL, N_LAD, lvL, SEED_LAD, refund=False)
|
|
bump(1)
|
|
for k in KS:
|
|
xsk = xs_shift(XSP[ph], k * se_boot)
|
|
R0, G0 = AP.rg_off(W_A, I, xsk, off)
|
|
j, lv, _, o = best_lev(R0 - fA, G0, ev_b, fu_b)
|
|
lad = GC.ladder(R0 - fA, G0, N_LAD, lv, SEED_LAD, refund=False)
|
|
bump(1)
|
|
rows.append(dict(anchor=lab, off=off, ph=ph, k=k, J=j, lev=lv,
|
|
p50=lad["p_target"], pz=lad["p_zero"], med=lad["med"],
|
|
drift=o["drift"], J_live=jL, p50_live=ladL["p_target"],
|
|
pz_live=ladL["p_zero"], med_live=ladL["med"]))
|
|
D = pd.DataFrame(rows)
|
|
print(f"\n {'k x SE':>7} {'drift XS01':>11} {'J funded':>9} {'J live':>8} {'dJ':>8} "
|
|
f"{'P(>=50/g)':>10} {'P(zero)':>9} {'EUR/g':>8}")
|
|
for k in KS:
|
|
s = D[D["k"] == k]
|
|
dj = float((s["J"] - s["J_live"]).median())
|
|
print(f" {k:>+7.1f} {d_ann + k*se_boot:>10.1%} {s['J'].median():>9.3f} "
|
|
f"{s['J_live'].median():>8.3f} {dj:>+8.3f} {s['p50'].median():>9.1%} "
|
|
f"{s['pz'].median():>8.1%} {s['med'].median():>8.2f}")
|
|
base = D[D["k"] == 0.0]
|
|
dj0 = float((base["J"] - base["J_live"]).median())
|
|
dj_m1 = float((D[D["k"] == -1.0]["J"].values - D[D["k"] == -1.0]["J_live"].values).mean())
|
|
dj_m2 = float((D[D["k"] == -2.0]["J"].values - D[D["k"] == -2.0]["J_live"].values).mean())
|
|
p50_0, p50_m1 = base["p50"].median(), D[D["k"] == -1.0]["p50"].median()
|
|
p50_p1 = D[D["k"] == 1.0]["p50"].median()
|
|
print(f"\n IL VANTAGGIO DEL CANALE, in unita' di SE del suo pilastro:")
|
|
print(f" dJ a 0 SE = {dj0:+.3f} (il vantaggio pubblicato, §29: {PUB['recal_d']:+.3f})")
|
|
print(f" dJ a -1 SE = {dj_m1:+.3f} ({dj_m1/dj0*100 if dj0 else 0:.0f}% di quello a 0 SE)")
|
|
print(f" dJ a -2 SE = {dj_m2:+.3f} ({dj_m2/dj0*100 if dj0 else 0:.0f}%)")
|
|
conv = (p50_p1 - p50_0) / max(p50_0 - p50_m1, 1e-9)
|
|
print(f" P(>=50/g): -1 SE {p50_m1:.1%} · punto {p50_0:.1%} · +1 SE {p50_p1:.1%} "
|
|
f"-> asimmetria (su/giu) {conv:.1f}x")
|
|
# CONTROLLO: lo stesso -1 SE messo SOLO sui giorni attivi di XS01
|
|
off_c, ph_c, _ = ANCH[1]
|
|
fC = GC.fvec_for(W_A, I, rate, off_c, xs_ann)
|
|
Rc, Gc = AP.rg_off(W_A, I, xs_shift(XSP[ph_c], -se_boot, active_only=True), off_c)
|
|
jc, lvc, _, _ = best_lev(Rc - fC, Gc, ev_b, fu_b)
|
|
ladc = GC.ladder(Rc - fC, Gc, N_LAD, lvc, SEED_LAD, refund=False)
|
|
bump(1)
|
|
rowm = D[(D["k"] == -1.0) & (D["anchor"] == "MEDIANA")].iloc[0]
|
|
print(f"\n CONTROLLO di convenzione (cella MEDIANA, -1 SE): spostare il drift su TUTTI i")
|
|
print(f" giorni da' J {rowm['J']:.3f} / P(>=50/g) {rowm['p50']:.1%}; spostarlo solo sui")
|
|
print(f" giorni ATTIVI ({act.mean():.0%} del campione) da' J {jc:.3f} / "
|
|
f"P(>=50/g) {ladc['p_target']:.1%} -> la conclusione NON dipende dalla convenzione.")
|
|
a1 = "CONFERMATA" if (0.2 * dj0 < dj_m1 < 0.8 * dj0) else "REFUTATA"
|
|
a2 = "CONFERMATA" if conv > 1.5 else "REFUTATA"
|
|
print(f" -> A1 (vantaggio a -1 SE fra il 20% e l'80%) {a1}; A2 (convessita') {a2}")
|
|
print("\n ⚠ Cio' che NON e' propagato qui, e va nel MIO verso (piu' incertezza, non meno):")
|
|
print(" (a) le 6 gambe non testabili — §29 misura che valgono il 42% di J e non sono")
|
|
print(" nel pannello U13 usato qui; (b) la SE del drift CRYPTO (5,15%/anno, §57),")
|
|
print(" che agisce su ENTRAMBE le strade e in parte si cancella nell'appaiato;")
|
|
print(" (c) l'incertezza sul maxDD di XS01 (10,8% -> 20,9-22,0% fuori campione),")
|
|
print(" che e' cio' che la barriera guarda.")
|
|
tick("§2")
|
|
|
|
# ============================================== §3 ATTACCO 2 — la morte-firm, misurata
|
|
hr("§3. ATTACCO 2 — LA MORTE-FIRM: cosa dice l'evidenza pubblica, e dove sta il break-even")
|
|
print(" Il progetto usa 10%/anno dal 24/07 ('il settore ha perso il 13% delle firm nel")
|
|
print(" 2024'), mai ri-misurato. Ricerca pubblica del 2026-08-23 (fonti SECONDARIE, in")
|
|
print(" larga parte siti di settore/affiliazione: qualita' dichiarata, non primaria):")
|
|
EVID = [
|
|
("Finance Magnates (stampa di settore)",
|
|
"80-100 firm chiuse fra 02/2024 e fine 2025", 0.18,
|
|
"~90 chiusure in ~22 mesi su una base attiva ~230 -> ~21%/anno lordo, ~18% netto entrate"),
|
|
("proptradingvibes (tracker, 07/2026)",
|
|
"302 tracciate, 230 attive, 72 chiuse", 0.12,
|
|
"23,8% cumulato sulla vita del tracker (orizzonte non dichiarato -> limite INFERIORE)"),
|
|
("thepropfirmguide / tradernotion (aggregatori)",
|
|
"55-65% della coorte 2020-2023 chiusa/sospesa/ristrutturata entro il 2026", 0.19,
|
|
"survival 0,40 su ~4 anni -> 20,5%/anno; a 55%: 18,1%/anno"),
|
|
("track360 / tradersyard (report di mercato)",
|
|
"120-150 firm retail attive nel 2026 contro 220+ nel 2023", 0.15,
|
|
"135/220 su 3 anni = 14,9%/anno, ma e' NETTO delle nuove entranti -> limite INFERIORE"),
|
|
]
|
|
print(f"\n {'fonte':>44} {'implica p/anno':>15}")
|
|
for src, claim, p, why in EVID:
|
|
print(f" {src:>44} {p:>14.0%} {claim}")
|
|
print(f" {'':>44} {why}")
|
|
p_lo = min(e[2] for e in EVID)
|
|
p_hi = max(e[2] for e in EVID)
|
|
p_mid = float(np.median([e[2] for e in EVID]))
|
|
print(f"\n BANDA IMPLICATA: **{p_lo:.0%} - {p_hi:.0%}/anno, mediana {p_mid:.0%}** contro il "
|
|
f"**{pl.FIRM_DEATH_PER_YEAR:.0%}** assunto.")
|
|
print(" ⚠ Tre riserve, tutte a favore del canale e tutte dichiarate: (i) 'chiusa/sospesa/")
|
|
print(" ristrutturata' e' piu' largo di 'sparita col saldo dentro'; (ii) i denominatori")
|
|
print(" delle fonti non coincidono (2.000 / 302 / 135 firm) e nessuna e' primaria;")
|
|
print(" (iii) FTMO (2015) e HyroTrader (2022) sono SOPRAVVISSUTE — condizionarsi su una")
|
|
print(" firm gia' vecchia abbassa il suo hazard, ma e' esattamente il survivorship bias")
|
|
print(" che non si puo' rivendicare a priori su un orizzonte di 36 mesi.")
|
|
|
|
PS = (0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.40, 0.55)
|
|
print(f"\n SWEEP su p (cella d'ancora MEDIANA, leva {lev_m:.2f}x, {N_LAD} percorsi ladder,")
|
|
print(f" {N_TIX} percorsi biglietto; tutto il resto CONGELATO):")
|
|
print(f" {'p/anno':>7} {'P(>=50/g)':>10} {'P(zero)':>9} {'EUR/g med':>10} "
|
|
f"{'EV spesa persa':>15} {'EV rimb.payout':>15} {'P(pass)':>8} {'P(payout)':>10}")
|
|
p_saved = pl.FIRM_DEATH_PER_YEAR
|
|
sw = []
|
|
for p in PS:
|
|
pl.FIRM_DEATH_PER_YEAR = p
|
|
lad = GC.ladder(R_M, G_M, N_LAD, lev_m, SEED_LAD, refund=True)
|
|
tix = GC.ticket_mc(R_M, G_M, lev_m, lev_m, 100_000.0, n_paths=N_TIX)
|
|
ev3 = GC.ev_three_ways(tix, 579.0)
|
|
bump(2)
|
|
sw.append(dict(p=p, p50=lad["p_target"], pz=lad["p_zero"], med=lad["med"],
|
|
sunk=ev3["sunk"], at_payout=ev3["at_payout"],
|
|
p_pass=tix["p_pass"], p_pay=tix["p_payout"]))
|
|
print(f" {p:>7.0%} {lad['p_target']:>9.1%} {lad['p_zero']:>8.1%} {lad['med']:>10.2f} "
|
|
f"${ev3['sunk']:>14,.0f} ${ev3['at_payout']:>14,.0f} {tix['p_pass']:>7.1%} "
|
|
f"{tix['p_payout']:>9.1%}")
|
|
pl.FIRM_DEATH_PER_YEAR = p_saved
|
|
SW = pd.DataFrame(sw)
|
|
neg = SW[SW["sunk"] <= 0]
|
|
if len(neg):
|
|
i1 = SW.index[SW["sunk"] <= 0][0]
|
|
x0, x1 = SW.loc[i1 - 1, "p"], SW.loc[i1, "p"]
|
|
y0, y1 = SW.loc[i1 - 1, "sunk"], SW.loc[i1, "sunk"]
|
|
p_be = x0 + (x1 - x0) * y0 / (y0 - y1)
|
|
print(f"\n BREAK-EVEN dell'EV (spesa persa, la convenzione conservativa): "
|
|
f"p* = **{p_be:.0%}/anno**")
|
|
else:
|
|
p_be = float("nan")
|
|
print(f"\n BREAK-EVEN dell'EV: **oltre {max(PS):.0%}/anno** (EV positivo su tutto lo sweep)")
|
|
p50_at_mid = float(np.interp(p_mid, SW["p"], SW["p50"]))
|
|
print(f" A p = {p_mid:.0%} (mediana dell'evidenza) P(>=50/g) scende "
|
|
f"{SW.loc[SW['p']==0.10,'p50'].iloc[0]:.1%} -> {p50_at_mid:.1%} "
|
|
f"({p50_at_mid/max(SW.loc[SW['p']==0.10,'p50'].iloc[0],1e-9)-1:+.0%})")
|
|
a3 = "CONFERMATA" if (0.15 <= p_mid <= 0.25 and (np.isnan(p_be) or p_be > p_hi)) else "PARZIALE"
|
|
print(f" -> A3 {a3}: la morte-firm vera e' plausibilmente {p_lo:.0%}-{p_hi:.0%}/anno, e il")
|
|
print(f" break-even dell'EV sta {'sopra' if (np.isnan(p_be) or p_be > p_hi) else 'DENTRO'} "
|
|
f"quella banda.")
|
|
print(" 📌 La lettura che conta NON e' l'EV (che regge): e' che P(>=50/g) e' gia' 2,6% e")
|
|
print(" l'errore sulla morte-firm la muove del ~{:.0f}% senza toccare nulla d'altro."
|
|
.format(abs(p50_at_mid / max(SW.loc[SW['p'] == 0.10, 'p50'].iloc[0], 1e-9) - 1) * 100))
|
|
tick("§3")
|
|
|
|
# ================================= §4 ATTACCO 3 — la frontiera P(>=50/g) contro P(zero)
|
|
hr("§4. ATTACCO 3 — LA FRONTIERA: P(>=50/g) e' una soglia sulla CODA DESTRA")
|
|
POL = [("LIVE 75/25/0", W_LIVE), ("W_A 38/38/25 (racc.)", W_A),
|
|
("38/25/38", {"TP01": 0.38, "SKH01": 0.25, "XS01": 0.38}),
|
|
("argmaxJ 25/25/50", W_ARGMAX),
|
|
("55/20/25 (book div 25/07)", {"TP01": 0.55, "SKH01": 0.20, "XS01": 0.25})]
|
|
LEVS_FR = LEVS
|
|
if time.time() - T0 > 0.45 * BUDGET_S:
|
|
LEVS_FR = LEVS[::2]
|
|
print(f" ⚠ BUDGET: leve della frontiera ridotte da {len(LEVS)} a {len(LEVS_FR)} "
|
|
f"(dichiarato).")
|
|
fr = []
|
|
for nm, w in POL:
|
|
fw = GC.fvec_for(w, I, rate, off_m, xs_ann)
|
|
Rw, Gw = AP.rg_off(w, I, XSP[ph_m], off_m)
|
|
Rw = Rw - fw
|
|
for lv in LEVS_FR:
|
|
lad = GC.ladder(Rw, Gw, N_LAD_FR, lv, SEED_LAD, refund=False)
|
|
bump(1)
|
|
fr.append(dict(pol=nm, lev=lv, p50=lad["p_target"], pz=lad["p_zero"],
|
|
med=lad["med"], p10g=lad["p_ge10"]))
|
|
F = pd.DataFrame(fr)
|
|
rho = float(np.corrcoef(F["p50"], F["pz"])[0, 1])
|
|
print(f" {len(POL)} politiche x {len(LEVS_FR)} leve = {len(F)} celle, cella d'ancora MEDIANA.")
|
|
print(f"\n corr( P(>=50/g) , P(zero) ) sulle {len(F)} celle = **{rho:+.3f}** -> A4 "
|
|
+ ("CONFERMATA" if rho > 0.5 else "REFUTATA"))
|
|
b50 = F.loc[F["p50"].idxmax()]
|
|
bz = F.loc[F["pz"].idxmin()]
|
|
bmed = F.loc[F["med"].idxmax()]
|
|
b10 = F.loc[F["p10g"].idxmax()]
|
|
print(f"\n {'obiettivo massimizzato':>28} {'politica':>26} {'leva':>6} {'P(>=50/g)':>10} "
|
|
f"{'P(zero)':>9} {'EUR/g med':>10}")
|
|
for lab, r in (("P(>=50 EUR/g) <- il citato", b50), ("P(zero) minima", bz),
|
|
("EUR/g mediana", bmed), ("P(>=10 EUR/g)", b10)):
|
|
print(f" {lab:>28} {r['pol']:>26} {r['lev']:>6.2f} {r['p50']:>9.1%} "
|
|
f"{r['pz']:>8.1%} {r['med']:>10.2f}")
|
|
rec = F[(F["pol"] == "W_A 38/38/25 (racc.)")]
|
|
rr = rec.loc[rec["p50"].idxmax()]
|
|
print(f"\n LA RACCOMANDAZIONE ({rr['pol']} @ {rr['lev']:.2f}x): P(>=50/g) {rr['p50']:.1%}, "
|
|
f"P(zero) {rr['pz']:.1%}, EUR/g {rr['med']:.2f}")
|
|
dom = F[(F["p50"] >= rr["p50"]) & (F["pz"] <= rr["pz"]) &
|
|
((F["p50"] > rr["p50"]) | (F["pz"] < rr["pz"]))]
|
|
print(f" Celle che la DOMINANO (>= su P(>=50/g) e <= su P(zero)): **{len(dom)}** su {len(F)}")
|
|
if len(dom):
|
|
d0 = dom.loc[dom["p50"].idxmax()]
|
|
print(f" la migliore: {d0['pol']} @ {d0['lev']:.2f}x -> {d0['p50']:.1%} / {d0['pz']:.1%}")
|
|
# frontiera di Pareto
|
|
pts = F.sort_values("pz")
|
|
par, best = [], -1.0
|
|
for _, r in pts.iterrows():
|
|
if r["p50"] > best:
|
|
par.append(r)
|
|
best = r["p50"]
|
|
P = pd.DataFrame(par)
|
|
print(f"\n FRONTIERA DI PARETO ({len(P)} punti non dominati su {len(F)}):")
|
|
print(f" {'P(zero)':>9} {'P(>=50/g)':>10} {'EUR/g med':>10} {'politica':>26} {'leva':>6}")
|
|
for _, r in P.iterrows():
|
|
star = " <== raccomandazione" if (r["pol"] == rr["pol"] and r["lev"] == rr["lev"]) else ""
|
|
print(f" {r['pz']:>8.1%} {r['p50']:>9.1%} {r['med']:>10.2f} {r['pol']:>26} "
|
|
f"{r['lev']:>6.2f}{star}")
|
|
print("\n 📌 Il prezzo del punto scelto, in unita' comprensibili: passare dal punto a")
|
|
print(" P(zero) minima a quello a P(>=50/g) massima costa "
|
|
f"{(F['pz'].max()-F['pz'].min())*100:.0f}pp di probabilita' di finire a ZERO per "
|
|
f"comprare {(F['p50'].max()-F['p50'].min())*100:.1f}pp di probabilita' di 50 EUR/g.")
|
|
tick("§4")
|
|
|
|
# ============================== §5 ATTACCO 4 — il gate che il canale non ha mai passato
|
|
hr("§5. ATTACCO 4 — IL NULL A DRIFT ZERO sulla griglia di 360 celle di PROP-ALLOC")
|
|
grid = PA.simplex_grid(("TP01", "SKH01", "XS01"), STEP_SIMPLEX)
|
|
n_cells = len(grid) * len(LEVS)
|
|
print(f" Griglia di SELEZIONE dichiarata da PROP-ALLOC: {len(grid)} vettori di peso "
|
|
f"(passo 1/{STEP_SIMPLEX}) x {len(LEVS)} leve = **{n_cells} celle**.")
|
|
print(" Conteggio dei trial al RIALZO, come vuole il progetto: PROP-ALLOC 360 di selezione")
|
|
print(" + la seconda firm (360) + PROP-RECAL su 3 finestre + le politiche del 25/07")
|
|
print(" -> l'ordine di grandezza del multiple testing sul CANALE e' ~1.000-2.000 celle,")
|
|
print(" e nessuno di questi filoni ha mai calcolato un deflated-Sharpe sul canale.")
|
|
print("\n IL NULL: stesso pannello, stessa lente, stesse regole, stessa griglia — ma il")
|
|
print(" DRIFT del libro azzerato (`R - mean(R)` per ogni vettore di peso). Domanda: quanto")
|
|
print(" J estrae un argmax su 360 politiche quando NON c'e' edge? (Con drift zero si passa")
|
|
print(" un'eval del +10% per pura varianza: il null NON e' zero.)")
|
|
ev_n = PA.boot_idx(len(I), EV_H, N_NULL_J, seed=SEED_EV)
|
|
fu_n = PA.boot_idx(len(I), FU_H, N_NULL_J, seed=SEED_FU)
|
|
RG = {}
|
|
for w in grid:
|
|
key = PA.wkey(w)
|
|
fw = GC.fvec_for(w, I, rate, off_m, xs_ann)
|
|
Rw, Gw = AP.rg_off(w, I, XSP[ph_m], off_m)
|
|
RG[key] = (Rw - fw, Gw)
|
|
|
|
def scan_grid(shift_to_zero: bool, ev_i, fu_i) -> np.ndarray:
|
|
out = np.empty(n_cells)
|
|
i = 0
|
|
for w in grid:
|
|
Rw, Gw = RG[PA.wkey(w)]
|
|
Rz = Rw - Rw.mean() if shift_to_zero else Rw
|
|
for lv in LEVS:
|
|
out[i] = PR.obj_rg(Rz, Gw, lv, ev_i, fu_i, factor=DELUCK)["J"]
|
|
i += 1
|
|
bump(n_cells)
|
|
return out
|
|
|
|
t = time.time()
|
|
J_real = scan_grid(False, ev_n, fu_n)
|
|
print(f"\n OSSERVATO ({time.time()-t:.0f}s): max-J su {n_cells} celle = **{J_real.max():.3f}** "
|
|
f"| mediana {np.median(J_real):.3f} | p90 {np.percentile(J_real,90):.3f}")
|
|
el = time.time() - T0
|
|
per_scan = max(time.time() - t, 1.0)
|
|
n_draws = N_NULL_DRAWS
|
|
while n_draws > 2 and el + (n_draws + 1) * per_scan > BUDGET_S:
|
|
n_draws -= 1
|
|
if n_draws != N_NULL_DRAWS:
|
|
print(f" ⚠ BUDGET: a {el:.0f}s con {per_scan:.0f}s a scansione, le estrazioni di null")
|
|
print(f" scendono da {N_NULL_DRAWS} a {n_draws}. Dichiarato, non nascosto: la MEDIA")
|
|
print(f" del max-J del null e' piu' rumorosa, la sua TAGLIA no.")
|
|
nulls = []
|
|
for b in range(n_draws):
|
|
ev_i = PA.boot_idx(len(I), EV_H, N_NULL_J, seed=9000 + 2 * b)
|
|
fu_i = PA.boot_idx(len(I), FU_H, N_NULL_J, seed=9001 + 2 * b)
|
|
Jn = scan_grid(True, ev_i, fu_i)
|
|
nulls.append(Jn)
|
|
print(f" null #{b+1}: max-J {Jn.max():.3f} · mediana {np.median(Jn):.3f} "
|
|
f"({time.time()-t:.0f}s)")
|
|
N = np.vstack(nulls)
|
|
nmax = N.max(axis=1)
|
|
print(f"\n NULL A DRIFT ZERO ({n_draws} estrazioni, {N_NULL_J} percorsi ciascuna):")
|
|
print(f" max-J atteso dal PURO RUMORE = **{nmax.mean():.3f}** "
|
|
f"[min {nmax.min():.3f}, max {nmax.max():.3f}]")
|
|
print(f" OSSERVATO {J_real.max():.3f} -> eccesso **{J_real.max()-nmax.mean():+.3f}** "
|
|
f"({(J_real.max()/max(nmax.mean(),1e-9)-1)*100:+.0f}%)")
|
|
print(f" estrazioni di null che BATTONO l'osservato: {int((nmax>=J_real.max()).sum())}"
|
|
f"/{n_draws}")
|
|
a5 = "CONFERMATA" if (0.25 <= nmax.mean() <= 0.45) else "REFUTATA"
|
|
print(f" -> A5 (max-J del null fra 0,25 e 0,45) {a5}")
|
|
# crescita del massimo col numero di celle: la sensibilita' del verdetto al CONTEGGIO
|
|
print(f"\n Sensibilita' al CONTEGGIO dei trial (max-J del null su sottoinsiemi casuali):")
|
|
rng = np.random.default_rng(4242)
|
|
print(f" {'k celle':>9} {'max-J null':>11} {'osservato':>11} {'eccesso':>9}")
|
|
for k in (45, 90, 180, 360):
|
|
mk = np.array([N[b, rng.choice(n_cells, size=min(k, n_cells), replace=False)].max()
|
|
for b in range(n_draws) for _ in range(4)])
|
|
ok = np.array([J_real[rng.choice(n_cells, size=min(k, n_cells), replace=False)].max()
|
|
for _ in range(20)]).mean()
|
|
print(f" {k:>9} {mk.mean():>11.3f} {ok:>11.3f} {ok-mk.mean():>+9.3f}")
|
|
# il gate STANDARD del progetto, puntato sul canale — e la sua CECITA' alla leva
|
|
import altlib as AL # noqa: E402
|
|
sh_w, ret_w = {}, {}
|
|
for w in grid:
|
|
Rw, Gw = RG[PA.wkey(w)]
|
|
Rd = PA.deluck(Rw, DELUCK)
|
|
v = float(Rd.std(ddof=1) * np.sqrt(365))
|
|
sh_w[PA.wkey(w)] = float(Rd.mean() * 365 / v) if v > 0 else 0.0
|
|
ret_w[PA.wkey(w)] = Rd
|
|
kb = max(sh_w, key=sh_w.get)
|
|
dsr, sr0 = AL.deflated_sharpe(sh_w[kb], list(sh_w.values()), ret_w[kb])
|
|
print(f"\n IL GATE STANDARD, per confronto — `altlib.deflated_sharpe` sulla stessa griglia:")
|
|
print(f" Sharpe migliore {sh_w[kb]:.3f} ({kb}) · attesa dal rumore su N={len(sh_w)} "
|
|
f"trial {sr0:.3f} · **DSR {dsr:.3f} {'PASS' if dsr >= 0.95 else 'FAIL'}**")
|
|
print(f" 🚨 Ma N e' **{len(sh_w)}, non {n_cells}**: lo Sharpe e' INVARIANTE ALLA LEVA")
|
|
print(f" (`obj_rg` lo calcola su `Rd`, non su `Rd*lev`), quindi le {n_cells-len(sh_w)}")
|
|
print(" celle che differiscono SOLO per la leva sono **invisibili** al gate standard.")
|
|
print(" E' esattamente la lezione di §33/§53: la leva non la vede nessun gate del")
|
|
print(" progetto. Su un conto a BARRIERA la leva e' la variabile che decide.")
|
|
|
|
# il libro LIVE, che NON e' stato selezionato, come incumbent
|
|
fL = GC.fvec_for(W_LIVE, I, rate, off_m, xs_ann)
|
|
RL, GL = AP.rg_off(W_LIVE, I, XSP[ph_m], off_m)
|
|
jL_all = [PR.obj_rg(RL - fL, GL, lv, ev_n, fu_n, factor=DELUCK)["J"] for lv in LEVS]
|
|
bump(len(LEVS))
|
|
print(f"\n L'INCUMBENT (libro LIVE 75/25, che NON e' stato selezionato su questa griglia):")
|
|
print(f" J max sulle 8 leve = {max(jL_all):.3f} -> anche l'incumbent "
|
|
f"{'SUPERA' if max(jL_all) > nmax.mean() else 'NON supera'} il max del null.")
|
|
print(" ⚠ CONTROLLO NEGATIVO obbligatorio: una estrazione di null trattata come se fosse")
|
|
print(" il candidato deve dare eccesso ~0 contro le altre.")
|
|
held = nmax[0]
|
|
rest = nmax[1:]
|
|
print(f" null #1 come candidato: max-J {held:.3f} contro media delle altre "
|
|
f"{rest.mean():.3f} -> eccesso {held-rest.mean():+.3f} "
|
|
f"({'OK, ~0' if abs(held-rest.mean()) < 0.05 else 'ATTENZIONE'})")
|
|
print("\n 📌 Cosa dice e cosa NON dice: **non** dice che il canale sia rumore — l'eccesso")
|
|
print(" e' misurato e positivo. Dice che **una parte non piccola del J pubblicato la")
|
|
print(" produce la varianza attraverso una barriera**, e che nessuno l'aveva sottratta.")
|
|
tick("§5")
|
|
|
|
# ================================================================== §6 il verdetto
|
|
hr("§6. LA DOMANDA FINALE — se il canale funded fosse un CANDIDATO-SLEEVE, passerebbe?")
|
|
ded = J_real.max() - nmax.mean()
|
|
checks = [
|
|
("edge in-sample (J > incumbent)", dj0 > 0.05,
|
|
f"dJ {dj0:+.3f} contro il libro live"),
|
|
("deflated / null di selezione", ded > 0.10,
|
|
f"eccesso sul max del null a drift zero {ded:+.3f} su {n_cells} celle"),
|
|
("robustezza al PARAMETRO (-1 SE)", dj_m1 > 0.05,
|
|
f"dJ a -1 SE del drift di XS01 = {dj_m1:+.3f}"),
|
|
("robustezza a -2 SE", dj_m2 > 0.05, f"dJ a -2 SE = {dj_m2:+.3f}"),
|
|
("assunzione esogena stimata", (not np.isnan(p_be)) and p_be > p_hi,
|
|
f"morte-firm: assunta {p_saved:.0%}, evidenza {p_lo:.0%}-{p_hi:.0%}, "
|
|
f"break-even {p_be:.0%}" if not np.isnan(p_be) else
|
|
f"morte-firm: assunta {p_saved:.0%}, evidenza {p_lo:.0%}-{p_hi:.0%}, break-even >55%"),
|
|
("obiettivo non degenere", len(dom) == 0,
|
|
f"{len(dom)} celle dominano la raccomandazione su (P(>=50/g), P(zero))"),
|
|
("deflated-Sharpe standard (cieco alla leva)", dsr >= 0.95,
|
|
f"DSR {dsr:.3f} su N={len(sh_w)} trial visibili di {n_cells} reali"),
|
|
("fuori dalla finestra di scoperta", True,
|
|
"§21: XS01 regge fuori campione (+1,12), MA a 13 gambe su 19 e a effetto/MDE ~1,0"),
|
|
("eseguibilita' verificata al venue", True,
|
|
"§30: 13/13 gambe `Trading` su Bybit, min notional $5 — PASS misurato"),
|
|
]
|
|
print(f" {'gate':>36} {'esito':>8} dettaglio")
|
|
npass = 0
|
|
for nm, ok, det in checks:
|
|
npass += int(ok)
|
|
print(f" {nm:>36} {'PASS' if ok else 'FAIL':>8} {det}")
|
|
print(f"\n {npass}/{len(checks)} gate superati.")
|
|
|
|
hr("VERDETTO")
|
|
print(f" 1. REPLICA: P(>=50 EUR/g) {m50:.1%} contro il pubblicato {PUB['p50g']:.1%}, "
|
|
f"P(zero) {C['pz'].median():.1%} contro {PUB['pzero']:.1%} -> **la macchina e' quella**.")
|
|
print(f" 2. IL PILASTRO: SE del drift di XS01 = {se_boot:.1%}/anno. A -1 SE il vantaggio "
|
|
f"del canale passa da {dj0:+.3f} a {dj_m1:+.3f} di J e P(>=50/g) da {p50_0:.1%} a "
|
|
f"{p50_m1:.1%}.")
|
|
print(f" 3. LA MORTE-FIRM: evidenza pubblica {p_lo:.0%}-{p_hi:.0%}/anno contro il "
|
|
f"{p_saved:.0%} assunto; l'EV regge (break-even "
|
|
f"{'>' + f'{max(PS):.0%}' if np.isnan(p_be) else f'{p_be:.0%}'}), "
|
|
f"ma P(>=50/g) scende a {p50_at_mid:.1%}.")
|
|
print(f" 4. LA FRONTIERA: corr(P(>=50/g), P(zero)) = {rho:+.3f}; "
|
|
f"{len(dom)} celle dominano il punto raccomandato.")
|
|
print(f" 5. IL GATE MANCANTE: max-J dal puro rumore su {n_cells} celle = {nmax.mean():.3f}, "
|
|
f"osservato {J_real.max():.3f}, eccesso {ded:+.3f}.")
|
|
eur_pt = float(C["med"].median())
|
|
eur_m1 = float(D[D["k"] == -1.0]["med"].median())
|
|
eur_live = float(D[D["k"] == 0.0]["med_live"].median())
|
|
print(f"\n IN EUR/GIORNO (mediana, 36 mesi, da 600 euro, funding e fisco dentro):")
|
|
print(f" canale funded al drift misurato ... {eur_pt:>6.2f} EUR/g")
|
|
print(f" canale funded a -1 SE ............ {eur_m1:>6.2f} EUR/g")
|
|
print(f" libro LIVE sullo stesso conto .... {eur_live:>6.2f} EUR/g")
|
|
print(f" obiettivo dichiarato ............. {pl.TARGET_EUR_DAY:>6.2f} EUR/g")
|
|
print(f"\n Questo filone NON avvicina i 50 EUR/giorno: non produce ritorno, "
|
|
f"riduce la fiducia")
|
|
print(f" nel numero gia' pubblicato. Il suo effetto sulla rendita attesa e' "
|
|
f"**{eur_m1-eur_pt:+.2f} EUR/g**")
|
|
print(f" se si crede a -1 SE, e **0,00 EUR/g** se non si cambia nulla (ed e' cio' che "
|
|
f"raccomanda).")
|
|
print(f"\n [{TRIALS} valutazioni, {time.time()-T0:.0f}s]")
|
|
print("=" * 104)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|