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