merge: piano 5k+500/mese + strozzatura cap post-deposito
This commit is contained in:
@@ -152,3 +152,86 @@ E sopra tutte, fuori scala: **a p=5% di rischio venue il risultato mediano e' ze
|
|||||||
piu' importante del filone.
|
piu' importante del filone.
|
||||||
4. **Quando l'obiettivo dichiarato non e' raggiungibile, la tabella utile e' quella inversa**:
|
4. **Quando l'obiettivo dichiarato non e' raggiungibile, la tabella utile e' quella inversa**:
|
||||||
non "quando arrivo a X" ma "cosa compro con quello che ho".
|
non "quando arrivo a X" ma "cosa compro con quello che ho".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Addendum — il piano dichiarato: €5.000 subito + €500/mese
|
||||||
|
|
||||||
|
L'operatore ha dato i numeri veri. Cambiano la scala: €5.000 portano il conto da **$597 a $6.047
|
||||||
|
(10.1x)** e €500/mese e' il doppio del livello tabulato sopra. Ricalcolato in dedicata
|
||||||
|
(`r0726_piano_5k500.py`), non estrapolato.
|
||||||
|
|
||||||
|
## Quando
|
||||||
|
|
||||||
|
```
|
||||||
|
traguardo ($272.061): p10 9.4a MEDIANA 11.6a p90 14.4a
|
||||||
|
P(entro 10a) 18% P(entro 12a) 57% P(entro 15a) 94% P(entro 20a) 100%
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
anno capitale p10 MEDIANO p90 rendita mediana
|
||||||
|
1$ 12,408$ 14,057$ 16,405 2.58 €/g
|
||||||
|
3$ 28,401$ 34,674$ 44,000 6.37 €/g
|
||||||
|
5$ 48,306$ 62,952$ 86,121 11.57 €/g
|
||||||
|
10$ 130,089$ 191,398$ 299,555 35.18 €/g
|
||||||
|
15$ 288,993$ 476,825$ 847,266 87.63 €/g
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ **Validazione incrociata:** la variante "solo €500/mese da $600" da' **12.4 anni**, che e'
|
||||||
|
*esattamente* il numero pubblicato il 25/07 con macchineria diversa. Replica indipendente.
|
||||||
|
|
||||||
|
## Quanto vale il versamento iniziale: 0.8 anni
|
||||||
|
|
||||||
|
```
|
||||||
|
€5.000 subito (il piano) 11.6a
|
||||||
|
€5.000 spalmati sul 1° anno 11.7a
|
||||||
|
€5.000 al 5° anno 12.1a
|
||||||
|
niente lump, solo €500/mese 12.4a
|
||||||
|
```
|
||||||
|
|
||||||
|
⚠️ **Meno di quanto suggerisse il "fattore 6" del §3**, e la ragione e' aritmetica: €5.000 sono
|
||||||
|
**~10 mesi** di versamenti a €500, quindi comprano ~10 mesi. Il fattore 6 riguardava lo spostamento
|
||||||
|
di €60.000 su vent'anni. **Anticipare vale in proporzione a quanto si anticipa** — ovvio a
|
||||||
|
posteriori, ma la mia aspettativa era piu' alta e va corretta.
|
||||||
|
|
||||||
|
## Le soglie, e la data che compare
|
||||||
|
|
||||||
|
```
|
||||||
|
$ 3,000 GIA' SUPERATA il giorno 1 GTAA01 eseguibile
|
||||||
|
$ 5,000 GIA' SUPERATA il giorno 1 XSR01 eseguibile (gate pre-registrato 23/10)
|
||||||
|
$ 13,000 ~0.9 anni GTAA01 entra nel book deployable
|
||||||
|
$ 20,000 ~1.6 anni SOGLIA DELLA DECISIONE VENUE
|
||||||
|
$117,000 ~7.6 anni XS01 eseguibile
|
||||||
|
```
|
||||||
|
|
||||||
|
**La decisione "niente split fino a $20k" smette di essere un'ipotesi e diventa una data: ~19
|
||||||
|
mesi.** E le soglie $3k/$5k sono superate il primo giorno — il che NON autorizza ad anticipare il
|
||||||
|
gate XSR01 del 23/10 (anticiparlo e' selezione sull'hold-out), ma rende viva la sua domanda di
|
||||||
|
eseguibilita'.
|
||||||
|
|
||||||
|
## Con il rischio di venue dentro
|
||||||
|
|
||||||
|
```
|
||||||
|
p annua P(traguardo) P(perso TUTTO) cap. mediano 25a
|
||||||
|
0.0% 100.0% 0.0% $2,540,508
|
||||||
|
0.5% 94.3% 11.7% $2,304,596
|
||||||
|
1.0% 88.0% 23.2% $2,018,727
|
||||||
|
2.0% 78.8% 39.6% $1,464,013
|
||||||
|
5.0% 55.4% 72.0% $0
|
||||||
|
```
|
||||||
|
|
||||||
|
Il piano regge bene fino a p=2%. A p=5% il capitale mediano e' **zero**: e' la stessa conclusione
|
||||||
|
del §3, e vale a maggior ragione ora che il capitale in gioco e' 10x.
|
||||||
|
|
||||||
|
## ⚠️ AZIONE OPERATIVA PRIMA DEL VERSAMENTO — il cap fisso diventa una strozzatura
|
||||||
|
|
||||||
|
`src/live/book._cap` usa il cap **dinamico** (equity/2) solo quando l'equity reale e' leggibile;
|
||||||
|
altrimenti ripiega su `max_notional_per_asset_usd = $300`. **A $597 il fallback era INERTE**
|
||||||
|
(equity/2 = $298 ≈ $300). **Dopo il versamento l'equity/2 vale ~$3.023**, quindi un fallback
|
||||||
|
strozzerebbe il book a **~10% del target** — silenziosamente, con la sola allerta `eq_fallback`.
|
||||||
|
|
||||||
|
E' esattamente 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`: **fallisce** se
|
||||||
|
si deposita senza adeguare `max_notional_per_asset_usd`.
|
||||||
|
|
||||||
|
**Non eseguita**: e' un cambio di config su soldi veri, decisione dell'operatore.
|
||||||
|
|||||||
@@ -250,6 +250,49 @@ def main() -> None:
|
|||||||
print(" resta strozzato (vedi la guardia in `_cap`: col cap dinamico attivo, cap =")
|
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).")
|
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__":
|
if __name__ == "__main__":
|
||||||
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()
|
||||||
@@ -131,3 +131,59 @@ def test_hazard_giornaliero_ricompone_alla_probabilita_annua():
|
|||||||
for p in (0.005, 0.01, 0.02, 0.05):
|
for p in (0.005, 0.01, 0.02, 0.05):
|
||||||
haz = 1.0 - (1.0 - p) ** (1.0 / 365.0)
|
haz = 1.0 - (1.0 - p) ** (1.0 / 365.0)
|
||||||
assert (1.0 - haz) ** 365 == pytest.approx(1.0 - p, rel=1e-9)
|
assert (1.0 - haz) ** 365 == pytest.approx(1.0 - p, rel=1e-9)
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
# il PIANO dichiarato (€5.000 subito + €500/mese) — r0726_piano_5k500.py
|
||||||
|
# ===========================================================================
|
||||||
|
def test_il_piano_riproduce_il_numero_gia_pubblicato_senza_lump():
|
||||||
|
"""Controllo incrociato: 'solo €500/mese da $600' deve dare ~12.4 anni, che e' il numero
|
||||||
|
pubblicato il 25/07 con macchineria DIVERSA. Se diverge, una delle due tabelle mente."""
|
||||||
|
import r0725_capcurve as CC2
|
||||||
|
import r0726_capwall_refresh as WR2
|
||||||
|
import r0726_piano_5k500 as PL
|
||||||
|
|
||||||
|
r = DP.deluck_returns()
|
||||||
|
gross = CC2.TARGET_EUR_DAY * 365 * CC2.EURUSD / (1 - CC2.TAX_RATE)
|
||||||
|
_, _, wall = WR2.perp_and_wall(r, 1.0, gross)
|
||||||
|
rng = np.random.default_rng(PL.SEED)
|
||||||
|
paths = CC2._boot_paths(r, 1500, 365 * 25, PL.BLOCK, rng)
|
||||||
|
res = PL.run(paths, PL.CONTO_OGGI, 500.0 * CC2.EURUSD, wall)
|
||||||
|
fin = res["yrs"][~np.isnan(res["yrs"])]
|
||||||
|
assert 11.5 < float(np.percentile(fin, 50)) < 13.5
|
||||||
|
|
||||||
|
|
||||||
|
def test_il_versamento_iniziale_anticipa_il_traguardo():
|
||||||
|
"""Direzione: partire da piu' in alto non puo' allungare il tempo."""
|
||||||
|
import r0725_capcurve as CC2
|
||||||
|
import r0726_capwall_refresh as WR2
|
||||||
|
import r0726_piano_5k500 as PL
|
||||||
|
|
||||||
|
r = DP.deluck_returns()
|
||||||
|
gross = CC2.TARGET_EUR_DAY * 365 * CC2.EURUSD / (1 - CC2.TAX_RATE)
|
||||||
|
_, _, wall = WR2.perp_and_wall(r, 1.0, gross)
|
||||||
|
rng = np.random.default_rng(PL.SEED)
|
||||||
|
paths = CC2._boot_paths(r, 1500, 365 * 25, PL.BLOCK, rng)
|
||||||
|
dep = 500.0 * CC2.EURUSD
|
||||||
|
senza = PL.run(paths, PL.CONTO_OGGI, dep, wall)["yrs"]
|
||||||
|
con = PL.run(paths, PL.CONTO_OGGI + 5_000 * CC2.EURUSD, dep, wall)["yrs"]
|
||||||
|
m_senza = float(np.nanpercentile(senza, 50))
|
||||||
|
m_con = float(np.nanpercentile(con, 50))
|
||||||
|
assert m_con < m_senza
|
||||||
|
|
||||||
|
|
||||||
|
def test_il_cap_fisso_diventa_una_strozzatura_dopo_un_deposito():
|
||||||
|
"""⚠️ Il punto operativo trovato col piano: `_cap` ripiega su max_notional_per_asset_usd
|
||||||
|
quando l'equity reale non e' leggibile. A $600 era INERTE (equity/2 = $300 = cap fisso);
|
||||||
|
dopo un deposito da €5.000 il fallback strozzerebbe il book a ~10% del target.
|
||||||
|
Questo test FALLISCE se qualcuno deposita senza adeguare il cap fisso — che e' l'azione
|
||||||
|
gia' pre-registrata il 2026-07-02 ('al deposito alzare il cap a equity/2')."""
|
||||||
|
import json
|
||||||
|
|
||||||
|
cfg = json.loads((ROOT / "config" / "live.json").read_text())
|
||||||
|
fisso = float(cfg["max_notional_per_asset_usd"])
|
||||||
|
frac = float(cfg["max_notional_per_asset_frac"])
|
||||||
|
equity_attuale = 596.92
|
||||||
|
assert fisso >= equity_attuale * frac * 0.95, (
|
||||||
|
f"cap fisso ${fisso:.0f} molto sotto equity/2 = ${equity_attuale*frac:.0f}: se l'equity "
|
||||||
|
"reale non e' leggibile il book gira strozzato. Alzare max_notional_per_asset_usd.")
|
||||||
|
|||||||
Reference in New Issue
Block a user