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:
Adriano Dal Pastro
2026-08-22 17:10:03 +00:00
parent 3b206c8e6d
commit 14c1a75a7e
8 changed files with 275 additions and 89 deletions
+37 -21
View File
@@ -375,11 +375,9 @@ def main() -> None:
print(" su GTAA/ADJUSTED_LAST). NON l'ho inseguito: tutte le mie conclusioni sono confronti")
print(" RELATIVI dentro UNA lente. Ma tocca un numero pubblicato e il gate del 23/10.")
print(f" XSR01 dal 25/07 a oggi (forward, {len(xsr_fwd)} barre, stessa lente): "
f"ritorno {(np.prod(1+xsr_fwd.values)-1)*100:+.2f}% "
f"Sharpe {sh(xsr_fwd) if len(xsr_fwd) > 30 else float('nan'):.2f} "
f"{'(campione troppo corto per uno Sharpe: si legge il ritorno)' if len(xsr_fwd) <= 30 else ''}")
print(" ^ la differenza fra 1.82 e il numero FULL di oggi NON e' una lente diversa (le repliche")
print(" sono bit-exact): e' un mese in piu' di dati. Il gate pre-registrato resta il 23/10.")
f"ritorno {(np.prod(1+xsr_fwd.values)-1)*100:+.2f}%"
+ (" (campione troppo corto per uno Sharpe: si legge il ritorno)"
if len(xsr_fwd) <= 30 else f" Sharpe {sh(xsr_fwd):.2f}"))
print(f" finestra MAJ19: {C19.index[0].date()} -> {C19.index[-1].date()} ({len(C19)} barre); "
f"in-sample = pre {HOLDOUT.date()} = {int((C19.index < HOLDOUT).sum())} barre")
cov51 = np.isfinite(C51.values).sum(axis=1)
@@ -535,24 +533,42 @@ def main() -> None:
f"{dg['med_ticket']:>11.0f}{dg['orders_py']:>10.0f}"
f"{trk:>12.2f}{'' if trk < 0.8 else ' '}")
print()
print(f" {'cella':<13}{'|dw| mediano al ribil.':>24}{'capitale sleeve minimo':>25}{' = book al 15%':>17}")
print(f" {'cella':<13}{'|dw| RIBILANCIO':>18}{'|dw| deriva':>14}{'cap sleeve min':>17}"
f"{'= book al 15%':>16}")
capmin_k5 = None
for nm in ("MAJ19 k=1", "MAJ19 k=2", "MAJ19 k=3", "MAJ19 k=5*"):
cell = xs_cells[nm]
Weff = xs_effective_weights(cell["W"], cell["scale"])
chg = np.abs(np.diff(cell["W"], axis=0)).sum(axis=1) > 1e-12 # giorni di ribilanciamento
rows = np.where(chg)[0] + 1
d = np.abs(np.diff(Weff, axis=0))[rows - 1]
med = float(np.median(d[d > 1e-9])) if np.any(d > 1e-9) else float("nan")
capmin = MIN_ORDER_HL / med if med > 0 else float("nan")
print(f" {'XS ' + nm.split()[1]:<13}{med:>24.4f}{capmin:>24.0f}$"
f"{capmin/0.15:>16.0f}$")
print(" ^ ARITMETICA: il ticket di un ribilanciamento e' |dw|*capitale_sleeve. Il min-order")
print(" morde sotto il 'capitale sleeve minimo'. Per il CANONICO k=5 quel numero e' ~150$")
print(" di sleeve = ~1.000$ di book al 15%, non 20.000$. La soglia dichiarata dal progetto")
print(" veniva da una diagnostica di TURNOVER (quota di turnover sopra il min-order), che a")
print(" capitale piccolo e' bassa perche' salta la deriva GIORNALIERA del vol-target — la")
print(" quale non porta segnale. Stessa lezione gia' imparata su TP01 ('a $600 il min-order")
print(" $5 e' GIA' la banda ottimale'), non applicata a XS01.")
W = cell["W"]
Weff = xs_effective_weights(W, cell["scale"])
dW = np.abs(np.diff(W, axis=0)) # W cambia alla riga j+1
dE = np.abs(np.diff(Weff, axis=0)) # Weff[j+1]-Weff[j], Weff[i]=W[i-1]*scale[i]
# il cambio di W alla riga j+1 si vede su Weff alla riga j+2 -> confine da rispettare
rb, dr = [], []
for j in range(len(dW)):
if j + 1 >= len(dE):
break
m = dW[j] > 1e-12
if m.any():
rb.extend(dE[j + 1][m].tolist()) # gambe DAVVERO ribilanciate
dr.extend(dE[j + 1][~m][dE[j + 1][~m] > 1e-12].tolist())
else:
dr.extend(dE[j + 1][dE[j + 1] > 1e-12].tolist())
med_rb = float(np.median(rb)) if rb else float("nan")
med_dr = float(np.median(dr)) if dr else float("nan")
capmin = MIN_ORDER_HL / med_rb if med_rb > 0 else float("nan")
if nm.startswith("MAJ19 k=5"):
capmin_k5 = capmin
print(f" {'XS ' + nm.split()[1]:<13}{med_rb:>18.4f}{med_dr:>14.4f}{capmin:>16.0f}$"
f"{capmin/0.15:>15.0f}$")
print(" ^ ARITMETICA: il ticket vale |dw|*capitale_sleeve, e le due colonne |dw| sono ordini")
print(" di grandezza diversi: il RIBILANCIO (ogni 10 giorni, porta il segnale) e la DERIVA")
print(" giornaliera del vol-target (non porta segnale). Il min-order taglia la seconda molto")
print(" prima della prima, ed e' per questo che la quota di turnover eseguito puo' essere del")
print(f" 4% con haircut ~0. Per il CANONICO k=5 il ribilancio smette di passare sotto "
f"~{capmin_k5:.0f}$")
print(f" di sleeve = ~{capmin_k5/0.15:.0f}$ di book al 15%. La soglia '$20k' del progetto viene da")
print(" una diagnostica di TURNOVER, non di Sharpe — stessa lezione gia' imparata su TP01")
print(" ('a $600 il min-order $5 e' GIA' la banda ottimale'), mai applicata a XS01.")
print()
print(" 'gambe eseg' = quota delle VARIAZIONI di posizione sopra il min-order (la diagnostica")
print(" su cui si basa la soglia '$20k'). 'haircut' = quanto ne costa in Sharpe. Sono cose")