research: il piano dichiarato EUR 5.000 + EUR 500/mese, e la strozzatura del cap
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>
This commit is contained in:
@@ -250,6 +250,49 @@ def main() -> None:
|
||||
print(" resta strozzato (vedi la guardia in `_cap`: col cap dinamico attivo, cap =")
|
||||
print(" equity/2 = esattamente il nozionale massimo che il book puo' chiedere).")
|
||||
|
||||
# ------------------------------------------------------------ (F) frontiera di sostenibilita'
|
||||
# "Quanto posso versare?" non ha una risposta da modello: dipende dal bilancio di chi versa.
|
||||
# Ma la domanda DIETRO ("conviene tirare?") si': si misura come iso-risultato fra un importo
|
||||
# alto tenuto poco e uno basso tenuto a lungo.
|
||||
print("\n" + "=" * 100)
|
||||
print(" (F) FRONTIERA DI SOSTENIBILITA' — conviene tirare, o restare comodi piu' a lungo?")
|
||||
print("=" * 100)
|
||||
print("\n Capitale mediano a 20 anni per (importo mensile × anni per cui riesci a tenerlo):")
|
||||
amounts = (100, 150, 200, 250, 400, 600)
|
||||
spans = (3, 5, 10, 15, 20)
|
||||
print(f"\n {'€/mese':>8}" + "".join(f"{s:>12}a" for s in spans))
|
||||
grid = {}
|
||||
for eur in amounts:
|
||||
cells = []
|
||||
for sp in spans:
|
||||
sch = monthly_schedule(n_days, float(eur), stop_year=sp)
|
||||
cap, _ = simulate(paths, sch)
|
||||
grid[(eur, sp)] = float(np.median(cap))
|
||||
cells.append(f"${grid[(eur, sp)]:>11,.0f}")
|
||||
print(f" {eur:>8}" + "".join(cells))
|
||||
|
||||
print("\n Rendita netta mediana corrispondente (€/giorno a 20 anni):")
|
||||
print(f"\n {'€/mese':>8}" + "".join(f"{s:>12}a" for s in spans))
|
||||
for eur in amounts:
|
||||
cells = [f"{rent_from_capital(np.array([grid[(eur, sp)]]), perp)[0]:>11.2f}€"
|
||||
for sp in spans]
|
||||
print(f" {eur:>8}" + "".join(cells))
|
||||
|
||||
# il confronto che risponde alla domanda: tirare poco tempo vs comodo a lungo
|
||||
print("\n IL CONFRONTO CHE DECIDE — stessa fatica percepita, esiti opposti:")
|
||||
for hi, hi_yrs, lo, lo_yrs in ((400, 5, 200, 20), (600, 3, 250, 15), (400, 10, 250, 20)):
|
||||
a, b = grid[(hi, hi_yrs)], grid[(lo, lo_yrs)]
|
||||
tot_a = monthly_schedule(n_days, float(hi), stop_year=hi_yrs).sum()
|
||||
tot_b = monthly_schedule(n_days, float(lo), stop_year=lo_yrs).sum()
|
||||
win = "il PRIMO" if a > b else "il SECONDO"
|
||||
print(f" €{hi}/m per {hi_yrs:>2}a (${tot_a:>7,.0f} versati) -> ${a:>9,.0f} vs "
|
||||
f"€{lo}/m per {lo_yrs:>2}a (${tot_b:>7,.0f}) -> ${b:>9,.0f} vince {win}")
|
||||
|
||||
print("\n REGOLA CHE NE ESCE: l'importo massimo NON e' l'obiettivo. L'obiettivo e' il")
|
||||
print(" massimo importo che sopravvive a un anno brutto — perche' l'unico modo di")
|
||||
print(" perdere davvero e' smettere presto (§A: i primi 5 anni sono il 58% del")
|
||||
print(" risultato), e si smette presto quando si e' versato piu' del sostenibile.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user