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>
910 lines
51 KiB
Python
910 lines
51 KiB
Python
"""r0823b_retry — §61 RETRY: la POLITICA DI RI-ACQUISTO del biglietto funded (2026-08-23).
|
|
|
|
IL BUCO CHE CHIUDE
|
|
------------------
|
|
Un conto funded che rompe una regola muore. Il progetto ha modellato tutto il resto del ciclo
|
|
(prezzo, regole, leva, lente accoppiata, funding, morte-firm, fisco) e **mai la politica di
|
|
RI-ACQUISTO**: quando ricomprare dopo un bust, con quali soldi, e con quale leva sul conto nuovo.
|
|
Nei simulatori esistenti (`r0725_prop_ladder.simulate`, `r0725_prop_coupled.simulate`,
|
|
`r0822d_gate_c.ladder`) la politica c'e' — e' **cablata**: *compra sempre, subito, il biglietto
|
|
piu' GRANDE che la cassa permette, in tutti e 6 gli slot*. E' la politica piu' aggressiva della
|
|
famiglia, non e' mai stata dichiarata come scelta, e **tutti i numeri operativi del canale funded
|
|
escono da li'** (42% -> 7,8% -> 4,4% -> 2,6%).
|
|
|
|
E' diventata una domanda vera solo ieri, per due misure:
|
|
(a) §30: la quota d'ingresso e' un **deposito RIMBORSABILE** ("Refundable Challenge Deposit,
|
|
returned in full alongside your first funded payout") -> un tentativo fallito costa la
|
|
quota, uno riuscito no. E' una struttura a OPZIONE, e le opzioni si comprano piu' volte.
|
|
(b) §37: il biglietto batte il libro **solo perche' il piano di versamento c'e'**, cioe'
|
|
**perche' si puo' giocare piu' di una volta** ("una lotteria a EV positivo che un conto da
|
|
$654 puo' giocare UNA VOLTA SOLA").
|
|
Se il valore sta nel NUMERO DI TENTATIVI, la politica di ri-acquisto e' la variabile principale
|
|
e non e' mai stata ottimizzata.
|
|
|
|
LE ATTESE, REGISTRATE PRIMA DI MISURARE (poi riportate confermate/refutate)
|
|
--------------------------------------------------------------------------
|
|
A1. **Compromesso monotono.** IMMEDIATA massimizza sia il numero di giocate sia P(cassa a zero);
|
|
MAI li minimizza entrambi. Nessuna politica dovrebbe dominare su tutte e due le colonne.
|
|
A2. **La dose-risposta di §37 dovrebbe essere ripida.** Se il meccanismo e' "quante volte si
|
|
gioca", passare da max 1 biglietto a illimitati deve muovere P(>=50 EUR/g) di molto. Se non
|
|
lo muove, il meccanismo dichiarato da §37 e' sbagliato (o e' un'altra cosa).
|
|
A3. **Il biglietto PICCOLO ($249/$25k) compra piu' giocate e perde sul bersaglio.** 50 EUR/g netti
|
|
sono ~$59k di payout netti in 3 anni: con nozionale $25k servono troppi cicli. Mi aspetto
|
|
P(zero) piu' bassa e P(>=50/g) piu' bassa. Dominato sul bersaglio, non sulla sopravvivenza.
|
|
A4. **SOLO-PAYOUT ~= MAI senza versamenti** (senza payout non c'e' cassa per ricomprare) e
|
|
molto piu' vicina a IMMEDIATA con versamenti.
|
|
A5. **Il RIMBORSO alza i livelli di ~1-3pp (§37) ma NON cambia l'ordine delle politiche.**
|
|
Previsione forte: l'ottimo e' lo stesso nelle tre convenzioni.
|
|
A6. 🚨 **Le regole di leva condizionate all'esito passato sono NULL DEL DE-LEVERING.** In un
|
|
bootstrap a blocchi il passato non porta informazione sul futuro: una regola "riduci la leva
|
|
dopo un bust" puo' agire SOLO cambiando la leva MEDIA. Il controllo obbligatorio e' la leva
|
|
COSTANTE alla stessa media realizzata. Mi aspetto REFUTED 3/3 (8ª occorrenza).
|
|
A7. **La politica conta meno del versamento.** Mi aspetto uno spread fra politiche di pochi punti
|
|
percentuali contro i ~+10pp del passaggio EUR 0 -> EUR 500/mese misurato da §37.
|
|
|
|
CONVENZIONI (tutte EREDITATE e congelate — nessuna scelta di strategia in questo file)
|
|
--------------------------------------------------------------------------------------
|
|
* Lente **ACCOPPIATA** (`r0725_prop_coupled`): su una regola a UN GIORNO la close-only e' cieca.
|
|
* Conto funded = **W_A TP01 38 / SKH01 38 / XS01 25** (centro della regione robusta di §29),
|
|
XS01 = **U13 Binance** fuori campione, finestra **W-LONG 2021+**, de-luck sul drift **x0,89**.
|
|
* **Funding dentro** (§35/§37), morte-firm **10%/anno** (assunzione del progetto), cap
|
|
**$200k/trader**, fisco **33%**, split **80%**, soglia di prelievo **$100** di quota trader.
|
|
* Banda d'ancora: **23 offset x 3 fasi (0,3,7)** = lo stesso sottocampione dichiarato da §32/§37.
|
|
La cella di riferimento e' la **MEDIANA** della banda, non la canonica (91° pctl).
|
|
* Cassa non spesa: **FERMA** per default (convenzione di §37) e **SUL LIBRO** come sensibilita'
|
|
dichiarata in §7 — perche' la convenzione "ferma" penalizza per costruzione le politiche
|
|
prudenti, che sono esattamente quelle sotto esame.
|
|
* Bootstrap **CONDIVISO** fra tutte le politiche di una stessa tabella -> confronti **APPAIATI**.
|
|
|
|
COSA NON E' QUESTO FILE: non e' una selezione di strategia (meccanismo, pesi, leva ottima, lente
|
|
e soglie sono tutti ereditati); e' la mappatura di una politica OPERATIVA che finora era cablata.
|
|
L'argmax su ~30 politiche resta pero' una selezione: si riportano il PLATEAU e la risoluzione MC.
|
|
|
|
Uso: `nice -n 19 timeout 900 uv run python scripts/research/r0823b_retry.py`
|
|
Rete: NESSUNA (parquet certificati + cache Binance di r0822b + cache funding di r0822d).
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
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_prop_coupled as pc # noqa: E402 lente accoppiata (25/07)
|
|
import r0725_prop_ladder as pl # noqa: E402 regole firm + scala di conti (25/07)
|
|
import r0822_prop_alloc as PA # noqa: E402 J, bootstrap (22/08)
|
|
import r0822b_prop_recal as PR # noqa: E402 PROP-RECAL (22/08)
|
|
import r0822b_xs01_oos as X # noqa: E402 motore XS01 + cache Binance
|
|
import r0822c_skh_anchor_prop as AP # noqa: E402 offset di SKH01 (gamba b)
|
|
import r0822d_funding as FD # noqa: E402 il funding (22/08)
|
|
import r0822d_gate_c as GC # noqa: E402 ladder di riferimento (§37)
|
|
from src.portfolio.sleeves import XS_UNIVERSE # noqa: E402
|
|
|
|
FD.USE_NET = False
|
|
|
|
EURUSD = pl.EURUSD
|
|
TAX_RATE = pl.TAX_RATE
|
|
SPLIT = pl.SPLIT
|
|
DELUCK = PA.DELUCK
|
|
W_A = GC.W_A
|
|
OFFSETS = AP.OFFSETS
|
|
PHASES_SUB = AP.PHASES_SUB
|
|
MIN_PAYOUT = GC.MIN_PAYOUT_TRADER
|
|
HORIZON_DAYS = GC.HORIZON_DAYS
|
|
SEED_LAD = GC.SEED_LAD
|
|
XS_FUND_ANN = 0.0145 # §37: XS01 PAGA +1,45%/anno di funding (misurato)
|
|
|
|
N_BAND = 1000 # percorsi per la banda d'ancora (riproduzione §32/§37)
|
|
N_MAIN = 2000 # percorsi per la griglia di politiche (cella di rif.)
|
|
N_ROB = 1000 # percorsi per la robustezza fra celle
|
|
# Sottocampione di offset dichiarato A PRIORI: gli stessi 8 uniformi di `r0726_skh_live_book`
|
|
# (la griglia piena e' 23; su 2 CPU condivise con altri 5 processi la piena non sta nel budget,
|
|
# e un sottocampione uniforme dichiarato prima e' l'unica forma onesta di ridurla).
|
|
BAND_OFFSETS = tuple(range(0, 690, 90))
|
|
|
|
# numeri PUBBLICATI da riprodurre prima di produrne di nuovi
|
|
PUB = dict(anchor_p50g=0.044, anchor_p10=0.025, anchor_p90=0.082, anchor_pzero=0.353,
|
|
gatec_p50g=0.026, gatec_p10=0.015, gatec_p90=0.047, gatec_pzero=0.403,
|
|
refund_pp_lo=0.01, refund_pp_hi=0.03)
|
|
|
|
TRIALS = 0
|
|
T0 = time.time()
|
|
CHEAPEST = min(t[1] for F in pl.FIRMS for t in F["tickets"]) # $249
|
|
|
|
|
|
def bump(n: int = 1) -> None:
|
|
global TRIALS
|
|
TRIALS += n
|
|
|
|
|
|
def hr(t: str = "") -> None:
|
|
print("\n" + "-" * 104)
|
|
if t:
|
|
print(f" {t}")
|
|
print("-" * 104)
|
|
|
|
|
|
def tick(lab: str) -> None:
|
|
print(f" [{time.time() - T0:6.0f}s] {lab}", flush=True)
|
|
|
|
|
|
# ============================================================================ LE POLITICHE
|
|
def POL(when="immediate", k_reserve=1.0, cadence=0, max_tickets=10 ** 9, size="largest",
|
|
lev_rule="const", lev_dn=1.0, lev_up=1.0, lev_lo=0.25, lev_hi=1.50,
|
|
max_open=pl.MAX_CONCURRENT) -> dict:
|
|
"""Una politica di ri-acquisto. `when`:
|
|
immediate — compra appena uno slot e' libero e la cassa basta (= il CABLATO di oggi)
|
|
threshold — compra solo se la cassa e' >= k_reserve x il prezzo (tiene una riserva)
|
|
earned — il PRIMO biglietto dai propri soldi, i successivi SOLO dai payout/rimborsi
|
|
cadence — al piu' un acquisto ogni `cadence` giorni
|
|
none — non compra MAI (controllo positivo: deve dare P(>=50/g)=0)
|
|
`size`: 'largest' (cablato) o 'cheapest' (piu' giocate, nozionale minore).
|
|
`max_open`: quanti conti al MASSIMO possono essere aperti insieme (cablato: 6 = tutti gli
|
|
slot). E' l'altra faccia del ri-acquisto: giocare in SERIE invece che in PARALLELO.
|
|
`lev_rule`: 'const' | 'down_on_bust' (x lev_dn a ogni bust) | 'up_on_pass' (x lev_up a ogni
|
|
pass). La leva di un conto e' fissata all'ACQUISTO e non cambia piu'."""
|
|
return dict(when=when, k_reserve=k_reserve, cadence=cadence, max_tickets=max_tickets,
|
|
size=size, lev_rule=lev_rule, lev_dn=lev_dn, lev_up=lev_up,
|
|
lev_lo=lev_lo, lev_hi=lev_hi, max_open=max_open)
|
|
|
|
|
|
P_BASE = POL() # il cablato, bit-exact vs §37
|
|
|
|
|
|
# ============================================================================ IL SIMULATORE
|
|
def ladder_rt(R, G, n_paths, lev, seed, *, pol=None, refund_mode="none", deposit_eur=0.0,
|
|
start_eur=pl.START_EUR, min_payout=0.0, boot=None, block=20, deluck=DELUCK,
|
|
rb_boot=None, tax=TAX_RATE) -> dict:
|
|
"""`r0822d_gate_c.ladder` generalizzato alla POLITICA DI RI-ACQUISTO. Tre aggiunte:
|
|
1. `pol` — quando/quale biglietto/con quali soldi/a che leva (sopra);
|
|
2. `refund_mode` — 'none' (spesa persa, `pc.simulate`) | 'pass' (`r0725_hyro:200`) |
|
|
'payout' (la regola vera della firm);
|
|
3. `rb_boot` — se dato, la cassa NON spesa gira sul LIBRO LIVE invece di restare
|
|
ferma, con lo STESSO sorteggio di bootstrap (stesso mercato) e lo
|
|
stesso fisco annuale di `GC.book_road`.
|
|
Con `pol=P_BASE, refund_mode='none', deposit_eur=0, min_payout=0, rb_boot=None` dev'essere
|
|
**BIT-EXACT** rispetto a `GC.ladder(..., refund=False)` e quindi a `pc.simulate`: e' §1.
|
|
|
|
🚨 Zero e' ASSORBENTE ed e' modellato come tale: senza conti vivi e con cassa sotto il
|
|
biglietto piu' economico ($249) non si gioca piu'; solo un versamento puo' riaprire il gioco.
|
|
"""
|
|
pol = pol or P_BASE
|
|
rng = np.random.default_rng(seed)
|
|
n_days = HORIZON_DAYS
|
|
L = len(R)
|
|
Rm = R.astype(float).reshape(-1, 1).copy()
|
|
Gm = G.astype(float).reshape(-1, 1).copy()
|
|
Rm = Rm - (1.0 - deluck) * Rm.mean(axis=0, keepdims=True)
|
|
if boot is None:
|
|
n_blocks = int(np.ceil(n_days / block))
|
|
starts = rng.integers(0, L - block, size=(n_paths, n_blocks))
|
|
idx = (starts[:, :, None] + np.arange(block)[None, None, :]).reshape(n_paths, -1)[:, :n_days]
|
|
else:
|
|
idx = boot
|
|
n_slot = pl.MAX_CONCURRENT
|
|
# (paths, days): tutti gli slot girano lo STESSO libro -> una colonna basta. La leva
|
|
# moltiplica per-slot (serve alle regole di leva) invece che globalmente: `x*lev` e' la
|
|
# stessa moltiplicazione in virgola mobile, quindi il bit-exact regge (verificato in §1).
|
|
Rb = Rm[idx][:, :, 0]
|
|
Gb = Gm[idx][:, :, 0]
|
|
|
|
FIRMS = pl.FIRMS
|
|
cash = np.full(n_paths, start_eur * EURUSD)
|
|
basis = cash.copy()
|
|
carry = np.zeros(n_paths)
|
|
earned = np.zeros(n_paths) # borsellino dei soli payout/rimborsi
|
|
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)
|
|
lev_slot = np.full((n_paths, n_slot), lev)
|
|
# soglie della firm materializzate PER SLOT all'acquisto: la dinamica diventa una sola
|
|
# espressione vettoriale invece di un loop sulle firm. Le maschere per firm sono disgiunte,
|
|
# quindi il risultato e' identico (verificato bit-exact in §1).
|
|
TH_TGT = np.zeros((n_paths, n_slot))
|
|
TH_DD = np.ones((n_paths, n_slot))
|
|
TH_DL = np.ones((n_paths, n_slot))
|
|
TH_ML = np.ones((n_paths, n_slot))
|
|
TH_DLF = np.ones((n_paths, n_slot))
|
|
mult = np.ones(n_paths)
|
|
firm_alive = np.ones((n_paths, len(FIRMS)), bool)
|
|
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)
|
|
lev_sum = np.zeros(n_paths)
|
|
last_buy = np.full(n_paths, -10 ** 9)
|
|
days_out = np.zeros(n_paths, int)
|
|
ever_passed = np.zeros(n_paths, bool)
|
|
p_death_day = 1.0 - (1.0 - pl.FIRM_DEATH_PER_YEAR) ** (1 / 365.0)
|
|
tickets_order = {}
|
|
for fi, F in enumerate(FIRMS):
|
|
tickets_order[fi] = sorted(F["tickets"], key=(lambda x: x[0]) if pol["size"] == "cheapest"
|
|
else (lambda x: -x[0]))
|
|
|
|
for t in range(n_days):
|
|
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():
|
|
hit = (firm == fi) & kill[:, None]
|
|
phase[hit] = -1
|
|
notional[hit] = 0.0
|
|
|
|
# ---------------------------------------------------------------- acquisti
|
|
if pol["when"] == "none":
|
|
budget = np.zeros(n_paths, int)
|
|
elif pol["when"] == "cadence":
|
|
budget = ((t - last_buy) >= pol["cadence"]).astype(int)
|
|
else:
|
|
budget = np.full(n_paths, n_slot, int)
|
|
budget = np.minimum(budget, np.maximum(pol["max_tickets"] - n_tickets, 0))
|
|
if pol["max_open"] < n_slot:
|
|
budget = np.minimum(budget, np.maximum(pol["max_open"] - (phase >= 0).sum(axis=1), 0))
|
|
if budget.any() and (cash >= CHEAPEST).any():
|
|
for k in range(n_slot):
|
|
free = (phase[:, k] == -1) & (budget > 0)
|
|
if not free.any():
|
|
continue
|
|
for fi, F in enumerate(FIRMS):
|
|
used = np.where(firm == fi, notional, 0.0).sum(axis=1)
|
|
for (size, price) in tickets_order[fi]:
|
|
buy = (free & firm_alive[:, fi] & (cash >= price)
|
|
& (used + size <= F["cap"]))
|
|
if pol["when"] == "threshold":
|
|
buy &= cash >= pol["k_reserve"] * price
|
|
elif pol["when"] == "earned":
|
|
buy &= (n_tickets == 0) | (earned >= price)
|
|
if not buy.any():
|
|
continue
|
|
cash[buy] -= price
|
|
basis[buy] -= price
|
|
if pol["when"] == "earned":
|
|
take = buy & (n_tickets > 0)
|
|
earned[take] -= price
|
|
spent[buy] += price
|
|
n_tickets[buy] += 1
|
|
budget[buy] -= 1
|
|
last_buy[buy] = t
|
|
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
|
|
lv = np.clip(lev * mult, pol["lev_lo"], pol["lev_hi"])
|
|
lev_slot[buy, k] = lv[buy]
|
|
TH_TGT[buy, k], TH_DD[buy, k], TH_DL[buy, k] = F["ev"]
|
|
TH_ML[buy, k], TH_DLF[buy, k] = F["fu"]
|
|
lev_sum[buy] += lv[buy]
|
|
free = free & ~buy
|
|
if not free.any():
|
|
break
|
|
if not free.any():
|
|
break
|
|
|
|
# ---------------------------------------------------------------- dinamica dei conti
|
|
r = Rb[:, t][:, None] * lev_slot
|
|
g = Gb[:, t][:, None] * lev_slot
|
|
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
|
|
|
|
in_ev = act & (phase == 0)
|
|
in_fu = act & (phase == 1)
|
|
bust = ((in_ev & ((eq_low < 1.0 - TH_DD) | (r_low < -TH_DL)))
|
|
| (in_fu & ((eq_low < 1.0 - TH_ML) | (r_low < -TH_DLF))))
|
|
phase[bust] = -1
|
|
notional[bust] = 0.0
|
|
passed = in_ev & (eq >= 1.0 + TH_TGT) & ~bust
|
|
phase[passed] = 1
|
|
eq[passed] = 1.0
|
|
if pol["lev_rule"] == "down_on_bust":
|
|
mult *= pol["lev_dn"] ** bust.sum(axis=1)
|
|
elif pol["lev_rule"] == "up_on_pass":
|
|
mult *= pol["lev_up"] ** passed.sum(axis=1)
|
|
if refund_mode == "pass" and passed.any():
|
|
back = passed & ~refunded
|
|
amt = np.where(back, price_paid, 0.0).sum(axis=1)
|
|
cash += amt
|
|
basis += amt
|
|
earned += amt
|
|
got_back += amt
|
|
refunded |= back
|
|
ever_passed |= passed.any(axis=1)
|
|
|
|
# ---------------------------------------------------------------- la cassa propria
|
|
if rb_boot is not None:
|
|
cash = np.maximum(cash * (1.0 + rb_boot[:, t]), 0.0)
|
|
|
|
# ---------------------------------------------------------------- mensile
|
|
if (t + 1) % 30 == 0:
|
|
fu = phase == 1
|
|
pay = fu & (eq > 1.0) & (((eq - 1.0) * notional * SPLIT) >= min_payout)
|
|
gain = np.where(pay, (eq - 1.0) * notional, 0.0)
|
|
net = gain.sum(axis=1) * SPLIT * (1.0 - TAX_RATE)
|
|
cum_payout += net
|
|
cash += net
|
|
basis += net
|
|
earned += net
|
|
n_payouts += (pay.sum(axis=1) > 0).astype(int)
|
|
eq = np.where(pay, 1.0, eq)
|
|
if refund_mode == "payout":
|
|
back = pay & ~refunded
|
|
amt = np.where(back, price_paid, 0.0).sum(axis=1)
|
|
cash += amt
|
|
basis += amt
|
|
earned += amt
|
|
got_back += amt
|
|
refunded |= back
|
|
if deposit_eur:
|
|
cash += deposit_eur * EURUSD
|
|
basis += deposit_eur * EURUSD
|
|
deposited += deposit_eur * EURUSD
|
|
# fisco annuale sulla sola cassa propria (i payout sono gia' netti)
|
|
if rb_boot is not None and (t + 1) % 365 == 0:
|
|
gtax = (cash - basis) + carry
|
|
due = np.where(gtax > 0, tax * gtax, 0.0)
|
|
carry = np.where(gtax > 0, 0.0, gtax)
|
|
cash = cash - due
|
|
basis = cash.copy()
|
|
|
|
out = (~(phase >= 0).any(axis=1)) & (cash < CHEAPEST)
|
|
days_out += out
|
|
|
|
eur_day = cum_payout / EURUSD / (n_days / 365.0) / 365.0
|
|
stuck = (~(phase >= 0).any(axis=1)) & (cash < CHEAPEST)
|
|
return dict(eur_day=eur_day, med=float(np.median(eur_day)),
|
|
p10=float(np.percentile(eur_day, 10)), p90=float(np.percentile(eur_day, 90)),
|
|
mean_eur=float(eur_day.mean()),
|
|
p_ge10=float((eur_day >= 10.0).mean()),
|
|
p_target=float((eur_day >= pl.TARGET_EUR_DAY).mean()),
|
|
p_zero=float((cum_payout <= 0).mean()),
|
|
p_payout=float((n_payouts > 0).mean()),
|
|
p_pass=float(ever_passed.mean()),
|
|
p_stuck=float(stuck.mean()),
|
|
n_tix=n_tickets, med_tix=float(np.median(n_tickets)),
|
|
mean_tix=float(n_tickets.mean()),
|
|
lev_mean=float(lev_sum.sum() / max(n_tickets.sum(), 1)),
|
|
days_out=float(np.median(days_out)),
|
|
cash=cash, 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()),
|
|
contributed=float(start_eur * EURUSD + deposited.mean()))
|
|
|
|
|
|
def se_pair(a: np.ndarray, b: np.ndarray, thr: float = pl.TARGET_EUR_DAY) -> float:
|
|
"""SE della DIFFERENZA fra due proporzioni APPAIATE (stessi sorteggi): McNemar.
|
|
Con bootstrap condiviso la parte comune si cancella e la risoluzione e' molto migliore
|
|
di quella dei livelli — ma non e' zero, e va stampata."""
|
|
A = a >= thr
|
|
B = b >= thr
|
|
n01 = float((A & ~B).sum())
|
|
n10 = float((~A & B).sum())
|
|
n = len(a)
|
|
return float(np.sqrt(max(n01 + n10 - (n01 - n10) ** 2 / n, 0.0)) / n)
|
|
|
|
|
|
# ==================================================================================== main
|
|
def main() -> None:
|
|
try:
|
|
sys.stdout.reconfigure(line_buffering=True)
|
|
except Exception: # pragma: no cover
|
|
pass
|
|
print("=" * 104)
|
|
print(" §61 RETRY — la POLITICA DI RI-ACQUISTO del biglietto funded, mappata invece che")
|
|
print(" cablata. Quando ricomprare, con quali soldi, a che leva — e quanto conta.")
|
|
print("=" * 104)
|
|
print(" ATTESE REGISTRATE PRIMA: A1 compromesso monotono giocate/rovina; A2 dose-risposta")
|
|
print(" RIPIDA se il meccanismo di §37 e' 'il numero di volte che si gioca'; A3 biglietto")
|
|
print(" piccolo = piu' giocate, meno bersaglio; A4 SOLO-PAYOUT ~= MAI senza versamenti;")
|
|
print(" A5 il rimborso NON cambia l'ottimo; A6 le regole di leva sono null del de-levering")
|
|
print(" (REFUTED 3/3 atteso); A7 la politica conta meno del versamento.")
|
|
|
|
# ============================================================== §0 pannello
|
|
hr("§0. IL PANNELLO — le stesse serie di §29/§37 (U13 Binance, W-LONG 2021+, funding dentro)")
|
|
rate = GC.funding_rate()
|
|
if rate is None:
|
|
print(" !! cache funding assente: il filone si ferma. Un numero senza funding NON e'")
|
|
print(" confrontabile con §37, e confrontarlo sarebbe l'errore che §35 ha appena")
|
|
print(" pagato. 'non misurabile' e' un risultato.")
|
|
return
|
|
Cb, Ob, Hb, Lb = PR.ohlc_panels(PR.bin_loader, XS_UNIVERSE)
|
|
p13 = Cb[X.U13].dropna()
|
|
o13 = Ob.reindex(p13.index)[X.U13]
|
|
h13 = Hb.reindex(p13.index)[X.U13]
|
|
l13 = Lb.reindex(p13.index)[X.U13]
|
|
XSP = {ph: PR.xs_tuples(p13, o13, h13, l13, 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
|
|
print(f" W-LONG: {len(I)} giorni {I[0].date()} -> {I[-1].date()} | de-luck x{DELUCK} | "
|
|
f"cassa iniziale EUR {pl.START_EUR:.0f} = ${pl.START_EUR*EURUSD:,.0f}")
|
|
print(f" orizzonte {HORIZON_DAYS} giorni ({HORIZON_DAYS/30:.0f} versamenti da 30 giorni: "
|
|
f"§57 avverte che 'EUR X/mese' qui vuol dire ogni 30 giorni — su 36 mesi torna esatto)")
|
|
print(f" biglietti disponibili: " + " | ".join(
|
|
f"{F['name']} " + ", ".join(f"${s/1000:.0f}k@${p:.0f}" for s, p in F['tickets'])
|
|
for F in pl.FIRMS))
|
|
print(f" biglietto piu' economico ${CHEAPEST:.0f} -> sotto questa cassa e senza conti vivi")
|
|
print(f" il percorso e' FUORI GIOCO (zero assorbente).")
|
|
tick("§0")
|
|
|
|
# ============================================================== §1 controlli
|
|
hr("§1. CONTROLLI — bit-exact contro il simulatore pubblicato, e il controllo POSITIVO")
|
|
R0, G0 = AP.rg_off(W_A, I, XSP[0], 0)
|
|
f0 = GC.fvec_for(W_A, I, rate, 0, XS_FUND_ANN)
|
|
Rc0 = R0 - f0
|
|
print(" §1.1 la mia `ladder_rt` col cablato (immediate/largest/const, nessun rimborso,")
|
|
print(" nessun versamento, soglia 0, cassa ferma) contro `GC.ladder` di §37:")
|
|
a = GC.ladder(Rc0, G0, N_BAND, 0.75, SEED_LAD, refund=False)
|
|
b = ladder_rt(Rc0, G0, N_BAND, 0.75, SEED_LAD, pol=P_BASE)
|
|
d1 = float(np.abs(a["eur_day"] - b["eur_day"]).max())
|
|
print(f" max|diff| percorso per percorso = {d1:.3e} -> "
|
|
f"{'IDENTICO' if d1 < 1e-15 else 'DIVERGE — STOP'}")
|
|
if d1 >= 1e-15:
|
|
print(" Senza questo controllo i numeri sotto sarebbero di un'altra macchina.")
|
|
return
|
|
bump(2)
|
|
print("\n §1.2 e `GC.ladder` e' a sua volta bit-exact contro `pc.simulate` (§37 §4.1);")
|
|
print(" la catena e' quindi: ladder_rt == GC.ladder == pc.simulate.")
|
|
cfg = tuple(W_A.get(n, 0.0) for n in PA.CRYPTO_PROP)
|
|
key = (cfg[0], cfg[1], cfg[2], "shared")
|
|
saved = pc._TUP_CACHE.pop(key, None)
|
|
pc._TUP_CACHE[key] = (Rc0, G0, I)
|
|
ref = pc.simulate([cfg], [0] * pl.MAX_CONCURRENT, n_paths=N_BAND, lev=0.75,
|
|
seed=SEED_LAD, lens="coupled", start=str(I[0].date()))
|
|
pc._TUP_CACHE.pop(key, None)
|
|
if saved is not None:
|
|
pc._TUP_CACHE[key] = saved
|
|
d2 = float(np.abs(ref["eur_day"] - b["eur_day"]).max())
|
|
print(f" max|diff| ladder_rt vs pc.simulate = {d2:.3e} -> "
|
|
f"{'IDENTICO' if d2 < 1e-15 else 'DIVERGE'}")
|
|
bump(1)
|
|
|
|
print("\n §1.3 CONTROLLO POSITIVO del selettore di politica — un interruttore che non")
|
|
print(" cambia niente e' indistinguibile da un interruttore rotto.")
|
|
rngb = np.random.default_rng(51224)
|
|
nb = int(np.ceil(HORIZON_DAYS / 20))
|
|
st = rngb.integers(0, len(I) - 20, size=(N_MAIN, nb))
|
|
BOOT = (st[:, :, None] + np.arange(20)[None, None, :]).reshape(N_MAIN, -1)[:, :HORIZON_DAYS]
|
|
rb_full = FD.book_75_25(rate, alloc=654.0)
|
|
if rb_full.index.tz is None:
|
|
rb_full.index = rb_full.index.tz_localize("UTC")
|
|
rb = np.nan_to_num(rb_full.reindex(I).values.astype(float))
|
|
rb_dl = rb - (1.0 - DELUCK) * rb.mean()
|
|
RB_BOOT = rb_dl[BOOT]
|
|
none_pol = ladder_rt(Rc0, G0, N_MAIN, 0.75, SEED_LAD, pol=POL(when="none"), boot=BOOT)
|
|
bump(1)
|
|
print(f" politica 'MAI COMPRARE': biglietti mediani {none_pol['med_tix']:.0f}, "
|
|
f"P(>=50/g) {none_pol['p_target']:.1%}, P(zero payout) {none_pol['p_zero']:.1%} -> "
|
|
f"{'OK' if none_pol['p_target'] == 0 and none_pol['p_zero'] == 1 else 'ROTTO'}")
|
|
print("\n §1.4 CONTROLLO POSITIVO della cassa-sul-libro: 'MAI COMPRARE' + cassa sul libro")
|
|
print(" dev'essere la strada LIBRO di §37 (`GC.book_road`), stessi sorteggi.")
|
|
nb_book = ladder_rt(Rc0, G0, N_MAIN, 0.75, SEED_LAD, pol=POL(when="none"), boot=BOOT,
|
|
rb_boot=RB_BOOT)
|
|
road = GC.book_road(rb, BOOT, pl.START_EUR, 0.0)
|
|
d3 = float(np.abs(nb_book["cash"] - road["cash"]).max())
|
|
bump(2)
|
|
print(f" max|diff| sulla cassa finale = {d3:.3e} -> "
|
|
f"{'IDENTICO' if d3 < 1e-9 else 'DIVERGE'} | mediana ${nb_book['med_cash']:,.0f} "
|
|
f"(GC.book_road ${road['med_cash']:,.0f})")
|
|
tick("§1")
|
|
|
|
# ============================================================== §2 banda d'ancora
|
|
hr("§2. LA BANDA D'ANCORA — riproduzione di §32/§37 e scelta della cella MEDIANA")
|
|
print(f" {len(BAND_OFFSETS)} offset (sottocampione uniforme a priori di r0726, la griglia")
|
|
print(f" piena e' 23) x {len(PHASES_SUB)} fasi {PHASES_SUB} = "
|
|
f"{len(BAND_OFFSETS)*len(PHASES_SUB)} celle, funding DENTRO, politica CABLATA (quella")
|
|
print(f" con cui i numeri sono stati pubblicati), {N_BAND} percorsi per cella.")
|
|
ev_b = PA.boot_idx(len(I), GC.EV_H, PA.N_PATHS, seed=GC.SEED_EV)
|
|
fu_b = PA.boot_idx(len(I), GC.FU_H, PA.N_PATHS, seed=GC.SEED_FU)
|
|
cells = []
|
|
for off in BAND_OFFSETS:
|
|
fA = GC.fvec_for(W_A, I, rate, off, XS_FUND_ANN)
|
|
for ph in PHASES_SUB:
|
|
Rx, Gx = AP.rg_off(W_A, I, XSP[ph], off)
|
|
Rf = Rx - fA
|
|
j, lv, _ = GC.best_lev(Rf, Gx, ev_b, fu_b)
|
|
lad = ladder_rt(Rf, Gx, N_BAND, lv, SEED_LAD, pol=P_BASE)
|
|
cells.append(dict(off=off, ph=ph, J=j, lev=lv, p50=lad["p_target"],
|
|
pz=lad["p_zero"], med=lad["med"]))
|
|
bump(1)
|
|
C = pd.DataFrame(cells)
|
|
print(f"\n P(>=50 EUR/g) mediana {C['p50'].median():.1%} (PUB §37 {PUB['gatec_p50g']:.1%}) | "
|
|
f"banda [{np.percentile(C['p50'],10):.1%}, {np.percentile(C['p50'],90):.1%}] "
|
|
f"(PUB [{PUB['gatec_p10']:.1%}, {PUB['gatec_p90']:.1%}]) | "
|
|
f"P(zero) {C['pz'].median():.1%} (PUB {PUB['gatec_pzero']:.1%})")
|
|
can = C[(C["off"] == 0) & (C["ph"] == 0)].iloc[0]
|
|
pct_can = float((C["p50"] <= can["p50"]).mean())
|
|
print(f" cella CANONICA (off 0, fase 0): P(>=50/g) {can['p50']:.1%} = {pct_can:.0%}° pctl "
|
|
f"della banda -> il gia' noto: la canonica e' la cella FORTUNATA, non la stima.")
|
|
k_med = int((C["p50"] - C["p50"].median()).abs().idxmin())
|
|
OFF_M, PH_M, LEV_M = int(C.loc[k_med, "off"]), int(C.loc[k_med, "ph"]), float(C.loc[k_med, "lev"])
|
|
print(f" CELLA DI RIFERIMENTO (mediana della banda): offset {OFF_M} min, fase {PH_M}, "
|
|
f"leva {LEV_M:.2f}x, P(>=50/g) {C.loc[k_med,'p50']:.1%}")
|
|
fM = GC.fvec_for(W_A, I, rate, OFF_M, XS_FUND_ANN)
|
|
RxM, G_M = AP.rg_off(W_A, I, XSP[PH_M], OFF_M)
|
|
R_M = RxM - fM
|
|
tick("§2")
|
|
|
|
# ============================================================== §3 dose-risposta
|
|
hr("§3. QUANTE VOLTE SI GIOCA — la dose-risposta che §37 dichiara come IL meccanismo")
|
|
print(" Politica IMMEDIATA (il cablato) con un tetto DURO al numero di biglietti mai")
|
|
print(" comprati. E' l'esperimento pulito: cambia SOLO quante volte si puo' giocare.")
|
|
print(f" Cella di riferimento, {N_MAIN} percorsi, bootstrap condiviso, deposito rimborsato")
|
|
print(" al primo payout (la regola vera), soglia di prelievo $100, nessun versamento.")
|
|
print(f"\n {'tetto':>8} {'giocate med':>12} {'giocate medie':>14} {'P(>=50/g)':>10} "
|
|
f"{'P(>=10/g)':>10} {'P(zero payout)':>15} {'P(fuori gioco)':>15} {'EUR/g medio':>12}")
|
|
dose = {}
|
|
for mt in (1, 2, 3, 5, 8, 10 ** 9):
|
|
r = ladder_rt(R_M, G_M, N_MAIN, LEV_M, SEED_LAD, pol=POL(max_tickets=mt),
|
|
refund_mode="payout", min_payout=MIN_PAYOUT, boot=BOOT)
|
|
dose[mt] = r
|
|
bump(1)
|
|
lab = "illim." if mt > 100 else str(mt)
|
|
print(f" {lab:>8} {r['med_tix']:>12.0f} {r['mean_tix']:>14.2f} {r['p_target']:>10.1%} "
|
|
f"{r['p_ge10']:>10.1%} {r['p_zero']:>15.1%} {r['p_stuck']:>15.1%} "
|
|
f"{r['mean_eur']:>12.2f}")
|
|
d1_, dinf = dose[1], dose[10 ** 9]
|
|
se = se_pair(dinf["eur_day"], d1_["eur_day"])
|
|
print(f"\n DA 1 GIOCATA A ILLIMITATE: P(>=50/g) {d1_['p_target']:.1%} -> "
|
|
f"{dinf['p_target']:.1%} ({(dinf['p_target']-d1_['p_target'])*100:+.2f}pp, "
|
|
f"SE appaiata {se*100:.2f}pp) | giocate mediane {d1_['med_tix']:.0f} -> "
|
|
f"{dinf['med_tix']:.0f} | P(zero) {d1_['p_zero']:.1%} -> {dinf['p_zero']:.1%}")
|
|
ripida = (dinf["p_target"] - d1_["p_target"]) > 3 * se
|
|
print(f" >>> ATTESA A2 ('dose-risposta RIPIDA se il meccanismo e' il numero di giocate'): "
|
|
f"{'CONFERMATA' if ripida else 'REFUTATA'}")
|
|
tick("§3")
|
|
|
|
# ============================================================== §4 le politiche
|
|
hr("§4. LE POLITICHE DI RI-ACQUISTO — 7 regole di TEMPO x 2 di TAGLIA, senza versamenti")
|
|
print(" Le due colonne che vanno SEMPRE lette insieme: 'giocate' e 'P(fuori gioco)'. Una")
|
|
print(" politica aggressiva aumenta entrambe, ed e' esattamente la trappola dichiarata.")
|
|
print("\n Prima di misurare, l'ARITMETICA del listino — prezzo per $1.000 di nozionale:")
|
|
for F in pl.FIRMS:
|
|
print(" " + f"{F['name']:>5} " + " ".join(
|
|
f"${s_/1000:.0f}k: ${p_/(s_/1000):.2f}/k" for s_, p_ in F["tickets"]))
|
|
print(" -> il biglietto GRANDE costa MENO per unita' di nozionale (HYRO $5,79/k contro")
|
|
print(" $9,96/k). Piu' giocate piccole NON sono piu' esposizione: sono meno.")
|
|
POLS = [
|
|
("IMMEDIATA (il cablato)", POL()),
|
|
("SOGLIA 2x il prezzo", POL(when="threshold", k_reserve=2.0)),
|
|
("SOGLIA 3x il prezzo", POL(when="threshold", k_reserve=3.0)),
|
|
("SOLO DAI PAYOUT", POL(when="earned")),
|
|
("CADENZA 90 giorni", POL(when="cadence", cadence=90)),
|
|
("CADENZA 180 giorni", POL(when="cadence", cadence=180)),
|
|
("UN CONTO ALLA VOLTA", POL(max_open=1)),
|
|
("DUE CONTI ALLA VOLTA", POL(max_open=2)),
|
|
("MAI (1 biglietto)", POL(max_tickets=1)),
|
|
]
|
|
res4 = {}
|
|
for size in ("largest", "cheapest"):
|
|
lab_s = "biglietto PIU' GRANDE ($100k)" if size == "largest" else "biglietto PIU' ECONOMICO ($25k)"
|
|
print(f"\n --- {lab_s} ---")
|
|
print(f" {'politica':>26} {'giocate med':>12} {'P(>=50/g)':>10} {'P(>=10/g)':>10} "
|
|
f"{'P(zero)':>9} {'P(fuori gioco)':>15} {'EUR/g med':>10} {'cassa fin. med':>15}")
|
|
for name, p in POLS:
|
|
pp = dict(p)
|
|
pp["size"] = size
|
|
r = ladder_rt(R_M, G_M, N_MAIN, LEV_M, SEED_LAD, pol=pp, refund_mode="payout",
|
|
min_payout=MIN_PAYOUT, boot=BOOT)
|
|
res4[(size, name)] = r
|
|
bump(1)
|
|
print(f" {name:>26} {r['med_tix']:>12.0f} {r['p_target']:>10.1%} "
|
|
f"{r['p_ge10']:>10.1%} {r['p_zero']:>9.1%} {r['p_stuck']:>15.1%} "
|
|
f"{r['med']:>10.2f} ${r['med_cash']:>14,.0f}")
|
|
base_r = res4[("largest", "IMMEDIATA (il cablato)")]
|
|
print(f"\n DIFFERENZE APPAIATE contro il cablato (stessi sorteggi), su P(>=50 EUR/g):")
|
|
print(f" {'politica':>26} {'dP(>=50/g)':>12} {'SE appaiata':>13} {'dP(fuori gioco)':>17} "
|
|
f"{'d giocate med':>14}")
|
|
rows4 = []
|
|
for size in ("largest", "cheapest"):
|
|
for name, _ in POLS:
|
|
r = res4[(size, name)]
|
|
if (size, name) == ("largest", "IMMEDIATA (il cablato)"):
|
|
continue
|
|
s = se_pair(r["eur_day"], base_r["eur_day"])
|
|
tag = name if size == "largest" else "[pic] " + name
|
|
rows4.append(dict(tag=tag, d=r["p_target"] - base_r["p_target"], se=s,
|
|
dz=r["p_stuck"] - base_r["p_stuck"],
|
|
dt=r["med_tix"] - base_r["med_tix"], p50=r["p_target"],
|
|
pstuck=r["p_stuck"]))
|
|
print(f" {tag:>26} {(r['p_target']-base_r['p_target'])*100:>11.2f}pp "
|
|
f"{s*100:>12.2f}pp {(r['p_stuck']-base_r['p_stuck'])*100:>16.2f}pp "
|
|
f"{r['med_tix']-base_r['med_tix']:>14.0f}")
|
|
R4 = pd.DataFrame(rows4)
|
|
sig = R4[R4["d"].abs() > 2 * R4["se"]]
|
|
print(f"\n Politiche che si distinguono dal cablato oltre 2 SE appaiate: "
|
|
f"{len(sig)}/{len(R4)}"
|
|
+ (f" (fra cui {', '.join(list(sig['tag'].str.strip())[:4])}...)" if len(sig) else ""))
|
|
allp = pd.concat([R4[["tag", "p50", "pstuck"]],
|
|
pd.DataFrame([dict(tag="IMMEDIATA (il cablato)", p50=base_r["p_target"],
|
|
pstuck=base_r["p_stuck"])])], ignore_index=True)
|
|
print(f" Spread fra la politica migliore e la peggiore su P(>=50/g): "
|
|
f"{(allp['p50'].max()-allp['p50'].min())*100:.2f}pp "
|
|
f"(max {allp.loc[allp['p50'].idxmax(),'tag'].strip()} {allp['p50'].max():.1%}, "
|
|
f"min {allp.loc[allp['p50'].idxmin(),'tag'].strip()} {allp['p50'].min():.1%})")
|
|
s3 = res4[("largest", "SOGLIA 3x il prezzo")]
|
|
print(f"\n ⚠ A $654 la SOGLIA 3x e' degenere: il biglietto piu' economico costa ${CHEAPEST:.0f}")
|
|
print(f" e 3x sono ${3*CHEAPEST:.0f} > ${pl.START_EUR*EURUSD:,.0f} -> giocate mediane "
|
|
f"{s3['med_tix']:.0f}. Non e' una politica prudente, e' 'non giocare' scritta")
|
|
print(f" con un'altra sintassi. La soglia in VALUTA ASSOLUTA dipende dal capitale:")
|
|
print(f" stessa lezione della banda di GTAA01 (27/07), su un altro parametro.")
|
|
pic = res4[("cheapest", "IMMEDIATA (il cablato)")]
|
|
a3a = pic["p_target"] < base_r["p_target"]
|
|
a3b = pic["p_stuck"] < base_r["p_stuck"]
|
|
print(f"\n >>> ATTESA A3: il biglietto piccolo perde sul bersaglio "
|
|
f"({pic['p_target']:.1%} contro {base_r['p_target']:.1%}) -> "
|
|
f"{'confermata' if a3a else 'REFUTATA'}; e sulla ROVINA "
|
|
f"({pic['p_stuck']:.1%} contro {base_r['p_stuck']:.1%}) -> "
|
|
f"{'confermata' if a3b else 'REFUTATA'}. Verdetto A3: "
|
|
f"{'CONFERMATA' if (a3a and a3b) else 'PARZIALE/REFUTATA'}")
|
|
tick("§4")
|
|
|
|
# ============================================================== §5 la leva
|
|
hr("§5. LA LEVA DEL CONTO NUOVO — e il NULL DEL DE-LEVERING, dichiarato PRIMA")
|
|
print(" 🚨 In un bootstrap a blocchi il passato NON porta informazione sul futuro: una")
|
|
print(" regola 'riduci la leva dopo un bust' puo' agire SOLO cambiando la leva MEDIA. Il")
|
|
print(" controllo obbligatorio e' la leva COSTANTE alla stessa media realizzata. Se la")
|
|
print(" regola non la batte, e' de-levering travestito (sarebbe l'8ª occorrenza).")
|
|
lev_rules = [
|
|
("costante (il cablato)", POL()),
|
|
("RIDOTTA x0,80 a ogni bust", POL(lev_rule="down_on_bust", lev_dn=0.80)),
|
|
("RIDOTTA x0,60 a ogni bust", POL(lev_rule="down_on_bust", lev_dn=0.60)),
|
|
("CRESCENTE x1,25 a ogni pass", POL(lev_rule="up_on_pass", lev_up=1.25)),
|
|
]
|
|
print(f"\n {'regola di leva':>30} {'leva media':>11} {'P(>=50/g)':>10} {'P(zero)':>9} "
|
|
f"{'P(fuori gioco)':>15} {'EUR/g medio':>12}")
|
|
lev_res = {}
|
|
for name, p in lev_rules:
|
|
r = ladder_rt(R_M, G_M, N_MAIN, LEV_M, SEED_LAD, pol=p, refund_mode="payout",
|
|
min_payout=MIN_PAYOUT, boot=BOOT)
|
|
lev_res[name] = r
|
|
bump(1)
|
|
print(f" {name:>30} {r['lev_mean']:>11.3f} {r['p_target']:>10.1%} {r['p_zero']:>9.1%} "
|
|
f"{r['p_stuck']:>15.1%} {r['mean_eur']:>12.2f}")
|
|
print(f"\n IL NULL: leva COSTANTE alla stessa media realizzata di ciascuna regola")
|
|
print(f" {'regola':>30} {'leva cost.':>11} {'P(>=50/g) null':>15} "
|
|
f"{'P(>=50/g) regola':>17} {'delta':>9} {'SE':>8} {'P(fuori) null':>14} {'verdetto':>9}")
|
|
n_ref = 0
|
|
for name, _ in lev_rules[1:]:
|
|
r = lev_res[name]
|
|
lv_eq = r["lev_mean"]
|
|
nul = ladder_rt(R_M, G_M, N_MAIN, lv_eq, SEED_LAD, pol=POL(), refund_mode="payout",
|
|
min_payout=MIN_PAYOUT, boot=BOOT)
|
|
bump(1)
|
|
d = r["p_target"] - nul["p_target"]
|
|
s = se_pair(r["eur_day"], nul["eur_day"])
|
|
ok = d > 2 * s
|
|
n_ref += 0 if ok else 1
|
|
print(f" {name:>30} {lv_eq:>11.3f} {nul['p_target']:>15.1%} {r['p_target']:>17.1%} "
|
|
f"{d*100:>8.2f}pp {s*100:>7.2f}pp {nul['p_stuck']:>14.1%} "
|
|
f"{'BATTE' if ok else 'REFUTED':>9}")
|
|
print(f"\n >>> ATTESA A6 ('null del de-levering, REFUTED 3/3'): "
|
|
f"{'CONFERMATA' if n_ref == 3 else f'PARZIALE ({n_ref}/3 refutate)'}")
|
|
print(" Lettura: la leva del conto nuovo non e' una politica, e' un LIVELLO — e il livello")
|
|
print(" ottimo e' gia' cercato simmetricamente da `best_lev` in ogni cella (§29).")
|
|
print("\n §5.1 DIAGNOSTICO (non una proposta): il LIVELLO di leva, costante, spazzato sulla")
|
|
print(" stessa griglia LEVS di §29. `best_lev` massimizza J = P(pass) x P(vivo), NON")
|
|
print(" P(>=50 EUR/g): §29 lo dichiara ('J e P(>=50/g) hanno argmax diversi'). Sceglierla")
|
|
print(" qui sul bersaglio sarebbe selezione sulla metrica d'uscita — si mostra il PREZZO.")
|
|
print(f"\n {'leva':>8} {'P(>=50/g)':>10} {'P(>=10/g)':>10} {'P(zero payout)':>15} "
|
|
f"{'P(fuori gioco)':>15} {'EUR/g medio':>12} {'EUR/g mediano':>14}")
|
|
for lv in PR.LEVS:
|
|
r = ladder_rt(R_M, G_M, N_MAIN, lv, SEED_LAD, pol=POL(), refund_mode="payout",
|
|
min_payout=MIN_PAYOUT, boot=BOOT)
|
|
bump(1)
|
|
star = " <- best_lev (J)" if abs(lv - LEV_M) < 1e-9 else ""
|
|
print(f" {lv:>8.3f} {r['p_target']:>10.1%} {r['p_ge10']:>10.1%} {r['p_zero']:>15.1%} "
|
|
f"{r['p_stuck']:>15.1%} {r['mean_eur']:>12.2f} {r['med']:>14.2f}{star}")
|
|
tick("§5")
|
|
|
|
# ============================================================== §6 le tre convenzioni
|
|
hr("§6. LE TRE CONVENZIONI SUL DEPOSITO — cambia l'OTTIMO, o solo il livello?")
|
|
print(" (i) SPESA PERSA `pc.simulate` — da cui esce il numero operativo del progetto;")
|
|
print(" (ii) RIMBORSO AL PASS `r0725_hyro:200` — l'altro modello, contraddittorio;")
|
|
print(" (iii) RIMBORSO AL PRIMO PAYOUT — la regola dichiarata dalla firm (§30).")
|
|
sel6 = ["IMMEDIATA (il cablato)", "SOGLIA 2x il prezzo", "SOLO DAI PAYOUT",
|
|
"CADENZA 180 giorni", "MAI (1 biglietto)"]
|
|
conv = [("(i) spesa persa", "none"), ("(ii) al pass", "pass"), ("(iii) al primo payout", "payout")]
|
|
tab6 = {}
|
|
print(f"\n {'politica':>26} " + " ".join(f"{c[0]:>22}" for c in conv))
|
|
for name in sel6:
|
|
p = dict([x for x in POLS if x[0] == name][0][1])
|
|
line = f" {name:>26} "
|
|
for lab, rm in conv:
|
|
r = ladder_rt(R_M, G_M, N_MAIN, LEV_M, SEED_LAD, pol=p, refund_mode=rm,
|
|
min_payout=MIN_PAYOUT, boot=BOOT)
|
|
tab6[(name, rm)] = r
|
|
bump(1)
|
|
line += f"{r['p_target']:>10.1%}/{r['p_stuck']:>9.1%} "
|
|
print(line + " <- P(>=50/g) / P(fuori gioco)")
|
|
print("\n ARGMAX per convenzione (su P(>=50 EUR/g)):")
|
|
same = True
|
|
arg0 = None
|
|
for lab, rm in conv:
|
|
vals = {n: tab6[(n, rm)]["p_target"] for n in sel6}
|
|
arg = max(vals, key=vals.get)
|
|
if arg0 is None:
|
|
arg0 = arg
|
|
same &= (arg == arg0)
|
|
print(f" {lab:>22}: {arg:>26} ({vals[arg]:.1%})")
|
|
dpp = np.median([tab6[(n, "payout")]["p_target"] - tab6[(n, "none")]["p_target"] for n in sel6])
|
|
print(f"\n Valore del rimborso (mediana su {len(sel6)} politiche, da 'spesa persa' a")
|
|
print(f" 'rimborso al primo payout'): {dpp*100:+.2f}pp su P(>=50/g) "
|
|
f"(PUB §37: +1-3pp atteso, misurato {'dentro' if PUB['refund_pp_lo'] <= dpp <= PUB['refund_pp_hi'] else 'FUORI'} banda)")
|
|
print(f" Quota media restituita: ${tab6[(sel6[0],'payout')]['mean_back']:,.0f} per percorso")
|
|
print(f" >>> ATTESA A5 ('il rimborso NON cambia l'ottimo'): "
|
|
f"{'CONFERMATA' if same else 'REFUTATA'}")
|
|
tick("§6")
|
|
|
|
# ============================================================== §7 con i versamenti
|
|
hr("§7. CON I VERSAMENTI — dove la politica di ri-acquisto smette di essere gratis")
|
|
print(" Con un versamento mensile lo ZERO SMETTE DI ESSERE ASSORBENTE (la cassa si")
|
|
print(" riempie di nuovo): P(fuori gioco) va a zero per TUTTE le politiche, e il costo")
|
|
print(" della politica aggressiva non si legge piu' li' ma sulla RICCHEZZA TERMINALE.")
|
|
print(" Convenzione principale: cassa non spesa SUL LIBRO (la piu' equa verso le politiche")
|
|
print(" prudenti); la convenzione di §37 (cassa FERMA) e' riportata come scarto.")
|
|
dep_rows = []
|
|
for dep in (0.0, 250.0, 500.0):
|
|
print(f"\n --- versamento EUR {dep:,.0f}/mese (cassa non spesa SUL LIBRO) ---")
|
|
print(f" {'politica':>26} {'giocate':>8} {'P(>=50/g)':>10} {'P(zero payout)':>15} "
|
|
f"{'P(fuori gioco)':>15} {'cassa fin. med':>15} {'P(<versato)':>12} "
|
|
f"{'speso/entrato':>14}")
|
|
for name in sel6:
|
|
p_ = dict([x for x in POLS if x[0] == name][0][1])
|
|
a_ = ladder_rt(R_M, G_M, N_MAIN, LEV_M, SEED_LAD, pol=p_, refund_mode="payout",
|
|
min_payout=MIN_PAYOUT, boot=BOOT, deposit_eur=dep)
|
|
b_ = ladder_rt(R_M, G_M, N_MAIN, LEV_M, SEED_LAD, pol=p_, refund_mode="payout",
|
|
min_payout=MIN_PAYOUT, boot=BOOT, deposit_eur=dep, rb_boot=RB_BOOT)
|
|
bump(2)
|
|
ploss = float((b_["cash"] < b_["contributed"]).mean())
|
|
dep_rows.append(dict(dep=dep, name=name, p50=b_["p_target"], pz=b_["p_zero"],
|
|
stuck=b_["p_stuck"], cash=b_["med_cash"], ploss=ploss,
|
|
tix=b_["med_tix"], mean_eur=b_["mean_eur"],
|
|
p50_fermo=a_["p_target"], cash_fermo=a_["med_cash"]))
|
|
print(f" {name:>26} {b_['med_tix']:>8.0f} {b_['p_target']:>10.1%} "
|
|
f"{b_['p_zero']:>15.1%} {b_['p_stuck']:>15.1%} ${b_['med_cash']:>14,.0f} "
|
|
f"{ploss:>12.1%} {b_['med_spent']/b_['contributed']:>14.1%}")
|
|
if dep == 500.0 and name == sel6[0]:
|
|
dep500 = b_
|
|
if dep == 0.0 and name == sel6[0]:
|
|
dep0 = b_
|
|
if dep == 500.0 and name == "SOLO DAI PAYOUT":
|
|
dep500_safe = b_
|
|
D7 = pd.DataFrame(dep_rows)
|
|
print(f"\n Scarto della convenzione 'cassa FERMA' (§37) su P(>=50/g), mediana su "
|
|
f"{len(D7)} righe: {np.median(D7['p50_fermo']-D7['p50'])*100:+.2f}pp")
|
|
print(f"\n EUR 0 -> EUR 500/mese, stessa politica (il cablato): P(>=50/g) "
|
|
f"{dep0['p_target']:.1%} -> {dep500['p_target']:.1%} "
|
|
f"({(dep500['p_target']-dep0['p_target'])*100:+.2f}pp)")
|
|
print(f" Spread fra POLITICHE (§4, senza versamenti): "
|
|
f"{(allp['p50'].max()-allp['p50'].min())*100:.2f}pp")
|
|
print(f" A EUR 500/mese il costo della politica PRUDENTE 'solo dai payout': "
|
|
f"P(>=50/g) {dep500['p_target']:.1%} -> {dep500_safe['p_target']:.1%} "
|
|
f"({(dep500_safe['p_target']-dep500['p_target'])*100:+.2f}pp), cassa mediana "
|
|
f"${dep500['med_cash']:,.0f} -> ${dep500_safe['med_cash']:,.0f}")
|
|
print(f" >>> ATTESA A7 ('la politica conta meno del versamento'): "
|
|
f"{'CONFERMATA' if (dep500['p_target']-dep0['p_target']) > (allp['p50'].max()-allp['p50'].min()) else 'REFUTATA'}")
|
|
tick("§7")
|
|
|
|
# ============================================================== §8 robustezza fra celle
|
|
hr("§8. ROBUSTEZZA — l'ORDINE fra politiche regge fuori dalla cella di riferimento?")
|
|
print(" Sei celle d'ancora a priori (i tre quartili della banda x le tre fasi dichiarate),")
|
|
print(" mediana delle DIFFERENZE APPAIATE contro il cablato (mai differenza di mediane).")
|
|
Cs = C.sort_values("p50").reset_index(drop=True)
|
|
picks = [Cs.iloc[int(q * (len(Cs) - 1))] for q in (0.15, 0.35, 0.50, 0.65, 0.85)]
|
|
sel8 = ["SOGLIA 2x il prezzo", "SOLO DAI PAYOUT", "MAI (1 biglietto)"]
|
|
st2 = np.random.default_rng(7761).integers(0, len(I) - 20, size=(N_ROB, nb))
|
|
BOOT2 = (st2[:, :, None] + np.arange(20)[None, None, :]).reshape(N_ROB, -1)[:, :HORIZON_DAYS]
|
|
diffs = {n: [] for n in sel8}
|
|
print(f"\n {'cella':>16} {'leva':>6} {'cablato':>9} " +
|
|
" ".join(f"{n[:16]:>18}" for n in sel8))
|
|
for row in picks:
|
|
off, ph = int(row["off"]), int(row["ph"])
|
|
fX = GC.fvec_for(W_A, I, rate, off, XS_FUND_ANN)
|
|
Rx, Gx = AP.rg_off(W_A, I, XSP[ph], off)
|
|
Rf = Rx - fX
|
|
lv = float(row["lev"])
|
|
b0 = ladder_rt(Rf, Gx, N_ROB, lv, SEED_LAD, pol=POL(), refund_mode="payout",
|
|
min_payout=MIN_PAYOUT, boot=BOOT2)
|
|
bump(1)
|
|
line = f" off{off:>4} fase{ph:>2} {lv:>6.2f} {b0['p_target']:>9.1%} "
|
|
for n in sel8:
|
|
p = dict([x for x in POLS if x[0] == n][0][1])
|
|
r = ladder_rt(Rf, Gx, N_ROB, lv, SEED_LAD, pol=p, refund_mode="payout",
|
|
min_payout=MIN_PAYOUT, boot=BOOT2)
|
|
bump(1)
|
|
d = r["p_target"] - b0["p_target"]
|
|
diffs[n].append(d)
|
|
line += f"{d*100:>+17.2f}pp"
|
|
print(line)
|
|
print(f"\n {'politica':>26} {'mediana delle diff. appaiate':>30} {'celle > 0':>11}")
|
|
for n in sel8:
|
|
v = np.array(diffs[n])
|
|
print(f" {n:>26} {np.median(v)*100:>29.2f}pp {int((v > 0).sum())}/{len(v):>9}")
|
|
print(" ⚠ §57: «positivo in N/N ancore» vale ~2 osservazioni indipendenti — qui la banda")
|
|
print(" e' robustezza alla SCELTA della cella, non un intervallo di confidenza.")
|
|
tick("§8")
|
|
|
|
# ============================================================== §9 verdetto
|
|
hr("§9. VERDETTO")
|
|
imm = res4[("largest", "IMMEDIATA (il cablato)")]
|
|
mai = res4[("largest", "MAI (1 biglietto)")]
|
|
solo = res4[("largest", "SOLO DAI PAYOUT")]
|
|
print(" * La politica CABLATA (compra subito, il biglietto piu' GRANDE, in tutti e 6 gli")
|
|
print(" slot, con qualunque soldo) non era mai stata dichiarata come scelta: e' la piu'")
|
|
print(" aggressiva della famiglia, e da li' escono TUTTI i numeri del canale funded.")
|
|
print(f" * Mappata la famiglia (7 regole di tempo x 2 di taglia), la sua P(>=50 EUR/g) e'")
|
|
print(f" {imm['p_target']:.1%}; massimo di famiglia {allp['p50'].max():.1%} "
|
|
f"({allp.loc[allp['p50'].idxmax(),'tag'].strip()}), minimo {allp['p50'].min():.1%}, "
|
|
f"spread {(allp['p50'].max()-allp['p50'].min())*100:.2f}pp.")
|
|
print(f" * LE DUE COLONNE INSIEME, senza versamenti: cablato {imm['med_tix']:.0f} giocate e "
|
|
f"P(fuori gioco) {imm['p_stuck']:.1%};")
|
|
print(f" una giocata sola {mai['med_tix']:.0f} e {mai['p_stuck']:.1%}. Il prezzo della "
|
|
f"prudenza e' {(imm['p_target']-mai['p_target'])*100:.2f}pp di bersaglio per "
|
|
f"{(imm['p_stuck']-mai['p_stuck'])*100:.2f}pp di rovina.")
|
|
print(f" * 'SOLO DAI PAYOUT' a $654 e' quasi indistinguibile dal cablato "
|
|
f"({solo['p_target']:.1%} contro {imm['p_target']:.1%}): dopo il primo biglietto da "
|
|
f"${579:.0f}")
|
|
print(f" restano ${pl.START_EUR*EURUSD-579:.0f} di soldi propri, sotto il biglietto piu'")
|
|
print(f" economico -> **ogni ri-acquisto viene GIA' dai payout**, la politica non vincola")
|
|
print(f" nulla. Vincola solo quando c'e' un versamento (§7).")
|
|
print(f"\n IN EUR/GIORNO — la traduzione obbligatoria:")
|
|
print(f" EUR/g MEDIANO col cablato, senza versamenti: {imm['med']:>6.2f} EUR/g")
|
|
print(f" EUR/g MEDIO col cablato, senza versamenti: {imm['mean_eur']:>6.2f} EUR/g")
|
|
fam = {n: tab6[(n, 'payout')]['mean_eur'] for n in sel6}
|
|
bn = max(fam, key=fam.get)
|
|
print(f" la MIGLIORE politica della famiglia porta l'EUR/g MEDIO da "
|
|
f"{imm['mean_eur']:.2f} a {fam[bn]:.2f} ({fam[bn]-imm['mean_eur']:+.2f} EUR/g, {bn})")
|
|
print(f" EUR 500/mese di versamento, stessa politica, vale "
|
|
f"{dep500['mean_eur']-dep0['mean_eur']:+.2f} EUR/g sulla stessa metrica.")
|
|
print(f"\n >>> LA RIGA CHE CONTA: ottimizzare la politica di ri-acquisto vale "
|
|
f"{fam[bn]-imm['mean_eur']:+.2f} EUR/g;")
|
|
print(f" versare EUR 500/mese ne vale {dep500['mean_eur']-dep0['mean_eur']:+.2f}. "
|
|
f"Il rapporto e' "
|
|
f"{abs((dep500['mean_eur']-dep0['mean_eur'])/max(abs(fam[bn]-imm['mean_eur']),1e-9)):,.0f}x.")
|
|
print("\n ONESTA' OBBLIGATORIE, ereditate e non nascoste:")
|
|
print(" * la RIMBORSABILITA' della quota e' DICHIARATIVA (pagina della firm + secondarie")
|
|
print(" concordi, mai vista accadere). §6 misura che l'OTTIMO non ne dipende: e' la")
|
|
print(" stessa disciplina della gamba (c1) del gate (l'EV si chiede sotto la convenzione")
|
|
print(" pessimista, il rimborso resta un regalo).")
|
|
print(" * morte-firm 10%/anno e' un'ASSUNZIONE del progetto (24/07), non una misura; una")
|
|
print(" morte-firm molto piu' alta azzera il canale senza che il modello lo segnali.")
|
|
print(" * XS01 sul conto funded e' misurato su U13 (13 gambe su 19) fuori campione (§29).")
|
|
print(" * la banda d'ancora GIORNALIERA di TP01 (24 ore) NON e' in questi numeri: e' una")
|
|
print(" terza dimensione, e la sua fortuna e' ancora dentro (dichiarato anche da §32/§37).")
|
|
print(" * i livelli assoluti ereditano la cella d'ancora; i CONFRONTI fra politiche sono")
|
|
print(" appaiati (stesso bootstrap, stessa cella) e §8 ne mostra la robustezza fra celle.")
|
|
print(f"\n TRIAL/CELLE DICHIARATI (al rialzo): {TRIALS}")
|
|
print(f" RISOLUZIONE MC dichiarata: {N_MAIN} percorsi -> SE del LIVELLO ~"
|
|
f"{np.sqrt(max(imm['p_target'],1e-9)*(1-imm['p_target'])/N_MAIN)*100:.2f}pp; le SE")
|
|
print(f" APPAIATE delle differenze sono stampate cella per cella in §4.")
|
|
print(f" fatto in {time.time()-T0:.0f}s")
|
|
print("=" * 104)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|