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:
@@ -282,6 +282,17 @@ def leverage_series(base: pd.Series, tv: float | None, w: int, cap: float,
|
||||
return L.fillna(1.0)
|
||||
|
||||
|
||||
def activity_L(base: pd.Series, w: int, cap: float) -> pd.Series:
|
||||
"""L_t da sola ATTIVITA': quante barre ATTIVE (trade chiusi) negli ultimi w giorni, riferita
|
||||
alla mediana espandente causale. Serve a separare i DUE canali dentro `rv` calcolata su tutte
|
||||
le barre di uno sleeve all'88% di zeri: la MAGNITUDINE degli esiti e la FREQUENZA dei trade.
|
||||
Se il guadagno vive qui, la variante non e' 'vol-target' ma un filtro di frequenza."""
|
||||
act = (base != 0.0).astype(float)
|
||||
cnt = act.rolling(w, min_periods=max(5, w // 3)).sum().shift(1).replace(0.0, np.nan)
|
||||
ref = cnt.expanding(min_periods=VT_WARM).median()
|
||||
return (ref / cnt).clip(lower=1.0 / cap, upper=cap).fillna(1.0)
|
||||
|
||||
|
||||
def sizes_from_L(ex: dict, L: pd.Series) -> np.ndarray:
|
||||
"""La size di ogni trade = L al giorno d'INGRESSO. E' la versione CAUSALE del vol-target su
|
||||
uno sleeve a equity a gradino: L al giorno di CHIUSURA non era nota all'ingresso."""
|
||||
@@ -389,11 +400,12 @@ def main() -> None:
|
||||
for w in (30, 90, 180):
|
||||
for cp in (2.0, 3.0):
|
||||
BVT[f"BOOKVT w{w} cap{cp:.0f}"] = dict(tv=0.10, w=w, cap=cp)
|
||||
ACT = {f"ACT w{w}": dict(w=w, cap=3.0) for w in (90, 180)}
|
||||
NAIVE = {f"NAIVE-VT w{w}": dict(tv=0.20, w=w, cap=3.0, active_only=False) for w in (30, 90)}
|
||||
ALL = list(SZ) + list(VTL) + list(BVT) + list(NAIVE)
|
||||
ALL = list(SZ) + list(VTL) + list(ACT) + list(BVT) + list(NAIVE)
|
||||
print(f"\n GRIGLIA DICHIARATA: {len(SZ)} size per-trade + {len(VTL)} vol-target di gamba + "
|
||||
f"{len(BVT)} vol-target di libro + {len(NAIVE)} controlli non-causali = {len(ALL)} celle "
|
||||
f"(+ baseline).")
|
||||
f"{len(ACT)} filtro-attivita' + {len(BVT)} vol-target di libro + {len(NAIVE)} controlli "
|
||||
f"non-causali = {len(ALL)} celle (+ baseline).")
|
||||
|
||||
# ------------------------------------------------------------------ serie
|
||||
TP = A.tp01_baseline_daily()
|
||||
@@ -420,6 +432,16 @@ def main() -> None:
|
||||
MTP[nm][o] = 1.0
|
||||
if o == offs[0]:
|
||||
SIZEMED[nm] = float(np.median(np.concatenate(list(sz.values()))))
|
||||
for nm, sp in ACT.items():
|
||||
SLE[nm], BK[nm], MTP[nm] = {}, {}, {}
|
||||
for o in offs:
|
||||
sz = {a: sizes_from_L(EX[o][a], activity_L(EX[o][a]["base_daily"], **sp))
|
||||
for a in ASSETS}
|
||||
SLE[nm][o] = leg_daily(EX[o], sz)
|
||||
BK[nm][o] = book(TP, SLE[nm][o])
|
||||
MTP[nm][o] = 1.0
|
||||
if o == offs[0]:
|
||||
SIZEMED[nm] = float(np.median(np.concatenate(list(sz.values()))))
|
||||
for nm, sp in BVT.items():
|
||||
SLE[nm], BK[nm], MTP[nm] = {}, {}, {}
|
||||
for o in offs:
|
||||
@@ -467,7 +489,7 @@ def main() -> None:
|
||||
f"{maxdd(s)*100:>8.1f}%{maxdd(iv)*100:>9.1f}%{cagr(iv)*100:>8.1f}%{m}")
|
||||
|
||||
prow("BASELINE (size fissa)", base_s, 1.00)
|
||||
for nm in list(SZ) + list(VTL) + list(NAIVE):
|
||||
for nm in list(SZ) + list(VTL) + list(ACT) + list(NAIVE):
|
||||
prow(nm, SLE[nm][can], SIZEMED.get(nm))
|
||||
|
||||
# ------------------------------------------------------------------ §3 libro, banda appaiata
|
||||
@@ -592,7 +614,7 @@ def main() -> None:
|
||||
print("\n" + "-" * 112)
|
||||
print(" 7. GATE — marginal_vs_tp01 / implausible_sharpe / weights_tilt_null")
|
||||
print("-" * 112)
|
||||
best_sleeve = max([n for n in list(SZ) + list(VTL)], key=lambda x: is_med[x])
|
||||
best_sleeve = max([n for n in list(SZ) + list(VTL) + list(ACT)], key=lambda x: is_med[x])
|
||||
for nm in ("BASE", best_sleeve):
|
||||
m = A.marginal_vs_tp01(SLE[nm][can])
|
||||
b25 = m.get("blends", {}).get("w25", {})
|
||||
@@ -606,7 +628,7 @@ def main() -> None:
|
||||
f"attive {imp.get('active_frac', 0)*100:.1f}% perdite/attive "
|
||||
f"{imp.get('loss_frac', 0)*100:.1f}% Calmar {imp.get('calmar', 0):.1f}")
|
||||
cols = {"TP01": TP, "SKH01": SLE["BASE"][can]}
|
||||
for nm in [best, best_sleeve] + list(BVT)[:2]:
|
||||
for nm in dict.fromkeys([best, best_sleeve] + list(BVT)[:2]):
|
||||
m_s = vol(SLE[nm][can]) / vol(SLE["BASE"][can])
|
||||
wp = {"TP01": W_TP * MTP[nm][can], "SKH01": W_SKH * m_s}
|
||||
g = weights_tilt_null(cols, {"TP01": W_TP, "SKH01": W_SKH}, wp,
|
||||
@@ -630,7 +652,7 @@ def main() -> None:
|
||||
no = ns = 0
|
||||
for a in ASSETS:
|
||||
df1h = TO.get1h(a)
|
||||
tgt = netted_target(a, EX[can][a], nm, SZ, VTL, BVT, SLE, TP, can, df1h)
|
||||
tgt = netted_target(a, EX[can][a], nm, SZ, VTL, ACT, BVT, SLE, TP, can, df1h)
|
||||
ev = A.eval_weights_smallcap(df1h, tgt, capital=CAPITAL, min_order=MIN_ORDER)
|
||||
tm += 0.5 * ev["modeled"]["sharpe"]
|
||||
tr += 0.5 * ev["realistic"]["sharpe"]
|
||||
@@ -647,8 +669,8 @@ def main() -> None:
|
||||
print(" un contributo positivo si scompone per ANNO prima di crederci) + deriva della size")
|
||||
print("-" * 112)
|
||||
yrs = sorted({int(y) for y in BK["BASE"][can].index.year})
|
||||
top = [best, best_iso] + [max(BVT, key=lambda x: RES[x]["iF"])]
|
||||
top = list(dict.fromkeys(top))
|
||||
top = list(dict.fromkeys([best, best_iso, max(ACT, key=lambda x: RES[x]["iF"]),
|
||||
max(BVT, key=lambda x: RES[x]["iF"])]))
|
||||
print(f" {'variante':<28}" + "".join(f"{y:>8}" for y in yrs))
|
||||
for nm in top:
|
||||
cells = []
|
||||
@@ -668,11 +690,12 @@ def main() -> None:
|
||||
if nm in SZ:
|
||||
szs = np.concatenate([SZ[nm](EX[can][a]) for a in ASSETS])
|
||||
dys = pd.DatetimeIndex(np.concatenate([EX[can][a]["day_ent"].values for a in ASSETS]))
|
||||
elif nm in VTL:
|
||||
elif nm in VTL or nm in ACT:
|
||||
mk = ((lambda b: leverage_series(b, **VTL[nm])) if nm in VTL
|
||||
else (lambda b: activity_L(b, **ACT[nm])))
|
||||
szs, dys = [], []
|
||||
for a in ASSETS:
|
||||
szs.append(sizes_from_L(EX[can][a],
|
||||
leverage_series(EX[can][a]["base_daily"], **VTL[nm])))
|
||||
szs.append(sizes_from_L(EX[can][a], mk(EX[can][a]["base_daily"])))
|
||||
dys.append(EX[can][a]["day_ent"].values)
|
||||
szs = np.concatenate(szs); dys = pd.DatetimeIndex(np.concatenate(dys))
|
||||
else:
|
||||
@@ -697,7 +720,7 @@ def main() -> None:
|
||||
print("=" * 112)
|
||||
|
||||
|
||||
def netted_target(asset: str, ex: dict, nm, SZ, VTL, BVT, SLE, TP, can, df1h) -> np.ndarray:
|
||||
def netted_target(asset: str, ex: dict, nm, SZ, VTL, ACT, BVT, SLE, TP, can, df1h) -> np.ndarray:
|
||||
"""Peso NETTO per asset sul grid 1h: 0.75*target TP01 + 0.25*posizione SKH (dir*size), poi
|
||||
l'eventuale leva di libro sulla gamba TP01. E' cio' che il cron manda a Deribit come UNA
|
||||
posizione netta sullo stesso strumento."""
|
||||
@@ -711,6 +734,8 @@ def netted_target(asset: str, ex: dict, nm, SZ, VTL, BVT, SLE, TP, can, df1h) ->
|
||||
sizes = SZ[nm](ex)
|
||||
elif nm in VTL:
|
||||
sizes = sizes_from_L(ex, leverage_series(ex["base_daily"], **VTL[nm]))
|
||||
elif nm in ACT:
|
||||
sizes = sizes_from_L(ex, activity_L(ex["base_daily"], **ACT[nm]))
|
||||
else: # BOOKVT
|
||||
L = leverage_series(book(TP, SLE["BASE"][can]), BVT[nm]["tv"], BVT[nm]["w"], BVT[nm]["cap"])
|
||||
sizes = sizes_from_L(ex, L)
|
||||
|
||||
Reference in New Issue
Block a user