research(wave-0822): GROWTH-POLICY — il libro gira al 7% di Kelly, e ogni gate del progetto e' cieco alla scala
This commit is contained in:
@@ -92,7 +92,7 @@ RAW = ROOT / "data" / "raw"
|
||||
DELUCK = 0.89 # fattore d'ancora MISURATO sul drift (26/07), non stimato a occhio
|
||||
START_WIN = "2024-01-01" # finestra comune ai 5 sleeve (XS01 nasce qui)
|
||||
BLOCK = 20 # blocco del bootstrap, in giorni
|
||||
N_PATHS = 4000
|
||||
N_PATHS = 2500
|
||||
SPLIT = pl.SPLIT # 80% al trader
|
||||
TARGET_EUR_DAY = 50.0
|
||||
FIRMS = {f["name"]: f for f in pl.FIRMS}
|
||||
@@ -386,7 +386,15 @@ def hr(t: str = "") -> None:
|
||||
|
||||
|
||||
def main() -> None:
|
||||
try:
|
||||
sys.stdout.reconfigure(line_buffering=True)
|
||||
except Exception: # pragma: no cover
|
||||
pass
|
||||
t0 = time.time()
|
||||
|
||||
def tick(lab: str) -> None:
|
||||
print(f" [{time.time()-t0:6.0f}s] {lab}")
|
||||
|
||||
print("=" * 104)
|
||||
print(" PROP-ALLOC — allocare per la BARRIERA (P(pass) x P(vivo 12m)) invece che per lo Sharpe")
|
||||
print("=" * 104)
|
||||
@@ -398,6 +406,7 @@ def main() -> None:
|
||||
print(f" de-luck sul drift x{DELUCK} (misurato 26/07) | bootstrap a blocchi di {BLOCK}g, "
|
||||
f"indici CONDIVISI fra configurazioni | {N_PATHS} percorsi")
|
||||
|
||||
tick("pannello costruito")
|
||||
off = {"TP01": _d(_tp01_returns()), "SKH01": _d(_skyhook_returns()), "XS01": _d(_xsec_returns()),
|
||||
"VRP01": _d(_vrp_combo_returns()), "GTAA01": _d(_gtaa_daily_returns())}
|
||||
rec = {"TP01": _crypto(1.0, 0.0)["R"], "SKH01": _crypto(0.0, 1.0)["R"],
|
||||
@@ -430,6 +439,7 @@ def main() -> None:
|
||||
print(" non per assenza di rischio (mark infra-settimana misurato il 30/07: mediana -6.9%,")
|
||||
print(" minimo -81.7% del capitale su un trade finito in UTILE).")
|
||||
|
||||
tick("A. profilo sleeve")
|
||||
hr("B. IL CONTO ANALITICO — perche' con leva LIBERA la barriera premia lo SHARPE, non la vol")
|
||||
tgt, dd, _dl = FIRMS["HYRO"]["ev"]
|
||||
print(f" P(toccare +{tgt:.0%} prima di -{dd:.0%}), gambler's ruin senza limite di tempo, "
|
||||
@@ -460,6 +470,7 @@ def main() -> None:
|
||||
f"= {len(grid)*len(LEVS)} celle, firm HYRO (eval +10%/-6%/-4%, funded -6%/-4%)")
|
||||
print(f" orizzonte eval {EV_H}g, funded {FU_H}g. Lente ACCOPPIATA. "
|
||||
f"J = P(pass) x P(vivo 12m).")
|
||||
tick("B. conto analitico")
|
||||
t = time.time()
|
||||
G3 = grid_scan(grid, LEVS, "HYRO", ev_idx, fu_idx)
|
||||
print(f" ({time.time()-t:.0f}s)")
|
||||
@@ -513,14 +524,15 @@ def main() -> None:
|
||||
print(" lette insieme: la leva alta non aumenta la probabilita' di farcela, aumenta la FRETTA.")
|
||||
|
||||
# ---------------------------------------------------------------- D. i null
|
||||
tick("C. griglia + leva")
|
||||
hr("D. NULL 1 — l'argmax e' speciale, o e' solo il MASSIMO DI 45 ESTRAZIONI?")
|
||||
rw = random_weights(300, CRYPTO_PROP, seed=8223)
|
||||
rw = random_weights(200, CRYPTO_PROP, seed=8223)
|
||||
lev_star = float(best["lev"])
|
||||
Jr = np.array([objective(w, lev_star, "HYRO", ev_idx, fu_idx)["J"] for w in rw])
|
||||
pctl = float((Jr < best["J"]).mean() * 100.0)
|
||||
bestofk = 100.0 * len(grid) / (len(grid) + 1)
|
||||
print(f" J dell'argmax = {best['J']:.3f} alla leva {lev_star:g}")
|
||||
print(f" null: 300 vettori di peso casuali uniformi sul simplesso (stesso campionatore di")
|
||||
print(f" null: {len(rw)} vettori di peso casuali uniformi sul simplesso (stesso campionatore di")
|
||||
print(f" `portfolio.weights_tilt_null`), valutati alla STESSA leva")
|
||||
print(f" mediana {np.median(Jr):.3f} | p90 {np.percentile(Jr, 90):.3f} | "
|
||||
f"max {Jr.max():.3f} | frazione sopra il LIVE 75/25 = {(Jr > best_live['J']).mean():.0%}")
|
||||
@@ -531,6 +543,7 @@ def main() -> None:
|
||||
print(f" (la meta' bassa del null e' pero' informativa: il LIVE 75/25 e' battuto dal "
|
||||
f"{(Jr > best_live['J']).mean():.0%} dei pesi CASUALI)")
|
||||
|
||||
tick("D. null casuale")
|
||||
hr("D-bis. NULL 2 — selezione IN-SAMPLE-ONLY (la scelta guardando J e' selezione come ogni altra)")
|
||||
half = n_hist // 2
|
||||
R_all = {}
|
||||
@@ -569,6 +582,7 @@ def main() -> None:
|
||||
print(" -> se rho e' alto la mappa e' stabile e la scelta si trasferisce; se e' ~0 l'argmax")
|
||||
print(" e' rumore e la decisione va presa sul MECCANISMO (la leva), non sulla cella.")
|
||||
|
||||
tick("D-bis. selezione in-sample")
|
||||
hr("D-ter. NULL DEL DE-LEVERING — la leva e' GIA' dentro l'ottimizzazione, ma va detto")
|
||||
print(" Il null classico ('esiste k<1 che da' lo stesso DD con Sharpe migliore?') qui e'")
|
||||
print(" strutturale: ogni configurazione e' valutata a OTTO leve, quindi nessuna puo' vincere")
|
||||
@@ -586,8 +600,11 @@ def main() -> None:
|
||||
print(" Ri-fare i 24x23x10 offset col recon MTM orario e' fuori budget. Si applica il fattore")
|
||||
print(" di drift MISURATO il 26/07 (x0.89 sul libro live) e si controlla cio' che conta: se")
|
||||
print(" l'ORDINE fra le configurazioni sopravvive alla banda 0.60 (stima 25/07, ri-misurata")
|
||||
print(" troppo severa) - 1.00 (nessuna correzione).")
|
||||
print(" troppo severa) - 1.00 (nessuna correzione). L'argmax e' ri-cercato sulle 12 celle-peso")
|
||||
print(" migliori (non su tutte le 45: costo), il che e' conservativo per la STABILITA'.")
|
||||
cands = [best["w"], best_sh["w"], best_live["w"]]
|
||||
top_keys = list(G3.sort_values("J", ascending=False).drop_duplicates("w")["w"].head(12))
|
||||
top_w = [dict(zip(CRYPTO_PROP, [int(x) / 100 for x in k.split("/")])) for k in top_keys]
|
||||
print(f"\n {'fattore':>9} " + " ".join(f"{('J ' + c):>14}" for c in dict.fromkeys(cands))
|
||||
+ f" {'argmax pesi':>14} {'argmax leva':>11}")
|
||||
for fct in (1.00, 0.89, 0.60):
|
||||
@@ -596,12 +613,13 @@ def main() -> None:
|
||||
wc = dict(zip(CRYPTO_PROP, [int(x) / 100 for x in c.split("/")]))
|
||||
sub = [objective(wc, lv, "HYRO", ev_idx, fu_idx, factor=fct)["J"] for lv in LEVS]
|
||||
cells.append(max(sub))
|
||||
g = grid_scan(grid, LEVS, "HYRO", ev_idx, fu_idx, factor=fct)
|
||||
g = grid_scan(top_w, LEVS, "HYRO", ev_idx, fu_idx, factor=fct)
|
||||
b = g.loc[g["J"].idxmax()]
|
||||
print(f" {fct:>9.2f} " + " ".join(f"{v:>14.3f}" for v in cells)
|
||||
+ f" {b['w']:>14} {b['lev']:>11.3f}")
|
||||
|
||||
# ---------------------------------------------------------------- F. VRP01 / GTAA01
|
||||
tick("E. banda ancora")
|
||||
hr("F. E GLI SLEEVE 'INUTILI A $600'? — l'asimmetria del 25/07 §4 vale per XS01, non per tutti")
|
||||
print(" Su un conto funded da $100k gli sleeve fuori portata a $600 diventano ESEGUIBILI per")
|
||||
print(" TAGLIA. Ma la taglia non e' l'unico vincolo: una firm CRYPTO non lista opzioni Deribit")
|
||||
@@ -625,11 +643,12 @@ def main() -> None:
|
||||
print(" 6% verrebbe sfondata dal mark, non dal risultato.")
|
||||
|
||||
# ---------------------------------------------------------------- G. FTMO
|
||||
tick("F. VRP/GTAA")
|
||||
hr("G. LA BARRIERA CAMBIA LA RISPOSTA? — HYRO (-6% statico) vs FTMO (-10% statico)")
|
||||
print(f" {'firm':>6} {'argmax pesi':>16} {'leva':>6} {'J':>7} {'P(pass)':>8} {'P(vivo12m)':>11} "
|
||||
f"{'E[pay/a]':>10} | {'LIVE 75/25: leva':>17} {'J':>7}")
|
||||
for fm in ("HYRO", "FTMO"):
|
||||
g = grid_scan(grid, LEVS, fm, ev_idx, fu_idx)
|
||||
g = G3 if fm == "HYRO" else grid_scan(grid, LEVS, fm, ev_idx, fu_idx)
|
||||
b = g.loc[g["J"].idxmax()]
|
||||
lv2 = g[g["w"] == "75/25/0"]
|
||||
b2 = lv2.loc[lv2["J"].idxmax()]
|
||||
@@ -640,12 +659,13 @@ def main() -> None:
|
||||
print(" vincolo binding e' la regola di DD, non il capitale'), qui misurata sull'ottimo.")
|
||||
|
||||
# ---------------------------------------------------------------- H. gate ufficiale sui pesi
|
||||
tick("G. FTMO")
|
||||
hr("H. GATE UFFICIALE SUI PESI (`portfolio.weights_tilt_null`) — sullo SHARPE, come previsto")
|
||||
cols = {nm: off[nm] for nm in CRYPTO_PROP}
|
||||
try:
|
||||
res = weights_tilt_null(cols, {"TP01": 0.75, "SKH01": 0.25, "XS01": 0.0001},
|
||||
{nm: max(w_best.get(nm, 0.0), 1e-4) for nm in CRYPTO_PROP},
|
||||
floor=0.0, n=300, k_seen=len(grid) * len(LEVS))
|
||||
floor=0.0, n=200, k_seen=len(grid) * len(LEVS))
|
||||
print(f" proposta {best['w']} vs LIVE 75/25 : delta_insample {res['delta_insample']:+.4f} | "
|
||||
f"delta_hold {res['delta_hold']:+.4f} | pctl_hold {res['pctl_hold']:.1f}° "
|
||||
f"(best-of-k {res['bestofk_pctl']:.1f}°) | gate_pass = {res['gate_pass']}")
|
||||
@@ -656,6 +676,7 @@ def main() -> None:
|
||||
print(" e' il libro live e non passa da questo gate.")
|
||||
|
||||
# ---------------------------------------------------------------- I. tavola operativa
|
||||
tick("H. gate pesi")
|
||||
hr("I. TAVOLA OPERATIVA — le 3 configurazioni migliori, e quanti conti servono per 50 EUR/g")
|
||||
top = G3.sort_values("J", ascending=False).drop_duplicates("w").head(3)
|
||||
rows_ops = []
|
||||
@@ -681,6 +702,7 @@ def main() -> None:
|
||||
print(f" -> al massimo {cap_tot/100_000:.0f} conti da $100k. Se ne servono di piu', servono "
|
||||
f"ALTRE FIRM (e ognuna aggiunge rischio di controparte).")
|
||||
|
||||
tick("I. tavola operativa")
|
||||
hr("I-bis. LA SCALA DI CONTI — macchineria del 25/07 (`r0725_prop_coupled.simulate`), "
|
||||
"de-luck portato a x0.89")
|
||||
print(f" 36 mesi, EUR {pl.START_EUR:.0f} di cassa iniziale, max {pl.MAX_CONCURRENT} conti, "
|
||||
@@ -698,7 +720,7 @@ def main() -> None:
|
||||
f"{'P(>=50/g)':>10} {'P(zero)':>8}")
|
||||
for pname, (cfgs, slots) in pols.items():
|
||||
for lev in (float(best["lev"]), 0.75):
|
||||
r = pc.simulate(cfgs, slots, n_paths=2000, lev=lev, seed=20260822,
|
||||
r = pc.simulate(cfgs, slots, n_paths=1500, lev=lev, seed=20260822,
|
||||
lens="coupled", start=START_WIN)
|
||||
print(f" {pname:>44} {lev:>6.3f} {r['med']:>10.2f} {r['p90']:>8.2f} "
|
||||
f"{r['p_ge10']:>10.1%} {r['p_target']:>10.1%} {r['p_zero']:>8.1%}")
|
||||
|
||||
Reference in New Issue
Block a user