1227b2baab
L'operatore ha dato i numeri veri: EUR 5.000 subito + EUR 500/mese. Cambia la scala (conto da $597 a $6.047 = 10.1x), quindi ricalcolato in dedicata invece che estrapolato. QUANDO: traguardo EUR 50/g ($272.061) a p10 9.4a / MEDIANA 11.6a / p90 14.4a; P(entro 15a) 94%, P(entro 20a) 100%. Rendita mediana: EUR 6.37/g a 3 anni, EUR 11.57/g a 5, EUR 35.18/g a 10, EUR 87.63/g a 15. VALIDAZIONE INCROCIATA: la variante "solo EUR 500/mese da $600" da' 12.4 anni = esattamente il numero pubblicato il 25/07 con macchineria diversa. IL LUMP VALE 0.8 ANNI (12.4 -> 11.6), MENO di quanto suggerisse il "fattore 6" del calendario, e la ragione e' aritmetica: EUR 5.000 sono ~10 mesi di versamenti a EUR 500, quindi comprano ~10 mesi. Anticipare vale in proporzione a quanto si anticipa. La mia aspettativa era piu' alta ed e' corretta. SOGLIE: $3k e $5k superate il giorno 1 (GTAA01 e XSR01 diventano eseguibili); $13k a ~0.9 anni (GTAA01 entra nel book deployable); $20k a ~1.6 anni = LA DECISIONE VENUE DEL 26/07 SMETTE DI ESSERE UN'IPOTESI E DIVENTA UNA DATA (~19 mesi); $117k a ~7.6 anni (XS01). Le soglie superate subito NON autorizzano ad anticipare il gate XSR01 del 23/10. CON IL RISCHIO DI VENUE: P(traguardo) 100% -> 88% a p=1% (P(perso tutto) 23.2%); a p=5% il capitale mediano e' ZERO. Il piano regge fino a p=2%. ⚠️ AZIONE OPERATIVA TROVATA (non eseguita, e' config su soldi veri): `book._cap` ripiega su max_notional_per_asset_usd=$300 quando l'equity reale non e' leggibile. A $597 il fallback era INERTE (equity/2 = $298 ~ $300); dopo il versamento equity/2 vale ~$3.023, quindi un fallback strozzerebbe il book al ~10% del target, in silenzio. E' l'azione gia' pre-registrata il 2026-07-02 ("al deposito alzare il cap a equity/2"). Cablato test_il_cap_fisso_diventa_una_strozzatura_dopo_un_deposito, che FALLISCE se si deposita senza adeguare il cap. Aggiunta anche la sezione (F) frontiera di sostenibilita' a r0726_deposits.py: capitale e rendita per (importo x anni sostenuti), e il confronto "tirare poco tempo vs comodo a lungo" — EUR 400/m per 5a batte EUR 200/m per 20a, ma EUR 600/m per 3a perde contro EUR 250/m per 15a. Book, pesi, cron, config INVARIATI. 380 test verdi (+3). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
201 lines
9.1 KiB
Python
201 lines
9.1 KiB
Python
#!/usr/bin/env python
|
||
"""r0726_piano_5k500.py — il piano DICHIARATO dall'operatore: €5.000 subito + €500/mese.
|
||
|
||
Non e' una griglia di scenari: e' il calcolo del piano vero, perche' cambia la scala del problema.
|
||
Il conto oggi e' $597; €5.000 lo portano a ~$6.050 = **9x**, e €500/mese e' il doppio del livello
|
||
tabulato finora. Tre cose vanno ricalcolate e non estrapolate:
|
||
|
||
1. la TRAIETTORIA (quando si tocca il capitale-rendita, con che dispersione);
|
||
2. quanto vale il VERSAMENTO INIZIALE rispetto agli stessi soldi versati piu' tardi — il 26/07
|
||
ha misurato che il calendario vale un fattore 6, quindi un anticipo di €5.000 non e'
|
||
"un mese in piu'";
|
||
3. le SOGLIE OPERATIVE che si attraversano (GTAA01 a ~$13k, XS01 a ~$117k, XSR01 a ~$5k con
|
||
gate 23/10), perche' cambiano il book eseguibile.
|
||
|
||
Ipotesi dichiarate: fattore d'ancora ×0.89 MISURATO, book live TP01+SKH01 (i backtest sono a
|
||
0.10% RT), fisco 33%, EURUSD 1.09, bersaglio €50/g netti = $272.061.
|
||
|
||
uv run python scripts/research/r0726_piano_5k500.py
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
import numpy as np
|
||
|
||
ROOT = Path(__file__).resolve().parents[2]
|
||
sys.path.insert(0, str(ROOT))
|
||
sys.path.insert(0, str(ROOT / "scripts" / "research"))
|
||
|
||
import r0725_capcurve as CC # noqa: E402
|
||
import r0726_capwall_refresh as WR # noqa: E402
|
||
import r0726_deposits as DP # noqa: E402
|
||
|
||
CONTO_OGGI = 596.92 # equity reale letta dal conto Deribit
|
||
LUMP_EUR = 5_000.0
|
||
MENSILE_EUR = 500.0
|
||
N_PATHS = 6000
|
||
BLOCK = 20
|
||
YEARS = 25
|
||
SEED = 20260726
|
||
|
||
|
||
def run(paths: np.ndarray, start: float, dep_usd: float, wall: float,
|
||
lump_at_day: int | None = 0, lump: float = 0.0) -> dict:
|
||
"""Traiettoria con capitale iniziale, versamento mensile e un eventuale versamento una-tantum."""
|
||
n_paths, n_days = paths.shape
|
||
cap = np.full(n_paths, start)
|
||
hit = np.full(n_paths, -1, int)
|
||
snaps = {}
|
||
for t in range(n_days):
|
||
if lump and lump_at_day == t:
|
||
cap += lump
|
||
cap = cap * (1.0 + paths[:, t])
|
||
if t % 30 == 0 and t > 0:
|
||
cap += dep_usd
|
||
newly = (hit < 0) & (cap >= wall)
|
||
hit[newly] = t
|
||
if (t + 1) % 365 == 0:
|
||
snaps[(t + 1) // 365] = cap.copy()
|
||
yrs = np.where(hit >= 0, hit / 365.0, np.nan)
|
||
return dict(yrs=yrs, snaps=snaps, cap=cap)
|
||
|
||
|
||
def main() -> None:
|
||
print("=" * 100)
|
||
print(" IL PIANO: €5.000 subito + €500/mese — calcolo dedicato")
|
||
print("=" * 100)
|
||
|
||
r = DP.deluck_returns()
|
||
gross = CC.TARGET_EUR_DAY * 365 * CC.EURUSD / (1 - CC.TAX_RATE)
|
||
_, perp, wall = WR.perp_and_wall(r, 1.0, gross)
|
||
lump = LUMP_EUR * CC.EURUSD
|
||
dep = MENSILE_EUR * CC.EURUSD
|
||
start = CONTO_OGGI + lump
|
||
|
||
print(f" conto oggi ${CONTO_OGGI:,.2f}")
|
||
print(f" versamento subito €{LUMP_EUR:,.0f} = ${lump:,.0f} -> partenza ${start:,.0f} "
|
||
f"({start/CONTO_OGGI:.1f}x il conto attuale)")
|
||
print(f" versamento mensile€{MENSILE_EUR:,.0f} = ${dep:,.0f}")
|
||
print(f" bersaglio €{CC.TARGET_EUR_DAY:.0f}/g netti = ${wall:,.0f} "
|
||
f"(rendita perpetua {perp:.2%}, fattore d'ancora ×{DP.DELUCK})")
|
||
|
||
rng = np.random.default_rng(SEED)
|
||
n_days = 365 * YEARS
|
||
paths = CC._boot_paths(r, N_PATHS, n_days, BLOCK, rng)
|
||
|
||
# ------------------------------------------------------------------ traiettoria
|
||
res = run(paths, start, dep, wall)
|
||
yrs = res["yrs"]
|
||
fin = yrs[~np.isnan(yrs)]
|
||
print("\n" + "=" * 100)
|
||
print(" QUANDO")
|
||
print("=" * 100)
|
||
print(f"\n traguardo (${wall:,.0f}): p10 {np.percentile(fin,10):.1f}a "
|
||
f"MEDIANA {np.percentile(fin,50):.1f}a p90 {np.percentile(fin,90):.1f}a")
|
||
print(f"\n {'entro':>8}{'probabilita':>14}")
|
||
for h in (5, 8, 10, 12, 15, 20):
|
||
print(f" {h:>6}a {float((yrs <= h).sum())/len(yrs):>13.1%}")
|
||
|
||
print(f"\n {'anno':>6}{'capitale p10':>16}{'MEDIANO':>14}{'p90':>16}{'rendita mediana':>18}")
|
||
for y in (1, 2, 3, 5, 8, 10, 15, 20):
|
||
s = res["snaps"][y]
|
||
rent = float(np.median(s)) * perp * (1 - CC.TAX_RATE) / 365.0 / CC.EURUSD
|
||
print(f" {y:>6}${np.percentile(s,10):>15,.0f}${np.median(s):>13,.0f}"
|
||
f"${np.percentile(s,90):>15,.0f}{rent:>16.2f} €/g")
|
||
|
||
# ------------------------------------------------------------------ quanto vale l'anticipo
|
||
print("\n" + "=" * 100)
|
||
print(" QUANTO VALE VERSARE I €5.000 SUBITO invece che spalmati")
|
||
print("=" * 100)
|
||
alt = {
|
||
"€5.000 subito (il piano)": (start, 0, 0.0),
|
||
"€5.000 spalmati sul 1° anno": (CONTO_OGGI, None, 0.0),
|
||
"€5.000 al 5° anno": (CONTO_OGGI, 365 * 5, lump),
|
||
"niente lump, solo €500/mese": (CONTO_OGGI, None, 0.0),
|
||
}
|
||
print(f"\n {'variante':<30}{'mediana traguardo':>20}{f'cap. mediano {YEARS}a':>20}")
|
||
for lab, (st, day, lp) in alt.items():
|
||
if lab == "€5.000 spalmati sul 1° anno":
|
||
# 12 rate extra da lump/12 nel primo anno
|
||
cap = np.full(N_PATHS, CONTO_OGGI)
|
||
hit = np.full(N_PATHS, -1, int)
|
||
for t in range(n_days):
|
||
cap = cap * (1.0 + paths[:, t])
|
||
if t % 30 == 0 and t > 0:
|
||
cap += dep + (lump / 12.0 if t <= 365 else 0.0)
|
||
newly = (hit < 0) & (cap >= wall)
|
||
hit[newly] = t
|
||
y = np.where(hit >= 0, hit / 365.0, np.nan)
|
||
f = y[~np.isnan(y)]
|
||
print(f" {lab:<30}{np.percentile(f,50):>19.1f}a${np.median(cap):>19,.0f}")
|
||
continue
|
||
rr = run(paths, st, dep, wall, lump_at_day=day, lump=lp)
|
||
f = rr["yrs"][~np.isnan(rr["yrs"])]
|
||
print(f" {lab:<30}{np.percentile(f,50):>19.1f}a${np.median(rr['cap']):>19,.0f}")
|
||
|
||
# ------------------------------------------------------------------ soglie
|
||
print("\n" + "=" * 100)
|
||
print(" SOGLIE OPERATIVE CHE SI ATTRAVERSANO (e quando, al mediano)")
|
||
print("=" * 100)
|
||
soglie = [(3_000, "GTAA01 eseguibile (GTAA_MIN_CAPITAL) — ma nel BOOK entra a ~$13k"),
|
||
(5_000, "XSR01 diventa eseguibile (gate pre-registrato 2026-10-23)"),
|
||
(13_000, "GTAA01 entra nel book deployable (23% x cap >= $3k)"),
|
||
(20_000, "SOGLIA DELLA DECISIONE VENUE: si riapre lo split (26/07)"),
|
||
(117_000, "XS01 eseguibile (~$20k sulla gamba = 19 gambe HL)")]
|
||
med_by_day = None
|
||
for lv, nota in soglie:
|
||
# primo giorno in cui il capitale MEDIANO supera la soglia
|
||
if med_by_day is None:
|
||
cap = np.full(N_PATHS, start)
|
||
med_by_day = np.empty(n_days)
|
||
for t in range(n_days):
|
||
cap = cap * (1.0 + paths[:, t])
|
||
if t % 30 == 0 and t > 0:
|
||
cap += dep
|
||
med_by_day[t] = np.median(cap)
|
||
idx = np.nonzero(med_by_day >= lv)[0]
|
||
when = f"{idx[0]/365.0:.1f} anni" if len(idx) else "oltre l'orizzonte"
|
||
gia = " (GIA' SUPERATA il giorno 1)" if lv <= start else ""
|
||
print(f" ${lv:>7,} {when:>18}{gia} {nota}")
|
||
|
||
print("\n ⚠️ Le soglie sono attraversate dal capitale MEDIANO: meta' dei percorsi ci arriva")
|
||
print(" prima, meta' dopo. Non sono date, sono ordini di grandezza.")
|
||
|
||
# ------------------------------------------------------------------ rischio di venue
|
||
# Tutto quanto sopra assume che il conto ESISTA ancora. Dopo il lavoro del 26/07 quella
|
||
# non e' un'ipotesi neutra: e' la modalita' dominante di perdere tutto, e va messa dentro.
|
||
print("\n" + "=" * 100)
|
||
print(" LO STESSO PIANO, CON IL RISCHIO CHE IL CONTO SPARISCA")
|
||
print("=" * 100)
|
||
print(" Le tabelle sopra assumono che Deribit ci sia ancora. Il 26/07 ha misurato che e' la")
|
||
print(" modalita' DOMINANTE di perdere tutto, non diversificabile dagli sleeve.")
|
||
print(f"\n {'p annua':>9}{'P(traguardo)':>15}{'P(perso TUTTO)':>17}{'cap. mediano ' + str(YEARS) + 'a':>22}")
|
||
rng_v = np.random.default_rng(SEED + 99)
|
||
for p in (0.0, 0.005, 0.01, 0.02, 0.05):
|
||
haz = 1.0 - (1.0 - p) ** (1.0 / 365.0)
|
||
alive = np.cumprod(rng_v.random((N_PATHS, n_days)) > haz, axis=1) if p > 0 \
|
||
else np.ones((N_PATHS, n_days))
|
||
cap = np.full(N_PATHS, start)
|
||
hit = np.full(N_PATHS, -1, int)
|
||
for t in range(n_days):
|
||
cap = cap * (1.0 + paths[:, t])
|
||
if t % 30 == 0 and t > 0:
|
||
cap += dep
|
||
cap = cap * alive[:, t]
|
||
newly = (hit < 0) & (cap >= wall)
|
||
hit[newly] = t
|
||
print(f" {p:>8.1%}{float((hit >= 0).mean()):>14.1%}"
|
||
f"{float((alive[:, -1] == 0).mean()):>16.1%}${np.median(cap):>21,.0f}")
|
||
print("\n Nota: 'perso TUTTO' vuol dire il capitale, non solo i versamenti mancati — e con")
|
||
print(" un conto solo 'almeno un fallimento' COINCIDE con 'perso tutto'. Il tripwire")
|
||
print(" cablato oggi copre la parte di questo rischio che ha una FINESTRA (venue gated),")
|
||
print(" non quella improvvisa (furto di chiavi, sequestro).")
|
||
print(" ⚠️ E il traguardo cade DOPO la soglia dei $20k: la decisione 'niente split fino a")
|
||
print(" $20k' ha ora una data, non un'ipotesi.")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|