8c0b5f97ee
I dodici filoni della sesta ondata erano rimasti `_in corso_`: la sessione si e' chiusa dopo che gli agenti avevano scritto gli script e prima che i verdetti fossero consolidati, e i loro messaggi finali sono persi. I numeri qui NON vengono da quei messaggi: vengono dalla riesecuzione dei dodici script (07:38-08:06 UTC, sequenziale, log in logs/r0823b/ che e' gitignored). E' il motivo per cui l'ondata non e' andata persa: ogni script calcola il proprio verdetto a runtime. Cosa tocca decisioni gia' prese: - §67 il gate pre-registrato XSR01 del 23/10 legge un monitor tarato sul pavimento del venue sbagliato (C* $15-20k, non ~$3k) - §64 la raccomandazione di §51 (raccogliere la catena USDC) non e' giustificata dalla ragione che porta: le due superfici sono la stessa - §60 la politica MISTO scelta il 25/07 non e' piu' l'ottimo (oggi MISTO-A) - §63 domanda fiscale NUOVA, diversa da quella aperta il 07/08 Il risultato piu' grande e' di §58: l'obiettivo del progetto ha DUE definizioni operative in uso che danno 33,9% contro 0,33% sulla stessa domanda, e non era mai stato detto quale si stesse ottimizzando. r0823b_quasi_passati.py (§68) terminava con IndexError: Griglia.combo() indicizzava con self.idx (2720 giorni) un sottoinsieme di 958 righe. Corretto col parametro idx esplicito piu' un controllo di lunghezza; la rinormalizzazione e' riga per riga, quindi i valori sono quelli dell'intento dell'autore. La correzione e' del coordinatore, non dell'autore, ed e' dichiarata nel registro. Libro, pesi, cron, config INVARIATI. Nessun ordine. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
878 lines
47 KiB
Python
878 lines
47 KiB
Python
#!/usr/bin/env python
|
|
"""r0823b_obiettivo_diretto — OTTIMIZZARE DIRETTAMENTE L'OBIETTIVO DEL PROGETTO (2026-08-23).
|
|
|
|
§58 dell'ondata. Libro, pesi, cron, config: NON TOCCATI (isolamento provato in §0).
|
|
|
|
IL BUCO
|
|
-------
|
|
Ogni ottimizzazione fatta in questo progetto ha massimizzato **un'altra cosa**:
|
|
* lo **Sharpe** (ogni filone di ricerca direzionale);
|
|
* **J = P(pass) x P(alive)** (PROP-ALLOC §17, PROP-RECAL §29, SKH-ANCHOR-PROP §32);
|
|
* **E[payout]** (r0725_hyro, §37);
|
|
* il **capitale mediano** o **P(arrivare al capitale-rendita)** (§36, r0807_piano_netto).
|
|
E il critico di §37 ha scritto la frase che apre questo filone:
|
|
«piu' leva sul funded ALZA E[payout] e ABBASSA P(payout): sono due obiettivi diversi».
|
|
**Nessuno ha mai ottimizzato DIRETTAMENTE `P(reddito >= 50 EUR/giorno entro H)`**, che e'
|
|
l'obiettivo DICHIARATO del progetto. I numeri operativi pubblicati (42% -> 7,8% -> 4,4% ->
|
|
2,6%) sono tutti **letti alla cella che massimizza J**, non alla cella che massimizza P.
|
|
|
|
LE DUE DEFINIZIONI, DICHIARATE PRIMA DI MISURARE
|
|
------------------------------------------------
|
|
Il progetto ne usa DUE senza mai metterle sulla stessa pagina, e danno risposte opposte:
|
|
|
|
**A — RICCHEZZA (rendita sostenibile).** «reddito >= 50 EUR/g» = il patrimonio PROPRIO
|
|
raggiunge il **capitale-rendita** (muro), cioe' il capitale da cui si preleva 50 EUR/g con
|
|
P(capitale a 20 anni >= capitale iniziale) >= 90% (definizione 25/07, invariata). E' la
|
|
definizione dei muri, delle traiettorie e di tutto il piano (§36). E' **sostenibile per
|
|
costruzione**.
|
|
|
|
**B — FLUSSO (produzione realizzata).** «reddito >= 50 EUR/g» = la media dei payout incassati
|
|
sull'orizzonte, `cum_payout / giorni`, >= 50 EUR/g. E' la definizione di `r0725_prop_ladder`
|
|
e quindi di **ogni numero del canale funded** (§29 7,8% · §32 4,4% · §37 2,6%). **NON e'
|
|
sostenibile**: un conto funded che busta il giorno dopo l'orizzonte azzera il flusso, e la
|
|
stessa cassa che il contatore chiama «reddito» resta nel conto (nel simulatore ufficiale la
|
|
cassa si accumula E conta come reddito: e' una PRODUZIONE, non un prelievo).
|
|
|
|
Qui si misurano **entrambe**, sullo stesso Monte Carlo e sugli stessi percorsi, e si riporta
|
|
come cambia la risposta. Primaria = **A**, perche' e' quella su cui il progetto ha costruito il
|
|
muro, i versamenti e la decisione di venue; B si riporta perche' e' quella dei numeri pubblicati.
|
|
|
|
LO SPAZIO DI POLITICA — dichiarato esaustivamente PRIMA di misurare
|
|
--------------------------------------------------------------------
|
|
1. `k` leva del libro proprio {1,00 · 1,25 · 1,50} (§38: la chiave NON esiste)
|
|
2. `own` ripartizione del libro proprio {LIVE 75/25 · W_A 38/38/25}
|
|
3. `lev_fu` leva del conto funded {0,50 · 0,75 · 1,00 · 1,25}
|
|
4. `n_max` numero di conti concorrenti {0 · 1 · 3 · 6} (0 = nessun biglietto)
|
|
5. `pref` taglia del biglietto {grande · piccolo}
|
|
6. `rebuy` ri-comprare dopo un bust {si' · no}
|
|
7. `floor` capitale proprio sotto cui NON si compra {$0 · $2.000 · $10.000}
|
|
8. `dep` versamento mensile {EUR 0 · 250 · 500} (il piano in corso e' 500)
|
|
9. `invest` la cassa lavora sul libro invece di restare ferma {si' · no}
|
|
Il punto (9) e' un difetto misurabile della macchineria pubblicata: in `pl.simulate` /
|
|
`pc.simulate` / `gate_c.ladder` **la cassa dei payout resta FERMA per 36 mesi**.
|
|
|
|
LE MIE ATTESE, REGISTRATE PRIMA DI MISURARE
|
|
--------------------------------------------
|
|
A1. L'argmax di P(>=50/g) sara' a leva **piu' alta** dell'argmax di J, e la differenza sara'
|
|
grande (fattore >2 su P). *Motivo: P(>=50/g) e' una probabilita' di superare una soglia
|
|
alta = coda destra; J e' una probabilita' di sopravvivenza = coda sinistra.*
|
|
A2. Sotto la definizione **A** (ricchezza) il canale funded **non** portera' P(36 mesi) sopra
|
|
zero in modo apprezzabile: il muro e' ~$313k e 36 mesi da $635 sono ~3 ordini di grandezza.
|
|
Quindi la risposta alla domanda «esiste una politica sopra il 20% a 36 mesi?» sara'
|
|
**SI' sotto B e NO sotto A**, e questa e' la vera risposta del filone.
|
|
A3. Investire la cassa invece di lasciarla ferma valdra' poco su B (il flusso non dipende dalla
|
|
cassa) e molto su A. *Se sbaglio e vale poco anche su A, lo dico.*
|
|
A4. L'ottimo di P sara' accompagnato da P(rovina) molto alta (>50%) — cioe' sara' un ottimo che
|
|
il progetto NON dovrebbe adottare, e va riportato come informazione, non come consiglio.
|
|
A5. Il vincolo binding a 36 mesi non sara' ne' la leva ne' il numero di conti ma il **cap
|
|
$200k/trader** oppure il **capitale che entra**. Mi aspetto il secondo.
|
|
|
|
CONVENZIONI
|
|
-----------
|
|
* Lente **ACCOPPIATA** ovunque ci sia una barriera a un giorno (regola 25/07, riconfermata §33).
|
|
* Cella d'ancora **MEDIANA** (non la canonica, che sta al 91° pctl — §32), scelta con lo stesso
|
|
criterio di `gate_c` su un sottoinsieme d'ancore dichiarato PRIMA.
|
|
* Funding **DENTRO** (§35), de-luck **x0,89** sul drift (§ r0726_deluck_factor), fisco 33% +
|
|
patrimoniale 0,2% sul libro proprio (§ r0807), morte-firm 10%/anno, cap $200k/trader.
|
|
* Baseline del libro: **`hourly`** (la lente di ogni muro e traiettoria) — dichiarata perche' il
|
|
critico di §57 ha misurato che sotto «75/25» convivono tre baseline diverse.
|
|
* Muro: **CONGELATO** a k=1 come primaria (si de-leva al traguardo: la leva non deve poter
|
|
spostare il bersaglio); **MOBILE** come sensibilita' (§53 dichiara che la differenza vale 1,2
|
|
anni). Banda del muro propagata dai valori di §57.
|
|
* Monte Carlo: la risoluzione si dichiara. 1500 percorsi -> SE ~1,0pp su una P vicina al 20%.
|
|
|
|
Uso: nice -n 19 timeout 900 uv run python scripts/research/r0823b_obiettivo_diretto.py
|
|
... --fast (griglia ridotta, ~1/3 del tempo)
|
|
Rete: NESSUNA (solo cache su disco: parquet certificati, cache Binance di §21, cache funding
|
|
di §35). Nessuna scrittura fuori da stdout.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import subprocess
|
|
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 r0725_capcurve as CC # noqa: E402 TAX_RATE, EURUSD, book_series
|
|
import r0725_prop_coupled as pc # noqa: E402 lente accoppiata
|
|
import r0725_prop_ladder as pl # noqa: E402 regole firm, scala di conti
|
|
import r0727_tasse as TX # noqa: E402 PATRIMONIALE, CARRY_ANNI
|
|
import r0807_piano_netto as PN # noqa: E402 passo_fiscale, accumula, muro
|
|
import r0822_prop_alloc as PA # noqa: E402 boot_idx, DELUCK
|
|
import r0822b_prop_recal as PR # noqa: E402 obj_rg, LEVS, pannelli Binance
|
|
import r0822b_xs01_oos as X # noqa: E402 U13, motore XS01
|
|
import r0822c_skh_anchor_prop as AP # noqa: E402 rg_off (offset di SKH01)
|
|
import r0822d_funding as FD # noqa: E402 il funding
|
|
import r0822d_gate_c as GC # noqa: E402 ladder, fvec_for, xs_funding
|
|
from src.portfolio.sleeves import XS_UNIVERSE # noqa: E402
|
|
|
|
FD.USE_NET = False
|
|
|
|
FAST = "--fast" in sys.argv
|
|
EURUSD = pl.EURUSD
|
|
TAX = CC.TAX_RATE
|
|
PATR = TX.PATRIMONIALE
|
|
DELUCK = PA.DELUCK # x0,89 misurato
|
|
W_LIVE = {"TP01": 0.75, "SKH01": 0.25}
|
|
W_A = {"TP01": 0.38, "SKH01": 0.38, "XS01": 0.25}
|
|
START_USD = 635.0 # il conto Deribit VERO di oggi
|
|
SEED_LAD = pl.HORIZON_MONTHS * 0 + 20260822 # lo stesso di gate_c (per la bit-exactness)
|
|
SEED_BOOT = 58023
|
|
N_PATHS = 900 if FAST else 1500
|
|
N_PATHS_J = PA.N_PATHS
|
|
MIN_PAYOUT = GC.MIN_PAYOUT_TRADER
|
|
ORIZZONTI_M = (24, 36, 60, 120)
|
|
H_MAX_D = max(ORIZZONTI_M) * 30
|
|
MARKS = tuple(m * 30 for m in ORIZZONTI_M)
|
|
OFFSETS_SUB = tuple(range(0, 690, 90)) if not FAST else (0, 180, 360, 540) # 8 (o 4) a priori
|
|
PHASES_SUB = AP.PHASES_SUB # (0, 3, 7) — dichiarato da §32
|
|
LEVS_FU = (0.50, 0.75, 1.00, 1.25)
|
|
NMAX = (1, 3, 6)
|
|
PREFS = ("grande", "piccolo")
|
|
REBUY = (True, False)
|
|
|
|
# numeri PUBBLICATI da riprodurre PRIMA di produrne di nuovi
|
|
PUB = dict(muro_L3=313_143.0, perp_L3=0.0635, drift_L3=0.1519,
|
|
p50g_fund=0.026, p50g_p10=0.015, p50g_p90=0.047, pzero_fund=0.403,
|
|
p50g_nofund=0.044,
|
|
muro_p90=186_623.0, muro_m1se=709_753.0) # banda di §57
|
|
|
|
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(" " + t)
|
|
print("=" * 104)
|
|
|
|
|
|
def tick(lab: str) -> None:
|
|
print(f" [{time.time() - T0:6.0f}s] {lab}", flush=True)
|
|
|
|
|
|
# ===========================================================================
|
|
# IL SIMULATORE UNIFICATO
|
|
# ===========================================================================
|
|
def uni(Ra: np.ndarray, Ga: np.ndarray, Rb: np.ndarray, boot: np.ndarray, *,
|
|
lev_fu: float = 1.0, n_max: int = 6, pref: str = "grande", rebuy: bool = True,
|
|
floor_usd: float = 0.0, k_book: float = 1.0, invest: bool = False,
|
|
dep_eur: float = 0.0, start_usd: float = pl.START_EUR * EURUSD,
|
|
seed: int = SEED_LAD, refund: bool = False, min_payout: float = 0.0,
|
|
tax: float = 0.0, patr: float = 0.0, walls: tuple = (),
|
|
marks: tuple = (), deposit_mode: str = "ladder",
|
|
deluck_f: float = DELUCK) -> dict:
|
|
"""UN simulatore per le DUE strade, sugli STESSI percorsi.
|
|
|
|
E' una GENERALIZZAZIONE STRETTA di `gate_c.ladder` (che a sua volta e' `pc.simulate` con
|
|
tre aggiunte): con `n_max=6, pref='grande', rebuy=True, floor=0, k_book=0, invest=False,
|
|
tax=0, patr=0, deposit_mode='ladder'` deve essere **BIT-EXACT** rispetto a `gate_c.ladder`.
|
|
E con `n_max=0, invest=True, tax=0.33, patr=0.002, deposit_mode='accumula'` deve essere
|
|
**BIT-EXACT** rispetto a `PN.accumula`. I due controlli sono in §1.
|
|
|
|
Cio' che aggiunge, e che nessuna macchina pubblicata ha:
|
|
* un **pot proprio** che compone al libro (`k_book x Rb`) invece di restare fermo, col
|
|
fisco d'accumulo dentro (stessa `PN.passo_fiscale` del piano);
|
|
* i **payout che rientrano nel pot** e quindi compongono;
|
|
* `floor_usd` — un capitale proprio sotto cui non si compra un altro biglietto;
|
|
* `rebuy` — la scelta di NON ricomprare dopo un bust;
|
|
* `pref` — biglietto piu' grande o piu' piccolo (piu' tentativi);
|
|
* il **primo passaggio** al capitale-rendita, per l'obiettivo A.
|
|
|
|
`Ra/Ga` = libro del conto FUNDED (ritorno e gap intraday accoppiato), `Rb` = libro PROPRIO.
|
|
Il de-luck si applica al DRIFT di R (non al gap): stessa convenzione di `ladder`.
|
|
"""
|
|
rng = np.random.default_rng(seed)
|
|
n_paths, n_days = boot.shape
|
|
Ram = np.asarray(Ra, float).copy()
|
|
Ram = Ram - (1.0 - deluck_f) * Ram.mean()
|
|
Gam = np.asarray(Ga, float)
|
|
Rbm = np.asarray(Rb, float).copy()
|
|
Rbm = Rbm - (1.0 - deluck_f) * Rbm.mean()
|
|
|
|
n_slot = int(n_max)
|
|
FIRMS = pl.FIRMS
|
|
cash = np.full(n_paths, float(start_usd))
|
|
basis = cash.copy() # base fiscale del pot
|
|
carry = np.zeros((n_paths, TX.CARRY_ANNI))
|
|
anno_start = cash.copy()
|
|
flusso = np.zeros(n_paths)
|
|
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)
|
|
n_tickets = np.zeros(n_paths, int)
|
|
n_pass = np.zeros(n_paths, int)
|
|
peak_notional = np.zeros(n_paths)
|
|
|
|
if n_slot:
|
|
Rs_full = Ram[boot] * lev_fu # (paths, days) — tutti i conti uguali
|
|
Gs_full = Gam[boot] * lev_fu
|
|
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)
|
|
used = np.zeros((n_paths, n_slot), bool)
|
|
firm_alive = np.ones((n_paths, len(FIRMS)), bool)
|
|
p_death_day = 1.0 - (1.0 - pl.FIRM_DEATH_PER_YEAR) ** (1 / 365.0)
|
|
Rbp = Rbm[boot] if (invest and k_book > 0) else None
|
|
|
|
hit = {w: np.full(n_paths, -1, int) for w in walls}
|
|
snap: dict[int, dict] = {}
|
|
fiscale = (tax > 0 or patr > 0)
|
|
|
|
for t in range(n_days):
|
|
# ---- morte di una firm (identico a ladder: il draw avviene SEMPRE, anche a 0 slot,
|
|
# solo se il canale funded esiste)
|
|
if n_slot:
|
|
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():
|
|
h = (firm == fi) & kill[:, None]
|
|
phase[h] = -1
|
|
notional[h] = 0.0
|
|
|
|
# ---- acquisto biglietti
|
|
for k in range(n_slot):
|
|
free = phase[:, k] == -1
|
|
if not rebuy:
|
|
free &= ~used[:, k]
|
|
if not free.any():
|
|
continue
|
|
for fi, F in enumerate(FIRMS):
|
|
u = np.where(firm == fi, notional, 0.0).sum(axis=1)
|
|
order = sorted(F["tickets"], key=(lambda x: -x[0]) if pref == "grande"
|
|
else (lambda x: x[0]))
|
|
for (size, price) in order:
|
|
buy = (free & firm_alive[:, fi] & (cash >= price)
|
|
& (cash - price >= floor_usd) & (u + size <= F["cap"]))
|
|
if not buy.any():
|
|
continue
|
|
cash[buy] -= price
|
|
flusso[buy] -= price
|
|
spent[buy] += price
|
|
n_tickets[buy] += 1
|
|
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
|
|
used[buy, k] = True
|
|
free = free & ~buy
|
|
if not free.any():
|
|
break
|
|
if not free.any():
|
|
break
|
|
|
|
# ---- dinamica dei conti
|
|
r = Rs_full[:, t][:, None]
|
|
g = Gs_full[:, t][:, None]
|
|
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
|
|
n_pass += passed.sum(axis=1)
|
|
peak_notional = np.maximum(peak_notional,
|
|
np.where(phase == 1, notional, 0.0).sum(axis=1))
|
|
|
|
# ---- il POT proprio compone
|
|
if Rbp is not None:
|
|
cash = cash * (1.0 + k_book * Rbp[:, t])
|
|
|
|
# ---- versamento (convenzione `accumula`: giorno 30, 60, ...)
|
|
if deposit_mode == "accumula" and dep_eur and t % 30 == 0 and t > 0:
|
|
cash += dep_eur * EURUSD
|
|
flusso += dep_eur * EURUSD
|
|
deposited += dep_eur * EURUSD
|
|
|
|
# ---- payout mensile + rimborso + versamento (convenzione `ladder`)
|
|
if (t + 1) % 30 == 0:
|
|
if n_slot:
|
|
fu = phase == 1
|
|
pay = fu & (eq > 1.0) & (((eq - 1.0) * notional * pl.SPLIT) >= min_payout)
|
|
gain = np.where(pay, (eq - 1.0) * notional, 0.0)
|
|
net = gain.sum(axis=1) * pl.SPLIT * (1.0 - TAX)
|
|
cum_payout += net
|
|
cash += net
|
|
flusso += 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
|
|
flusso += amt
|
|
got_back += amt
|
|
refunded |= back
|
|
if deposit_mode == "ladder" and dep_eur:
|
|
cash += dep_eur * EURUSD
|
|
flusso += dep_eur * EURUSD
|
|
deposited += dep_eur * EURUSD
|
|
|
|
# ---- primo passaggio al muro (obiettivo A)
|
|
for w, arr in hit.items():
|
|
nuovi = (arr < 0) & (cash >= w)
|
|
arr[nuovi] = t
|
|
|
|
# ---- fisco d'accumulo sul pot
|
|
if fiscale and (t + 1) % 365 == 0:
|
|
PN.passo_fiscale(cash, anno_start, flusso, carry, tax, patr)
|
|
anno_start = cash.copy()
|
|
flusso[:] = 0.0
|
|
basis = cash.copy()
|
|
|
|
if (t + 1) in marks:
|
|
d = t + 1
|
|
ed = cum_payout / EURUSD / (d / 365.0) / 365.0
|
|
contrib = float(start_usd) + deposited
|
|
snap[d] = dict(p_flow=float((ed >= pl.TARGET_EUR_DAY).mean()),
|
|
p_flow10=float((ed >= 10.0).mean()),
|
|
med_eur=float(np.median(ed)),
|
|
med_cash=float(np.median(cash)),
|
|
mean_cash=float(cash.mean()),
|
|
p10_cash=float(np.percentile(cash, 10)),
|
|
p_zero=float((cum_payout <= 0).mean()),
|
|
p_sotto=float((cash < contrib).mean()),
|
|
p_rovina=float((cash < 0.10 * contrib).mean()),
|
|
contrib=float(contrib.mean() if np.ndim(contrib) else contrib),
|
|
**{f"A{w:.0f}": float(((hit[w] >= 0) & (hit[w] < d)).mean())
|
|
for w in walls})
|
|
|
|
ed = cum_payout / EURUSD / (n_days / 365.0) / 365.0
|
|
out = dict(eur_day=ed, cash=cash, snap=snap, hit=hit,
|
|
p_target=float((ed >= pl.TARGET_EUR_DAY).mean()),
|
|
p_ge10=float((ed >= 10.0).mean()),
|
|
p_zero=float((cum_payout <= 0).mean()),
|
|
med=float(np.median(ed)),
|
|
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()),
|
|
n_tickets=float(n_tickets.mean()), n_pass=float(n_pass.mean()),
|
|
peak_notional=float(np.median(peak_notional)),
|
|
contributed=float(start_usd + deposited.mean()))
|
|
bump(1)
|
|
return out
|
|
|
|
|
|
def p_wall_entro(res: dict, wall: float, giorni: int) -> float:
|
|
"""P(il patrimonio PROPRIO tocca `wall` entro `giorni`) — obiettivo A."""
|
|
h = res["hit"][wall]
|
|
return float(((h >= 0) & (h < giorni)).mean())
|
|
|
|
|
|
def anni_med(res: dict, wall: float) -> tuple[float, float]:
|
|
"""(mediana INCONDIZIONATA degli anni al traguardo, P(arrivo entro la simulazione)).
|
|
Non-arrivo = +inf, MAI -1 (lezione 07/08)."""
|
|
h = res["hit"][wall]
|
|
y = np.where(h >= 0, h / 365.0, np.inf)
|
|
return float(np.median(y)), float(np.isfinite(y).mean())
|
|
|
|
|
|
# ===========================================================================
|
|
def main() -> None:
|
|
try:
|
|
sys.stdout.reconfigure(line_buffering=True)
|
|
except Exception: # pragma: no cover
|
|
pass
|
|
|
|
print("=" * 104)
|
|
print(" §58 OBIETTIVO-DIRETTO — ottimizzare `P(reddito >= 50 EUR/g entro H)`, e non altro")
|
|
print("=" * 104)
|
|
print(f" {'MODALITA RIDOTTA (--fast)' if FAST else 'griglia piena'} | "
|
|
f"{N_PATHS} percorsi | orizzonti {ORIZZONTI_M} mesi")
|
|
|
|
# ------------------------------------------------------------ §0 isolamento
|
|
hr("§0. ISOLAMENTO — questo script NON tocca la produzione (il cron gira dal working tree)")
|
|
prot = ["src", "config", "scripts/live", "tests", "data/paper_portfolio", "data/live"]
|
|
dirty = subprocess.run(["git", "status", "--porcelain", "--"] + prot,
|
|
cwd=ROOT, capture_output=True, text=True).stdout.strip()
|
|
print(f" git status sui percorsi protetti {prot}:")
|
|
print(f" {'PULITO — nessuna modifica' if not dirty else 'SPORCO: ' + dirty}")
|
|
if dirty:
|
|
raise SystemExit("La produzione risulta modificata: mi fermo.")
|
|
print(" Nessuna scrittura su disco in tutto lo script (solo stdout). Rete: nessuna.")
|
|
|
|
# ------------------------------------------------------------ §1 repliche
|
|
hr("§1. REPLICHE — prima di un numero nuovo, la macchina deve riprodurre i vecchi")
|
|
ok: dict[str, bool] = {}
|
|
|
|
# (R1) il muro L3 (fisco + funding), $313.143 — §36, replicato al dollaro da §57
|
|
print("\n R1 IL MURO della lente congiunta L3 (funding dentro, fisco dentro, de-luck x0,89)")
|
|
rateF = GC.funding_rate()
|
|
if rateF is None:
|
|
raise SystemExit("cache funding assente: senza di essa nessun numero di questo filone "
|
|
"sarebbe confrontabile coi pubblicati.")
|
|
bookA = FD.book_75_25(None)
|
|
bookF = FD.book_75_25(rateF).reindex(bookA.index)
|
|
idxF = pd.DatetimeIndex(bookA.index)
|
|
rL3 = np.asarray(bookF.values, float)
|
|
rL3 = rL3 - (1.0 - DELUCK) * rL3.mean()
|
|
prel = CC.TARGET_EUR_DAY * 365 * CC.EURUSD
|
|
t = time.time()
|
|
perp, MURO = PN.muro(rL3, prel, TAX, PATR)
|
|
ok["R1"] = abs(MURO - PUB["muro_L3"]) < 1.0
|
|
print(f" serie {len(rL3)} giorni {idxF.min().date()} -> {idxF.max().date()} | "
|
|
f"drift {rL3.mean()*365:.4%} (pubbl. {PUB['drift_L3']:.2%})")
|
|
print(f" perpetua {perp:.4%} (pubbl. {PUB['perp_L3']:.2%}) muro ${MURO:,.0f} "
|
|
f"(pubbl. ${PUB['muro_L3']:,.0f}) -> "
|
|
f"{'ESATTO AL DOLLARO' if ok['R1'] else 'DIVERSO'} ({time.time()-t:.0f}s)")
|
|
bump(1)
|
|
tick("R1")
|
|
|
|
# ------------------------------------------------- il pannello comune
|
|
print("\n Il pannello comune (identico a §29/§32/§37): U13 Binance, W-LONG 2021+.")
|
|
Cb, Ob, Hb, Lb = PR.ohlc_panels(PR.bin_loader, XS_UNIVERSE)
|
|
p13 = Cb[X.U13].dropna()
|
|
XSP = {ph: PR.xs_tuples(p13, Ob.reindex(p13.index)[X.U13], Hb.reindex(p13.index)[X.U13],
|
|
Lb.reindex(p13.index)[X.U13], phase=ph) for ph in PHASES_SUB}
|
|
xs0 = XSP[PHASES_SUB[0]]
|
|
base = pc.crypto_daily_tuples(1.0, 0.0).index.intersection(xs0.index)
|
|
first13 = xs0.index[np.flatnonzero(xs0["R"].values != 0.0)[0]]
|
|
I = base[base >= max(first13, pd.Timestamp("2021-01-01", tz="UTC"))]
|
|
pc.DELUCK = DELUCK
|
|
ev_b = PA.boot_idx(len(I), 365, N_PATHS_J, seed=8220)
|
|
fu_b = PA.boot_idx(len(I), 365, N_PATHS_J, seed=8221)
|
|
xsf = GC.xs_funding_measure(verbose=False)
|
|
xs_ann = xsf["ann"]
|
|
print(f" {len(I)} giorni {I[0].date()} -> {I[-1].date()} | "
|
|
f"XS01 paga {xs_ann*100:+.2f}%/anno di funding (misurato §37)")
|
|
tick("pannello")
|
|
|
|
# (R2) la cella d'ancora MEDIANA e il numero operativo 2,6% [1,5%, 4,7%]
|
|
print(f"\n R2 IL NUMERO OPERATIVO DI §37 — {len(OFFSETS_SUB)} offset x {len(PHASES_SUB)} "
|
|
f"fasi = {len(OFFSETS_SUB)*len(PHASES_SUB)} celle, funding DENTRO, leva argmax-J")
|
|
print(" (sottoinsieme d'ancore dichiarato PRIMA; §37 ne usa 23x3, qui se ne usano "
|
|
f"{len(OFFSETS_SUB)}x{len(PHASES_SUB)} per il tempo macchina)")
|
|
cells = []
|
|
for off in OFFSETS_SUB:
|
|
fA = GC.fvec_for(W_A, I, rateF, off, xs_ann)
|
|
for ph in PHASES_SUB:
|
|
R0, G0 = AP.rg_off(W_A, I, XSP[ph], off)
|
|
Rx = R0 - fA
|
|
jb, lb = -1.0, None
|
|
for lv in PR.LEVS:
|
|
j = PR.obj_rg(Rx, G0, lv, ev_b, fu_b)["J"]
|
|
if j > jb:
|
|
jb, lb = j, lv
|
|
bump(len(PR.LEVS))
|
|
lad = GC.ladder(Rx, G0, N_PATHS, lb, SEED_LAD, refund=False)
|
|
cells.append(dict(off=off, ph=ph, J=jb, lev=lb, p50=lad["p_target"],
|
|
pz=lad["p_zero"], med=lad["med"]))
|
|
bump(1)
|
|
C = pd.DataFrame(cells)
|
|
med_p, p10, p90 = C["p50"].median(), np.percentile(C["p50"], 10), np.percentile(C["p50"], 90)
|
|
ok["R2"] = PUB["p50g_p10"] <= med_p <= PUB["p50g_p90"]
|
|
print(f" P(>=50/g) mediana {med_p:.1%} banda [{p10:.1%}, {p90:.1%}] "
|
|
f"P(zero) {C['pz'].median():.1%}")
|
|
print(f" pubblicato §37: {PUB['p50g_fund']:.1%} [{PUB['p50g_p10']:.1%}, "
|
|
f"{PUB['p50g_p90']:.1%}], P(zero) {PUB['pzero_fund']:.1%} -> "
|
|
f"{'DENTRO LA BANDA' if ok['R2'] else 'FUORI'}")
|
|
k_med = int((C["p50"] - C["p50"].median()).abs().idxmin())
|
|
OFF_M, PH_M, LEV_J = int(C.loc[k_med, "off"]), int(C.loc[k_med, "ph"]), float(C.loc[k_med, "lev"])
|
|
print(f" CELLA D'ANCORA MEDIANA: offset {OFF_M} min, fase {PH_M}, leva argmax-J "
|
|
f"{LEV_J:.2f}x (non la canonica: 91° pctl, §32)")
|
|
fM = GC.fvec_for(W_A, I, rateF, OFF_M, xs_ann)
|
|
R_M0, G_M = AP.rg_off(W_A, I, XSP[PH_M], OFF_M)
|
|
R_M = R_M0 - fM
|
|
tick("R2")
|
|
|
|
# il libro PROPRIO sullo stesso pannello
|
|
rb_full = FD.book_75_25(rateF)
|
|
if rb_full.index.tz is None:
|
|
rb_full.index = rb_full.index.tz_localize("UTC")
|
|
RB_LIVE = np.nan_to_num(rb_full.reindex(I).values.astype(float))
|
|
fA_own = GC.fvec_for(W_A, I, rateF, OFF_M, xs_ann)
|
|
RB_WA = AP.rg_off(W_A, I, XSP[PH_M], OFF_M)[0] - fA_own
|
|
print(f"\n libro PROPRIO sul pannello comune:")
|
|
for lab, r in (("LIVE 75/25", RB_LIVE), ("W_A 38/38/25", RB_WA)):
|
|
print(f" {lab:<14} drift {r.mean()*365:>7.2%} vol {r.std()*365**.5:>6.2%} "
|
|
f"Sharpe {r.mean()*365/(r.std()*365**.5):>5.2f}")
|
|
|
|
# bootstrap CONDIVISO (confronto appaiato fra tutte le politiche)
|
|
rngb = np.random.default_rng(SEED_BOOT)
|
|
nb = int(np.ceil(H_MAX_D / 20))
|
|
st = rngb.integers(0, len(I) - 20, size=(N_PATHS, nb))
|
|
BOOT = (st[:, :, None] + np.arange(20)[None, None, :]).reshape(N_PATHS, -1)[:, :H_MAX_D]
|
|
B36 = BOOT[:, :36 * 30]
|
|
print(f" bootstrap CONDIVISO: {N_PATHS} percorsi x {H_MAX_D} giorni, blocchi 20 -> "
|
|
f"OGNI confronto di questo filone e' APPAIATO")
|
|
|
|
# (R3) bit-exactness: `uni` in modalita' ladder == `gate_c.ladder`
|
|
print("\n R3 BIT-EXACTNESS — `uni` in modalita' ladder deve essere `gate_c.ladder`")
|
|
ref = GC.ladder(R_M, G_M, N_PATHS, LEV_J, SEED_LAD, refund=False, boot=B36)
|
|
mine = uni(R_M, G_M, RB_LIVE, B36, lev_fu=LEV_J, n_max=pl.MAX_CONCURRENT, pref="grande",
|
|
rebuy=True, floor_usd=0.0, k_book=0.0, invest=False, dep_eur=0.0,
|
|
start_usd=pl.START_EUR * EURUSD, seed=SEED_LAD, refund=False, min_payout=0.0,
|
|
tax=0.0, patr=0.0, deposit_mode="ladder")
|
|
d1 = float(np.abs(ref["eur_day"] - mine["eur_day"]).max())
|
|
d2 = float(np.abs(ref["cash"] - mine["cash"]).max())
|
|
ok["R3"] = (d1 == 0.0 and d2 == 0.0)
|
|
print(f" max|dif| EUR/g = {d1:.3e} | max|dif| cassa = {d2:.3e} -> "
|
|
f"{'BIT-EXACT' if ok['R3'] else 'DIVERSO'}")
|
|
|
|
# (R4) bit-exactness: `uni` senza conti == `PN.accumula`
|
|
print("\n R4 BIT-EXACTNESS — `uni` senza canale funded deve essere `PN.accumula`")
|
|
paths_own = (RB_LIVE - (1.0 - DELUCK) * RB_LIVE.mean())[B36]
|
|
a = PN.accumula(paths_own, 500.0, MURO, TAX, PATR, start=START_USD)
|
|
m = uni(R_M, G_M, RB_LIVE, B36, n_max=0, k_book=1.0, invest=True, dep_eur=500.0,
|
|
start_usd=START_USD, tax=TAX, patr=PATR, walls=(MURO,),
|
|
deposit_mode="accumula")
|
|
d3 = float(np.abs(a["cap"] - m["cash"]).max())
|
|
d4 = int(np.abs(np.where(a["colpito"] >= 0, a["colpito"], -1) - m["hit"][MURO]).max())
|
|
ok["R4"] = (d3 < 1e-6 and d4 == 0)
|
|
print(f" max|dif| capitale = ${d3:.3e} | max|dif| giorno di primo passaggio = {d4} -> "
|
|
f"{'BIT-EXACT' if ok['R4'] else 'DIVERSO'}")
|
|
print(f" (capitale mediano a 36 mesi ${np.median(m['cash']):,.0f}, "
|
|
f"P(muro) {float((m['hit'][MURO]>=0).mean()):.1%})")
|
|
|
|
print(f"\n ESITO REPLICHE: {sum(ok.values())}/{len(ok)} — "
|
|
+ ", ".join(f"{k}={'OK' if v else 'NO'}" for k, v in ok.items()))
|
|
if not all(ok.values()):
|
|
raise SystemExit("REPLICA INCOMPLETA: un numero nuovo sarebbe indistinguibile da un bug.")
|
|
tick("§1")
|
|
# ---- fine repliche; da qui i numeri NUOVI
|
|
|
|
# muro coerente col PANNELLO su cui gira la simulazione (2021+), come sensibilita'
|
|
rI = RB_LIVE - (1.0 - DELUCK) * RB_LIVE.mean()
|
|
lo, hi = 0.0, 0.40
|
|
for _ in range(13):
|
|
mid = (lo + hi) / 2
|
|
sv = PN.sopravvivenza(rI, 1e6, 1e6 * mid, 20, TAX, PATR, n_paths=1500)
|
|
lo, hi = (mid, hi) if sv["p10_end"] >= 1e6 else (lo, mid)
|
|
MURO_I = prel / lo if lo > 0.002 else float("inf")
|
|
WALLS = (PUB["muro_p90"], MURO, PUB["muro_m1se"])
|
|
print(f"\n IL BERSAGLIO (obiettivo A) — e la sua banda, propagata da §57:")
|
|
print(f" muro PRIMARIO (pubblicato, finestra piena 2019+): ${MURO:,.0f} "
|
|
f"(perpetua {perp:.2%})")
|
|
print(f" banda del drift +-1SE (§57): p90 ${PUB['muro_p90']:,.0f} ... "
|
|
f"-1SE ${PUB['muro_m1se']:,.0f}")
|
|
print(f" muro COERENTE col pannello 2021+ (drift {rI.mean()*365:.2%}): "
|
|
+ (f"${MURO_I:,.0f}" if np.isfinite(MURO_I) else "IRRAGGIUNGIBILE (perpetua ~0)"))
|
|
print(" ⚠️ La simulazione gira sul pannello 2021+ (l'unico dove XS01 esiste) mentre il muro")
|
|
print(" pubblicato e' calcolato sulla finestra piena: la disparita' e' DICHIARATA e la")
|
|
print(" riga qui sopra la quantifica. Primaria resta la pubblicata, per confrontabilita'.")
|
|
tick("muro")
|
|
|
|
def run(**kw) -> dict:
|
|
base = dict(lev_fu=1.0, n_max=0, pref="grande", rebuy=True, floor_usd=0.0,
|
|
k_book=1.0, invest=True, dep_eur=500.0, start_usd=START_USD,
|
|
seed=SEED_LAD, refund=True, min_payout=MIN_PAYOUT, tax=TAX, patr=PATR,
|
|
walls=WALLS, marks=MARKS, deposit_mode="ladder")
|
|
base.update(kw)
|
|
boot = base.pop("boot", BOOT)
|
|
return uni(base.pop("Ra", R_M), base.pop("Ga", G_M), base.pop("Rb", RB_LIVE),
|
|
boot, **base)
|
|
|
|
# ======================================================================
|
|
hr("§2. LE DUE DEFINIZIONI DI «REDDITO >= 50 EUR/g», SULLA STESSA POLITICA E SUGLI STESSI PATH")
|
|
print(" A = RICCHEZZA: il patrimonio PROPRIO tocca il capitale-rendita (sostenibile).")
|
|
print(" B = FLUSSO : media dei payout incassati >= 50 EUR/g (la definizione dei numeri")
|
|
print(" pubblicati del canale funded — NON sostenibile).")
|
|
print(f" Versamento: EUR 500/mese (il piano in `config/live.json`), start ${START_USD:,.0f}.")
|
|
REF = {
|
|
"LIBRO (oggi: 75/25, k=1, nessun biglietto)": dict(n_max=0),
|
|
"PROP come pubblicato (6 conti, leva argmax-J, cassa FERMA)":
|
|
dict(n_max=6, lev_fu=LEV_J, invest=False, k_book=0.0, tax=0.0, patr=0.0),
|
|
"PROP + cassa che lavora sul libro (stessa leva)":
|
|
dict(n_max=6, lev_fu=LEV_J, invest=True, k_book=1.0),
|
|
}
|
|
refres = {k: run(**v) for k, v in REF.items()}
|
|
print(f"\n {'politica':<52} {'H':>5} {'A: P(muro)':>11} {'B: P(>=50/g)':>13} "
|
|
f"{'mediana $':>11} {'P(<versato)':>12}")
|
|
for lab, r in refres.items():
|
|
for m in ORIZZONTI_M:
|
|
sn = r["snap"][m * 30]
|
|
print(f" {lab if m == ORIZZONTI_M[0] else '':<52} {m:>4}m "
|
|
f"{sn[f'A{MURO:.0f}']:>11.1%} {sn['p_flow']:>13.1%} "
|
|
f"${sn['med_cash']:>10,.0f} {sn['p_sotto']:>12.1%}")
|
|
print("\n 📌 Le due definizioni NON ordinano le politiche allo stesso modo, e a 36 mesi non")
|
|
print(" sono nemmeno commensurabili: sotto A il canale funded e' un ACCELERATORE di")
|
|
print(" capitale, sotto B e' l'unico modo di avere un flusso.")
|
|
tick("§2")
|
|
|
|
# ======================================================================
|
|
hr("§3. LA CURVA DI LEVA — dove sta l'argmax di CIASCUN obiettivo (attesa A1)")
|
|
print(f" Cella d'ancora mediana (offset {OFF_M}, fase {PH_M}), 6 conti, cassa che lavora,")
|
|
print(" EUR 500/mese. J e' calcolato con la STESSA `PR.obj_rg` di §29/§32/§37.")
|
|
print(f"\n {'leva':>6} {'J':>7} {'B: P(>=50/g) 36m':>17} {'A: P(muro) 120m':>16} "
|
|
f"{'P(zero)':>8} {'P(<versato)':>12} {'mediana $ 120m':>15}")
|
|
LEVS_ALL = PR.LEVS
|
|
curve = []
|
|
for lv in LEVS_ALL:
|
|
j = PR.obj_rg(R_M, G_M, lv, ev_b, fu_b)["J"]
|
|
bump(1)
|
|
r = run(n_max=6, lev_fu=lv)
|
|
s36, s120 = r["snap"][36 * 30], r["snap"][120 * 30]
|
|
curve.append(dict(lev=lv, J=j, B36=s36["p_flow"], A120=s120[f"A{MURO:.0f}"],
|
|
pz=s36["p_zero"], psotto=s120["p_sotto"], med=s120["med_cash"]))
|
|
print(f" {lv:>6.3f} {j:>7.3f} {s36['p_flow']:>17.1%} {s120[f'A{MURO:.0f}']:>16.1%} "
|
|
f"{s36['p_zero']:>8.1%} {s120['p_sotto']:>12.1%} ${s120['med_cash']:>14,.0f}")
|
|
CU = pd.DataFrame(curve)
|
|
aJ = float(CU.loc[CU["J"].idxmax(), "lev"])
|
|
aB = float(CU.loc[CU["B36"].idxmax(), "lev"])
|
|
aA = float(CU.loc[CU["A120"].idxmax(), "lev"])
|
|
print(f"\n argmax J = {aJ:.2f}x · argmax B(36m) = {aB:.2f}x · "
|
|
f"argmax A(120m) = {aA:.2f}x")
|
|
print(f" B alla leva argmax-J: {float(CU.loc[CU['J'].idxmax(),'B36']):.1%} -> "
|
|
f"B al proprio argmax: {CU['B36'].max():.1%} "
|
|
f"(fattore {CU['B36'].max()/max(float(CU.loc[CU['J'].idxmax(),'B36']),1e-9):.1f}x)")
|
|
print(f" ATTESA A1 ({'CONFERMATA' if aB > aJ and CU['B36'].max() > 2*float(CU.loc[CU['J'].idxmax(),'B36']) else 'PARZIALE/SMENTITA'}): "
|
|
"l'argmax di P sta a leva piu' alta di quello di J.")
|
|
tick("§3")
|
|
|
|
# ======================================================================
|
|
hr("§4. LO SPAZIO DI POLITICA, OTTIMIZZATO SU P DIRETTAMENTE (36 mesi, obiettivo B)")
|
|
grid = [dict(lev_fu=lv, n_max=nm, pref=pf, rebuy=rb)
|
|
for lv in LEVS_FU for nm in NMAX for pf in PREFS for rb in REBUY]
|
|
print(f" {len(grid)} celle dichiarate PRIMA: leva {LEVS_FU} x conti {NMAX} x "
|
|
f"taglia {PREFS} x rebuy {REBUY}")
|
|
print(f" fisse: libro proprio LIVE 75/25 k=1, cassa che lavora, floor $0, EUR 500/mese.")
|
|
rows = []
|
|
for g in grid:
|
|
r = run(**g)
|
|
s36, s120 = r["snap"][36 * 30], r["snap"][120 * 30]
|
|
rows.append(dict(**g, B36=s36["p_flow"], B24=r["snap"][24 * 30]["p_flow"],
|
|
B60=r["snap"][60 * 30]["p_flow"], B120=s120["p_flow"],
|
|
A36=s36[f"A{MURO:.0f}"], A120=s120[f"A{MURO:.0f}"],
|
|
A120lo=s120[f"A{PUB['muro_p90']:.0f}"],
|
|
A120hi=s120[f"A{PUB['muro_m1se']:.0f}"],
|
|
pz=s36["p_zero"], psotto36=s36["p_sotto"],
|
|
rov36=s36["p_rovina"], med120=s120["med_cash"],
|
|
ntick=r["n_tickets"]))
|
|
G = pd.DataFrame(rows)
|
|
print(f"\n LE 8 MIGLIORI SU B(36 mesi) — e cosa costano sugli altri assi:")
|
|
print(f" {'leva':>5} {'conti':>6} {'taglia':>8} {'rebuy':>6} {'B 24m':>7} {'B 36m':>7} "
|
|
f"{'B 60m':>7} {'A 120m':>7} {'P(zero)':>8} {'P(<vers) 36m':>13} {'bigl.':>6}")
|
|
for _, r in G.sort_values("B36", ascending=False).head(8).iterrows():
|
|
print(f" {r['lev_fu']:>5.2f} {int(r['n_max']):>6} {r['pref']:>8} "
|
|
f"{'si' if r['rebuy'] else 'no':>6} {r['B24']:>7.1%} {r['B36']:>7.1%} "
|
|
f"{r['B60']:>7.1%} {r['A120']:>7.1%} {r['pz']:>8.1%} {r['psotto36']:>13.1%} "
|
|
f"{r['ntick']:>6.1f}")
|
|
print(f"\n LE 6 MIGLIORI SU A(120 mesi, muro pubblicato ${MURO:,.0f}):")
|
|
print(f" {'leva':>5} {'conti':>6} {'taglia':>8} {'rebuy':>6} {'A 36m':>7} {'A 120m':>7} "
|
|
f"{'A120 p90':>9} {'A120 -1SE':>10} {'B 36m':>7} {'mediana $ 120m':>15}")
|
|
for _, r in G.sort_values("A120", ascending=False).head(6).iterrows():
|
|
print(f" {r['lev_fu']:>5.2f} {int(r['n_max']):>6} {r['pref']:>8} "
|
|
f"{'si' if r['rebuy'] else 'no':>6} {r['A36']:>7.1%} {r['A120']:>7.1%} "
|
|
f"{r['A120lo']:>9.1%} {r['A120hi']:>10.1%} {r['B36']:>7.1%} "
|
|
f"${r['med120']:>14,.0f}")
|
|
bestB = G.loc[G["B36"].idxmax()]
|
|
bestA = G.loc[G["A120"].idxmax()]
|
|
print(f"\n ARGMAX B(36m) = leva {bestB['lev_fu']:.2f}x, {int(bestB['n_max'])} conti, "
|
|
f"biglietto {bestB['pref']}, rebuy {'si' if bestB['rebuy'] else 'no'} -> "
|
|
f"{bestB['B36']:.1%}")
|
|
print(f" ARGMAX A(120m) = leva {bestA['lev_fu']:.2f}x, {int(bestA['n_max'])} conti, "
|
|
f"biglietto {bestA['pref']}, rebuy {'si' if bestA['rebuy'] else 'no'} -> "
|
|
f"{bestA['A120']:.1%}")
|
|
print(f" I due argmax {'COINCIDONO' if (bestB[['lev_fu','n_max','pref','rebuy']].tolist() == bestA[['lev_fu','n_max','pref','rebuy']].tolist()) else 'SONO DIVERSI'}.")
|
|
tick("§4")
|
|
|
|
# ======================================================================
|
|
hr("§5. LE DIMENSIONI RESTANTI — floor, versamento, leva del libro, ripartizione propria")
|
|
bp = dict(lev_fu=float(bestB["lev_fu"]), n_max=int(bestB["n_max"]),
|
|
pref=str(bestB["pref"]), rebuy=bool(bestB["rebuy"]))
|
|
print(f" Politica di riferimento: l'argmax di B {bp}")
|
|
print(f"\n (a) FLOOR — capitale proprio sotto cui non si compra un altro biglietto")
|
|
print(f" {'floor':>10} {'B 36m':>8} {'A 120m':>8} {'P(zero)':>9} {'mediana $ 120m':>15}")
|
|
for fl in (0.0, 2_000.0, 10_000.0):
|
|
r = run(floor_usd=fl, **bp)
|
|
s36, s120 = r["snap"][36 * 30], r["snap"][120 * 30]
|
|
print(f" ${fl:>9,.0f} {s36['p_flow']:>8.1%} {s120[f'A{MURO:.0f}']:>8.1%} "
|
|
f"{s36['p_zero']:>9.1%} ${s120['med_cash']:>14,.0f}")
|
|
print(f"\n (b) VERSAMENTO MENSILE — la leva che il progetto misura come dominante")
|
|
print(f" {'EUR/mese':>10} {'strada':>10} {'B 36m':>8} {'A 120m':>8} {'A 240m':>8} "
|
|
f"{'mediana $ 120m':>15}")
|
|
for dep in (0.0, 250.0, 500.0, 1_000.0):
|
|
for lab, kw in (("PROP", bp), ("LIBRO", dict(n_max=0))):
|
|
r = run(dep_eur=dep, **kw)
|
|
s36, s120 = r["snap"][36 * 30], r["snap"][120 * 30]
|
|
a240 = anni_med(r, MURO)
|
|
print(f" {dep:>10,.0f} {lab:>10} {s36['p_flow']:>8.1%} "
|
|
f"{s120[f'A{MURO:.0f}']:>8.1%} {a240[1]:>8.1%} ${s120['med_cash']:>14,.0f}")
|
|
print(" (la colonna 'A 240m' e' P(traguardo) sull'intera simulazione = 120 mesi: coincide")
|
|
print(" con A 120m — la si stampa per rendere esplicito che l'orizzonte massimo e' 10 anni)")
|
|
print(f"\n (c) LEVA DEL LIBRO PROPRIO — muro CONGELATO (primaria) e MOBILE (sensibilita')")
|
|
print(" ⚠️ §38: la chiave di scala NON ESISTE in `config/live.json`; §57: a k=1,50x il")
|
|
print(" peggior giorno e' 21,48% e la guardia dello scettico (<=20%) lo BOCCIA.")
|
|
print(f" {'k':>5} {'muro mobile':>13} {'A 120m congel.':>15} {'A 120m mobile':>14} "
|
|
f"{'B 36m':>7} {'mediana $ 120m':>15}")
|
|
for k in (1.00, 1.25, 1.50):
|
|
rk = rI * k
|
|
lo, hi = 0.0, 0.40
|
|
for _ in range(11):
|
|
mid = (lo + hi) / 2
|
|
sv = PN.sopravvivenza(np.asarray(rL3, float) * k, 1e6, 1e6 * mid, 20, TAX, PATR,
|
|
n_paths=1200)
|
|
lo, hi = (mid, hi) if sv["p10_end"] >= 1e6 else (lo, mid)
|
|
mk = prel / lo if lo > 0.002 else float("inf")
|
|
r = run(k_book=k, walls=(MURO, mk) if np.isfinite(mk) else (MURO,), **bp)
|
|
s36, s120 = r["snap"][36 * 30], r["snap"][120 * 30]
|
|
am = s120.get(f"A{mk:.0f}", float("nan")) if np.isfinite(mk) else float("nan")
|
|
print(f" {k:>5.2f} " + (f"${mk:>12,.0f}" if np.isfinite(mk) else f"{'IRRAGG.':>13}")
|
|
+ f" {s120[f'A{MURO:.0f}']:>15.1%} "
|
|
+ (f"{am:>14.1%}" if am == am else f"{'—':>14}")
|
|
+ f" {s36['p_flow']:>7.1%} ${s120['med_cash']:>14,.0f}")
|
|
bump(1)
|
|
print(f"\n (d) RIPARTIZIONE DEL LIBRO PROPRIO — LIVE 75/25 contro W_A 38/38/25")
|
|
print(" (la seconda mette XS01 anche sul capitale proprio: §46 ha falsificato la soglia")
|
|
print(" dei $20k da min-order, ma la decisione di venue del 26/07 tiene tutto su")
|
|
print(" Deribit fino a $20k -> e' una sensibilita', non una proposta)")
|
|
print(f" {'libro proprio':>16} {'B 36m':>8} {'A 120m':>8} {'mediana $ 120m':>15}")
|
|
for lab, rbk in (("LIVE 75/25", RB_LIVE), ("W_A 38/38/25", RB_WA)):
|
|
r = run(Rb=rbk, **bp)
|
|
s36, s120 = r["snap"][36 * 30], r["snap"][120 * 30]
|
|
print(f" {lab:>16} {s36['p_flow']:>8.1%} {s120[f'A{MURO:.0f}']:>8.1%} "
|
|
f"${s120['med_cash']:>14,.0f}")
|
|
print(f"\n (e) LA CASSA CHE LAVORA — attesa A3")
|
|
print(f" {'cassa':>16} {'B 36m':>8} {'A 120m':>8} {'mediana $ 36m':>14} "
|
|
f"{'mediana $ 120m':>15}")
|
|
inv = {}
|
|
for lab, kw in (("ferma (pubbl.)", dict(invest=False, k_book=0.0, tax=0.0, patr=0.0)),
|
|
("investita", dict(invest=True, k_book=1.0))):
|
|
r = run(**{**bp, **kw})
|
|
inv[lab] = r
|
|
s36, s120 = r["snap"][36 * 30], r["snap"][120 * 30]
|
|
print(f" {lab:>16} {s36['p_flow']:>8.1%} {s120[f'A{MURO:.0f}']:>8.1%} "
|
|
f"${s36['med_cash']:>13,.0f} ${s120['med_cash']:>14,.0f}")
|
|
dB = (inv["investita"]["snap"][36 * 30]["p_flow"]
|
|
- inv["ferma (pubbl.)"]["snap"][36 * 30]["p_flow"])
|
|
dA = (inv["investita"]["snap"][120 * 30][f"A{MURO:.0f}"]
|
|
- inv["ferma (pubbl.)"]["snap"][120 * 30][f"A{MURO:.0f}"])
|
|
print(f" ATTESA A3: dB {dB*100:+.2f}pp, dA {dA*100:+.2f}pp -> "
|
|
f"{'CONFERMATA' if abs(dB) < 0.02 and dA > 0.02 else 'da leggere nel testo'}")
|
|
tick("§5")
|
|
|
|
# ======================================================================
|
|
hr("§6. QUANTO COSTA LA DIFFERENZA FRA L'OTTIMO E CIO' CHE IL PROGETTO FA OGGI")
|
|
oggi = run(n_max=0)
|
|
pubb = run(n_max=6, lev_fu=LEV_J, invest=False, k_book=0.0, tax=0.0, patr=0.0)
|
|
optB = run(**bp)
|
|
optA = run(lev_fu=float(bestA["lev_fu"]), n_max=int(bestA["n_max"]),
|
|
pref=str(bestA["pref"]), rebuy=bool(bestA["rebuy"]))
|
|
print(f" {'politica':<44} {'B 36m':>8} {'A 36m':>8} {'A 120m':>8} {'anni al muro':>13} "
|
|
f"{'P(<versato) 36m':>16}")
|
|
for lab, r in (("OGGI: libro 75/25 k=1, nessun biglietto", oggi),
|
|
("PUBBLICATA: 6 conti a leva argmax-J", pubb),
|
|
("ARGMAX B (flusso)", optB),
|
|
("ARGMAX A (ricchezza)", optA)):
|
|
s36, s120 = r["snap"][36 * 30], r["snap"][120 * 30]
|
|
med, parr = anni_med(r, MURO)
|
|
print(f" {lab:<44} {s36['p_flow']:>8.1%} {s36[f'A{MURO:.0f}']:>8.1%} "
|
|
f"{s120[f'A{MURO:.0f}']:>8.1%} "
|
|
+ (f"{med:>12.1f}a" if np.isfinite(med) else f"{'>10a':>13}")
|
|
+ f" {s36['p_sotto']:>16.1%}")
|
|
dB36 = optB["snap"][36 * 30]["p_flow"] - oggi["snap"][36 * 30]["p_flow"]
|
|
dA120 = optA["snap"][120 * 30][f"A{MURO:.0f}"] - oggi["snap"][120 * 30][f"A{MURO:.0f}"]
|
|
print(f"\n COSTO DI NON OTTIMIZZARE: su B(36m) {dB36*100:+.1f}pp, "
|
|
f"su A(120m) {dA120*100:+.1f}pp.")
|
|
tick("§6")
|
|
|
|
# ======================================================================
|
|
hr("§7. LA DOMANDA DECISIVA — esiste una politica con P(>=50 EUR/g entro 36 mesi) > 20%?")
|
|
maxB = float(G["B36"].max())
|
|
maxA36 = float(G["A36"].max())
|
|
rowB = G.loc[G["B36"].idxmax()]
|
|
print(f" Sotto la definizione B (FLUSSO, quella dei numeri pubblicati):")
|
|
print(f" massimo su {len(G)} politiche = {maxB:.1%} "
|
|
f"(leva {rowB['lev_fu']:.2f}x, {int(rowB['n_max'])} conti, biglietto {rowB['pref']}, "
|
|
f"rebuy {'si' if rowB['rebuy'] else 'no'})")
|
|
print(f" ma con P(zero payout) {rowB['pz']:.1%} e P(finire sotto il versato) "
|
|
f"{rowB['psotto36']:.1%}")
|
|
print(f" -> {'SI, SOPRA IL 20%' if maxB > 0.20 else 'NO, SOTTO IL 20%'}")
|
|
print(f"\n Sotto la definizione A (RICCHEZZA sostenibile, il muro del progetto):")
|
|
print(f" massimo su {len(G)} politiche a 36 mesi = {maxA36:.2%} "
|
|
f"(a 120 mesi: {float(G['A120'].max()):.1%})")
|
|
print(f" -> {'SI' if maxA36 > 0.20 else 'NO'}, e non per poco: il muro e' "
|
|
f"${MURO:,.0f} e a 36 mesi il capitale mediano migliore e' "
|
|
f"${float(G['med120'].max()):,.0f} a 120 mesi.")
|
|
print(f"\n ATTESA A2 ({'CONFERMATA' if (maxB > 0.20 and maxA36 < 0.05) else 'SMENTITA'}): "
|
|
"«SI' sotto B e NO sotto A».")
|
|
print(f" ATTESA A4 ({'CONFERMATA' if rowB['pz'] > 0.50 or rowB['psotto36'] > 0.50 else 'SMENTITA'}): "
|
|
"l'ottimo di P e' una politica ad alta rovina.")
|
|
|
|
# il vincolo che fissa il massimo
|
|
print(f"\n QUALE VINCOLO FISSA IL MASSIMO — si toglie un vincolo per volta (obiettivo B, 36m):")
|
|
print(f" {'scenario':<46} {'B 36m':>8} {'delta':>8}")
|
|
base_b = float(rowB["B36"])
|
|
scen = []
|
|
r = run(start_usd=10_000.0, **bp); scen.append(("capitale iniziale $10.000 invece di $635", r))
|
|
r = run(dep_eur=2_000.0, **bp); scen.append(("versamento EUR 2.000/mese invece di 500", r))
|
|
cap_old = [F["cap"] for F in pl.FIRMS]
|
|
for F in pl.FIRMS:
|
|
F["cap"] *= 5
|
|
r = run(**bp); scen.append(("cap per firm x5 ($1M / $2M)", r))
|
|
for F, c in zip(pl.FIRMS, cap_old):
|
|
F["cap"] = c
|
|
dead_old = pl.FIRM_DEATH_PER_YEAR
|
|
pl.FIRM_DEATH_PER_YEAR = 0.0
|
|
r = run(**bp); scen.append(("morte-firm 0%/anno invece di 10%", r))
|
|
pl.FIRM_DEATH_PER_YEAR = dead_old
|
|
r = run(n_max=6, **{k: v for k, v in bp.items() if k != "n_max"})
|
|
scen.append(("6 conti concorrenti (se non lo e' gia')", r))
|
|
for lab, r in scen:
|
|
b = r["snap"][36 * 30]["p_flow"]
|
|
print(f" {lab:<46} {b:>8.1%} {(b-base_b)*100:>+7.1f}pp")
|
|
print(f" ATTESA A5: il vincolo binding e' il capitale che entra "
|
|
f"(riga 'versamento' e riga 'capitale iniziale').")
|
|
|
|
# ======================================================================
|
|
hr("VERDETTO")
|
|
eur_g_oggi = oggi["snap"][36 * 30]["med_cash"]
|
|
print(f" Trial contati al rialzo: {TRIALS}. Tempo {time.time()-T0:.0f}s. "
|
|
f"Percorsi {N_PATHS} (SE ~{100*np.sqrt(0.2*0.8/N_PATHS):.1f}pp su una P vicina al 20%).")
|
|
print(f"\n * L'obiettivo del progetto ha DUE definizioni operative in uso e danno risposte")
|
|
print(f" opposte alla stessa domanda. Con la definizione del canale funded (FLUSSO) il")
|
|
print(f" massimo a 36 mesi e' {maxB:.1%}; con quella dei muri e del piano (RICCHEZZA")
|
|
print(f" SOSTENIBILE) e' {maxA36:.2%}.")
|
|
print(f" * L'ottimo di P(>=50/g) NON e' l'ottimo di J: {CU['B36'].max():.1%} contro "
|
|
f"{float(CU.loc[CU['J'].idxmax(),'B36']):.1%} alla leva argmax-J.")
|
|
print(f" * In EUR/giorno, la mediana di cio' che il libro di oggi produce a 36 mesi resta")
|
|
print(f" ZERO (non preleva), e il capitale mediano e' ${eur_g_oggi:,.0f}: la rendita")
|
|
print(f" perpetua su quel capitale vale {perp*eur_g_oggi/365/EURUSD:.2f} EUR/giorno.")
|
|
print("=" * 104)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|