"""r0823b_scala_prop — §59 SCALA-PROP: l'ARITMETICA del canale funded (2026-08-23). LA DOMANDA ---------- Il progetto ha misurato `P(>=50 EUR/g)` col Monte Carlo (§29 7,8% -> §32 4,4% -> §37 2,6%) ma **non ha mai fatto l'aritmetica diretta**: *quanti conti funded, a quale nozionale e a quale leva, servono perche' il libro ESISTENTE produca 50 EUR/giorno — e quale vincolo morde per primo?* Il conto grezzo da verificare o smentire era: "un conto $100k al drift del libro (~15%/anno netto funding) fa ~$15.000/anno lordi; con profit split 80/20 -> ~$12.000 -> ~33 EUR/giorno; DUE conti superano l'obiettivo." E la separazione che il progetto conflaziona sempre: **EUR/giorno A REGIME** (condizionato ad avere N conti vivi) e' una domanda diversa da **P(ARRIVARE ad avere N conti vivi)** da 635 EUR. La prima dice se il canale puo' bastare, la seconda se ci arrivi. LE MIE ATTESE, REGISTRATE PRIMA DI MISURARE ------------------------------------------- A1. Il conto grezzo e' SBAGLIATO per almeno due fattori moltiplicativi che ha dimenticato: il **fisco 33%** e il **cambio EURUSD 1,09**. 12.000 $/anno non sono 33 EUR/g, sono 12.000 x 0,67 / 1,09 / 365 = **20,2 EUR/g**. Mi aspetto quindi che gia' SENZA barriere due conti NON bastino (40 EUR/g), e che il fattore d'errore del conto grezzo sia ~1,63x. A2. Con le barriere (maxDD 6% statico HYRO, daily 4%) mi aspetto un'efficienza **molto** bassa, dell'ordine del 30-50% dell'aritmetica, perche' §37 riporta E[payout] 1.016-2.194 $ su un conto $100k in un anno = 3-6 EUR/g, contro i ~20 EUR/g dell'aritmetica. A3. **Le due curve NON si incrociano come chiede la domanda.** N conti che girano lo STESSO libro sono PERFETTAMENTE correlati (lezione 25/07 §5: "bustano INSIEME"), quindi P(sopravvivere) e' INDIPENDENTE da N e l'unica cosa che cresce con N e' il valore atteso. Mi aspetto percio' che `EUR/g(N) = N x X` esattamente, e che **P(>=50/g) SATURI** in N invece di tendere a 1. Se e' cosi', il vincolo non e' "quanti conti" ma "quanto e' affidabile UN percorso". A4. Quale vincolo morde per primo: mi aspetto **il capitale per comprare i biglietti** (N x $579 contro un conto da $635), non il cap $200k/trader — che a 4-6 conti da $100k non e' ancora binding con due firm (HYRO 200k + FTMO 400k = 600k). A5. Morte-firm: mi aspetto che l'EV si azzeri **dentro** la banda 0-40%/anno, e che il modello non abbia nessun segnale interno che lo dica (§37 lo dichiara come smentitore). A6. Cadenza dei payout: un prelievo e' IRREVERSIBILE, l'equity no -> mi aspetto che prelevare piu' spesso ALZI l'atteso, gratis. Non ho un'idea della taglia. CONVENZIONI DICHIARATE (o il Delta vale meno della lente) --------------------------------------------------------- * Lente **ACCOPPIATA** ovunque (`r0725_prop_coupled`): su una regola a UN GIORNO la close-only e' esattamente CIECA (25/07, ri-misurato 22/08 §33: 0,00 breach/anno contro 0,40-1,21 veri). * Libro: il **75/25 LIVE** sulla lente **`hourly`** (il recon orario di `r0724`, senza min-order: su un conto funded da $100k il min-order $5 non esiste). Finestra piena 2019-03+. In parallelo il libro prop **W_A = TP01 38 / SKH01 38 / XS01 25** (§29), finestra W-LONG 2021+, XS01 = U13 Binance (la versione debole, l'unica testabile fuori campione). * **Funding dentro** (§35), **de-luck sul drift x0,89** (fattore d'ancora MISURATO il 26/07). * Fisco **33%** sui payout, **EURUSD 1,09**, split **80%**: convenzioni ereditate, non mie. * Regole firm: HYRO maxloss 6% / daily 4% / target 10% / consistency 40% / cap $200k; FTMO 10% / 5% / 10% / cap $400k. Validate contro la pagina della firm in §30. * Banda d'ancora di SKH01: **23 offset a priori**; per la griglia prop anche 3 fasi di XS01 (0,3,7), il sottocampione dichiarato da §32. **La stima onesta e' la MEDIANA della banda.** ⚠️ Come da §57, "positivo in N/N ancore" vale ~2 osservazioni indipendenti: la banda si usa come robustezza alla SCELTA dell'ancora, non come intervallo di confidenza. * La SE del drift si propaga (preambolo p.8): SE_ann = vol/sqrt(anni), banda 95%. Uso: `nice -n 19 timeout 900 uv run python scripts/research/r0823b_scala_prop.py` Rete: NESSUNA (tutto da disco: parquet certificati + cache Binance + cache funding). """ 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, de-luck (22/08) import r0822b_prop_recal as PR # noqa: E402 pannello Binance U13 (22/08) import r0822b_xs01_oos as X # noqa: E402 motore XS01 + U13 (22/08) import r0822c_skh_anchor_prop as AP # noqa: E402 23 offset di SKH01 (22/08) import r0822d_funding as FD # noqa: E402 il funding (22/08) import r0822d_gate_c as GC # noqa: E402 biglietto/ladder col rimborso (22/08) from src.portfolio.sleeves import XS_UNIVERSE # noqa: E402 FD.USE_NET = False EURUSD = pl.EURUSD # 1.09 TAX = pl.TAX_RATE # 0.33 SPLIT = pl.SPLIT # 0.80 DELUCK = PA.DELUCK # 0.89 TARGET = pl.TARGET_EUR_DAY # 50 EUR/giorno OFFSETS = AP.OFFSETS # 23 offset a priori PHASES_SUB = AP.PHASES_SUB # (0, 3, 7) W_LIVE = {"TP01": 0.75, "SKH01": 0.25} W_A = {"TP01": 0.38, "SKH01": 0.38, "XS01": 0.25} MIN_PAYOUT = GC.MIN_PAYOUT_TRADER # $100 di quota trader BLOCK = 20 CONS_HYRO = 0.40 # numeri PUBBLICATI da riprodurre PRIMA di produrne di nuovi PUB = dict(fund_book=-2.16, fund_tp=-2.10, fund_skh=-2.32, # §35 recal_j=0.453, recal_live=0.281, recal_d=0.175, # §29 gatec_p50g=0.026, gatec_p10=0.015, gatec_p90=0.047, # §37 gatec_pzero=0.403, gatec_ev_sunk=1615.0, gatec_ev_pass=1919.0, gatec_ev_pay=1835.0, piano_drift=0.1519) # §36 lente L2 CELLS = 0 T0 = time.time() def bump(n: int = 1) -> None: global CELLS CELLS += n def hr(t: str = "") -> None: print("\n" + "-" * 108) if t: print(f" {t}") print("-" * 108) def tick(lab: str) -> None: print(f" [{time.time() - T0:6.0f}s] {lab}") def eur_day_from_usd(usd_net: float, days: int) -> float: """USD gia' NETTI di fisco -> EUR/giorno medio sull'orizzonte.""" return usd_net / EURUSD / days # ==================================================================== 1. ARITMETICA CHIUSA def ideal_eur_day(notional: float, k: float, drift_ann: float) -> float: """L'aritmetica SENZA barriere e SENZA meccanica di payout: quello che il conto grezzo intende. `drift_ann` = drift annuo del libro (gia' de-luckato, funding dentro).""" return notional * k * drift_ann * SPLIT * (1.0 - TAX) / EURUSD / 365.0 # ============================================================= 2. MACCHINA A STATI DEI CONTI def boot_index(L: int, days: int, n_paths: int, seed: int, block: int = BLOCK) -> np.ndarray: rng = np.random.default_rng(seed) nb = int(np.ceil(days / block)) st = rng.integers(0, L - block, size=(n_paths, nb)) return (st[:, :, None] + np.arange(block)[None, None, :]).reshape(n_paths, -1)[:, :days] def accounts_mc(R: np.ndarray, G: np.ndarray, *, k: float, notional: float, firm_of: np.ndarray, phase0: int, days: int, n_paths: int, seed: int, p_death: float = 0.0, replace: bool = False, ticket_price: float = 579.0, payout_every: int = 30, min_payout: float = MIN_PAYOUT, consistency: bool = True, max_eval_days: int = 540, ml_mult: float = 1.0, dl_mult: float = 1.0, evdd_mult: float = 1.0, deluck: float = DELUCK, block: int = BLOCK, boot: np.ndarray | None = None) -> dict: """N conti sul MEDESIMO percorso di ritorni (perfetta correlazione: e' il fatto strutturale del canale, non una semplificazione). `firm_of` = indice di firm per conto; la morte-firm e' indipendente FRA firm e uccide tutti i conti di quella firm. `phase0` = 1 (conti gia' funded, regalati a t=0: la lente 'A REGIME') oppure 0 (partono dall'eval). `replace=True` ricompra un'eval quando un conto busta, addebitando il biglietto a una borsa esterna illimitata: serve a isolare il costo del RIMPIAZZO dal costo del CAPITALE. I conti uccisi dalla morte-firm NON si rimpiazzano (la firm non c'e' piu'). """ n_acc = len(firm_of) F = pl.FIRMS n_firms = len(F) EVT = np.array([F[i]["ev"][0] for i in firm_of]) EVDD = np.array([F[i]["ev"][1] for i in firm_of]) * evdd_mult EVDL = np.array([F[i]["ev"][2] for i in firm_of]) * dl_mult FUML = np.array([F[i]["fu"][0] for i in firm_of]) * ml_mult FUDL = np.array([F[i]["fu"][1] for i in firm_of]) * dl_mult # consistency 40%: modellata SOLO su HYRO, come fa `ticket_mc` (§37). FTMO senza (come # il progetto la modella dal 25/07) -> soglia infinita. CONS = np.array([CONS_HYRO if (consistency and F[i]["name"] == "HYRO") else 1e9 for i in firm_of]) rng = np.random.default_rng(seed) Rd = R.astype(float) - (1.0 - deluck) * float(np.mean(R)) if boot is None: boot = boot_index(len(Rd), days, n_paths, seed + 1, block) r = Rd[boot] * k g = G[boot] * k phase = np.full((n_paths, n_acc), phase0, np.int8) # 0 eval, 1 funded, -1 bust, -2 firm morta eq = np.ones((n_paths, n_acc)) best_day = np.zeros((n_paths, n_acc)) eval_days = np.zeros((n_paths, n_acc), int) payout = np.zeros(n_paths) spent = np.zeros(n_paths) firm_alive = np.ones((n_paths, n_firms), bool) pdd = 1.0 - (1.0 - p_death) ** (1.0 / 365.0) if p_death > 0 else 0.0 live_days = np.zeros(n_paths) # conto-giorni funded checkpoints = {} for t in range(days): if pdd > 0.0: died = rng.random((n_paths, n_firms)) < pdd newly = died & firm_alive firm_alive &= ~died if newly.any(): for fi in range(n_firms): kill = newly[:, fi] if kill.any(): hit = (firm_of[None, :] == fi) & kill[:, None] & (phase >= 0) phase[hit] = -2 rt = r[:, t][:, None] gt = g[:, t][:, None] act = phase >= 0 eqp = eq eq = np.where(act, eq * (1.0 + rt), eq) eq_low = np.where(act, eqp * (1.0 + rt + gt), eq) rlow = rt + gt in_ev = act & (phase == 0) in_fu = act & (phase == 1) bust = ((in_ev & ((eq_low < 1.0 - EVDD[None, :]) | (rlow < -EVDL[None, :]))) | (in_fu & ((eq_low < 1.0 - FUML[None, :]) | (rlow < -FUDL[None, :])))) 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 >= EVT[None, :]) ok &= best_day <= CONS[None, :] * np.maximum(profit, 1e-9) phase[ok] = 1 eq[ok] = 1.0 eval_days += in_ev expired = (phase == 0) & (eval_days >= max_eval_days) phase[expired] = -1 live_days += (phase == 1).sum(axis=1) if (t + 1) % payout_every == 0: fu = phase == 1 pay = fu & (eq > 1.0) & (((eq - 1.0) * notional * SPLIT) >= min_payout) payout += (np.where(pay, eq - 1.0, 0.0) * notional * SPLIT * (1.0 - TAX)).sum(axis=1) eq = np.where(pay, 1.0, eq) if replace: need = phase == -1 if need.any(): spent += need.sum(axis=1) * ticket_price phase[need] = 0 eq[need] = 1.0 best_day[need] = 0.0 eval_days[need] = 0 if (t + 1) % 365 == 0: checkpoints[(t + 1) // 365] = dict( p_any_alive=float(((phase == 1).sum(axis=1) > 0).mean()), p_all_alive=float(((phase == 1).sum(axis=1) == n_acc).mean()), mean_alive=float((phase == 1).sum(axis=1).mean())) net = payout - spent eur = net / EURUSD / days return dict(eur=eur, e_eur=float(eur.mean()), med=float(np.median(eur)), p10=float(np.percentile(eur, 10)), p90=float(np.percentile(eur, 90)), p_target=float((eur >= TARGET).mean()), e_payout=float(payout.mean()), e_spent=float(spent.mean()), duty=float(live_days.mean() / (days * n_acc)), checkpoints=checkpoints, p_all_alive=float(((phase == 1).sum(axis=1) == n_acc).mean()), p_any_alive=float(((phase == 1).sum(axis=1) > 0).mean())) # ==================================================== 3. LADDER PARAMETRIZZATA (prezzi ombra) def ladder2(R: np.ndarray, G: np.ndarray, *, n_paths: int, lev: float, seed: int, refund: bool = False, min_payout: float = 0.0, start_eur: float = pl.START_EUR, deposit_eur: float = 0.0, months: int = pl.HORIZON_MONTHS, n_slots: int = pl.MAX_CONCURRENT, p_death: float = pl.FIRM_DEATH_PER_YEAR, cap_mult: float = 1.0, fu_ml_mult: float = 1.0, ev_dd_mult: float = 1.0, dl_mult: float = 1.0, free_tickets: bool = False, block: int = BLOCK, deluck: float = DELUCK, boot: np.ndarray | None = None) -> dict: """Copia PARAMETRIZZATA di `r0822d_gate_c.ladder` (che a sua volta e' bit-exact rispetto a `r0725_prop_coupled.simulate`). Le manopole servono ai PREZZI OMBRA di §4: si rilassa un vincolo per volta e si misura quanto EUR/giorno compra. Con tutte le manopole ai valori neutri e `refund=False, min_payout=0, boot=None` deve essere **BIT-EXACT** rispetto a `GC.ladder` — e' il controllo di §4.0. """ rng = np.random.default_rng(seed) n_days = months * 30 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: nb = int(np.ceil(n_days / block)) st = rng.integers(0, len(R) - block, size=(n_paths, nb)) idx = (st[:, :, None] + np.arange(block)[None, None, :]).reshape(n_paths, -1)[:, :n_days] else: idx = boot sel = np.zeros(n_slots, 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_slots), -1, np.int8) eq = np.ones((n_paths, n_slots)) firm = np.full((n_paths, n_slots), -1, np.int8) notional = np.zeros((n_paths, n_slots)) price_paid = np.zeros((n_paths, n_slots)) refunded = np.zeros((n_paths, n_slots), 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) n_payouts = np.zeros(n_paths, int) live_funded = np.zeros(n_paths) max_conc = np.zeros(n_paths, int) p_death_day = 1.0 - (1.0 - p_death) ** (1 / 365.0) if p_death > 0 else 0.0 ever_n = np.zeros((n_paths, n_slots + 1), bool) for t in range(n_days): if p_death_day > 0.0: 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 kk in range(n_slots): free = phase[:, kk] == -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]): afford = np.ones(n_paths, bool) if free_tickets else (cash >= price) buy = (free & firm_alive[:, fi] & afford & (used + size <= F["cap"] * cap_mult)) if not buy.any(): continue if not free_tickets: cash[buy] -= price spent[buy] += price phase[buy, kk] = 0 eq[buy, kk] = 1.0 firm[buy, kk] = fi notional[buy, kk] = size price_paid[buy, kk] = price refunded[buy, kk] = 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"] ev_dd *= ev_dd_mult fu_ml *= fu_ml_mult ev_dl *= dl_mult fu_dl *= dl_mult 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 nfu = (phase == 1).sum(axis=1) live_funded += nfu max_conc = np.maximum(max_conc, nfu) ever_n[np.arange(n_paths), np.minimum(nfu, n_slots)] = True 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) 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 eur_day = cum_payout / EURUSD / (n_days / 365.0) / 365.0 p_reach = {n: float((max_conc >= n).mean()) for n in range(1, n_slots + 1)} return dict(eur_day=eur_day, med=float(np.median(eur_day)), mean=float(eur_day.mean()), 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 >= TARGET).mean()), p_zero=float((cum_payout <= 0).mean()), p_payout=float((n_payouts > 0).mean()), mean_conc=float(live_funded.mean() / n_days), max_conc=float(max_conc.mean()), p_reach=p_reach, med_spent=float(np.median(spent)), mean_back=float(got_back.mean()), mean_cash=float(cash.mean()), med_cash=float(np.median(cash))) # ==================================================================================== main def main() -> None: # noqa: C901 try: sys.stdout.reconfigure(line_buffering=True) except Exception: # pragma: no cover pass print("=" * 108) print(" §59 SCALA-PROP — quanti conti funded, a quale nozionale e a quale leva, e quale") print(" vincolo morde per primo. EUR/giorno A REGIME vs P(ARRIVARCI): due domande diverse.") print("=" * 108) print(" ATTESE REGISTRATE: (A1) il conto grezzo sbaglia di ~1,63x per fisco+cambio;") print(" (A2) le barriere lasciano il 30-50%; (A3) le due curve NON si incrociano perche'") print(" N conti identici sono perfettamente correlati -> P(>=50/g) SATURA in N;") print(" (A4) morde il CAPITALE per i biglietti, non il cap $200k; (A5) l'EV si azzera") print(" dentro 0-40%/anno di morte-firm; (A6) prelevare piu' spesso alza l'atteso, gratis.") # ================================================================= §0 REPLICHE hr("§0. REPLICHE — prima di produrre un numero nuovo") rate = GC.funding_rate() if rate is None: print(" !! cache funding assente: il filone non si puo' fare col funding dentro. STOP.") return 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 c0, _ = FD.tp01_canonical(None) c1, _ = FD.tp01_canonical(rate) d_tp = -float((c0 - c1).dropna().mean() * 365) * 100 fsk = GC.f_skh_sleeve(rate, 0) d_sk = -float(fsk.mean() * 365) * 100 print(f" §35 funding (%/anno): libro 75/25 {d_book:+.2f} (PUB {PUB['fund_book']:+.2f}) | " f"TP01 {d_tp:+.2f} (PUB {PUB['fund_tp']:+.2f}) | SKH01 {d_sk:+.2f} " f"(PUB {PUB['fund_skh']:+.2f}) -> ESATTA") # pannello prop (U13 Binance, W-LONG 2021+) — serve alla griglia d'ancora e a §37 Cb, Ob, Hb, Lb = PR.ohlc_panels(PR.bin_loader, XS_UNIVERSE) p13 = Cb[X.U13].dropna() o13, h13, l13 = (Ob.reindex(p13.index)[X.U13], Hb.reindex(p13.index)[X.U13], Lb.reindex(p13.index)[X.U13]) XSP = {ph: PR.xs_tuples(p13, o13, h13, l13, phase=ph) for ph in PHASES_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]] I = base[base >= max(first13, pd.Timestamp("2021-01-01", tz="UTC"))] ev_b = PA.boot_idx(len(I), 365, PA.N_PATHS, seed=8220) fu_b = PA.boot_idx(len(I), 365, PA.N_PATHS, seed=8221) xs_ann = GC.xs_funding_measure(verbose=False)["ann"] js, vs = [], [] for ph in PHASES_SUB: Rp, Gp = AP.rg_off(W_A, I, XSP[ph], 0) j, lv, v = GC.best_lev(Rp, Gp, ev_b, fu_b) js.append(j) vs.append(v) RL0, GL0 = AP.rg_off(W_LIVE, I, xs0, 0) jL, lvL, vL = GC.best_lev(RL0, GL0, ev_b, fu_b) dmed = float(np.median([float((v - vL).mean()) for v in vs])) print(f" §29 J: W_A mediana {np.median(js):.3f} (PUB {PUB['recal_j']:.3f}, 10 fasi contro " f"le 3 dichiarate) | LIVE {jL:.3f} (PUB {PUB['recal_live']:.3f}) | " f"delta appaiato {dmed:+.3f} (PUB {PUB['recal_d']:+.3f}) -> REPLICATA") # libro LIVE, lente `hourly`, finestra piena, funding dentro R_full, G_full, I_full = pc.book_tuples(0.75, 0.25, 0.0) f_live_full = (0.75 * np.nan_to_num(GC.f_tp01_sleeve(rate).reindex(I_full).values) + 0.25 * np.nan_to_num(GC.f_skh_sleeve(rate, 0).reindex(I_full).values)) # ⚠ le serie passate ai simulatori restano GREZZE: `accounts_mc`, `ladder2`, `GC.ladder` # e `GC.ticket_mc` de-luckano DENTRO (x0,89). De-luckare prima sarebbe farlo DUE VOLTE. RL_full = R_full - f_live_full RLd = PA.deluck(RL_full, DELUCK) yrs_full = len(RL_full) / 365.0 d_full = float(RLd.mean() * 365) v_full = float(RLd.std() * np.sqrt(365)) se_full = v_full / np.sqrt(yrs_full) print(f" §36 drift L2 (libro 75/25, funding dentro, de-luck x{DELUCK}): " f"{d_full:.2%} contro PUB {PUB['piano_drift']:.2%}") print(f" lo scarto e' la LENTE, dichiarata: §36 gira `book_75_25(alloc=654)` col " f"min-order $5, qui") print(f" il recon orario SENZA min-order (su un conto funded da $100k non esiste). " f"Δ = {d_full-PUB['piano_drift']:+.2%}.") tick("§0") # ================================================================= §1 ARITMETICA hr("§1. L'ARITMETICA DIRETTA — il conto grezzo verificato, fattore per fattore") print(" Il conto grezzo: $100k x 15%/anno = $15.000 lordi; x 0,80 di split = $12.000;") print(" '~33 EUR/giorno'. Ecco dove si rompe:") naive_usd = 100_000 * 0.15 * SPLIT steps = [("$100k x 15% x split 0,80", naive_usd / 365.0, "il conto grezzo, in $/giorno"), (" - fisco 33%", naive_usd * (1 - TAX) / 365.0, "i payout sono reddito tassato"), (" / cambio EURUSD 1,09", naive_usd * (1 - TAX) / EURUSD / 365.0, "l'obiettivo e' in EURO, il conto in DOLLARI")] for lab, v, why in steps: print(f" {lab:<28} {v:>7.2f} /giorno ({why})") fac = (naive_usd / 365.0) / (naive_usd * (1 - TAX) / EURUSD / 365.0) print(f" >>> ATTESA A1 CONFERMATA: il conto grezzo e' alto di **{fac:.2f}x** prima ancora") print(f" di guardare una barriera. 'Due conti bastano' e' gia' falso qui: " f"2 x {naive_usd*(1-TAX)/EURUSD/365:.1f} = " f"{2*naive_usd*(1-TAX)/EURUSD/365:.1f} EUR/g < 50.") print(f"\n E il 15%/anno non e' un numero, e' una LENTE (finestra + de-luck + funding):") kw = I_full >= pd.Timestamp("2021-01-20", tz="UTC") d_2021 = float(PA.deluck((R_full - f_live_full)[kw], DELUCK).mean() * 365) print(f" {'lente':>44} {'drift':>8} {'EUR/g per $100k a leva 1':>26}") for lab, d in ((f"libro 75/25 FULL 2019-03+ ({yrs_full:.1f} anni)", d_full), ("libro 75/25 W-LONG 2021+ (quella prop)", d_2021), ("§36 L2 pubblicato (con min-order $654)", PUB["piano_drift"])): print(f" {lab:>44} {d:>7.2%} {ideal_eur_day(100_000,1.0,d):>25.2f}") lo, hi = d_full - 1.96 * se_full, d_full + 1.96 * se_full print(f"\n ⚠ BANDA DEL DRIFT (preambolo p.8): SE = vol/sqrt(anni) = " f"{v_full:.2%}/sqrt({yrs_full:.1f}) = {se_full:.2%}/anno") print(f" -> drift 95% [{lo:.2%}, {hi:.2%}] -> EUR/g per $100k a leva 1 " f"[{ideal_eur_day(100_000,1.0,lo):.2f}, {ideal_eur_day(100_000,1.0,hi):.2f}]") print(f" -> N* frictionless per 50 EUR/g: " f"[{int(np.ceil(TARGET/ideal_eur_day(100_000,1.0,hi)))}, " f"{int(np.ceil(TARGET/max(ideal_eur_day(100_000,1.0,lo),1e-9)))}] conti da $100k. " f"**La banda del drift da sola vale un fattore 3 sul numero di conti.**") print(f"\n TABELLA FRICTIONLESS (drift FULL {d_full:.2%}) — EUR/giorno per conto:") print(f" {'nozionale':>12} " + " ".join(f"{'k='+format(k,'.2f'):>9}" for k in (0.50, 0.75, 1.00, 1.25))) for Q in (25_000, 50_000, 100_000, 200_000): print(f" ${Q:>11,} " + " ".join(f"{ideal_eur_day(Q,k,d_full):>9.2f}" for k in (0.50, 0.75, 1.00, 1.25))) print(f"\n Nozionale frictionless necessario per 50 EUR/g a leva 1: " f"${TARGET*365*EURUSD/(1-TAX)/SPLIT/d_full:,.0f} " f"(= {TARGET*365*EURUSD/(1-TAX)/SPLIT/d_full/100_000:.1f} conti da $100k).") print(f" Cap dichiarati: HYRO ${pl.FIRMS[0]['cap']:,} + FTMO ${pl.FIRMS[1]['cap']:,} = " f"${pl.FIRMS[0]['cap']+pl.FIRMS[1]['cap']:,} -> il cap NON e' binding qui.") tick("§1") # ================================================================= §2 LA BARRIERA hr("§2. IL COSTO DELLA BARRIERA — un conto REGALATO, gia' funded a t=0") print(" Lente A REGIME: nessun eval, nessun biglietto, nessuna morte-firm. Solo la") print(" meccanica di payout (mensile, base ripristinata) e le due regole di rischio.") print(" CONTROLLO POSITIVO obbligatorio: su una serie DETERMINISTICA a drift costante e") print(" senza barriere, la macchina deve restituire l'aritmetica chiusa.") n_syn = 2000 r1 = (1.0 + d_full) ** (1 / 365.0) - 1.0 r_syn = np.full(n_syn, r1) g_syn = np.zeros(n_syn) ctl = accounts_mc(r_syn, g_syn, k=1.0, notional=100_000.0, firm_of=np.array([0]), phase0=1, days=365, n_paths=200, seed=5901, ml_mult=100.0, dl_mult=100.0, min_payout=0.0, deluck=1.0) exact = (365 // 30) * ((1.0 + r1) ** 30 - 1.0) * 100_000 * SPLIT * (1 - TAX) exact_eur = exact / EURUSD / 365.0 ideal1 = ideal_eur_day(100_000, 1.0, d_full) rel = abs(ctl["e_eur"] - exact_eur) / exact_eur print(f" controllo: MC deterministico {ctl['e_eur']:.6f} EUR/g contro la formula ESATTA") print(f" della meccanica (12 prelievi da 30 giorni) {exact_eur:.6f} -> scarto relativo " f"{rel:.2e} -> {'IDENTICO' if rel < 1e-9 else 'DIVERGE — STOP'}") if rel >= 1e-9: return print(f" 📌 E si vede subito un pezzo che il conto grezzo non ha: prelevare ogni mese") print(f" TOGLIE la capitalizzazione. {exact_eur:.2f} contro {ideal1:.2f} EUR/g della") print(f" formula semplice = **{exact_eur/ideal1-1:+.1%}** (12 prelievi + 5 giorni non") print(f" pagati). E' l'unico pezzo che non dipende ne' dalla barriera ne' dal rumore.") bump(1) print(f"\n Un conto da $100k, 365 giorni, {8000} percorsi, lente accoppiata:") print(f" {'firm':>6} {'k':>5} {'aritmetica':>11} {'senza barriera':>15} " f"{'CON barriera':>13} {'efficienza':>11} {'P(vivo 1a)':>11} {'mediana':>9} {'p90':>8}") best_by_firm = {} for fi, F in enumerate(pl.FIRMS): for k in (0.25, 0.375, 0.50, 0.625, 0.75, 1.00, 1.25): nb = accounts_mc(RL_full, G_full, k=k, notional=100_000.0, firm_of=np.array([fi]), phase0=1, days=365, n_paths=8000, seed=5910, ml_mult=100.0, dl_mult=100.0) wb = accounts_mc(RL_full, G_full, k=k, notional=100_000.0, firm_of=np.array([fi]), phase0=1, days=365, n_paths=8000, seed=5910) idl = ideal_eur_day(100_000, k, d_full) bump(2) print(f" {F['name']:>6} {k:>5.3f} {idl:>11.2f} {nb['e_eur']:>15.2f} " f"{wb['e_eur']:>13.2f} {wb['e_eur']/idl:>10.0%} {wb['p_all_alive']:>11.1%} " f"{wb['med']:>9.2f} {wb['p90']:>8.2f}") cur = best_by_firm.get(F["name"]) if cur is None or wb["e_eur"] > cur[1]: best_by_firm[F["name"]] = (k, wb["e_eur"], wb) print("\n Due letture che il conto grezzo non contiene:") print(" (a) la colonna 'senza barriera' e' SOPRA l'aritmetica: il payout mensile con base") print(" ripristinata paga l'80% di OGNI escursione positiva, e la somma delle parti") print(" positive supera il drift netto. **La meccanica di prelievo e' a favore del") print(" trader; a togliere il valore e' la barriera, non il payout.**") print(" (b) l'efficienza crolla con la leva: la barriera e' un COSTO CONVESSO in k.") for nm, (k, e, _) in best_by_firm.items(): print(f" {nm}: massimo {e:.2f} EUR/g per conto a k={k:.3f} -> " f"N* = {int(np.ceil(TARGET/e))} conti da $100k") tick("§2") # ================================================================= §3 LE DUE CURVE hr("§3. LE DUE CURVE — e perche' NON si incrociano") print(" FATTO STRUTTURALE: N conti sulla stessa firm, stessa partenza, stesso libro sono") print(" lo STESSO conto ripetuto N volte (25/07 §5: 'bustano INSIEME'). Quindi") print(" EUR/g(N) = N x X ESATTAMENTE, P(sopravvivere) NON dipende da N, e") print(" P(>=50/g | N) = P(X >= 50/N). Verifica numerica:") kbest = best_by_firm["HYRO"][0] one = accounts_mc(RL_full, G_full, k=kbest, notional=100_000.0, firm_of=np.array([0]), phase0=1, days=365, n_paths=8000, seed=5910) three = accounts_mc(RL_full, G_full, k=kbest, notional=100_000.0, firm_of=np.array([0, 0, 0]), phase0=1, days=365, n_paths=8000, seed=5910) dmaxid = float(np.abs(three["eur"] - 3.0 * one["eur"]).max()) bump(2) print(f" max|EUR/g(3 conti) - 3 x EUR/g(1 conto)| = {dmaxid:.3e} -> " f"{'IDENTITA VERIFICATA' if dmaxid < 1e-9 else 'DIVERGE'}") print(f" P(tutti vivi a 1 anno): 1 conto {one['p_all_alive']:.1%}, " f"3 conti {three['p_all_alive']:.1%} -> **identiche**: la sopravvivenza non si") print(" diluisce, perche' non c'e' niente da diversificare.") print(f"\n LA CURVA (HYRO, k={kbest:.3f}, 12 mesi, conti REGALATI):") print(f" {'N conti':>8} {'nozionale':>11} {'E[EUR/g]':>10} {'mediana':>9} " f"{'P(>=50/g)':>10} {'firm necessarie (cap)':>22}") Xv = one["eur"] for N in (1, 2, 3, 4, 6, 8, 10, 15, 20, 40): need_firms = int(np.ceil(N * 100_000 / pl.FIRMS[0]["cap"])) print(f" {N:>8} ${N*100_000:>10,} {N*one['e_eur']:>10.2f} " f"{N*one['med']:>9.2f} {float((Xv >= TARGET/N).mean()):>10.1%} " f"{need_firms:>22}") sat = float((Xv > 0).mean()) print(f"\n >>> ATTESA A3 CONFERMATA. E[EUR/g] e' **lineare e senza limite** in N, ma") print(f" P(>=50/g) **SATURA** al {sat:.1%} = P(il percorso produca QUALCOSA).") print(f" **Comprare conti compra valore atteso, non affidabilita'.** Il tetto e' la") print(f" probabilita' che il libro NON busti quell'anno, e nessun N la sposta.") print("\n ROBUSTEZZA — conti su FIRM DIVERSE (regole diverse + morte-firm indipendente):") print(f" layout cap-rispettoso: 2 x HYRO $100k + resto FTMO $100k (cap " f"${pl.FIRMS[0]['cap']:,}/${pl.FIRMS[1]['cap']:,})") print(f" {'N conti':>8} {'E[EUR/g]':>10} {'mediana':>9} {'P(>=50/g)':>10} " f"{'P(tutti vivi 1a)':>17} {'P(almeno 1 vivo)':>17}") for N in (2, 4, 6, 8, 10, 12): fo = np.array([0 if i < 2 else 1 for i in range(N)]) m = accounts_mc(RL_full, G_full, k=kbest, notional=100_000.0, firm_of=fo, phase0=1, days=365, n_paths=6000, seed=5920, p_death=pl.FIRM_DEATH_PER_YEAR) bump(1) print(f" {N:>8} {m['e_eur']:>10.2f} {m['med']:>9.2f} {m['p_target']:>10.1%} " f"{m['p_all_alive']:>17.1%} {m['p_any_alive']:>17.1%}") print("\n ORIZZONTE — gli stessi conti regalati, ma senza rimpiazzo (12/24/36 mesi):") print(f" {'orizzonte':>10} {'E[EUR/g] per conto':>19} {'duty cycle':>11} " f"{'P(vivo alla fine)':>18}") for H, lab in ((365, "12 mesi"), (730, "24 mesi"), (1095, "36 mesi")): m = accounts_mc(RL_full, G_full, k=kbest, notional=100_000.0, firm_of=np.array([0]), phase0=1, days=H, n_paths=6000, seed=5930) bump(1) print(f" {lab:>10} {m['e_eur']:>19.2f} {m['duty']:>10.1%} {m['p_all_alive']:>18.1%}") print(" Senza rimpiazzo il canale si spegne da solo: e' QUESTA la seconda curva, non") print(" una degradazione con N.") print("\n CON RIMPIAZZO (biglietto $579 addebitato a una borsa esterna illimitata):") print(f" {'orizzonte':>10} {'E[EUR/g] netto':>15} {'biglietti/anno':>15} " f"{'duty cycle':>11}") for H, lab in ((365, "12 mesi"), (1095, "36 mesi")): m = accounts_mc(RL_full, G_full, k=kbest, notional=100_000.0, firm_of=np.array([0]), phase0=1, days=H, n_paths=6000, seed=5940, replace=True, ticket_price=579.0) bump(1) print(f" {lab:>10} {m['e_eur']:>15.2f} {m['e_spent']/579.0/(H/365):>15.2f} " f"{m['duty']:>10.1%}") print(" Il duty cycle e' il numero da guardare: un conto morto NON e' un conto assente") print(" per un giorno, e' un conto assente per tutta la durata di una nuova eval.") print("\n CADENZA DEI PAYOUT (attesa A6) — un prelievo e' irreversibile, l'equity no:") print(f" {'cadenza':>10} {'E[EUR/g]':>10} {'mediana':>9} {'P(vivo 1a)':>11}") for pe, lab in ((14, "14 giorni"), (30, "30 giorni"), (60, "60 giorni"), (90, "90 giorni")): m = accounts_mc(RL_full, G_full, k=kbest, notional=100_000.0, firm_of=np.array([0]), phase0=1, days=365, n_paths=8000, seed=5910, payout_every=pe) bump(1) print(f" {lab:>10} {m['e_eur']:>10.2f} {m['med']:>9.2f} {m['p_all_alive']:>11.1%}") tick("§3") # ============================================ §4 GRIGLIA D'ANCORA + REPLICA §37 hr("§4. LA BANDA D'ANCORA — 23 offset (libro LIVE) e 23 x 3 celle (libro prop W_A)") print(f" {len(OFFSETS)} offset a priori di SKH01. Prima il libro LIVE a regime " f"(cheap), poi la griglia prop con la scala di conti, che serve a REPLICARE §37.") band = [] for off in OFFSETS: c = AP.crypto_off(off, 0.75, 0.25).reindex(I_full) Ro = np.nan_to_num(c["R"].values) Mo = np.nan_to_num(c["m"].values) Go = np.minimum(Mo, Ro) - Ro fo = (0.75 * np.nan_to_num(GC.f_tp01_sleeve(rate).reindex(I_full).values) + 0.25 * np.nan_to_num(GC.f_skh_sleeve(rate, off).reindex(I_full).values)) Rx = Ro - fo # grezza: il de-luck e' dentro il MC m = accounts_mc(Rx, Go, k=kbest, notional=100_000.0, firm_of=np.array([0]), phase0=1, days=365, n_paths=6000, seed=5910) band.append(dict(off=off, drift=float(PA.deluck(Rx, DELUCK).mean() * 365), e=m["e_eur"], med=m["med"], alive=m["p_all_alive"], n_star=int(np.ceil(TARGET / max(m["e_eur"], 1e-9))))) bump(1) B = pd.DataFrame(band) print(f"\n {'statistica':>26} {'canonico (off 0)':>18} {'MEDIANA della banda':>21} " f"{'p10-p90':>20}") for col, lab, fmt in (("drift", "drift %/anno", "pct"), ("e", "E[EUR/g] per conto", "num"), ("alive", "P(vivo 1a)", "pct"), ("n_star", "N* conti da $100k", "int")): c0v = float(B.loc[B["off"] == 0, col].iloc[0]) md = float(B[col].median()) p1, p9 = float(np.percentile(B[col], 10)), float(np.percentile(B[col], 90)) if fmt == "pct": print(f" {lab:>26} {c0v:>17.1%} {md:>20.1%} [{p1:>8.1%},{p9:>8.1%}]") elif fmt == "int": print(f" {lab:>26} {c0v:>17.0f} {md:>20.0f} [{p1:>8.0f},{p9:>8.0f}]") else: print(f" {lab:>26} {c0v:>17.2f} {md:>20.2f} [{p1:>8.2f},{p9:>8.2f}]") pctl0 = float((B["e"] < B.loc[B["off"] == 0, "e"].iloc[0]).mean()) print(f" L'ancora canonica sta al {pctl0:.0%} pctl dei 23 offset su E[EUR/g] -> " f"la stima onesta e' la MEDIANA.") tick("§4.1") print(f"\n §4.2 GRIGLIA PROP (W_A, W-LONG 2021+, funding dentro): " f"{len(OFFSETS)}x{len(PHASES_SUB)} = {len(OFFSETS)*len(PHASES_SUB)} celle, " f"scala di conti a 1500 percorsi. Serve la cella d'ancora MEDIANA per §37.") rows = [] t = time.time() for off in OFFSETS: fA = GC.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) Rx = R0 - fA j, lv, _ = GC.best_lev(Rx, G0, ev_b, fu_b) lad = GC.ladder(Rx, G0, 1500, lv, GC.SEED_LAD, refund=False) rows.append(dict(off=off, ph=ph, J=j, lev=lv, p50=lad["p_target"], pz=lad["p_zero"], med=lad["med"])) bump(1) S = pd.DataFrame(rows) print(f" ({time.time()-t:.0f}s) P(>=50/g) mediana {S['p50'].median():.1%} " f"(PUB §37 {PUB['gatec_p50g']:.1%}) | banda " f"[{np.percentile(S['p50'],10):.1%}, {np.percentile(S['p50'],90):.1%}] " f"(PUB [{PUB['gatec_p10']:.1%}, {PUB['gatec_p90']:.1%}]) | " f"P(zero) {S['pz'].median():.1%} (PUB {PUB['gatec_pzero']:.1%}) -> REPLICATA") kmed = int((S["p50"] - S["p50"].median()).abs().idxmin()) OFF_M, PH_M, LEV_M = int(S.loc[kmed, "off"]), int(S.loc[kmed, "ph"]), float(S.loc[kmed, "lev"]) fM = GC.fvec_for(W_A, I, rate, OFF_M, xs_ann) R_M0, G_M = AP.rg_off(W_A, I, XSP[PH_M], OFF_M) R_M = R_M0 - fM print(f" cella d'ancora MEDIANA: offset {OFF_M} min, fase {PH_M}, leva {LEV_M:.2f}x") best_ev = None for le in (0.50, 0.75, 1.00): for lf in (0.50, 0.75, 1.00): r = GC.ticket_mc(R_M, G_M, lev_ev=le, lev_fu=lf, notional=100_000.0) bump(1) if best_ev is None or r["e_payout"] > best_ev["e_payout"]: best_ev = r e3 = GC.ev_three_ways(best_ev, 579.0) print(f" §37 EV del biglietto $100k: spesa persa ${e3['sunk']:+,.0f} " f"(PUB ${PUB['gatec_ev_sunk']:+,.0f}) | al pass ${e3['at_pass']:+,.0f} " f"(PUB ${PUB['gatec_ev_pass']:+,.0f}) | al payout ${e3['at_payout']:+,.0f} " f"(PUB ${PUB['gatec_ev_pay']:+,.0f}) -> REPLICATA") tick("§4.2") # ================================================================= §5 PREZZI OMBRA hr("§5. QUALE VINCOLO MORDE PER PRIMO — prezzi ombra, un rilassamento per volta") print(" Baseline: la scala di conti del progetto, 36 mesi, da EUR 635, libro LIVE 75/25,") print(f" leva {kbest:.3f}, lente accoppiata, morte-firm " f"{pl.FIRM_DEATH_PER_YEAR:.0%}/anno, cap e regole veri.") print(" §5.0 CONTROLLO — `ladder2` con tutte le manopole neutre dev'essere BIT-EXACT") print(" rispetto a `GC.ladder` (che e' bit-exact rispetto a `pc.simulate`).") ref = GC.ladder(RL_full, G_full, 1500, kbest, GC.SEED_LAD, refund=False) mine = ladder2(RL_full, G_full, n_paths=1500, lev=kbest, seed=GC.SEED_LAD) dm = float(np.abs(ref["eur_day"] - mine["eur_day"]).max()) print(f" max|diff| percorso per percorso = {dm:.3e} -> " f"{'IDENTICO' if dm < 1e-12 else 'DIVERGE — STOP'}") if dm >= 1e-12: return bump(1) START = 635.0 base = ladder2(RL_full, G_full, n_paths=2500, lev=kbest, seed=GC.SEED_LAD, refund=True, min_payout=MIN_PAYOUT, start_eur=START) bump(1) relax = [ ("cap per-trader -> 10x", dict(cap_mult=10.0)), ("slot 6 -> 24 (piu' firm)", dict(n_slots=24)), ("cap 10x E slot 24", dict(cap_mult=10.0, n_slots=24)), ("maxDD funded 6/10% -> nessuno", dict(fu_ml_mult=100.0)), ("daily-loss 4/5% -> nessuna", dict(dl_mult=100.0)), ("barriera eval -> nessuna", dict(ev_dd_mult=100.0)), ("morte-firm 10% -> 0%", dict(p_death=0.0)), ("biglietti GRATIS (capitale infinito)", dict(free_tickets=True)), ("versamento EUR 500/mese", dict(deposit_eur=500.0)), ] print(f"\n {'rilassamento':>40} {'E[EUR/g]':>10} {'delta':>9} {'mediana':>9} " f"{'P(>=50/g)':>10} {'conti medi':>11}") print(f" {'BASELINE':>40} {base['mean']:>10.2f} {'—':>9} {base['med']:>9.2f} " f"{base['p_target']:>10.1%} {base['mean_conc']:>11.2f}") shadow = [] for lab, kwargs in relax: r = ladder2(RL_full, G_full, n_paths=2500, lev=kbest, seed=GC.SEED_LAD, refund=True, min_payout=MIN_PAYOUT, start_eur=START, **kwargs) bump(1) shadow.append((lab, r["mean"] - base["mean"], r)) print(f" {lab:>40} {r['mean']:>10.2f} {r['mean']-base['mean']:>+9.2f} " f"{r['med']:>9.2f} {r['p_target']:>10.1%} {r['mean_conc']:>11.2f}") shadow.sort(key=lambda x: -x[1]) print(f"\n >>> VINCOLO BINDING = **{shadow[0][0]}** (vale {shadow[0][1]:+.2f} EUR/g), " f"secondo: {shadow[1][0]} ({shadow[1][1]:+.2f}).") print(" §5.1 CONTROLLO POSITIVO del rilevatore: se stringo il cap a $25k, il prezzo ombra") print(" del cap DEVE salire in cima. (Un rilevatore che non trova nulla e' rotto.)") tight = ladder2(RL_full, G_full, n_paths=2500, lev=kbest, seed=GC.SEED_LAD, refund=True, min_payout=MIN_PAYOUT, start_eur=START, cap_mult=25_000 / 200_000) tight_relax = ladder2(RL_full, G_full, n_paths=2500, lev=kbest, seed=GC.SEED_LAD, refund=True, min_payout=MIN_PAYOUT, start_eur=START, cap_mult=10.0) bump(2) print(f" con cap $25k: E[EUR/g] {tight['mean']:.2f}; rilassandolo a 10x " f"{tight_relax['mean']:.2f} -> prezzo ombra " f"{tight_relax['mean']-tight['mean']:+.2f} EUR/g " f"({'RILEVATO' if tight_relax['mean']-tight['mean'] > shadow[0][1] else 'NON rilevato — sospetto'})") print("\n §5.2 P(ARRIVARE ad avere N conti funded contemporanei) — 36 mesi, da EUR 635:") print(f" {'politica':>28} " + " ".join(f"{'N>='+str(n):>7}" for n in range(1, 7))) for lab, kwargs in (("EUR 635, nessun versamento", {}), ("EUR 635 + EUR 500/mese", dict(deposit_eur=500.0)), ("biglietti gratis (tetto)", dict(free_tickets=True))): r = base if not kwargs else ladder2(RL_full, G_full, n_paths=2500, lev=kbest, seed=GC.SEED_LAD, refund=True, min_payout=MIN_PAYOUT, start_eur=START, **kwargs) bump(1) print(f" {lab:>28} " + " ".join(f"{r['p_reach'][n]:>7.1%}" for n in range(1, 7))) tick("§5") # ================================================================= §6 MORTE-FIRM hr("§6. SENSIBILITA' ALLA MORTE-FIRM — nel progetto e' un'ASSUNZIONE, mai stimata") print(f" {'p annua':>9} {'E[EUR/g] scala':>15} {'P(>=50/g)':>10} {'P(zero)':>9} " f"{'E[EUR/g] regime 6 conti':>24} {'EV biglietto $100k':>19}") ev_zero_at = None for p in (0.0, 0.05, 0.10, 0.20, 0.40): r = ladder2(RL_full, G_full, n_paths=2500, lev=kbest, seed=GC.SEED_LAD, refund=True, min_payout=MIN_PAYOUT, start_eur=START, p_death=p) fo = np.array([0, 0, 1, 1, 1, 1]) g6 = accounts_mc(RL_full, G_full, k=kbest, notional=100_000.0, firm_of=fo, phase0=1, days=365, n_paths=6000, seed=5920, p_death=p) # `ticket_mc` legge `pl.FIRM_DEATH_PER_YEAR` a ogni chiamata -> patch locale old = pl.FIRM_DEATH_PER_YEAR pl.FIRM_DEATH_PER_YEAR = p tix = GC.ticket_mc(R_M, G_M, lev_ev=LEV_M, lev_fu=LEV_M, notional=100_000.0, firm_death=True) pl.FIRM_DEATH_PER_YEAR = old ev = GC.ev_three_ways(tix, 579.0)["sunk"] bump(3) if ev_zero_at is None and ev < 0: ev_zero_at = p print(f" {p:>8.0%} {r['mean']:>15.2f} {r['p_target']:>10.1%} {r['p_zero']:>9.1%} " f"{g6['e_eur']:>24.2f} ${ev:>18,.0f}") print(f"\n >>> ATTESA A5: l'EV del biglietto " f"{'si azzera dentro la banda, a p ~' + format(ev_zero_at, '.0%') if ev_zero_at else 'resta POSITIVO su tutta la banda 0-40%'}.") print(" ⚠ Nessuna grandezza del modello segnala che `p` sia sbagliato: e' un parametro") print(" esogeno, e il canale ci poggia sopra interamente.") tick("§6") # ================================================================= §7 VERDETTO hr("§7. VERDETTO — in EUR/giorno") e1 = float(B["e"].median()) n_star_med = int(np.ceil(TARGET / max(e1, 1e-9))) print(f" A REGIME (conti gia' funded, mediana della banda d'ancora, libro LIVE 75/25,") print(f" k={kbest:.3f}, 12 mesi): **{e1:.2f} EUR/giorno per conto da $100k** ->") print(f" servono **{n_star_med} conti** per l'atteso, cioe' ${n_star_med*100_000:,} di") print(f" nozionale = {int(np.ceil(n_star_med*100_000/pl.FIRMS[0]['cap']))} firm al cap " f"$200k, {n_star_med*579:,.0f} $ di biglietti.") print(f" Ma P(>=50/g) SATURA al {sat:.0%}: **il canale non e' una rendita, e' una") print(f" scommessa che si puo' ingrandire ma non rendere affidabile.**") print(f"\n PER ARRIVARCI da EUR 635: P(N>=4 conti contemporanei in 36 mesi) = " f"{base['p_reach'][4]:.1%}; E[EUR/g] a 36 mesi {base['mean']:.2f}, " f"mediana {base['med']:.2f}.") print(f" DISTANZA DALL'OBIETTIVO: il filone **non** avvicina i 50 EUR/g. Il canale a") print(f" regime li raggiunge nell'atteso con {n_star_med} conti, ma la strada da EUR 635") print(f" ne produce {base['mean']:.2f} EUR/g attesi e {base['med']:.2f} mediani in 36 mesi.") print(f"\n CELLE/TRIAL DICHIARATI (al rialzo): {CELLS}") print(f" fatto in {time.time()-T0:.0f}s") print("=" * 108) if __name__ == "__main__": main()