Files
PythagorasGoal/scripts/research/r0823b_scala_prop.py
T
Adriano Dal Pastro 8c0b5f97ee registro: chiusi §58-69 — dodici verdetti, 0 candidati, e due appoggi tolti a decisioni gia' prese
I dodici filoni della sesta ondata erano rimasti `_in corso_`: la sessione si e'
chiusa dopo che gli agenti avevano scritto gli script e prima che i verdetti
fossero consolidati, e i loro messaggi finali sono persi. I numeri qui NON
vengono da quei messaggi: vengono dalla riesecuzione dei dodici script
(07:38-08:06 UTC, sequenziale, log in logs/r0823b/ che e' gitignored).
E' il motivo per cui l'ondata non e' andata persa: ogni script calcola il
proprio verdetto a runtime.

Cosa tocca decisioni gia' prese:
- §67 il gate pre-registrato XSR01 del 23/10 legge un monitor tarato sul
  pavimento del venue sbagliato (C* $15-20k, non ~$3k)
- §64 la raccomandazione di §51 (raccogliere la catena USDC) non e'
  giustificata dalla ragione che porta: le due superfici sono la stessa
- §60 la politica MISTO scelta il 25/07 non e' piu' l'ottimo (oggi MISTO-A)
- §63 domanda fiscale NUOVA, diversa da quella aperta il 07/08

Il risultato piu' grande e' di §58: l'obiettivo del progetto ha DUE definizioni
operative in uso che danno 33,9% contro 0,33% sulla stessa domanda, e non era
mai stato detto quale si stesse ottimizzando.

r0823b_quasi_passati.py (§68) terminava con IndexError: Griglia.combo()
indicizzava con self.idx (2720 giorni) un sottoinsieme di 958 righe. Corretto
col parametro idx esplicito piu' un controllo di lunghezza; la
rinormalizzazione e' riga per riga, quindi i valori sono quelli dell'intento
dell'autore. La correzione e' del coordinatore, non dell'autore, ed e'
dichiarata nel registro.

Libro, pesi, cron, config INVARIATI. Nessun ordine.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-23 12:40:30 +00:00

895 lines
48 KiB
Python

"""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()