"""r0822d_gate_c — GATE PROP-01, gamba (c): il BIGLIETTO riprezzato come OPZIONE (2026-08-22). IL BUCO CHE CHIUDE ------------------ `GATE PROP-01` (RESULTS-0822 §29) ha tre gambe. Due sono state chiuse stanotte: (a) LISTINO -> PASS 13/13, misurato sul venue (Bybit `instruments-info`, 833 strumenti); (b) ANCORA SKH01 -> PASS, delta appaiato +0,116 sui 23 offset, 230/230 celle congiunte positive — ma il numero OPERATIVO e' sceso a P(>=50 EUR/g) = 4,4% [2,5-8,2%], P(zero) 35,3%. La gamba (c) e' rimasta una CONSTATAZIONE, non un criterio: "un'eval HYRO $100k costa $579 su un conto di $635 = il 91%" e per giunta poggia su un'economia sbagliata. La pagina della firm dichiara la quota come **"Refundable Challenge Deposit, returned in full alongside your first funded payout"**: non e' una fee affondata, e' un **deposito rimborsabile al PRIMO PAYOUT**. Immobilizza il 91% del conto; non lo brucia. Va riprezzato come **struttura a opzione a DUE STADI** (passare l'eval **E POI** produrre abbastanza profitto da chiedere un prelievo), non come spesa. ⚠ E qui c'e' un fatto che nessuno ha notato: **il progetto contiene GIA' DUE modelli che si contraddicono fra loro.** * `r0725_hyro.py:200` -> `cost = price * (1.0 - pe)` = quota **rimborsata al PASS**; * `r0725_prop_ladder.simulate` / `r0725_prop_coupled.simulate` -> `cash[buy] -= price` e la quota **non torna MAI** = spesa affondata. Il primo e' OTTIMISTA (rimborsa a un evento che accade prima e piu' spesso di quello vero), il secondo e' PESSIMISTA (non rimborsa affatto). **Il vero sta in mezzo, e il numero operativo del progetto — P(>=50 EUR/g) — viene dal secondo.** LA PREVISIONE, REGISTRATA PRIMA DI MISURARE ------------------------------------------- 1. Il rimborso al primo payout vale **poco** sull'EV del biglietto e **piu'** sulla scala di conti (dove la cassa che torna ricompra un biglietto e composta). Mi aspetto: EV +5-15%, P(>=50/g) +1-3 punti percentuali. 2. `P(primo payout | pass)` mi aspetto **alta** (>80%): la soglia di prelievo e' $100 di quota trader = 0,125% di nozionale su $100k, cioe' un'inezia rispetto al 10% appena prodotto nell'eval. Quindi la differenza fra le due convenzioni di rimborso e' **piccola**. 3. Il **funding** su un conto funded fa male in proporzione alla leva: a 0,75x mi aspetto ~-1,6%/anno di equity del conto (cioe' 0,75 x 2,16%), e su un conto a barriera un costo di drift si paga sulla P(pass) piu' che sul maxDD. 4. **XS01: mi aspetto che INCASSI funding**, perche' e' market-neutral e i perp con funding piu' caro sono tipicamente quelli in forte trend, che il momentum cross-sezionale compra... ma comprarli vuol dire PAGARE il loro funding. Le due forze tirano in verso opposto: **non lo so, e per questo si misura.** Se il segno esce negativo (costo) lo dico. 5. **La domanda che decide**: con P(>=50 EUR/g) 4,4% e P(zero) 35,3%, il biglietto ha EV positivo? E batte l'alternativa ovvia (tenere i $635 sul libro e versare)? Mi aspetto: **EV positivo sulla MEDIA e negativo sulla MEDIANA** — cioe' una lotteria con valore atteso positivo, che e' esattamente la forma che un conto da $635 non puo' permettersi molte volte. CONVENZIONI DICHIARATE ---------------------- * Lente **ACCOPPIATA** ovunque (`r0725_prop_coupled`): su una regola a UN GIORNO la close-only e' esattamente CIECA. La close-only compare solo come controllo. * Configurazione del conto funded: **W_A = TP01 38 / SKH01 38 / XS01 25** (centro della regione robusta di PROP-RECAL), XS01 = **U13 Binance** (la versione piu' debole e l'unica testabile fuori campione), finestra **W-LONG 2021+**, de-luck sul drift **x0,89**. * Il conto PROPRIO gira invece il **libro LIVE 75/25 a $654 con min-order $5** (XS01 non e' eseguibile a quel capitale): l'asimmetria e' REALE ed e' il punto del canale funded. * Banda d'ancora: **23 offset x 3 fasi (0,3,7)**, lo stesso sottocampione dichiarato da `r0822c_skh_anchor_prop`. La stima onesta e' la **MEDIANA**, non l'ancora canonica. * **Firm death 10%/anno** e **cap $200k/trader**: assunzioni gia' in uso nel progetto (24/07), dichiarate e non nascoste. * Soglia di prelievo **$100 di quota trader** (fonte primaria-dichiarativa della firm, non misurata) -> profitto lordo >= $125 su $100k. * Monte Carlo: la risoluzione si dichiara. 1500 percorsi sulla scala di conti (+-1pp su P(>=50/g)), 6000 sul biglietto a conto singolo, 2500 su J. Uso: `nice -n 19 timeout 900 uv run python scripts/research/r0822d_gate_c.py` Rete: NESSUNA (tutto da disco: parquet certificati, cache Binance di r0822b, cache funding di r0822d_funding). Se la cache del funding manca, la sezione 1 lo dichiara e si ferma li'. """ from __future__ import annotations import contextlib import io import re 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 r0724_goal50_intraday_mc as mc # noqa: E402 recon orario per-sleeve (24/07) 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 gamba (b), offset di SKH01 (22/08) import r0822d_funding as FD # noqa: E402 il funding (22/08) from src.portfolio.sleeves import XS_UNIVERSE # noqa: E402 FD.USE_NET = False RAW = ROOT / "data" / "raw" HYRO = "HYRO" ASSETS = ("BTC", "ETH") EURUSD = pl.EURUSD TAX_RATE = pl.TAX_RATE SPLIT = pl.SPLIT DELUCK = PA.DELUCK # x0,89, fattore d'ancora MISURATO (26/07) LEVS = PR.LEVS OFFSETS = AP.OFFSETS # 23 offset a priori di SKH01 PHASES_SUB = AP.PHASES_SUB # (0, 3, 7) — dichiarato da r0822c W_A = {"TP01": 0.38, "SKH01": 0.38, "XS01": 0.25} W_LIVE = {"TP01": 0.75, "SKH01": 0.25} N_PATHS_J = PA.N_PATHS # 2500 N_PATHS_LAD = 1500 N_PATHS_TIX = 6000 EV_H = FU_H = 365 SEED_EV, SEED_FU = 8220, 8221 SEED_LAD = 20260822 MIN_PAYOUT_TRADER = 100.0 # quota trader minima per un prelievo (firm) HORIZON_DAYS = pl.HORIZON_MONTHS * 30 # 1080 = 36 mesi # numeri PUBBLICATI da riprodurre prima di produrne di nuovi PUB = dict( hyro_ev_close=6789.0, hyro_ev_intra=1166.0, # r0725_hyro (25/07) recal_j=0.453, recal_live=0.281, recal_d=0.175, recal_p50g=0.078, # §29 anchor_p50g=0.044, anchor_p10=0.025, anchor_p90=0.082, anchor_pzero=0.353, # §32 fund_book=-2.16, fund_tp=-2.10, fund_skh=-2.32, # §35 (%/anno) ) TRIALS = 0 T0 = time.time() def bump(n: int = 1) -> None: global TRIALS TRIALS += n def hr(t: str = "") -> None: print("\n" + "-" * 104) if t: print(f" {t}") print("-" * 104) def tick(lab: str) -> None: print(f" [{time.time()-T0:6.0f}s] {lab}") # ============================================================================ 1. IL FUNDING def funding_rate() -> dict | None: """Serie oraria del funding per asset (LINEARE dove esiste, INVERSE prima come proxy dichiarato). Legge SOLO la cache di `r0822d_funding` — nessuna rete.""" try: F = {} for instr, (start, _a, _k) in FD.INSTR.items(): d = FD.fetch_funding(instr, start).copy() d["dt"] = pd.to_datetime(d["ts"], unit="ms", utc=True) F[instr] = d.drop_duplicates(subset="ts").sort_values("dt").set_index("dt") return FD.build_rate(F) except Exception as exc: # pragma: no cover print(f" !! cache funding assente/illeggibile ({exc}) — il funding NON viene messo dentro") return None _FTP: dict[int, pd.Series] = {} def f_tp01_sleeve(rate: dict) -> pd.Series: """Costo di funding GIORNALIERO dello sleeve TP01 a peso 1.0, sulla STESSA lente delle gambe orarie del recon (`mc.tp01_daily_frame` = TP01 canonico, senza min-order: su un conto funded da $100k il min-order $5 non esiste, e questa e' la lente giusta li'). Convenzione temporale identica a `FD.tp01_canonical`: la posizione tenuta nel giorno d e' decisa a close[d-1], e paga il funding sulle 24 ore (d, d+24h].""" if 0 in _FTP: return _FTP[0] out = None for a in ASSETS: tpf = mc.tp01_daily_frame(a) days = pd.DatetimeIndex(tpf.index) s = pd.Series(tpf["w"].values * FD.daily_funding(rate[a], days), index=days) out = s if out is None else out.add(s, fill_value=0.0) _FTP[0] = out return out _FSKH: dict[int, pd.Series] = {} def f_skh_sleeve(rate: dict, off: int = 0) -> pd.Series: """Costo di funding giornaliero dello sleeve SKH01 (50/50) all'offset `off`, come DIFFERENZA fra il path live senza e con funding (`FD.skh_book`). E' esatto al primo ordine (l'equity compone: il secondo ordine e' ~1e-6/giorno) e riproduce il -2,32%/anno di §35.""" if off in _FSKH: return _FSKH[off] a0 = FD.skh_book(off, "hourly", None) a1 = FD.skh_book(off, "hourly", rate) if a0.index.tz is None: a0.index = a0.index.tz_localize("UTC") if a1.index.tz is None: a1.index = a1.index.tz_localize("UTC") s = (a0 - a1).dropna() _FSKH[off] = s return s def xs_funding_measure(verbose: bool = True) -> dict: """IL SEGNO DEL FUNDING SU XS01, MISURATO (non assunto). XS01 e' long 5 / short 5 a pesi uguali -> `sum(W) = 0` ESATTAMENTE. Quindi `sum(W_i f_i) = sum(W_i (f_i - fbar))`: **il LIVELLO del funding non entra, entra solo la sua DISPERSIONE CROSS-SEZIONALE**. Cio' significa due cose: * non e' vero ne' che 'incassa' ne' che 'paga' per costruzione: dipende da come il funding e' distribuito FRA gli asset che il momentum compra e vende; * il fatto noto che il funding Hyperliquid sia ~2,1x quello del venue vero (§35) si trasferisce alla dispersione, quindi si riporta una BANDA, non un punto. Dato: `data/raw/hlfund__1h.parquet` (19 major, 2023-05 -> 2026-06).""" P = X.hl_panel() det: dict = {} X.xsec_engine(P, phase=0, detail=det) W, scale, idx = det["W"], det["scale"], det["index"] cols = list(P.columns) gross_sum = float(np.abs(W.sum(axis=1)).max()) fun = {} for s in cols: f = RAW / f"hlfund_{s.lower()}_1h.parquet" if not f.exists(): continue d = pd.read_parquet(f)["funding"] d.index = pd.DatetimeIndex(d.index).tz_convert("UTC") fun[s] = d.groupby(d.index.floor("D")).sum() have = [s for s in cols if s in fun] Fm = pd.DataFrame({s: fun[s] for s in have}).reindex(idx) cover = Fm.notna().mean(axis=1) Fv = Fm.fillna(0.0).values sel = [cols.index(s) for s in have] Wp = np.vstack([np.zeros((1, len(cols))), W[:-1]]) # posizioni TENUTE nel giorno i cost = pd.Series((Wp[:, sel] * Fv).sum(axis=1) * scale, index=idx) gross = pd.Series(np.abs(Wp[:, sel]).sum(axis=1) * scale, index=idx) ok = cover >= 0.95 c = cost[ok] if verbose: print(f" pannello HL {P.shape[1]} gambe, {len(idx)} giorni; file di funding per " f"{len(have)}/{len(cols)} gambe") print(f" CONTROLLO di dollar-neutralita': max|sum(W)| = {gross_sum:.2e} " f"-> {'ESATTO' if gross_sum < 1e-12 else 'NON neutrale'}") print(f" giorni con copertura >=95% delle gambe: {int(ok.sum())} " f"({c.index.min().date()} -> {c.index.max().date()})") print(f"\n {'anno':>6} {'costo funding %/anno':>22} {'gross nozionale':>17} {'giorni':>7}") for y, g in c.groupby(c.index.year): gg = gross.reindex(g.index) print(f" {y:>6} {float(g.mean()*365)*100:>21.3f}% {float(gg.mean()):>16.3f}x " f"{len(g):>7}") ann = float(c.mean() * 365) se = float(c.std(ddof=1) / np.sqrt(len(c)) * 365) print(f" {'TUTTO':>6} {ann*100:>21.3f}% {float(gross.reindex(c.index).mean()):>16.3f}x " f"{len(c):>7} IC95 [{(ann-1.96*se)*100:+.3f}%, {(ann+1.96*se)*100:+.3f}%]") ann = float(c.mean() * 365) return dict(ann=ann, series=c, gross=float(gross.reindex(c.index).mean()), se=float(c.std(ddof=1) / np.sqrt(len(c)) * 365), n=len(c)) # ================================================================ 2. LIBRO CON FUNDING def fvec_for(w: dict, idx: pd.DatetimeIndex, rate: dict | None, off: int, xs_ann: float) -> np.ndarray | None: """Vettore giornaliero del costo di funding del LIBRO ai pesi `w`. TP01 e SKH01: serie misurate giorno per giorno. XS01: **tasso annuo costante** misurato su Hyperliquid 2024-2026 e applicato all'intera finestra — dichiarato, perche' `hlfund` parte nel 2023-05 e il pannello di XS01 usato qui e' Binance 2021+ (nessun funding su disco).""" if rate is None: return None f = np.zeros(len(idx)) wtp, wsk, wxs = w.get("TP01", 0.0), w.get("SKH01", 0.0), w.get("XS01", 0.0) if wtp: f += wtp * np.nan_to_num(f_tp01_sleeve(rate).reindex(idx).values) if wsk: f += wsk * np.nan_to_num(f_skh_sleeve(rate, off).reindex(idx).values) if wxs: f += wxs * (xs_ann / 365.0) return f def best_lev(R, G, ev, fu) -> tuple[float, float, np.ndarray]: jb, lb, vb = -1.0, None, None for lv in LEVS: o = PR.obj_rg(R, G, lv, ev, fu, factor=DELUCK) if o["J"] > jb: jb, lb, vb = o["J"], lv, o["jvec"] bump(len(LEVS)) return jb, lb, vb # ============================================ 3. SCALA DI CONTI COL DEPOSITO RIMBORSABILE def ladder(R: np.ndarray, G: np.ndarray, n_paths: int, lev: float, seed: int, refund: bool, deposit_eur: float = 0.0, start_eur: float = pl.START_EUR, min_payout: float = 0.0, boot: np.ndarray | None = None, block: int = 20, deluck: float = DELUCK) -> dict: """Copia di `r0725_prop_coupled.simulate` (un solo vettore di peso, `MAX_CONCURRENT` slot) con TRE aggiunte e nessun'altra differenza: 1. `refund` — la quota torna in cassa **al PRIMO PAYOUT** di quel conto (regola vera della firm). Se il conto busta prima di un payout, la quota e' persa. 2. `deposit_eur` — versamento mensile, per confrontare la strada-biglietto con la strada-versare a parita' di soldi che entrano. 3. `min_payout` — quota trader minima per poter prelevare ($100 secondo la firm). Con `refund=False, deposit_eur=0, min_payout=0, boot=None` deve essere **BIT-EXACT** rispetto a `pc.simulate`: e' il controllo di §3.1. Il funding, se c'e', e' gia' dentro `R` -> essendo `Rs = R*lev`, il costo di funding **scala con la leva**, che e' la sua vera economia (e' proporzionale all'esposizione).""" rng = np.random.default_rng(seed) n_days = HORIZON_DAYS L = len(R) Rm = R.astype(float).reshape(-1, 1).copy() Gm = G.astype(float).reshape(-1, 1).copy() Rm = Rm - (1.0 - deluck) * Rm.mean(axis=0, keepdims=True) if boot is None: n_blocks = int(np.ceil(n_days / block)) starts = rng.integers(0, L - block, size=(n_paths, n_blocks)) idx = (starts[:, :, None] + np.arange(block)[None, None, :]).reshape(n_paths, -1)[:, :n_days] else: idx = boot n_slot = pl.MAX_CONCURRENT sel = np.zeros(n_slot, dtype=int) Rs = Rm[idx][:, :, sel] * lev G_ = Gm[idx][:, :, sel] * lev FIRMS = pl.FIRMS cash = np.full(n_paths, start_eur * EURUSD) phase = np.full((n_paths, n_slot), -1, np.int8) eq = np.ones((n_paths, n_slot)) firm = np.full((n_paths, n_slot), -1, np.int8) notional = np.zeros((n_paths, n_slot)) price_paid = np.zeros((n_paths, n_slot)) refunded = np.zeros((n_paths, n_slot), bool) firm_alive = np.ones((n_paths, len(FIRMS)), bool) cum_payout = np.zeros(n_paths) spent = np.zeros(n_paths) got_back = np.zeros(n_paths) deposited = np.zeros(n_paths) n_payouts = np.zeros(n_paths, int) p_death_day = 1.0 - (1.0 - pl.FIRM_DEATH_PER_YEAR) ** (1 / 365.0) for t in range(n_days): died = rng.random((n_paths, len(FIRMS))) < p_death_day newly_dead = died & firm_alive firm_alive &= ~died if newly_dead.any(): for fi in range(len(FIRMS)): kill = newly_dead[:, fi] if kill.any(): hit = (firm == fi) & kill[:, None] phase[hit] = -1 notional[hit] = 0.0 for k in range(n_slot): free = phase[:, k] == -1 if not free.any(): continue for fi, F in enumerate(FIRMS): used = np.where(firm == fi, notional, 0.0).sum(axis=1) for (size, price) in sorted(F["tickets"], key=lambda x: -x[0]): buy = (free & firm_alive[:, fi] & (cash >= price) & (used + size <= F["cap"])) if not buy.any(): continue cash[buy] -= price spent[buy] += price phase[buy, k] = 0 eq[buy, k] = 1.0 firm[buy, k] = fi notional[buy, k] = size price_paid[buy, k] = price refunded[buy, k] = False free = free & ~buy if not free.any(): break if not free.any(): break r = Rs[:, t, :] g = G_[:, t, :] act = phase >= 0 eq_prev = eq eq = np.where(act, eq * (1.0 + r), eq) eq_low = np.where(act, eq_prev * (1.0 + r + g), eq) r_low = r + g for fi, F in enumerate(FIRMS): s_ = act & (firm == fi) if not s_.any(): continue ev_t, ev_dd, ev_dl = F["ev"] fu_ml, fu_dl = F["fu"] in_ev = s_ & (phase == 0) in_fu = s_ & (phase == 1) bust = ((in_ev & ((eq_low < 1.0 - ev_dd) | (r_low < -ev_dl))) | (in_fu & ((eq_low < 1.0 - fu_ml) | (r_low < -fu_dl)))) phase[bust] = -1 notional[bust] = 0.0 passed = in_ev & (eq >= 1.0 + ev_t) & ~bust phase[passed] = 1 eq[passed] = 1.0 if (t + 1) % 30 == 0: fu = phase == 1 pay = fu & (eq > 1.0) & (((eq - 1.0) * notional * SPLIT) >= min_payout) gain = np.where(pay, (eq - 1.0) * notional, 0.0) net = gain.sum(axis=1) * SPLIT * (1.0 - TAX_RATE) cum_payout += net cash += net n_payouts += (pay.sum(axis=1) > 0).astype(int) eq = np.where(pay, 1.0, eq) if refund: back = pay & ~refunded amt = np.where(back, price_paid, 0.0).sum(axis=1) cash += amt got_back += amt refunded |= back if deposit_eur: cash += deposit_eur * EURUSD deposited += deposit_eur * EURUSD eur_day = cum_payout / EURUSD / (n_days / 365.0) / 365.0 return dict(eur_day=eur_day, med=float(np.median(eur_day)), p10=float(np.percentile(eur_day, 10)), p90=float(np.percentile(eur_day, 90)), p_ge10=float((eur_day >= 10.0).mean()), p_target=float((eur_day >= pl.TARGET_EUR_DAY).mean()), p_zero=float((cum_payout <= 0).mean()), p_payout=float((n_payouts > 0).mean()), cash=cash, med_cash=float(np.median(cash)), mean_cash=float(cash.mean()), p10_cash=float(np.percentile(cash, 10)), p90_cash=float(np.percentile(cash, 90)), med_spent=float(np.median(spent)), mean_back=float(got_back.mean()), contributed=float(start_eur * EURUSD + deposited.mean())) def book_road(rb: np.ndarray, boot: np.ndarray, start_eur: float, deposit_eur: float, deluck: float = DELUCK, tax: float = TAX_RATE) -> dict: """La strada ALTERNATIVA: gli stessi soldi restano sul LIBRO LIVE (proprio capitale, nessuna barriera, nessuna firm) e si versa. Stessi sorteggi di bootstrap della strada-biglietto (confronto APPAIATO), stesso de-luck, stesso orizzonte. Fisco: 33% sulla variazione ANNUA di valore, con riporto delle minusvalenze (convenzione di `r0807_piano_netto`; il libro realizza quasi tutto entro l'anno -> limite superiore stretto).""" R = rb.astype(float).copy() R = R - (1.0 - deluck) * R.mean() r = R[boot] n_paths, n_days = r.shape cap = np.full(n_paths, start_eur * EURUSD) basis = cap.copy() carry = np.zeros(n_paths) deposited = 0.0 for t in range(n_days): cap = np.maximum(cap * (1.0 + r[:, t]), 0.0) if (t + 1) % 30 == 0 and deposit_eur: cap += deposit_eur * EURUSD basis += deposit_eur * EURUSD deposited += deposit_eur * EURUSD if (t + 1) % 365 == 0: g = (cap - basis) + carry due = np.where(g > 0, tax * g, 0.0) carry = np.where(g > 0, 0.0, g) cap = cap - due basis = cap.copy() return dict(cash=cap, med_cash=float(np.median(cap)), mean_cash=float(cap.mean()), p10_cash=float(np.percentile(cap, 10)), p90_cash=float(np.percentile(cap, 90)), contributed=float(start_eur * EURUSD + deposited)) # ==================================================== 4. IL BIGLIETTO A CONTO SINGOLO, 2 STADI def ticket_mc(R: np.ndarray, G: np.ndarray, lev_ev: float, lev_fu: float, notional: float, n_paths: int = N_PATHS_TIX, seed: int = 82241, max_eval_days: int = 540, funded_days: int = 365, min_payout: float = MIN_PAYOUT_TRADER, consistency: bool = True, firm_death: bool = True, block: int = 20, deluck: float = DELUCK, firm: str = HYRO) -> dict: """UN conto HYRO, i due stadi **CHAINED sullo stesso percorso** (eval -> funded), che e' la differenza rispetto a `PA.eval_sim`/`PA.funded_sim`, che girano su bootstrap INDIPENDENTI e moltiplicano le probabilita'. Ritorna cio' che serve a prezzare il deposito come opzione: p_pass = P(passare l'eval entro `max_eval_days`) p_payout = P(arrivare ad ALMENO un payout) <- l'evento che rimborsa la quota p_pay_given = P(payout | pass) e_payout = E[payout netti], incondizionato, sui `funded_days` successivi al pass """ tgt, dd, dl = pl.FIRMS[[f["name"] for f in pl.FIRMS].index(firm)]["ev"] ml, dlf = pl.FIRMS[[f["name"] for f in pl.FIRMS].index(firm)]["fu"] rng = np.random.default_rng(seed) Rd = R.astype(float) - (1.0 - deluck) * float(np.mean(R)) total = max_eval_days + funded_days L = len(Rd) nb = int(np.ceil(total / block)) st = rng.integers(0, L - block, size=(n_paths, nb)) bi = (st[:, :, None] + np.arange(block)[None, None, :]).reshape(n_paths, -1)[:, :total] rr = Rd[bi] gg = G[bi] eq = np.ones(n_paths) phase = np.zeros(n_paths, np.int8) # 0 eval, 1 funded, -1 morto, 2 finito best_day = np.zeros(n_paths) fu_day = np.zeros(n_paths, int) payout = np.zeros(n_paths) ever = np.zeros(n_paths, bool) passed = np.zeros(n_paths, bool) t_pass = np.full(n_paths, -1, int) p_death_day = 1.0 - (1.0 - pl.FIRM_DEATH_PER_YEAR) ** (1 / 365.0) if firm_death else 0.0 for t in range(total): if p_death_day: phase[(rng.random(n_paths) < p_death_day) & (phase >= 0)] = -1 alive = phase >= 0 if not alive.any(): break in_ev = alive & (phase == 0) in_fu = alive & (phase == 1) lev = np.where(in_fu, lev_fu, lev_ev) rt = rr[:, t] * lev gt = gg[:, t] * lev eqp = eq eq = np.where(alive, eq * (1.0 + rt), eq) eq_low = np.where(alive, eqp * (1.0 + rt + gt), eq) r_low = rt + gt bust = ((in_ev & ((eq_low < 1.0 - dd) | (r_low < -dl))) | (in_fu & ((eq_low < 1.0 - ml) | (r_low < -dlf)))) phase[bust] = -1 gain = np.maximum(eq - eqp, 0.0) best_day = np.where(in_ev & ~bust, np.maximum(best_day, gain), best_day) profit = eq - 1.0 ok = in_ev & ~bust & (profit >= tgt) if consistency: ok &= best_day <= pl.FIRMS[0].get("consistency", 0.40) * np.maximum(profit, 1e-9) phase[ok] = 1 eq[ok] = 1.0 fu_day[ok] = 0 passed |= ok t_pass[ok] = t still_ev = (phase == 0) & (t >= max_eval_days - 1) phase[still_ev] = -1 # eval scaduta now_fu = phase == 1 fu_day[now_fu & ~ok] += 1 chk = now_fu & (fu_day > 0) & (fu_day % 30 == 0) pay = chk & (eq > 1.0) & (((eq - 1.0) * notional * SPLIT) >= min_payout) payout += np.where(pay, (eq - 1.0) * notional * SPLIT * (1.0 - TAX_RATE), 0.0) ever |= pay eq = np.where(pay, 1.0, eq) phase[now_fu & (fu_day >= funded_days)] = 2 return dict(p_pass=float(passed.mean()), p_payout=float(ever.mean()), p_pay_given=float(ever.sum() / max(passed.sum(), 1)), e_payout=float(payout.mean()), e_payout_given=float(payout[ever].mean()) if ever.any() else 0.0, t_pass=float(np.median(t_pass[passed])) if passed.any() else float("nan")) def ev_three_ways(res: dict, price: float) -> dict: """Le tre convenzioni sul deposito, sullo STESSO Monte Carlo. (i) SPESA PERSA — la quota non torna mai (`pc.simulate`, il numero operativo) (ii) RIMBORSO AL PASS — la quota torna se passi (`r0725_hyro:200`) (iii) RIMBORSO AL PAYOUT — la quota torna col primo prelievo (la regola VERA della firm) """ e, pp, pa = res["e_payout"], res["p_pass"], res["p_payout"] return dict(sunk=e - price, at_pass=e - price * (1.0 - pp), at_payout=e - price * (1.0 - pa), p_break_even=float(np.clip((price - e) / price, 0.0, 1.0)) if price else 0.0) # ==================================================================================== main def main() -> None: try: sys.stdout.reconfigure(line_buffering=True) except Exception: # pragma: no cover pass print("=" * 104) print(" GATE PROP-01 gamba (c) — il BIGLIETTO come OPZIONE, il FUNDING dentro, e il") print(" confronto con l'alternativa ovvia: tenere i $635 sul libro e versare") print("=" * 104) print(" PREVISIONE REGISTRATA PRIMA: (1) il rimborso vale +5-15% di EV e +1-3pp su") print(" P(>=50/g); (2) P(primo payout | pass) > 80%; (3) il funding costa ~0,75 x 2,16%") print(" su un funded a 0,75x; (4) sul segno del funding di XS01 NON ho una previsione,") print(" le due forze tirano in verso opposto; (5) EV positivo sulla MEDIA e negativo") print(" sulla MEDIANA — una lotteria, non un investimento.") # ============================================================== §0.1 riproduzione r0725_hyro hr("§0.1 RIPRODUZIONE — `r0725_hyro` (25/07): l'EV del biglietto come fu pubblicato") buf = io.StringIO() t = time.time() import r0725_hyro as HY # noqa: E402 with contextlib.redirect_stdout(buf): HY.main() txt = buf.getvalue() lens_now, ev_now = None, {} for line in txt.splitlines(): if "[close-only]" in line: lens_now = "close" elif "[intraday]" in line: lens_now = "intra" m = re.match(r"\s*\$\s*100,000\s+\$\s*([\d,]+)\s+\$\s*([\d,]+)\s+\$\s*([\d,]+)\s+" r"\$\s*([\-\d,]+)", line) if m and lens_now: ev_now[lens_now] = float(m.group(4).replace(",", "")) print(f" rigirato oggi ({time.time()-t:.0f}s), stessa macchina, stessi semi, biglietto $100k:") print(f" {'lente':>14} {'EV oggi':>12} {'EV PUBBLICATO 25/07':>22} {'delta':>10}") for k, lab, p in (("close", "close-only", PUB["hyro_ev_close"]), ("intra", "intraday", PUB["hyro_ev_intra"])): v = ev_now.get(k, float("nan")) print(f" {lab:>14} ${v:>11,.0f} ${p:>21,.0f} {v-p:>+10,.0f}") print(" Lo scarto NON e' un errore di riproduzione: `r0725_hyro` ri-deriva le proprie serie") print(" dal feed CORRENTE, e dal 25/07 a oggi il feed ha ~4 settimane di barre in piu'.") print(" Ordine di grandezza e conclusione identici -> la macchina e' quella.") print("\n ⚠ E il codice riprodotto contiene gia' la contraddizione: `cost = price*(1-pe)`") print(" = quota RIMBORSATA AL PASS, mentre `pc.simulate` (da cui esce il numero") print(" operativo P(>=50/g)) fa `cash -= price` e non la restituisce MAI.") bump(1) tick("§0.1") # ============================================================== dati comuni hr("§0.2 IL PANNELLO — le stesse serie di PROP-RECAL (U13 Binance, W-LONG 2021+)") Cb, Ob, Hb, Lb = PR.ohlc_panels(PR.bin_loader, XS_UNIVERSE) p13 = Cb[X.U13].dropna() o13 = Ob.reindex(p13.index)[X.U13] h13 = Hb.reindex(p13.index)[X.U13] l13 = Lb.reindex(p13.index)[X.U13] XSP = {ph: PR.xs_tuples(p13, o13, h13, l13, phase=ph) for ph in PHASES_SUB} bump(len(PHASES_SUB)) xs0 = XSP[PHASES_SUB[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] ev_b = PA.boot_idx(len(I), EV_H, N_PATHS_J, seed=SEED_EV) fu_b = PA.boot_idx(len(I), FU_H, N_PATHS_J, seed=SEED_FU) print(f" W-LONG: {len(I)} giorni {I[0].date()} -> {I[-1].date()} | de-luck x{DELUCK} | " f"{N_PATHS_J} percorsi per J") pc.DELUCK = DELUCK # J all'ancora canonica, mediana sulle 3 fasi dichiarate js, jlv = [], [] for ph in PHASES_SUB: Rc, Gc = AP.rg_off(W_A, I, XSP[ph], 0) j, lv, _ = best_lev(Rc, Gc, ev_b, fu_b) js.append(j); jlv.append(lv) RL0, GL0 = AP.rg_off(W_LIVE, I, xs0, 0) jL, lvL, vL0 = best_lev(RL0, GL0, ev_b, fu_b) dpair = [] for ph in PHASES_SUB: Rc, Gc = AP.rg_off(W_A, I, XSP[ph], 0) _, _, vc = best_lev(Rc, Gc, ev_b, fu_b) dpair.append(float((vc - vL0).mean())) print(f" J(W_A 38/38/25) ancora canonica, fasi {PHASES_SUB}: mediana {np.median(js):.3f} " f"(PUB {PUB['recal_j']:.3f} su 10 fasi) | leve {jlv}") print(f" J(LIVE 75/25) : {jL:.3f} @{lvL:.2f}x " f"(PUB {PUB['recal_live']:.3f})") print(f" delta appaiato mediano : {np.median(dpair):+.3f} " f"(PUB {PUB['recal_d']:+.3f})") tick("§0.2") # ============================================================== §1 il funding hr("§1. IL FUNDING, MESSO DENTRO — e il segno su XS01, MISURATO") rate = funding_rate() if rate is None: print(" Senza cache non si mette dentro nulla: il resto gira SENZA funding e lo dichiara.") xs_ann = 0.0 else: fb0 = FD.book_75_25(None, alloc=654.0) fb1 = FD.book_75_25(rate, alloc=654.0) d_book = float((fb0 - fb1).dropna().mean() * 365) * 100 ftp = f_tp01_sleeve(rate) fsk = f_skh_sleeve(rate, 0) c0, _ = FD.tp01_canonical(None) c1, _ = FD.tp01_canonical(rate) d_tp = float((c0 - c1).dropna().mean() * 365) * 100 print(f" RIPRODUZIONE dei costi di §35 (%/anno, segno = costo):") print(f" {'serie':>36} {'oggi':>9} {'PUB §35':>9}") print(f" {'LIBRO 75/25 path live':>36} {-d_book:>8.2f}% {PUB['fund_book']:>8.2f}%") print(f" {'TP01 canonico':>36} {-d_tp:>8.2f}% {PUB['fund_tp']:>8.2f}%") print(f" {'SKH01 50/50 path live (off 0)':>36} " f"{-float(fsk.mean()*365)*100:>8.2f}% {PUB['fund_skh']:>8.2f}%") print(f" {'TP01 gambe orarie (lente prop)':>36} " f"{-float(ftp.reindex(I).mean()*365)*100:>8.2f}% {'—':>9}") print("\n ⚠ La riga 'gambe orarie' non e' la stessa di 'TP01 canonico': il recon orario") print(" usa `mc.tp01_daily_frame` (senza min-order, che e' la lente giusta su un") print(" conto funded da $100k). Le due lenti differiscono di poco e si dichiarano.") print("\n IL SEGNO DEL FUNDING SU XS01 — misurato, non assunto:") xsf = xs_funding_measure() xs_ann = xsf["ann"] print(f"\n >>> XS01 **PAGA** {xs_ann*100:+.2f}%/anno di funding (segno positivo = costo).") print(" La previsione registrata ('non lo so, le due forze tirano in verso opposto')") print(" si risolve cosi': il momentum cross-sezionale COMPRA i perp col funding piu'") print(" caro e VENDE quelli col funding piu' basso -> paga la dispersione.") print(" Essendo `sum(W)=0` il LIVELLO non entra: entra solo la DISPERSIONE.") print(f" Se il funding Hyperliquid e' ~2,1x quello del venue eseguibile (§35),") print(f" la banda onesta e' [{xs_ann/2.1*100:+.2f}%, {xs_ann*100:+.2f}%]/anno.") print("\n LA LEVA — perche' il funding non e' un costo fisso:") print(" il funding e' proporzionale all'ESPOSIZIONE, e l'esposizione di un conto funded") print(" e' `lev x` quella del libro -> nel simulatore `Rs = R*lev` porta dentro anche il") print(" costo, che quindi scala. Verifica numerica (libro W_A, costo %/anno di equity):") fA = fvec_for(W_A, I, rate, 0, xs_ann) for lv in (0.50, 0.75, 1.00, 1.25): print(f" leva {lv:>4.2f}x -> {-float(np.mean(fA))*365*100*lv:>6.2f}%/anno " f"(= {lv:.2f} x {-float(np.mean(fA))*365*100:.2f}%)") tick("§1") # ============================================================== §2 banda d'ancora hr("§2. LA BANDA D'ANCORA — 23 offset x 3 fasi, con e SENZA funding (riproduzione + delta)") print(f" Sottocampione dichiarato, identico a `r0822c`: {len(OFFSETS)} offset x " f"{len(PHASES_SUB)} fasi {PHASES_SUB} = {len(OFFSETS)*len(PHASES_SUB)} celle, " f"x2 (funding on/off).") print(f" Ladder: {N_PATHS_LAD} percorsi (risoluzione ~+-1pp su P(>=50/g)), lente ACCOPPIATA,") print(f" {pl.MAX_CONCURRENT} conti max, morte-firm {pl.FIRM_DEATH_PER_YEAR:.0%}/anno, " f"deposito NON rimborsato (= convenzione di `pc.simulate`, per riprodurre).") cells: list[dict] = [] t = time.time() for off in OFFSETS: fA = fvec_for(W_A, I, rate, off, xs_ann) for ph in PHASES_SUB: R0, G0 = AP.rg_off(W_A, I, XSP[ph], off) for tag, Rx in (("nofund", R0), ("fund", R0 - fA if fA is not None else None)): if Rx is None: continue j, lv, _ = best_lev(Rx, G0, ev_b, fu_b) lad = ladder(Rx, G0, N_PATHS_LAD, lv, SEED_LAD, refund=False) cells.append(dict(off=off, ph=ph, tag=tag, J=j, lev=lv, p50=lad["p_target"], pz=lad["p_zero"], med=lad["med"], ppay=lad["p_payout"])) bump(2) C = pd.DataFrame(cells) print(f" ({time.time()-t:.0f}s)") print(f"\n {'variante':>12} {'J mediana':>10} {'P(>=50/g)':>10} {'banda p10-p90':>18} " f"{'P(zero)':>9} {'EUR/g med':>10}") for tag, lab in (("nofund", "SENZA funding"), ("fund", "CON funding")): s = C[C["tag"] == tag] if not len(s): continue print(f" {lab:>12} {s['J'].median():>10.3f} {s['p50'].median():>10.1%} " f"[{np.percentile(s['p50'],10):>7.1%},{np.percentile(s['p50'],90):>7.1%}] " f"{s['pz'].median():>9.1%} {s['med'].median():>10.2f}") s_nf = C[C["tag"] == "nofund"] print(f"\n RIPRODUZIONE §32: P(>=50/g) mediana {s_nf['p50'].median():.1%} " f"(PUB {PUB['anchor_p50g']:.1%}) | banda " f"[{np.percentile(s_nf['p50'],10):.1%}, {np.percentile(s_nf['p50'],90):.1%}] " f"(PUB [{PUB['anchor_p10']:.1%}, {PUB['anchor_p90']:.1%}]) | " f"P(zero) {s_nf['pz'].median():.1%} (PUB {PUB['anchor_pzero']:.1%})") if rate is not None: s_f = C[C["tag"] == "fund"] m = C.pivot_table(index=["off", "ph"], columns="tag", values="p50") dpp = (m["fund"] - m["nofund"]) mj = C.pivot_table(index=["off", "ph"], columns="tag", values="J") dj = (mj["fund"] - mj["nofund"]) print(f"\n IL FUNDING, appaiato cella per cella (mediana delle DIFFERENZE, non") print(f" differenza delle mediane): dJ {dj.median():+.4f} " f"({int((dj<0).sum())}/{len(dj)} celle negative) | " f"dP(>=50/g) {dpp.median()*100:+.2f}pp " f"({int((dpp<0).sum())}/{len(dpp)} negative)") print(f" Il numero operativo col funding dentro: **P(>=50 EUR/g) " f"{s_f['p50'].median():.1%}** [{np.percentile(s_f['p50'],10):.1%}, " f"{np.percentile(s_f['p50'],90):.1%}], P(zero) {s_f['pz'].median():.1%}") # cella d'ancora MEDIANA su cui si prezza il biglietto ref_tag = "fund" if rate is not None else "nofund" S = C[C["tag"] == ref_tag].reset_index(drop=True) k_med = int((S["p50"] - S["p50"].median()).abs().idxmin()) OFF_M, PH_M, LEV_M = int(S.loc[k_med, "off"]), int(S.loc[k_med, "ph"]), float(S.loc[k_med, "lev"]) print(f"\n CELLA D'ANCORA MEDIANA scelta per prezzare il biglietto: offset {OFF_M} min, " f"fase {PH_M}, leva {LEV_M:.2f}x") print(" (non l'ancora canonica: quella e' al 91° pctl dei 23 offset — §32)") fM = 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 if fM is not None else R_M0 tick("§2") # ============================================================== §3 il biglietto a 2 stadi hr("§3. IL BIGLIETTO COME OPZIONE — i DUE stadi, sullo stesso percorso") print(" Stadio 1: passare l'eval (target 10%, maxDD 6% STATICO, daily 4%, consistency 40%).") print(" Stadio 2: sul funded, arrivare ad ALMENO un payout — che e' l'evento che") print(" rimborsa il deposito. Soglia: quota trader >= $100 (fonte dichiarativa della firm).") print(f" {N_PATHS_TIX} percorsi, blocchi di 20 giorni, morte-firm dentro, lente accoppiata.") rows = [] for lev_ev in (0.50, 0.75, 1.00): for lev_fu in (0.50, 0.75, 1.00): r = ticket_mc(R_M, G_M, lev_ev=lev_ev, lev_fu=lev_fu, notional=100_000.0) rows.append(dict(lev_ev=lev_ev, lev_fu=lev_fu, **{k: r[k] for k in ("p_pass", "p_payout", "p_pay_given", "e_payout", "e_payout_given", "t_pass")})) bump(1) T = pd.DataFrame(rows) print(f"\n {'leva eval':>10} {'leva fund':>10} {'P(pass)':>9} {'P(payout)':>10} " f"{'P(pay|pass)':>12} {'E[payout]':>11} {'E[pay|pay]':>12} {'gg al pass':>11}") for _, x in T.iterrows(): print(f" {x['lev_ev']:>10.2f} {x['lev_fu']:>10.2f} {x['p_pass']:>9.1%} " f"{x['p_payout']:>10.1%} {x['p_pay_given']:>12.1%} ${x['e_payout']:>10,.0f} " f"${x['e_payout_given']:>11,.0f} {x['t_pass']:>11.0f}") best = T.loc[T["e_payout"].idxmax()] LEV_EV, LEV_FU = float(best["lev_ev"]), float(best["lev_fu"]) print(f"\n Leve che massimizzano E[payout]: eval {LEV_EV:.2f}x / funded {LEV_FU:.2f}x") print(f" (per confronto, la leva che massimizza J alla stessa cella e' {LEV_M:.2f}x: J e'") print(" P(pass) x P(vivo), che NON e' E[payout] — l'ottimo non deve coincidere)") pg = T["p_pay_given"] print(f" >>> PREVISIONE (2) 'P(primo payout | pass) > 80%': vale in " f"{int((pg > 0.80).sum())}/{len(pg)} celle (banda {pg.min():.1%}-{pg.max():.1%}) -> " f"{'CONFERMATA salvo a leva funded 1.00x' if (pg > 0.80).sum() >= 6 else 'SMENTITA'}") print(" Il verso e' meccanico e vale la pena dirlo: piu' leva sul funded alza") print(" E[payout] e ABBASSA P(payout), perche' fa bustare prima del primo prelievo.") print(" Sono due obiettivi diversi: chi vuole il RIMBORSO non vuole la leva massima.") hr("§3.1 LE TRE CONVENZIONI SUL DEPOSITO — stesso Monte Carlo, tre economie") print(" (i) SPESA PERSA `pc.simulate` -> EV = E[payout] - prezzo") print(" (ii) RIMBORSO AL PASS `r0725_hyro` -> EV = E[payout] - prezzo x (1 - P(pass))") print(" (iii)RIMBORSO AL PAYOUT la firm -> EV = E[payout] - prezzo x (1 - P(payout))") print(f"\n {'biglietto':>12} {'prezzo':>8} {'E[payout]':>11} {'(i) persa':>11} " f"{'(ii) al pass':>13} {'(iii) al payout':>16} {'EUR/g atteso':>13}") tix_rows = [] for size, price in pl.FIRMS[0]["tickets"]: r = ticket_mc(R_M, G_M, lev_ev=LEV_EV, lev_fu=LEV_FU, notional=float(size)) e3 = ev_three_ways(r, price) tix_rows.append(dict(size=size, price=price, **r, **e3)) print(f" ${size:>11,} ${price:>7,.0f} ${r['e_payout']:>10,.0f} ${e3['sunk']:>10,.0f} " f"${e3['at_pass']:>12,.0f} ${e3['at_payout']:>15,.0f} " f"{e3['at_payout']/EURUSD/365:>13.2f}") bump(1) TX = pd.DataFrame(tix_rows) big = TX.iloc[-1] rJ = ticket_mc(R_M, G_M, lev_ev=LEV_M, lev_fu=LEV_M, notional=100_000.0) eJ = ev_three_ways(rJ, 579.0) bump(1) print(f"\n Lo STESSO biglietto $100k alla leva che massimizza J ({LEV_M:.2f}x su entrambi") print(" gli stadi, cioe' la leva con cui e' costruito tutto il resto di questo filone):") print(f" P(pass) {rJ['p_pass']:.1%} | P(payout) {rJ['p_payout']:.1%} | " f"E[payout] ${rJ['e_payout']:,.0f} | EV persa ${eJ['sunk']:+,.0f} | " f"EV al payout ${eJ['at_payout']:+,.0f}") print(" -> l'EV e' positivo a ENTRAMBE le leve: la conclusione non dipende da quale") print(" obiettivo (J o E[payout]) si sceglie per tararla.") d_conv = big["at_payout"] - big["sunk"] print(f"\n Quanto vale la CORREZIONE (da 'spesa persa' a 'rimborso al payout'), $100k: " f"${d_conv:+,.0f} = {d_conv/max(abs(big['sunk']),1e-9)*100:+.1f}% dell'EV.") print(f" Quanto separa le due convenzioni SBAGLIATE fra loro (al pass vs al payout): " f"${big['at_pass']-big['at_payout']:+,.0f} — cioe' prezzo x (P(pass)-P(payout)) = " f"${big['price']*(big['p_pass']-big['p_payout']):+,.0f}.") print(f" >>> PREVISIONE (1) 'il rimborso vale +5-15% di EV': " f"{'CONFERMATA' if 0.05 <= d_conv/max(abs(big['sunk']),1e-9) <= 0.15 else 'SMENTITA'}") print(f"\n SENSIBILITA': con E[payout] = ${big['e_payout']:,.0f} e prezzo ${big['price']:,.0f},") print(f" l'EV al payout resta positivo finche' P(payout) > " f"{max(0.0,(big['price']-big['e_payout'])/big['price']):.1%} — misurata " f"{big['p_payout']:.1%}.") print(" ⚠ E' un EV in DOLLARI ATTESI. Su un conto da $654 il biglietto si compra poche") print(" volte: la media non e' la grandezza che decide. Si guarda la distribuzione (§5).") print(" ⚠ RICONCILIAZIONE (tre P(payout) diverse compaiono in questo file, e NON sono in") print(" contraddizione): qui e' UN biglietto su UN anno di funded; la scala di conti") print(" (§4-§6) lascia correre 36 mesi e ricompra -> la sua P e' piu' alta. Confrontare") print(" le due senza dire la finestra e' l'errore che le farebbe sembrare incoerenti.") tick("§3") # ============================================================== §4 ladder col rimborso hr("§4. LA SCALA DI CONTI COL DEPOSITO RIMBORSABILE") print(" §4.1 CONTROLLO — con `refund=False, deposito=0, min_payout=0` il mio simulatore") print(" dev'essere BIT-EXACT rispetto a `pc.simulate` (stesso seme, stesso stream).") cfg = tuple(W_A.get(n, 0.0) for n in PA.CRYPTO_PROP) key = (cfg[0], cfg[1], cfg[2], "shared") saved = pc._TUP_CACHE.pop(key, None) pc._TUP_CACHE[key] = (R_M, G_M, I) ref = pc.simulate([cfg], [0] * pl.MAX_CONCURRENT, n_paths=N_PATHS_LAD, lev=LEV_M, seed=SEED_LAD, lens="coupled", start=str(I[0].date())) pc._TUP_CACHE.pop(key, None) if saved is not None: pc._TUP_CACHE[key] = saved mine = ladder(R_M, G_M, N_PATHS_LAD, LEV_M, SEED_LAD, refund=False) dmax = float(np.abs(np.sort(ref["eur_day"]) - np.sort(mine["eur_day"])).max()) dmax2 = float(np.abs(ref["eur_day"] - mine["eur_day"]).max()) print(f" max|diff| percorso per percorso = {dmax2:.3e} (ordinati: {dmax:.3e}) -> " f"{'IDENTICO' if dmax2 < 1e-12 else 'DIVERGE — STOP'}") if dmax2 >= 1e-12: print(" Il simulatore non replica: i numeri sotto sarebbero di un'altra macchina.") return bump(1) print("\n §4.2 QUANTO VALE IL RIMBORSO (cella d'ancora mediana, stessi semi)") print(f" {'convenzione':>34} {'EUR/g med':>10} {'P(>=10/g)':>10} {'P(>=50/g)':>10} " f"{'P(zero)':>9} {'cassa fin. med':>15} {'media':>10}") lad_rows = {} for tag, rf, mp in (("spesa persa (pubblicato)", False, 0.0), ("spesa persa + soglia $100", False, MIN_PAYOUT_TRADER), ("RIMBORSO al primo payout", True, MIN_PAYOUT_TRADER)): r = ladder(R_M, G_M, N_PATHS_LAD, LEV_M, SEED_LAD, refund=rf, min_payout=mp) lad_rows[tag] = r print(f" {tag:>34} {r['med']:>10.2f} {r['p_ge10']:>10.1%} {r['p_target']:>10.1%} " f"{r['p_zero']:>9.1%} ${r['med_cash']:>14,.0f} ${r['mean_cash']:>9,.0f}") bump(1) a = lad_rows["spesa persa + soglia $100"] b = lad_rows["RIMBORSO al primo payout"] print(f"\n Delta del rimborso: P(>=50/g) {(b['p_target']-a['p_target'])*100:+.2f}pp | " f"EUR/g mediana {b['med']-a['med']:+.2f} | cassa finale mediana " f"${b['med_cash']-a['med_cash']:+,.0f} | quota restituita media ${b['mean_back']:,.0f}") print(f" >>> PREVISIONE (1) 'il rimborso vale +1-3pp su P(>=50/g)': " f"{'CONFERMATA' if 0.01 <= (b['p_target']-a['p_target']) <= 0.03 else 'SMENTITA'} " f"({(b['p_target']-a['p_target'])*100:+.2f}pp)") tick("§4") # ============================================================== §5 biglietto vs versare hr("§5. IL BIGLIETTO CONTRO IL VERSARE — stessa metrica, stesso orizzonte, stessi sorteggi") print(" Strada A (BIGLIETTO): $654 comprano il biglietto piu' grande possibile; la scala di") print(" conti gira il libro W_A 38/38/25 sul conto della firm (XS01 LI' E' ESEGUIBILE),") print(" deposito rimborsabile, morte-firm, cap $200k/firm; la cassa resta FERMA.") print(" Strada B (LIBRO): gli stessi $654 restano sul libro LIVE 75/25 a min-order $5") print(" (XS01 NON e' eseguibile a $654), nessuna barriera, fisco 33% sull'anno.") print(" ⚠ L'asimmetria fra le due strade e' REALE ed e' il punto del canale funded: il") print(" conto della firm da' accesso a sleeve che il capitale proprio non puo' eseguire.") rb_full = FD.book_75_25(rate, alloc=654.0) if rate is not None else FD.book_75_25(None, alloc=654.0) if rb_full.index.tz is None: rb_full.index = rb_full.index.tz_localize("UTC") rb = np.nan_to_num(rb_full.reindex(I).values.astype(float)) print(f"\n libro LIVE a $654 (funding {'DENTRO' if rate is not None else 'FUORI'}): " f"drift {rb.mean()*365*100:.2f}%/anno, vol {rb.std()*np.sqrt(365)*100:.1f}%, " f"Sharpe {rb.mean()*365/(rb.std()*np.sqrt(365)):.2f} su {len(rb)} giorni") rngb = np.random.default_rng(51224) nb = int(np.ceil(HORIZON_DAYS / 20)) st = rngb.integers(0, len(I) - 20, size=(N_PATHS_LAD, nb)) BOOT = (st[:, :, None] + np.arange(20)[None, None, :]).reshape(N_PATHS_LAD, -1)[:, :HORIZON_DAYS] print(f" bootstrap CONDIVISO fra le due strade: {N_PATHS_LAD} percorsi x {HORIZON_DAYS} " f"giorni, blocchi 20 -> confronto APPAIATO") print(f"\n {'versamento':>12} {'strada':>10} {'mediana':>11} {'p10':>10} {'p90':>10} " f"{'media':>11} {'P(sotto il versato)':>20} {'P(>=50/g)':>10}") cmp_rows = [] for dep in (0.0, 250.0, 500.0): A = ladder(R_M, G_M, N_PATHS_LAD, LEV_M, SEED_LAD, refund=True, deposit_eur=dep, min_payout=MIN_PAYOUT_TRADER, boot=BOOT) B = book_road(rb, BOOT, pl.START_EUR, dep) bump(2) for lab, r in (("BIGLIETTO", A), ("LIBRO", B)): p_loss = float((r["cash"] < r["contributed"]).mean()) p50g = r.get("p_target", 0.0) print(f" {f'EUR {dep:,.0f}/mese':>12} {lab:>10} ${r['med_cash']:>10,.0f} " f"${r['p10_cash']:>9,.0f} ${r['p90_cash']:>9,.0f} ${r['mean_cash']:>10,.0f} " f"{p_loss:>20.1%} {p50g:>10.1%}") cmp_rows.append(dict(dep=dep, road=lab, med=r["med_cash"], mean=r["mean_cash"], p10=r["p10_cash"], p90=r["p90_cash"], ploss=p_loss)) print(f" {'':>12} {'delta':>10} ${A['med_cash']-B['med_cash']:>+10,.0f} " f"{'':>10} {'':>10} ${A['mean_cash']-B['mean_cash']:>+10,.0f}") CMP = pd.DataFrame(cmp_rows) print("\n ('P(>=50/g)' e' definita solo per la strada BIGLIETTO: la strada LIBRO non preleva") print(" durante l'accumulo. La colonna che si confronta e' la RICCHEZZA TERMINALE.)") tick("§5") # ============================================================== §6 la curva del capitale hr("§6. LA CURVA DEL CAPITALE — a che conto il biglietto smette di essere la strada migliore") print(" Stesso confronto appaiato, capitale iniziale variabile, nessun versamento (cosi' la") print(" sola variabile e' il capitale). La strada LIBRO resta il 75/25 a ogni taglia:") print(" e' una scelta CONSERVATIVA CONTRO la strada libro (a $5k/$20k il libro potrebbe") print(" aggiungere XSR01/XS01) -> il capitale d'incrocio misurato e' un LIMITE SUPERIORE.") cheap = min(t[1] for F in pl.FIRMS for t in F["tickets"]) print(f"\n {'capitale':>10} {'A mediana':>12} {'B mediana':>12} {'A media':>12} " f"{'B media':>12} {'A P(0 pay)':>11} {'A P(<${:.0f})'.format(cheap):>12} " f"{'A med|>0':>10} {'vince med.':>11}") caps = (600.0, 1_000.0, 2_000.0, 3_500.0, 6_000.0, 10_000.0, 20_000.0, 40_000.0) cross_med = cross_mean = None prev = None for ce in caps: A = ladder(R_M, G_M, N_PATHS_LAD, LEV_M, SEED_LAD, refund=True, start_eur=ce, min_payout=MIN_PAYOUT_TRADER, boot=BOOT) B = book_road(rb, BOOT, ce, 0.0) bump(2) wm = "BIGLIETTO" if A["med_cash"] > B["med_cash"] else "LIBRO" nz = A["cash"][A["cash"] >= cheap] p_stuck = float((A["cash"] < cheap).mean()) print(f" EUR{ce:>7,.0f} ${A['med_cash']:>11,.0f} ${B['med_cash']:>11,.0f} " f"${A['mean_cash']:>11,.0f} ${B['mean_cash']:>11,.0f} {A['p_zero']:>11.1%} " f"{p_stuck:>12.1%} ${np.median(nz) if len(nz) else 0:>9,.0f} {wm:>11}") cur = (A["med_cash"] > B["med_cash"], A["mean_cash"] > B["mean_cash"]) if prev is not None: if prev[0] != cur[0] and cross_med is None: cross_med = (prev[2], ce) if prev[1] != cur[1] and cross_mean is None: cross_mean = (prev[2], ce) prev = (cur[0], cur[1], ce) print(f"\n Incrocio sulla MEDIANA: {cross_med if cross_med else 'nessuno nella griglia'}") print(f" Incrocio sulla MEDIA : {cross_mean if cross_mean else 'nessuno nella griglia'}") print("\n ⚠ IL 'GRADINO' FRA EUR 1.000 E EUR 2.000 NON E' UN MECCANISMO: e' la MEDIANA") print(" che cambia MODO. La distribuzione della strada-biglietto e' BIMODALE — o si") print(" finisce SENZA nemmeno i soldi per ricomprare il biglietto piu' economico, o si") print(f" finisce con migliaia. La colonna `P(<${cheap:.0f})` e' la massa del modo basso e") print(" si muove LISCIA; la mediana complessiva salta di un ordine di grandezza appena") print(" quella massa attraversa il 50%. **La mediana non e' una statistica robusta qui**:") print(" quelle robuste sono `P(<$249)`, `P(0 payout)` e la media, che SATURANO.") print(" -> il 'capitale d'incrocio' e' il punto in cui una massa di probabilita' passa") print(" il 50%, NON una soglia con un meccanismo dietro. Va detto, perche' un numero") print(" cosi' netto e' esattamente quello che si finirebbe per citare come soglia.") print("\n §6.1 LA SECONDA FIRM — l'unico ingrediente STRUTTURALE che cambia con la cassa") print(" (FTMO: max loss 10% contro il 6% STATICO di HYRO — il vincolo che il") print(" progetto misura come binding su questo libro fin dal 25/07).") print(" Si legge su MEDIA e P(0 payout), non sulla mediana (vedi sopra).") all_firms = pl.FIRMS print(f"\n {'capitale':>10} {'firm ammesse':>16} {'mediana':>11} {'media':>11} " f"{'P(0 payout)':>11} {'cassa med':>11}") for ce in (1_000.0, 2_000.0, 3_500.0): for lab, fl in (("solo HYRO", [all_firms[0]]), ("HYRO+FTMO", all_firms)): pl.FIRMS = fl try: A = ladder(R_M, G_M, N_PATHS_LAD, LEV_M, SEED_LAD, refund=True, start_eur=ce, min_payout=MIN_PAYOUT_TRADER, boot=BOOT) finally: pl.FIRMS = all_firms bump(1) print(f" EUR{ce:>7,.0f} {lab:>16} ${A['med_cash']:>10,.0f} " f"${A['mean_cash']:>10,.0f} {A['p_zero']:>11.1%} ${np.median(A['cash']):>10,.0f}") print("\n Lettura: la seconda firm ALZA la media (piu' nozionale, e se una firm muore la") print(" cassa si ricicla sull'altra) e puo' ALZARE anche P(0 payout) al capitale piccolo,") print(" perche' converte in biglietti la cassa che altrimenti sarebbe rimasta ferma.") print(" Non e' un pasto gratis: e' piu' esposizione allo stesso rischio binario.") tick("§6") # ============================================================== §7 verdetto hr("§7. LA GAMBA (c), RIFORMULATA — e la risposta alla domanda che decide") ev_pay = float(big["at_payout"]) ev_sunk = float(big["sunk"]) A0 = [r for r in cmp_rows if r["dep"] == 0.0 and r["road"] == "BIGLIETTO"][0] B0 = [r for r in cmp_rows if r["dep"] == 0.0 and r["road"] == "LIBRO"][0] print(f" EV del biglietto $100k alla cella d'ancora MEDIANA, col funding dentro:") print(f" convenzione VERA (rimborso al primo payout) ${ev_pay:>+10,.0f}") print(f" convenzione PESSIMISTA (spesa persa) ${ev_sunk:>+10,.0f}") print(f" -> EV positivo in {'ENTRAMBE' if min(ev_pay, ev_sunk) > 0 else 'UNA SOLA' if max(ev_pay,ev_sunk)>0 else 'NESSUNA'} " f"le convenzioni.") print(f"\n A 36 mesi, senza versamenti, $654 iniziali (confronto appaiato):") print(f" BIGLIETTO mediana ${A0['med']:,.0f} media ${A0['mean']:,.0f} " f"p10 ${A0['p10']:,.0f} P(sotto il versato) {A0['ploss']:.1%}") print(f" LIBRO mediana ${B0['med']:,.0f} media ${B0['mean']:,.0f} " f"p10 ${B0['p10']:,.0f}") med_win = "BIGLIETTO" if A0["med"] > B0["med"] else "LIBRO" mean_win = "BIGLIETTO" if A0["mean"] > B0["mean"] else "LIBRO" print(f" -> sulla MEDIANA vince {med_win}; sulla MEDIA vince {mean_win}.") print(" (e la mediana qui e' una statistica BIMODALE: dice da che parte del 50% sta") print(" P(0 payout), non 'quanto' si guadagna — vedi §6)") print(f" >>> PREVISIONE (5) 'EV positivo sulla MEDIA e negativo sulla MEDIANA': " f"{'CONFERMATA' if (mean_win=='BIGLIETTO' and med_win=='LIBRO') else 'SMENTITA'}") print("\n ONESTA' OBBLIGATORIE, portate avanti e non nascoste:") print(f" * morte-firm {pl.FIRM_DEATH_PER_YEAR:.0%}/anno e' un'ASSUNZIONE del progetto") print(" (24/07), non una misura: sopra c'e' dentro, ma nessuno l'ha stimata.") print(" * la RIMBORSABILITA' e' DICHIARATIVA: viene dalla pagina della firm + fonti") print(" secondarie concordi. Nessuno l'ha vista accadere. Il progetto ha gia' pagato") print(" per un'assunzione mai verificata sul conto reale (GTAA01/PRIIPs).") print(f" * il cap ${pl.FIRMS[0]['cap']:,}/trader limita STRUTTURALMENTE il canale: il") print(" nozionale massimo su HYRO+FTMO e' $600k, cioe' un tetto al reddito, non al rischio.") print(" * XS01 sul conto funded e' misurato su U13 fuori campione, ma le 6 gambe che") print(" fuori campione non esistono restano non testabili (§29).") print(" * l'ancora GIORNALIERA di TP01 (24 ore) NON e' in questa banda: terza dimensione,") print(" dichiarata anche da §32, e la sua fortuna e' ancora dentro questi numeri.") hr("§7.1 IL TESTO PROPOSTO DELLA GAMBA (c)") price = float(big["price"]) p_nopay = 1.0 - float(big["p_payout"]) print(f" Quantita' misurate oggi, a costo ZERO (una corsa di questo script):") print(f" prezzo del biglietto $100k ${price:>10,.0f}") print(f" quota del conto ($654) immobilizzata {price/(pl.START_EUR*EURUSD):>10.1%}") print(f" P(nessun payout) -> quota persa {p_nopay:>10.1%}") print(f" PERDITA ATTESA = prezzo x P(nessun payout) ${price*p_nopay:>10,.0f}") print(f" EV con deposito NON rimborsato (pessimista) ${ev_sunk:>+10,.0f}") print(f" EV con deposito rimborsato al payout (vero) ${ev_pay:>+10,.0f}") print(f"\n Mesi di piano che il biglietto mette a rischio (quota / versamento mensile):") for dep in (250.0, 500.0, 800.0): print(f" a EUR {dep:>4,.0f}/mese: {price/(dep*EURUSD):>4.1f} mesi di versamenti " f"(perdita attesa = {price*p_nopay/(dep*EURUSD):.1f} mesi)") print("\n IL PUNTO DI STRUTTURA, che decide la FORMA del gate:") print(" * misurare l'EV costa UNA CORSA DI QUESTO SCRIPT — zero dollari, minuti;") print(" * misurare la RIMBORSABILITA' costa $579 e si puo' fare SOLO comprando.") print(" Un gate che aspettasse la seconda misura sarebbe un gate che si puo' superare solo") print(" dopo averlo violato. Percio' la gamba (c) va scritta in modo che la") print(" rimborsabilita' NON decida: si richiede EV positivo sotto la convenzione") print(" PESSIMISTA (quota mai restituita). Il rimborso diventa allora un regalo, non") print(" un'ipotesi su cui si e' scommesso.") print("\n >>> TESTO PROPOSTO — `GATE PROP-01 (c) CAPITALE`, due condizioni CONGIUNTE:") print(" (c1) ECONOMICA, misura a costo zero, da rifare a ogni cambio del libro o") print(" delle regole della firm:") print(" EV del biglietto > 0 con la quota trattata come SPESA PERSA, calcolato") print(" alla cella d'ancora MEDIANA (non canonica), col FUNDING dentro e sotto") print(" la lente ACCOPPIATA, con `r0822d_gate_c.py`.") print(f" Lettura di oggi: ${ev_sunk:+,.0f} -> " f"{'PASS' if ev_sunk > 0 else 'FAIL'}") print(" (c2) DI SOPRAVVIVENZA, verificabile dal saldo e dal piano di versamento:") print(" prezzo del biglietto <= 2 versamenti mensili del piano in corso.") print(" Motivo: il piano di accumulo e' la leva che il progetto ha misurato come") print(" dominante (26/07: da 'mai' a 16 anni), e zero e' ASSORBENTE. Un biglietto") print(" che costa piu' di due mensilita' non e' una scommessa sul canale funded:") print(" e' un ritardo del piano che lo finanzia.") print(f" Lettura di oggi (piano dichiarato EUR 500/mese, 26/07): " f"${price:,.0f} vs 2 x ${500*EURUSD:,.0f} = ${2*500*EURUSD:,.0f} -> " f"{'PASS' if price <= 2*500*EURUSD else 'FAIL'}") print(" SCADENZA: nessuna. (c1) e (c2) sono verificabili OGGI; un gate d'attesa qui") print(" non avrebbe un dato da aspettare, e stanotte due date sono cadute per questo.") print(" Se si compra, si registra PRIMA: il rimborso deve comparire col primo payout;") print(" se non compare, la convenzione vera e' 'spesa persa' — che (c1) ha gia'") print(" assunto, quindi la decisione non va rifatta, va solo annotata.") print(f"\n TRIAL/CELLE DICHIARATI (al rialzo): {TRIALS}") print(f" fatto in {time.time()-T0:.0f}s") print("=" * 104) if __name__ == "__main__": main()