"""r0823b_retry — §61 RETRY: la POLITICA DI RI-ACQUISTO del biglietto funded (2026-08-23). IL BUCO CHE CHIUDE ------------------ Un conto funded che rompe una regola muore. Il progetto ha modellato tutto il resto del ciclo (prezzo, regole, leva, lente accoppiata, funding, morte-firm, fisco) e **mai la politica di RI-ACQUISTO**: quando ricomprare dopo un bust, con quali soldi, e con quale leva sul conto nuovo. Nei simulatori esistenti (`r0725_prop_ladder.simulate`, `r0725_prop_coupled.simulate`, `r0822d_gate_c.ladder`) la politica c'e' — e' **cablata**: *compra sempre, subito, il biglietto piu' GRANDE che la cassa permette, in tutti e 6 gli slot*. E' la politica piu' aggressiva della famiglia, non e' mai stata dichiarata come scelta, e **tutti i numeri operativi del canale funded escono da li'** (42% -> 7,8% -> 4,4% -> 2,6%). E' diventata una domanda vera solo ieri, per due misure: (a) §30: la quota d'ingresso e' un **deposito RIMBORSABILE** ("Refundable Challenge Deposit, returned in full alongside your first funded payout") -> un tentativo fallito costa la quota, uno riuscito no. E' una struttura a OPZIONE, e le opzioni si comprano piu' volte. (b) §37: il biglietto batte il libro **solo perche' il piano di versamento c'e'**, cioe' **perche' si puo' giocare piu' di una volta** ("una lotteria a EV positivo che un conto da $654 puo' giocare UNA VOLTA SOLA"). Se il valore sta nel NUMERO DI TENTATIVI, la politica di ri-acquisto e' la variabile principale e non e' mai stata ottimizzata. LE ATTESE, REGISTRATE PRIMA DI MISURARE (poi riportate confermate/refutate) -------------------------------------------------------------------------- A1. **Compromesso monotono.** IMMEDIATA massimizza sia il numero di giocate sia P(cassa a zero); MAI li minimizza entrambi. Nessuna politica dovrebbe dominare su tutte e due le colonne. A2. **La dose-risposta di §37 dovrebbe essere ripida.** Se il meccanismo e' "quante volte si gioca", passare da max 1 biglietto a illimitati deve muovere P(>=50 EUR/g) di molto. Se non lo muove, il meccanismo dichiarato da §37 e' sbagliato (o e' un'altra cosa). A3. **Il biglietto PICCOLO ($249/$25k) compra piu' giocate e perde sul bersaglio.** 50 EUR/g netti sono ~$59k di payout netti in 3 anni: con nozionale $25k servono troppi cicli. Mi aspetto P(zero) piu' bassa e P(>=50/g) piu' bassa. Dominato sul bersaglio, non sulla sopravvivenza. A4. **SOLO-PAYOUT ~= MAI senza versamenti** (senza payout non c'e' cassa per ricomprare) e molto piu' vicina a IMMEDIATA con versamenti. A5. **Il RIMBORSO alza i livelli di ~1-3pp (§37) ma NON cambia l'ordine delle politiche.** Previsione forte: l'ottimo e' lo stesso nelle tre convenzioni. A6. 🚨 **Le regole di leva condizionate all'esito passato sono NULL DEL DE-LEVERING.** In un bootstrap a blocchi il passato non porta informazione sul futuro: una regola "riduci la leva dopo un bust" puo' agire SOLO cambiando la leva MEDIA. Il controllo obbligatorio e' la leva COSTANTE alla stessa media realizzata. Mi aspetto REFUTED 3/3 (8ª occorrenza). A7. **La politica conta meno del versamento.** Mi aspetto uno spread fra politiche di pochi punti percentuali contro i ~+10pp del passaggio EUR 0 -> EUR 500/mese misurato da §37. CONVENZIONI (tutte EREDITATE e congelate — nessuna scelta di strategia in questo file) -------------------------------------------------------------------------------------- * Lente **ACCOPPIATA** (`r0725_prop_coupled`): su una regola a UN GIORNO la close-only e' cieca. * Conto funded = **W_A TP01 38 / SKH01 38 / XS01 25** (centro della regione robusta di §29), XS01 = **U13 Binance** fuori campione, finestra **W-LONG 2021+**, de-luck sul drift **x0,89**. * **Funding dentro** (§35/§37), morte-firm **10%/anno** (assunzione del progetto), cap **$200k/trader**, fisco **33%**, split **80%**, soglia di prelievo **$100** di quota trader. * Banda d'ancora: **23 offset x 3 fasi (0,3,7)** = lo stesso sottocampione dichiarato da §32/§37. La cella di riferimento e' la **MEDIANA** della banda, non la canonica (91° pctl). * Cassa non spesa: **FERMA** per default (convenzione di §37) e **SUL LIBRO** come sensibilita' dichiarata in §7 — perche' la convenzione "ferma" penalizza per costruzione le politiche prudenti, che sono esattamente quelle sotto esame. * Bootstrap **CONDIVISO** fra tutte le politiche di una stessa tabella -> confronti **APPAIATI**. COSA NON E' QUESTO FILE: non e' una selezione di strategia (meccanismo, pesi, leva ottima, lente e soglie sono tutti ereditati); e' la mappatura di una politica OPERATIVA che finora era cablata. L'argmax su ~30 politiche resta pero' una selezione: si riportano il PLATEAU e la risoluzione MC. Uso: `nice -n 19 timeout 900 uv run python scripts/research/r0823b_retry.py` 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 (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 import r0822c_skh_anchor_prop as AP # noqa: E402 offset di SKH01 (gamba b) import r0822d_funding as FD # noqa: E402 il funding (22/08) import r0822d_gate_c as GC # noqa: E402 ladder di riferimento (§37) from src.portfolio.sleeves import XS_UNIVERSE # noqa: E402 FD.USE_NET = False EURUSD = pl.EURUSD TAX_RATE = pl.TAX_RATE SPLIT = pl.SPLIT DELUCK = PA.DELUCK W_A = GC.W_A OFFSETS = AP.OFFSETS PHASES_SUB = AP.PHASES_SUB MIN_PAYOUT = GC.MIN_PAYOUT_TRADER HORIZON_DAYS = GC.HORIZON_DAYS SEED_LAD = GC.SEED_LAD XS_FUND_ANN = 0.0145 # §37: XS01 PAGA +1,45%/anno di funding (misurato) N_BAND = 1000 # percorsi per la banda d'ancora (riproduzione §32/§37) N_MAIN = 2000 # percorsi per la griglia di politiche (cella di rif.) N_ROB = 1000 # percorsi per la robustezza fra celle # Sottocampione di offset dichiarato A PRIORI: gli stessi 8 uniformi di `r0726_skh_live_book` # (la griglia piena e' 23; su 2 CPU condivise con altri 5 processi la piena non sta nel budget, # e un sottocampione uniforme dichiarato prima e' l'unica forma onesta di ridurla). BAND_OFFSETS = tuple(range(0, 690, 90)) # numeri PUBBLICATI da riprodurre prima di produrne di nuovi PUB = dict(anchor_p50g=0.044, anchor_p10=0.025, anchor_p90=0.082, anchor_pzero=0.353, gatec_p50g=0.026, gatec_p10=0.015, gatec_p90=0.047, gatec_pzero=0.403, refund_pp_lo=0.01, refund_pp_hi=0.03) TRIALS = 0 T0 = time.time() CHEAPEST = min(t[1] for F in pl.FIRMS for t in F["tickets"]) # $249 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}", flush=True) # ============================================================================ LE POLITICHE def POL(when="immediate", k_reserve=1.0, cadence=0, max_tickets=10 ** 9, size="largest", lev_rule="const", lev_dn=1.0, lev_up=1.0, lev_lo=0.25, lev_hi=1.50, max_open=pl.MAX_CONCURRENT) -> dict: """Una politica di ri-acquisto. `when`: immediate — compra appena uno slot e' libero e la cassa basta (= il CABLATO di oggi) threshold — compra solo se la cassa e' >= k_reserve x il prezzo (tiene una riserva) earned — il PRIMO biglietto dai propri soldi, i successivi SOLO dai payout/rimborsi cadence — al piu' un acquisto ogni `cadence` giorni none — non compra MAI (controllo positivo: deve dare P(>=50/g)=0) `size`: 'largest' (cablato) o 'cheapest' (piu' giocate, nozionale minore). `max_open`: quanti conti al MASSIMO possono essere aperti insieme (cablato: 6 = tutti gli slot). E' l'altra faccia del ri-acquisto: giocare in SERIE invece che in PARALLELO. `lev_rule`: 'const' | 'down_on_bust' (x lev_dn a ogni bust) | 'up_on_pass' (x lev_up a ogni pass). La leva di un conto e' fissata all'ACQUISTO e non cambia piu'.""" return dict(when=when, k_reserve=k_reserve, cadence=cadence, max_tickets=max_tickets, size=size, lev_rule=lev_rule, lev_dn=lev_dn, lev_up=lev_up, lev_lo=lev_lo, lev_hi=lev_hi, max_open=max_open) P_BASE = POL() # il cablato, bit-exact vs §37 # ============================================================================ IL SIMULATORE def ladder_rt(R, G, n_paths, lev, seed, *, pol=None, refund_mode="none", deposit_eur=0.0, start_eur=pl.START_EUR, min_payout=0.0, boot=None, block=20, deluck=DELUCK, rb_boot=None, tax=TAX_RATE) -> dict: """`r0822d_gate_c.ladder` generalizzato alla POLITICA DI RI-ACQUISTO. Tre aggiunte: 1. `pol` — quando/quale biglietto/con quali soldi/a che leva (sopra); 2. `refund_mode` — 'none' (spesa persa, `pc.simulate`) | 'pass' (`r0725_hyro:200`) | 'payout' (la regola vera della firm); 3. `rb_boot` — se dato, la cassa NON spesa gira sul LIBRO LIVE invece di restare ferma, con lo STESSO sorteggio di bootstrap (stesso mercato) e lo stesso fisco annuale di `GC.book_road`. Con `pol=P_BASE, refund_mode='none', deposit_eur=0, min_payout=0, rb_boot=None` dev'essere **BIT-EXACT** rispetto a `GC.ladder(..., refund=False)` e quindi a `pc.simulate`: e' §1. 🚨 Zero e' ASSORBENTE ed e' modellato come tale: senza conti vivi e con cassa sotto il biglietto piu' economico ($249) non si gioca piu'; solo un versamento puo' riaprire il gioco. """ pol = pol or P_BASE 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 # (paths, days): tutti gli slot girano lo STESSO libro -> una colonna basta. La leva # moltiplica per-slot (serve alle regole di leva) invece che globalmente: `x*lev` e' la # stessa moltiplicazione in virgola mobile, quindi il bit-exact regge (verificato in §1). Rb = Rm[idx][:, :, 0] Gb = Gm[idx][:, :, 0] FIRMS = pl.FIRMS cash = np.full(n_paths, start_eur * EURUSD) basis = cash.copy() carry = np.zeros(n_paths) earned = np.zeros(n_paths) # borsellino dei soli payout/rimborsi 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) lev_slot = np.full((n_paths, n_slot), lev) # soglie della firm materializzate PER SLOT all'acquisto: la dinamica diventa una sola # espressione vettoriale invece di un loop sulle firm. Le maschere per firm sono disgiunte, # quindi il risultato e' identico (verificato bit-exact in §1). TH_TGT = np.zeros((n_paths, n_slot)) TH_DD = np.ones((n_paths, n_slot)) TH_DL = np.ones((n_paths, n_slot)) TH_ML = np.ones((n_paths, n_slot)) TH_DLF = np.ones((n_paths, n_slot)) mult = np.ones(n_paths) 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) n_tickets = np.zeros(n_paths, int) lev_sum = np.zeros(n_paths) last_buy = np.full(n_paths, -10 ** 9) days_out = np.zeros(n_paths, int) ever_passed = np.zeros(n_paths, bool) p_death_day = 1.0 - (1.0 - pl.FIRM_DEATH_PER_YEAR) ** (1 / 365.0) tickets_order = {} for fi, F in enumerate(FIRMS): tickets_order[fi] = sorted(F["tickets"], key=(lambda x: x[0]) if pol["size"] == "cheapest" else (lambda x: -x[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 # ---------------------------------------------------------------- acquisti if pol["when"] == "none": budget = np.zeros(n_paths, int) elif pol["when"] == "cadence": budget = ((t - last_buy) >= pol["cadence"]).astype(int) else: budget = np.full(n_paths, n_slot, int) budget = np.minimum(budget, np.maximum(pol["max_tickets"] - n_tickets, 0)) if pol["max_open"] < n_slot: budget = np.minimum(budget, np.maximum(pol["max_open"] - (phase >= 0).sum(axis=1), 0)) if budget.any() and (cash >= CHEAPEST).any(): for k in range(n_slot): free = (phase[:, k] == -1) & (budget > 0) 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 tickets_order[fi]: buy = (free & firm_alive[:, fi] & (cash >= price) & (used + size <= F["cap"])) if pol["when"] == "threshold": buy &= cash >= pol["k_reserve"] * price elif pol["when"] == "earned": buy &= (n_tickets == 0) | (earned >= price) if not buy.any(): continue cash[buy] -= price basis[buy] -= price if pol["when"] == "earned": take = buy & (n_tickets > 0) earned[take] -= price spent[buy] += price n_tickets[buy] += 1 budget[buy] -= 1 last_buy[buy] = t 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 lv = np.clip(lev * mult, pol["lev_lo"], pol["lev_hi"]) lev_slot[buy, k] = lv[buy] TH_TGT[buy, k], TH_DD[buy, k], TH_DL[buy, k] = F["ev"] TH_ML[buy, k], TH_DLF[buy, k] = F["fu"] lev_sum[buy] += lv[buy] free = free & ~buy if not free.any(): break if not free.any(): break # ---------------------------------------------------------------- dinamica dei conti r = Rb[:, t][:, None] * lev_slot g = Gb[:, t][:, None] * lev_slot 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 in_ev = act & (phase == 0) in_fu = act & (phase == 1) bust = ((in_ev & ((eq_low < 1.0 - TH_DD) | (r_low < -TH_DL))) | (in_fu & ((eq_low < 1.0 - TH_ML) | (r_low < -TH_DLF)))) phase[bust] = -1 notional[bust] = 0.0 passed = in_ev & (eq >= 1.0 + TH_TGT) & ~bust phase[passed] = 1 eq[passed] = 1.0 if pol["lev_rule"] == "down_on_bust": mult *= pol["lev_dn"] ** bust.sum(axis=1) elif pol["lev_rule"] == "up_on_pass": mult *= pol["lev_up"] ** passed.sum(axis=1) if refund_mode == "pass" and passed.any(): back = passed & ~refunded amt = np.where(back, price_paid, 0.0).sum(axis=1) cash += amt basis += amt earned += amt got_back += amt refunded |= back ever_passed |= passed.any(axis=1) # ---------------------------------------------------------------- la cassa propria if rb_boot is not None: cash = np.maximum(cash * (1.0 + rb_boot[:, t]), 0.0) # ---------------------------------------------------------------- mensile 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 basis += net earned += net n_payouts += (pay.sum(axis=1) > 0).astype(int) eq = np.where(pay, 1.0, eq) if refund_mode == "payout": back = pay & ~refunded amt = np.where(back, price_paid, 0.0).sum(axis=1) cash += amt basis += amt earned += amt got_back += amt refunded |= back if deposit_eur: cash += deposit_eur * EURUSD basis += deposit_eur * EURUSD deposited += deposit_eur * EURUSD # fisco annuale sulla sola cassa propria (i payout sono gia' netti) if rb_boot is not None and (t + 1) % 365 == 0: gtax = (cash - basis) + carry due = np.where(gtax > 0, tax * gtax, 0.0) carry = np.where(gtax > 0, 0.0, gtax) cash = cash - due basis = cash.copy() out = (~(phase >= 0).any(axis=1)) & (cash < CHEAPEST) days_out += out eur_day = cum_payout / EURUSD / (n_days / 365.0) / 365.0 stuck = (~(phase >= 0).any(axis=1)) & (cash < CHEAPEST) 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)), mean_eur=float(eur_day.mean()), 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()), p_pass=float(ever_passed.mean()), p_stuck=float(stuck.mean()), n_tix=n_tickets, med_tix=float(np.median(n_tickets)), mean_tix=float(n_tickets.mean()), lev_mean=float(lev_sum.sum() / max(n_tickets.sum(), 1)), days_out=float(np.median(days_out)), 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 se_pair(a: np.ndarray, b: np.ndarray, thr: float = pl.TARGET_EUR_DAY) -> float: """SE della DIFFERENZA fra due proporzioni APPAIATE (stessi sorteggi): McNemar. Con bootstrap condiviso la parte comune si cancella e la risoluzione e' molto migliore di quella dei livelli — ma non e' zero, e va stampata.""" A = a >= thr B = b >= thr n01 = float((A & ~B).sum()) n10 = float((~A & B).sum()) n = len(a) return float(np.sqrt(max(n01 + n10 - (n01 - n10) ** 2 / n, 0.0)) / n) # ==================================================================================== main def main() -> None: try: sys.stdout.reconfigure(line_buffering=True) except Exception: # pragma: no cover pass print("=" * 104) print(" §61 RETRY — la POLITICA DI RI-ACQUISTO del biglietto funded, mappata invece che") print(" cablata. Quando ricomprare, con quali soldi, a che leva — e quanto conta.") print("=" * 104) print(" ATTESE REGISTRATE PRIMA: A1 compromesso monotono giocate/rovina; A2 dose-risposta") print(" RIPIDA se il meccanismo di §37 e' 'il numero di volte che si gioca'; A3 biglietto") print(" piccolo = piu' giocate, meno bersaglio; A4 SOLO-PAYOUT ~= MAI senza versamenti;") print(" A5 il rimborso NON cambia l'ottimo; A6 le regole di leva sono null del de-levering") print(" (REFUTED 3/3 atteso); A7 la politica conta meno del versamento.") # ============================================================== §0 pannello hr("§0. IL PANNELLO — le stesse serie di §29/§37 (U13 Binance, W-LONG 2021+, funding dentro)") rate = GC.funding_rate() if rate is None: print(" !! cache funding assente: il filone si ferma. Un numero senza funding NON e'") print(" confrontabile con §37, e confrontarlo sarebbe l'errore che §35 ha appena") print(" pagato. 'non misurabile' e' un risultato.") return 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} xs0 = XSP[PHASES_SUB[0]] base = pc.crypto_daily_tuples(1.0, 0.0).index.intersection(xs0.index) first13 = xs0.index[np.flatnonzero(xs0["R"].values != 0.0)[0]] I = base[base >= max(first13, pd.Timestamp("2021-01-01", tz="UTC"))] pc.DELUCK = DELUCK print(f" W-LONG: {len(I)} giorni {I[0].date()} -> {I[-1].date()} | de-luck x{DELUCK} | " f"cassa iniziale EUR {pl.START_EUR:.0f} = ${pl.START_EUR*EURUSD:,.0f}") print(f" orizzonte {HORIZON_DAYS} giorni ({HORIZON_DAYS/30:.0f} versamenti da 30 giorni: " f"§57 avverte che 'EUR X/mese' qui vuol dire ogni 30 giorni — su 36 mesi torna esatto)") print(f" biglietti disponibili: " + " | ".join( f"{F['name']} " + ", ".join(f"${s/1000:.0f}k@${p:.0f}" for s, p in F['tickets']) for F in pl.FIRMS)) print(f" biglietto piu' economico ${CHEAPEST:.0f} -> sotto questa cassa e senza conti vivi") print(f" il percorso e' FUORI GIOCO (zero assorbente).") tick("§0") # ============================================================== §1 controlli hr("§1. CONTROLLI — bit-exact contro il simulatore pubblicato, e il controllo POSITIVO") R0, G0 = AP.rg_off(W_A, I, XSP[0], 0) f0 = GC.fvec_for(W_A, I, rate, 0, XS_FUND_ANN) Rc0 = R0 - f0 print(" §1.1 la mia `ladder_rt` col cablato (immediate/largest/const, nessun rimborso,") print(" nessun versamento, soglia 0, cassa ferma) contro `GC.ladder` di §37:") a = GC.ladder(Rc0, G0, N_BAND, 0.75, SEED_LAD, refund=False) b = ladder_rt(Rc0, G0, N_BAND, 0.75, SEED_LAD, pol=P_BASE) d1 = float(np.abs(a["eur_day"] - b["eur_day"]).max()) print(f" max|diff| percorso per percorso = {d1:.3e} -> " f"{'IDENTICO' if d1 < 1e-15 else 'DIVERGE — STOP'}") if d1 >= 1e-15: print(" Senza questo controllo i numeri sotto sarebbero di un'altra macchina.") return bump(2) print("\n §1.2 e `GC.ladder` e' a sua volta bit-exact contro `pc.simulate` (§37 §4.1);") print(" la catena e' quindi: ladder_rt == GC.ladder == pc.simulate.") 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] = (Rc0, G0, I) ref = pc.simulate([cfg], [0] * pl.MAX_CONCURRENT, n_paths=N_BAND, lev=0.75, 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 d2 = float(np.abs(ref["eur_day"] - b["eur_day"]).max()) print(f" max|diff| ladder_rt vs pc.simulate = {d2:.3e} -> " f"{'IDENTICO' if d2 < 1e-15 else 'DIVERGE'}") bump(1) print("\n §1.3 CONTROLLO POSITIVO del selettore di politica — un interruttore che non") print(" cambia niente e' indistinguibile da un interruttore rotto.") rngb = np.random.default_rng(51224) nb = int(np.ceil(HORIZON_DAYS / 20)) st = rngb.integers(0, len(I) - 20, size=(N_MAIN, nb)) BOOT = (st[:, :, None] + np.arange(20)[None, None, :]).reshape(N_MAIN, -1)[:, :HORIZON_DAYS] rb_full = FD.book_75_25(rate, 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)) rb_dl = rb - (1.0 - DELUCK) * rb.mean() RB_BOOT = rb_dl[BOOT] none_pol = ladder_rt(Rc0, G0, N_MAIN, 0.75, SEED_LAD, pol=POL(when="none"), boot=BOOT) bump(1) print(f" politica 'MAI COMPRARE': biglietti mediani {none_pol['med_tix']:.0f}, " f"P(>=50/g) {none_pol['p_target']:.1%}, P(zero payout) {none_pol['p_zero']:.1%} -> " f"{'OK' if none_pol['p_target'] == 0 and none_pol['p_zero'] == 1 else 'ROTTO'}") print("\n §1.4 CONTROLLO POSITIVO della cassa-sul-libro: 'MAI COMPRARE' + cassa sul libro") print(" dev'essere la strada LIBRO di §37 (`GC.book_road`), stessi sorteggi.") nb_book = ladder_rt(Rc0, G0, N_MAIN, 0.75, SEED_LAD, pol=POL(when="none"), boot=BOOT, rb_boot=RB_BOOT) road = GC.book_road(rb, BOOT, pl.START_EUR, 0.0) d3 = float(np.abs(nb_book["cash"] - road["cash"]).max()) bump(2) print(f" max|diff| sulla cassa finale = {d3:.3e} -> " f"{'IDENTICO' if d3 < 1e-9 else 'DIVERGE'} | mediana ${nb_book['med_cash']:,.0f} " f"(GC.book_road ${road['med_cash']:,.0f})") tick("§1") # ============================================================== §2 banda d'ancora hr("§2. LA BANDA D'ANCORA — riproduzione di §32/§37 e scelta della cella MEDIANA") print(f" {len(BAND_OFFSETS)} offset (sottocampione uniforme a priori di r0726, la griglia") print(f" piena e' 23) x {len(PHASES_SUB)} fasi {PHASES_SUB} = " f"{len(BAND_OFFSETS)*len(PHASES_SUB)} celle, funding DENTRO, politica CABLATA (quella") print(f" con cui i numeri sono stati pubblicati), {N_BAND} percorsi per cella.") ev_b = PA.boot_idx(len(I), GC.EV_H, PA.N_PATHS, seed=GC.SEED_EV) fu_b = PA.boot_idx(len(I), GC.FU_H, PA.N_PATHS, seed=GC.SEED_FU) cells = [] for off in BAND_OFFSETS: fA = GC.fvec_for(W_A, I, rate, off, XS_FUND_ANN) for ph in PHASES_SUB: Rx, Gx = AP.rg_off(W_A, I, XSP[ph], off) Rf = Rx - fA j, lv, _ = GC.best_lev(Rf, Gx, ev_b, fu_b) lad = ladder_rt(Rf, Gx, N_BAND, lv, SEED_LAD, pol=P_BASE) cells.append(dict(off=off, ph=ph, J=j, lev=lv, p50=lad["p_target"], pz=lad["p_zero"], med=lad["med"])) bump(1) C = pd.DataFrame(cells) print(f"\n P(>=50 EUR/g) mediana {C['p50'].median():.1%} (PUB §37 {PUB['gatec_p50g']:.1%}) | " f"banda [{np.percentile(C['p50'],10):.1%}, {np.percentile(C['p50'],90):.1%}] " f"(PUB [{PUB['gatec_p10']:.1%}, {PUB['gatec_p90']:.1%}]) | " f"P(zero) {C['pz'].median():.1%} (PUB {PUB['gatec_pzero']:.1%})") can = C[(C["off"] == 0) & (C["ph"] == 0)].iloc[0] pct_can = float((C["p50"] <= can["p50"]).mean()) print(f" cella CANONICA (off 0, fase 0): P(>=50/g) {can['p50']:.1%} = {pct_can:.0%}° pctl " f"della banda -> il gia' noto: la canonica e' la cella FORTUNATA, non la stima.") k_med = int((C["p50"] - C["p50"].median()).abs().idxmin()) OFF_M, PH_M, LEV_M = int(C.loc[k_med, "off"]), int(C.loc[k_med, "ph"]), float(C.loc[k_med, "lev"]) print(f" CELLA DI RIFERIMENTO (mediana della banda): offset {OFF_M} min, fase {PH_M}, " f"leva {LEV_M:.2f}x, P(>=50/g) {C.loc[k_med,'p50']:.1%}") fM = GC.fvec_for(W_A, I, rate, OFF_M, XS_FUND_ANN) RxM, G_M = AP.rg_off(W_A, I, XSP[PH_M], OFF_M) R_M = RxM - fM tick("§2") # ============================================================== §3 dose-risposta hr("§3. QUANTE VOLTE SI GIOCA — la dose-risposta che §37 dichiara come IL meccanismo") print(" Politica IMMEDIATA (il cablato) con un tetto DURO al numero di biglietti mai") print(" comprati. E' l'esperimento pulito: cambia SOLO quante volte si puo' giocare.") print(f" Cella di riferimento, {N_MAIN} percorsi, bootstrap condiviso, deposito rimborsato") print(" al primo payout (la regola vera), soglia di prelievo $100, nessun versamento.") print(f"\n {'tetto':>8} {'giocate med':>12} {'giocate medie':>14} {'P(>=50/g)':>10} " f"{'P(>=10/g)':>10} {'P(zero payout)':>15} {'P(fuori gioco)':>15} {'EUR/g medio':>12}") dose = {} for mt in (1, 2, 3, 5, 8, 10 ** 9): r = ladder_rt(R_M, G_M, N_MAIN, LEV_M, SEED_LAD, pol=POL(max_tickets=mt), refund_mode="payout", min_payout=MIN_PAYOUT, boot=BOOT) dose[mt] = r bump(1) lab = "illim." if mt > 100 else str(mt) print(f" {lab:>8} {r['med_tix']:>12.0f} {r['mean_tix']:>14.2f} {r['p_target']:>10.1%} " f"{r['p_ge10']:>10.1%} {r['p_zero']:>15.1%} {r['p_stuck']:>15.1%} " f"{r['mean_eur']:>12.2f}") d1_, dinf = dose[1], dose[10 ** 9] se = se_pair(dinf["eur_day"], d1_["eur_day"]) print(f"\n DA 1 GIOCATA A ILLIMITATE: P(>=50/g) {d1_['p_target']:.1%} -> " f"{dinf['p_target']:.1%} ({(dinf['p_target']-d1_['p_target'])*100:+.2f}pp, " f"SE appaiata {se*100:.2f}pp) | giocate mediane {d1_['med_tix']:.0f} -> " f"{dinf['med_tix']:.0f} | P(zero) {d1_['p_zero']:.1%} -> {dinf['p_zero']:.1%}") ripida = (dinf["p_target"] - d1_["p_target"]) > 3 * se print(f" >>> ATTESA A2 ('dose-risposta RIPIDA se il meccanismo e' il numero di giocate'): " f"{'CONFERMATA' if ripida else 'REFUTATA'}") tick("§3") # ============================================================== §4 le politiche hr("§4. LE POLITICHE DI RI-ACQUISTO — 7 regole di TEMPO x 2 di TAGLIA, senza versamenti") print(" Le due colonne che vanno SEMPRE lette insieme: 'giocate' e 'P(fuori gioco)'. Una") print(" politica aggressiva aumenta entrambe, ed e' esattamente la trappola dichiarata.") print("\n Prima di misurare, l'ARITMETICA del listino — prezzo per $1.000 di nozionale:") for F in pl.FIRMS: print(" " + f"{F['name']:>5} " + " ".join( f"${s_/1000:.0f}k: ${p_/(s_/1000):.2f}/k" for s_, p_ in F["tickets"])) print(" -> il biglietto GRANDE costa MENO per unita' di nozionale (HYRO $5,79/k contro") print(" $9,96/k). Piu' giocate piccole NON sono piu' esposizione: sono meno.") POLS = [ ("IMMEDIATA (il cablato)", POL()), ("SOGLIA 2x il prezzo", POL(when="threshold", k_reserve=2.0)), ("SOGLIA 3x il prezzo", POL(when="threshold", k_reserve=3.0)), ("SOLO DAI PAYOUT", POL(when="earned")), ("CADENZA 90 giorni", POL(when="cadence", cadence=90)), ("CADENZA 180 giorni", POL(when="cadence", cadence=180)), ("UN CONTO ALLA VOLTA", POL(max_open=1)), ("DUE CONTI ALLA VOLTA", POL(max_open=2)), ("MAI (1 biglietto)", POL(max_tickets=1)), ] res4 = {} for size in ("largest", "cheapest"): lab_s = "biglietto PIU' GRANDE ($100k)" if size == "largest" else "biglietto PIU' ECONOMICO ($25k)" print(f"\n --- {lab_s} ---") print(f" {'politica':>26} {'giocate med':>12} {'P(>=50/g)':>10} {'P(>=10/g)':>10} " f"{'P(zero)':>9} {'P(fuori gioco)':>15} {'EUR/g med':>10} {'cassa fin. med':>15}") for name, p in POLS: pp = dict(p) pp["size"] = size r = ladder_rt(R_M, G_M, N_MAIN, LEV_M, SEED_LAD, pol=pp, refund_mode="payout", min_payout=MIN_PAYOUT, boot=BOOT) res4[(size, name)] = r bump(1) print(f" {name:>26} {r['med_tix']:>12.0f} {r['p_target']:>10.1%} " f"{r['p_ge10']:>10.1%} {r['p_zero']:>9.1%} {r['p_stuck']:>15.1%} " f"{r['med']:>10.2f} ${r['med_cash']:>14,.0f}") base_r = res4[("largest", "IMMEDIATA (il cablato)")] print(f"\n DIFFERENZE APPAIATE contro il cablato (stessi sorteggi), su P(>=50 EUR/g):") print(f" {'politica':>26} {'dP(>=50/g)':>12} {'SE appaiata':>13} {'dP(fuori gioco)':>17} " f"{'d giocate med':>14}") rows4 = [] for size in ("largest", "cheapest"): for name, _ in POLS: r = res4[(size, name)] if (size, name) == ("largest", "IMMEDIATA (il cablato)"): continue s = se_pair(r["eur_day"], base_r["eur_day"]) tag = name if size == "largest" else "[pic] " + name rows4.append(dict(tag=tag, d=r["p_target"] - base_r["p_target"], se=s, dz=r["p_stuck"] - base_r["p_stuck"], dt=r["med_tix"] - base_r["med_tix"], p50=r["p_target"], pstuck=r["p_stuck"])) print(f" {tag:>26} {(r['p_target']-base_r['p_target'])*100:>11.2f}pp " f"{s*100:>12.2f}pp {(r['p_stuck']-base_r['p_stuck'])*100:>16.2f}pp " f"{r['med_tix']-base_r['med_tix']:>14.0f}") R4 = pd.DataFrame(rows4) sig = R4[R4["d"].abs() > 2 * R4["se"]] print(f"\n Politiche che si distinguono dal cablato oltre 2 SE appaiate: " f"{len(sig)}/{len(R4)}" + (f" (fra cui {', '.join(list(sig['tag'].str.strip())[:4])}...)" if len(sig) else "")) allp = pd.concat([R4[["tag", "p50", "pstuck"]], pd.DataFrame([dict(tag="IMMEDIATA (il cablato)", p50=base_r["p_target"], pstuck=base_r["p_stuck"])])], ignore_index=True) print(f" Spread fra la politica migliore e la peggiore su P(>=50/g): " f"{(allp['p50'].max()-allp['p50'].min())*100:.2f}pp " f"(max {allp.loc[allp['p50'].idxmax(),'tag'].strip()} {allp['p50'].max():.1%}, " f"min {allp.loc[allp['p50'].idxmin(),'tag'].strip()} {allp['p50'].min():.1%})") s3 = res4[("largest", "SOGLIA 3x il prezzo")] print(f"\n ⚠ A $654 la SOGLIA 3x e' degenere: il biglietto piu' economico costa ${CHEAPEST:.0f}") print(f" e 3x sono ${3*CHEAPEST:.0f} > ${pl.START_EUR*EURUSD:,.0f} -> giocate mediane " f"{s3['med_tix']:.0f}. Non e' una politica prudente, e' 'non giocare' scritta") print(f" con un'altra sintassi. La soglia in VALUTA ASSOLUTA dipende dal capitale:") print(f" stessa lezione della banda di GTAA01 (27/07), su un altro parametro.") pic = res4[("cheapest", "IMMEDIATA (il cablato)")] a3a = pic["p_target"] < base_r["p_target"] a3b = pic["p_stuck"] < base_r["p_stuck"] print(f"\n >>> ATTESA A3: il biglietto piccolo perde sul bersaglio " f"({pic['p_target']:.1%} contro {base_r['p_target']:.1%}) -> " f"{'confermata' if a3a else 'REFUTATA'}; e sulla ROVINA " f"({pic['p_stuck']:.1%} contro {base_r['p_stuck']:.1%}) -> " f"{'confermata' if a3b else 'REFUTATA'}. Verdetto A3: " f"{'CONFERMATA' if (a3a and a3b) else 'PARZIALE/REFUTATA'}") tick("§4") # ============================================================== §5 la leva hr("§5. LA LEVA DEL CONTO NUOVO — e il NULL DEL DE-LEVERING, dichiarato PRIMA") print(" 🚨 In un bootstrap a blocchi il passato NON porta informazione sul futuro: una") print(" regola 'riduci la leva dopo un bust' puo' agire SOLO cambiando la leva MEDIA. Il") print(" controllo obbligatorio e' la leva COSTANTE alla stessa media realizzata. Se la") print(" regola non la batte, e' de-levering travestito (sarebbe l'8ª occorrenza).") lev_rules = [ ("costante (il cablato)", POL()), ("RIDOTTA x0,80 a ogni bust", POL(lev_rule="down_on_bust", lev_dn=0.80)), ("RIDOTTA x0,60 a ogni bust", POL(lev_rule="down_on_bust", lev_dn=0.60)), ("CRESCENTE x1,25 a ogni pass", POL(lev_rule="up_on_pass", lev_up=1.25)), ] print(f"\n {'regola di leva':>30} {'leva media':>11} {'P(>=50/g)':>10} {'P(zero)':>9} " f"{'P(fuori gioco)':>15} {'EUR/g medio':>12}") lev_res = {} for name, p in lev_rules: r = ladder_rt(R_M, G_M, N_MAIN, LEV_M, SEED_LAD, pol=p, refund_mode="payout", min_payout=MIN_PAYOUT, boot=BOOT) lev_res[name] = r bump(1) print(f" {name:>30} {r['lev_mean']:>11.3f} {r['p_target']:>10.1%} {r['p_zero']:>9.1%} " f"{r['p_stuck']:>15.1%} {r['mean_eur']:>12.2f}") print(f"\n IL NULL: leva COSTANTE alla stessa media realizzata di ciascuna regola") print(f" {'regola':>30} {'leva cost.':>11} {'P(>=50/g) null':>15} " f"{'P(>=50/g) regola':>17} {'delta':>9} {'SE':>8} {'P(fuori) null':>14} {'verdetto':>9}") n_ref = 0 for name, _ in lev_rules[1:]: r = lev_res[name] lv_eq = r["lev_mean"] nul = ladder_rt(R_M, G_M, N_MAIN, lv_eq, SEED_LAD, pol=POL(), refund_mode="payout", min_payout=MIN_PAYOUT, boot=BOOT) bump(1) d = r["p_target"] - nul["p_target"] s = se_pair(r["eur_day"], nul["eur_day"]) ok = d > 2 * s n_ref += 0 if ok else 1 print(f" {name:>30} {lv_eq:>11.3f} {nul['p_target']:>15.1%} {r['p_target']:>17.1%} " f"{d*100:>8.2f}pp {s*100:>7.2f}pp {nul['p_stuck']:>14.1%} " f"{'BATTE' if ok else 'REFUTED':>9}") print(f"\n >>> ATTESA A6 ('null del de-levering, REFUTED 3/3'): " f"{'CONFERMATA' if n_ref == 3 else f'PARZIALE ({n_ref}/3 refutate)'}") print(" Lettura: la leva del conto nuovo non e' una politica, e' un LIVELLO — e il livello") print(" ottimo e' gia' cercato simmetricamente da `best_lev` in ogni cella (§29).") print("\n §5.1 DIAGNOSTICO (non una proposta): il LIVELLO di leva, costante, spazzato sulla") print(" stessa griglia LEVS di §29. `best_lev` massimizza J = P(pass) x P(vivo), NON") print(" P(>=50 EUR/g): §29 lo dichiara ('J e P(>=50/g) hanno argmax diversi'). Sceglierla") print(" qui sul bersaglio sarebbe selezione sulla metrica d'uscita — si mostra il PREZZO.") print(f"\n {'leva':>8} {'P(>=50/g)':>10} {'P(>=10/g)':>10} {'P(zero payout)':>15} " f"{'P(fuori gioco)':>15} {'EUR/g medio':>12} {'EUR/g mediano':>14}") for lv in PR.LEVS: r = ladder_rt(R_M, G_M, N_MAIN, lv, SEED_LAD, pol=POL(), refund_mode="payout", min_payout=MIN_PAYOUT, boot=BOOT) bump(1) star = " <- best_lev (J)" if abs(lv - LEV_M) < 1e-9 else "" print(f" {lv:>8.3f} {r['p_target']:>10.1%} {r['p_ge10']:>10.1%} {r['p_zero']:>15.1%} " f"{r['p_stuck']:>15.1%} {r['mean_eur']:>12.2f} {r['med']:>14.2f}{star}") tick("§5") # ============================================================== §6 le tre convenzioni hr("§6. LE TRE CONVENZIONI SUL DEPOSITO — cambia l'OTTIMO, o solo il livello?") print(" (i) SPESA PERSA `pc.simulate` — da cui esce il numero operativo del progetto;") print(" (ii) RIMBORSO AL PASS `r0725_hyro:200` — l'altro modello, contraddittorio;") print(" (iii) RIMBORSO AL PRIMO PAYOUT — la regola dichiarata dalla firm (§30).") sel6 = ["IMMEDIATA (il cablato)", "SOGLIA 2x il prezzo", "SOLO DAI PAYOUT", "CADENZA 180 giorni", "MAI (1 biglietto)"] conv = [("(i) spesa persa", "none"), ("(ii) al pass", "pass"), ("(iii) al primo payout", "payout")] tab6 = {} print(f"\n {'politica':>26} " + " ".join(f"{c[0]:>22}" for c in conv)) for name in sel6: p = dict([x for x in POLS if x[0] == name][0][1]) line = f" {name:>26} " for lab, rm in conv: r = ladder_rt(R_M, G_M, N_MAIN, LEV_M, SEED_LAD, pol=p, refund_mode=rm, min_payout=MIN_PAYOUT, boot=BOOT) tab6[(name, rm)] = r bump(1) line += f"{r['p_target']:>10.1%}/{r['p_stuck']:>9.1%} " print(line + " <- P(>=50/g) / P(fuori gioco)") print("\n ARGMAX per convenzione (su P(>=50 EUR/g)):") same = True arg0 = None for lab, rm in conv: vals = {n: tab6[(n, rm)]["p_target"] for n in sel6} arg = max(vals, key=vals.get) if arg0 is None: arg0 = arg same &= (arg == arg0) print(f" {lab:>22}: {arg:>26} ({vals[arg]:.1%})") dpp = np.median([tab6[(n, "payout")]["p_target"] - tab6[(n, "none")]["p_target"] for n in sel6]) print(f"\n Valore del rimborso (mediana su {len(sel6)} politiche, da 'spesa persa' a") print(f" 'rimborso al primo payout'): {dpp*100:+.2f}pp su P(>=50/g) " f"(PUB §37: +1-3pp atteso, misurato {'dentro' if PUB['refund_pp_lo'] <= dpp <= PUB['refund_pp_hi'] else 'FUORI'} banda)") print(f" Quota media restituita: ${tab6[(sel6[0],'payout')]['mean_back']:,.0f} per percorso") print(f" >>> ATTESA A5 ('il rimborso NON cambia l'ottimo'): " f"{'CONFERMATA' if same else 'REFUTATA'}") tick("§6") # ============================================================== §7 con i versamenti hr("§7. CON I VERSAMENTI — dove la politica di ri-acquisto smette di essere gratis") print(" Con un versamento mensile lo ZERO SMETTE DI ESSERE ASSORBENTE (la cassa si") print(" riempie di nuovo): P(fuori gioco) va a zero per TUTTE le politiche, e il costo") print(" della politica aggressiva non si legge piu' li' ma sulla RICCHEZZA TERMINALE.") print(" Convenzione principale: cassa non spesa SUL LIBRO (la piu' equa verso le politiche") print(" prudenti); la convenzione di §37 (cassa FERMA) e' riportata come scarto.") dep_rows = [] for dep in (0.0, 250.0, 500.0): print(f"\n --- versamento EUR {dep:,.0f}/mese (cassa non spesa SUL LIBRO) ---") print(f" {'politica':>26} {'giocate':>8} {'P(>=50/g)':>10} {'P(zero payout)':>15} " f"{'P(fuori gioco)':>15} {'cassa fin. med':>15} {'P(12} " f"{'speso/entrato':>14}") for name in sel6: p_ = dict([x for x in POLS if x[0] == name][0][1]) a_ = ladder_rt(R_M, G_M, N_MAIN, LEV_M, SEED_LAD, pol=p_, refund_mode="payout", min_payout=MIN_PAYOUT, boot=BOOT, deposit_eur=dep) b_ = ladder_rt(R_M, G_M, N_MAIN, LEV_M, SEED_LAD, pol=p_, refund_mode="payout", min_payout=MIN_PAYOUT, boot=BOOT, deposit_eur=dep, rb_boot=RB_BOOT) bump(2) ploss = float((b_["cash"] < b_["contributed"]).mean()) dep_rows.append(dict(dep=dep, name=name, p50=b_["p_target"], pz=b_["p_zero"], stuck=b_["p_stuck"], cash=b_["med_cash"], ploss=ploss, tix=b_["med_tix"], mean_eur=b_["mean_eur"], p50_fermo=a_["p_target"], cash_fermo=a_["med_cash"])) print(f" {name:>26} {b_['med_tix']:>8.0f} {b_['p_target']:>10.1%} " f"{b_['p_zero']:>15.1%} {b_['p_stuck']:>15.1%} ${b_['med_cash']:>14,.0f} " f"{ploss:>12.1%} {b_['med_spent']/b_['contributed']:>14.1%}") if dep == 500.0 and name == sel6[0]: dep500 = b_ if dep == 0.0 and name == sel6[0]: dep0 = b_ if dep == 500.0 and name == "SOLO DAI PAYOUT": dep500_safe = b_ D7 = pd.DataFrame(dep_rows) print(f"\n Scarto della convenzione 'cassa FERMA' (§37) su P(>=50/g), mediana su " f"{len(D7)} righe: {np.median(D7['p50_fermo']-D7['p50'])*100:+.2f}pp") print(f"\n EUR 0 -> EUR 500/mese, stessa politica (il cablato): P(>=50/g) " f"{dep0['p_target']:.1%} -> {dep500['p_target']:.1%} " f"({(dep500['p_target']-dep0['p_target'])*100:+.2f}pp)") print(f" Spread fra POLITICHE (§4, senza versamenti): " f"{(allp['p50'].max()-allp['p50'].min())*100:.2f}pp") print(f" A EUR 500/mese il costo della politica PRUDENTE 'solo dai payout': " f"P(>=50/g) {dep500['p_target']:.1%} -> {dep500_safe['p_target']:.1%} " f"({(dep500_safe['p_target']-dep500['p_target'])*100:+.2f}pp), cassa mediana " f"${dep500['med_cash']:,.0f} -> ${dep500_safe['med_cash']:,.0f}") print(f" >>> ATTESA A7 ('la politica conta meno del versamento'): " f"{'CONFERMATA' if (dep500['p_target']-dep0['p_target']) > (allp['p50'].max()-allp['p50'].min()) else 'REFUTATA'}") tick("§7") # ============================================================== §8 robustezza fra celle hr("§8. ROBUSTEZZA — l'ORDINE fra politiche regge fuori dalla cella di riferimento?") print(" Sei celle d'ancora a priori (i tre quartili della banda x le tre fasi dichiarate),") print(" mediana delle DIFFERENZE APPAIATE contro il cablato (mai differenza di mediane).") Cs = C.sort_values("p50").reset_index(drop=True) picks = [Cs.iloc[int(q * (len(Cs) - 1))] for q in (0.15, 0.35, 0.50, 0.65, 0.85)] sel8 = ["SOGLIA 2x il prezzo", "SOLO DAI PAYOUT", "MAI (1 biglietto)"] st2 = np.random.default_rng(7761).integers(0, len(I) - 20, size=(N_ROB, nb)) BOOT2 = (st2[:, :, None] + np.arange(20)[None, None, :]).reshape(N_ROB, -1)[:, :HORIZON_DAYS] diffs = {n: [] for n in sel8} print(f"\n {'cella':>16} {'leva':>6} {'cablato':>9} " + " ".join(f"{n[:16]:>18}" for n in sel8)) for row in picks: off, ph = int(row["off"]), int(row["ph"]) fX = GC.fvec_for(W_A, I, rate, off, XS_FUND_ANN) Rx, Gx = AP.rg_off(W_A, I, XSP[ph], off) Rf = Rx - fX lv = float(row["lev"]) b0 = ladder_rt(Rf, Gx, N_ROB, lv, SEED_LAD, pol=POL(), refund_mode="payout", min_payout=MIN_PAYOUT, boot=BOOT2) bump(1) line = f" off{off:>4} fase{ph:>2} {lv:>6.2f} {b0['p_target']:>9.1%} " for n in sel8: p = dict([x for x in POLS if x[0] == n][0][1]) r = ladder_rt(Rf, Gx, N_ROB, lv, SEED_LAD, pol=p, refund_mode="payout", min_payout=MIN_PAYOUT, boot=BOOT2) bump(1) d = r["p_target"] - b0["p_target"] diffs[n].append(d) line += f"{d*100:>+17.2f}pp" print(line) print(f"\n {'politica':>26} {'mediana delle diff. appaiate':>30} {'celle > 0':>11}") for n in sel8: v = np.array(diffs[n]) print(f" {n:>26} {np.median(v)*100:>29.2f}pp {int((v > 0).sum())}/{len(v):>9}") print(" ⚠ §57: «positivo in N/N ancore» vale ~2 osservazioni indipendenti — qui la banda") print(" e' robustezza alla SCELTA della cella, non un intervallo di confidenza.") tick("§8") # ============================================================== §9 verdetto hr("§9. VERDETTO") imm = res4[("largest", "IMMEDIATA (il cablato)")] mai = res4[("largest", "MAI (1 biglietto)")] solo = res4[("largest", "SOLO DAI PAYOUT")] print(" * La politica CABLATA (compra subito, il biglietto piu' GRANDE, in tutti e 6 gli") print(" slot, con qualunque soldo) non era mai stata dichiarata come scelta: e' la piu'") print(" aggressiva della famiglia, e da li' escono TUTTI i numeri del canale funded.") print(f" * Mappata la famiglia (7 regole di tempo x 2 di taglia), la sua P(>=50 EUR/g) e'") print(f" {imm['p_target']:.1%}; massimo di famiglia {allp['p50'].max():.1%} " f"({allp.loc[allp['p50'].idxmax(),'tag'].strip()}), minimo {allp['p50'].min():.1%}, " f"spread {(allp['p50'].max()-allp['p50'].min())*100:.2f}pp.") print(f" * LE DUE COLONNE INSIEME, senza versamenti: cablato {imm['med_tix']:.0f} giocate e " f"P(fuori gioco) {imm['p_stuck']:.1%};") print(f" una giocata sola {mai['med_tix']:.0f} e {mai['p_stuck']:.1%}. Il prezzo della " f"prudenza e' {(imm['p_target']-mai['p_target'])*100:.2f}pp di bersaglio per " f"{(imm['p_stuck']-mai['p_stuck'])*100:.2f}pp di rovina.") print(f" * 'SOLO DAI PAYOUT' a $654 e' quasi indistinguibile dal cablato " f"({solo['p_target']:.1%} contro {imm['p_target']:.1%}): dopo il primo biglietto da " f"${579:.0f}") print(f" restano ${pl.START_EUR*EURUSD-579:.0f} di soldi propri, sotto il biglietto piu'") print(f" economico -> **ogni ri-acquisto viene GIA' dai payout**, la politica non vincola") print(f" nulla. Vincola solo quando c'e' un versamento (§7).") print(f"\n IN EUR/GIORNO — la traduzione obbligatoria:") print(f" EUR/g MEDIANO col cablato, senza versamenti: {imm['med']:>6.2f} EUR/g") print(f" EUR/g MEDIO col cablato, senza versamenti: {imm['mean_eur']:>6.2f} EUR/g") fam = {n: tab6[(n, 'payout')]['mean_eur'] for n in sel6} bn = max(fam, key=fam.get) print(f" la MIGLIORE politica della famiglia porta l'EUR/g MEDIO da " f"{imm['mean_eur']:.2f} a {fam[bn]:.2f} ({fam[bn]-imm['mean_eur']:+.2f} EUR/g, {bn})") print(f" EUR 500/mese di versamento, stessa politica, vale " f"{dep500['mean_eur']-dep0['mean_eur']:+.2f} EUR/g sulla stessa metrica.") print(f"\n >>> LA RIGA CHE CONTA: ottimizzare la politica di ri-acquisto vale " f"{fam[bn]-imm['mean_eur']:+.2f} EUR/g;") print(f" versare EUR 500/mese ne vale {dep500['mean_eur']-dep0['mean_eur']:+.2f}. " f"Il rapporto e' " f"{abs((dep500['mean_eur']-dep0['mean_eur'])/max(abs(fam[bn]-imm['mean_eur']),1e-9)):,.0f}x.") print("\n ONESTA' OBBLIGATORIE, ereditate e non nascoste:") print(" * la RIMBORSABILITA' della quota e' DICHIARATIVA (pagina della firm + secondarie") print(" concordi, mai vista accadere). §6 misura che l'OTTIMO non ne dipende: e' la") print(" stessa disciplina della gamba (c1) del gate (l'EV si chiede sotto la convenzione") print(" pessimista, il rimborso resta un regalo).") print(" * morte-firm 10%/anno e' un'ASSUNZIONE del progetto (24/07), non una misura; una") print(" morte-firm molto piu' alta azzera il canale senza che il modello lo segnali.") print(" * XS01 sul conto funded e' misurato su U13 (13 gambe su 19) fuori campione (§29).") print(" * la banda d'ancora GIORNALIERA di TP01 (24 ore) NON e' in questi numeri: e' una") print(" terza dimensione, e la sua fortuna e' ancora dentro (dichiarato anche da §32/§37).") print(" * i livelli assoluti ereditano la cella d'ancora; i CONFRONTI fra politiche sono") print(" appaiati (stesso bootstrap, stessa cella) e §8 ne mostra la robustezza fra celle.") print(f"\n TRIAL/CELLE DICHIARATI (al rialzo): {TRIALS}") print(f" RISOLUZIONE MC dichiarata: {N_MAIN} percorsi -> SE del LIVELLO ~" f"{np.sqrt(max(imm['p_target'],1e-9)*(1-imm['p_target'])/N_MAIN)*100:.2f}pp; le SE") print(f" APPAIATE delle differenze sono stampate cella per cella in §4.") print(f" fatto in {time.time()-T0:.0f}s") print("=" * 104) if __name__ == "__main__": main()