Files
Adriano Dal Pastro 1227b2baab 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>
2026-07-26 20:34:00 +00:00

190 lines
8.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Test del piano versamenti (scripts/research/r0726_deposits.py).
Il test piu' importante e' `test_la_rendita_al_muro_e_esattamente_il_bersaglio`: lega la tabella
della "domanda inversa" al muro calcolato altrove. Se le due macchinerie divergono, una delle due
tabelle sta mentendo e nessun altro controllo se ne accorgerebbe.
"""
from __future__ import annotations
import sys
from pathlib import Path
import numpy as np
import pytest
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
sys.path.insert(0, str(ROOT / "scripts" / "research"))
import r0725_capcurve as CC # noqa: E402
import r0726_deposits as DP # noqa: E402
# ===========================================================================
# calendario dei versamenti
# ===========================================================================
def test_versamento_mensile_conta_i_periodi_giusti():
s = DP.monthly_schedule(365, 100.0)
assert (s > 0).sum() == 12, "12 versamenti in un anno di 365 giorni a passo 30"
assert s[0] == 0.0, "il primo versamento non e' al giorno 0 (li' c'e' il capitale iniziale)"
def test_stop_year_interrompe_davvero():
s = DP.monthly_schedule(365 * 10, 100.0, stop_year=3)
ultimo = np.nonzero(s)[0].max()
assert ultimo < 3 * 365
assert (s > 0).sum() == pytest.approx(36, abs=1)
def test_la_crescita_aumenta_gli_importi_nel_tempo():
s = DP.monthly_schedule(365 * 10, 100.0, growth=0.05)
nz = s[s > 0]
assert nz[-1] > nz[0] * 1.5, "dopo ~10 anni al 5% l'importo deve essere >1.5x"
piatto = DP.monthly_schedule(365 * 10, 100.0)
assert s.sum() > piatto.sum()
def test_crescita_zero_equivale_al_piatto():
a = DP.monthly_schedule(365 * 5, 200.0, growth=0.0)
b = DP.monthly_schedule(365 * 5, 200.0)
assert np.allclose(a, b)
# ===========================================================================
# simulazione
# ===========================================================================
def test_senza_versamenti_e_solo_capitalizzazione():
paths = np.full((3, 100), 0.001)
cap, paid = DP.simulate(paths, np.zeros(100))
atteso = DP.START * (1.001 ** 100)
assert cap[0] == pytest.approx(atteso, rel=1e-9)
assert paid[0] == pytest.approx(DP.START)
def test_a_rendimento_zero_il_capitale_e_la_somma_dei_versamenti():
paths = np.zeros((2, 365))
s = DP.monthly_schedule(365, 100.0)
cap, paid = DP.simulate(paths, s)
assert cap[0] == pytest.approx(DP.START + s.sum())
assert paid[0] == pytest.approx(DP.START + s.sum())
def test_il_totale_versato_include_il_capitale_iniziale():
"""Confrontare un capitale finale con un 'versato' che dimentica i $600 iniziali gonfia il
rapporto capitale/versato — errore gia' fatto una volta oggi nel contatore `paid`."""
s = DP.monthly_schedule(365, 50.0)
_, paid = DP.simulate(np.zeros((2, 365)), s)
assert paid[0] == pytest.approx(DP.START + s.sum())
assert paid[0] > s.sum()
# ===========================================================================
# il legame con il muro — il test che lega le due macchinerie
# ===========================================================================
def test_la_rendita_al_muro_e_esattamente_il_bersaglio():
"""Un capitale pari al muro deve dare, per costruzione, esattamente TARGET_EUR_DAY.
Se questa identita' si rompe, la tabella (D) e la tabella dei muri non parlano piu' della
stessa cosa."""
import r0726_capwall_refresh as WR
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)
rent = DP.rent_from_capital(np.array([wall]), perp)[0]
assert rent == pytest.approx(CC.TARGET_EUR_DAY, rel=1e-6)
def test_la_rendita_e_lineare_nel_capitale():
r = DP.rent_from_capital(np.array([100_000.0, 200_000.0]), 0.10)
assert r[1] == pytest.approx(2 * r[0])
# ===========================================================================
# i FINDING: se cambiano, e' una notizia
# ===========================================================================
def test_anticipare_batte_posticipare_a_pari_totale():
"""Il finding di (C). Su ritorni a drift positivo deterministico e' un'identita' algebrica,
e serve come controllo di direzione: se uscisse il contrario, la simulazione e' rotta."""
n = 365 * 20
paths = np.full((1, n), 0.0004)
tot = 60_000.0 * CC.EURUSD
presto, tardi = np.zeros(n), np.zeros(n)
k = len(range(30, 365 * 5, 30))
for t in range(30, 365 * 5, 30):
presto[t] = tot / k
k2 = len(range(365 * 10, n, 30))
for t in range(365 * 10, n, 30):
tardi[t] = tot / k2
assert presto.sum() == pytest.approx(tardi.sum(), rel=1e-9)
cap_p, _ = DP.simulate(paths, presto)
cap_t, _ = DP.simulate(paths, tardi)
assert cap_p[0] > cap_t[0] * 1.5
def test_il_fattore_deluck_e_quello_misurato_non_quello_a_occhio():
"""Il ×0.6 del 25/07 era scelto a occhio ed e' stato misurato ×0.87-0.91 il 26/07."""
assert DP.DELUCK == 0.89
def test_hazard_giornaliero_ricompone_alla_probabilita_annua():
"""Il salto di venue: (1-haz)^365 deve dare (1-p). Sbagliare qui cambia tutte le colonne."""
for p in (0.005, 0.01, 0.02, 0.05):
haz = 1.0 - (1.0 - p) ** (1.0 / 365.0)
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.")