212427ffa1
Ricerca onesta post-squeeze su 8 crypto (2018-2026), engine fee-aware con ingresso eseguibile a close[i], uscita TP/SL intrabar, OOS held-out, sweep fee. Lezione madre: shortare cripto perde OOS sistematicamente (campione net-bull) -> tutte le strategie robuste sono long-biased. Tre meccanismi distinti e complementari: - DIP01 dip-buy z-score reversion (long-only, 1h) robusto BTC/ETH/SOL - TR01 EMA 20/100 trend-following (long-only, 4h) robusto su 5/8 asset - ROT01 rotazione cross-sectional momentum sul paniere (1d) OOS +44%, param-insensitive Engine e validazione: scripts/analysis/honest_lab.py + honest_final.py (+ honest_candidates/diag/diag2/trend/rotation). Diario in docs/diary/. Onesto sull'obiettivo: €50/giorno su €1000 in pochi mesi non e' raggiungibile a rischio sano (~1825%/anno); edge reali 30-60% OOS pluriennale. Via realistica: portafoglio delle 3, leva moderata, crescita composta. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
104 lines
4.4 KiB
Python
104 lines
4.4 KiB
Python
"""Validazione FINALE delle 3 strategie oneste selezionate.
|
|
Per ciascuna: per-asset FULL/OOS/DD/anni-positivi + sweep fee (0/0.05/0.10/0.20% RT).
|
|
Tutto NETTO, ingresso eseguibile, OOS = ultimo 30%, leva 3x.
|
|
|
|
S1 DIP — long-only dip-buy z-score reversion (1h) [regime: reversione]
|
|
S2 TREND — long-only EMA 20/100 trend-following (4h) [regime: momentum singolo]
|
|
S3 ROT — rotazione cross-sectional momentum sul paniere (1d) [regime: forza relativa]
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import numpy as np
|
|
import pandas as pd
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
|
sys.path.insert(0, str(PROJECT_ROOT))
|
|
|
|
from scripts.analysis.honest_lab import atr, ema, get_df, simulate, oos_split, available_assets
|
|
from scripts.analysis.honest_trend import simulate_position, ema_dual_signal, oos as trend_oos
|
|
from scripts.analysis.honest_rotation import build_panel, simulate_rotation
|
|
|
|
FEES = [0.0, 0.0005, 0.001, 0.002]
|
|
|
|
|
|
# ---- S1 DIP ----
|
|
def dip_entries(df, n=50, z_in=2.5, sl_atr=2.5, max_bars=24):
|
|
c = df["close"].values
|
|
ma = pd.Series(c).rolling(n).mean().values
|
|
sd = pd.Series(c).rolling(n).std().values
|
|
a = atr(df, 14)
|
|
z = (c - ma) / np.where(sd == 0, np.nan, sd)
|
|
ents = []
|
|
for i in range(n + 14, len(c)):
|
|
if np.isnan(z[i]) or np.isnan(a[i]):
|
|
continue
|
|
if z[i] <= -z_in and z[i - 1] > -z_in:
|
|
ents.append({"i": i, "d": 1, "tp": ma[i], "sl": c[i] - sl_atr * a[i], "max_bars": max_bars})
|
|
return ents
|
|
|
|
|
|
def validate_dip(assets):
|
|
print("\n" + "=" * 100)
|
|
print(" S1 DIP — long-only dip-buy z-score reversion | 1h | n=50 z=2.5 sl=2.5ATR mb=24")
|
|
print("=" * 100)
|
|
print(f" {'Asset':<6s}{'Trd':>6s}{'Win%':>7s}{'FULL%':>9s}{'OOS%':>9s}{'DD%':>6s}{'Exp%':>6s}{'AnniP':>8s}"
|
|
f"{' fee-sweep OOS% (0/0.05/0.10/0.20)':<40s}")
|
|
ok = 0
|
|
for a in assets:
|
|
df = get_df(a, "1h"); ents = dip_entries(df)
|
|
if len(ents) < 30:
|
|
continue
|
|
full = simulate(ents, df); _, oe = oos_split(ents, df); oos = simulate(oe, df)
|
|
sweep = " ".join(f"{simulate(oe, df, fee_rt=f).ret:+.0f}" for f in FEES)
|
|
good = full.ret > 0 and oos.ret > 0
|
|
ok += good
|
|
print(f" {a:<6s}{full.trades:>6d}{full.win:>7.1f}{full.ret:>+9.0f}{oos.ret:>+9.0f}"
|
|
f"{full.dd:>6.0f}{full.exposure:>6.0f}{f'{full.pos_years}/{full.n_years}':>8s} [{sweep}]"
|
|
f"{' OK' if good else ''}")
|
|
print(f" -> robusto (FULL+OOS>0) su {ok}/{len(assets)} asset")
|
|
|
|
|
|
def validate_trend(assets):
|
|
print("\n" + "=" * 100)
|
|
print(" S2 TREND — long-only EMA 20/100 trend | 4h")
|
|
print("=" * 100)
|
|
print(f" {'Asset':<6s}{'Flip':>6s}{'FULL%':>9s}{'OOS%':>9s}{'DD%':>6s}{'Exp%':>6s}{'AnniP':>8s}")
|
|
ok = 0
|
|
for a in assets:
|
|
df = get_df(a, "4h"); sig = ema_dual_signal(df, 20, 100, long_only=True)
|
|
full = simulate_position(sig, df); oos = trend_oos(sig, df)
|
|
good = full["ret"] > 0 and oos["ret"] > 0
|
|
ok += good
|
|
print(f" {a:<6s}{full['flips']:>6d}{full['ret']:>+9.0f}{oos['ret']:>+9.0f}"
|
|
f"{full['dd']:>6.0f}{full['exposure']:>6.0f}{(str(full['pos_years'])+'/'+str(full['n_years'])):>8s}"
|
|
f"{' OK' if good else ''}")
|
|
print(f" -> robusto su {ok}/{len(assets)} asset")
|
|
|
|
|
|
def validate_rot(assets):
|
|
print("\n" + "=" * 100)
|
|
print(" S3 ROT — rotazione cross-sectional momentum | 1d | lb=60 top2 su tutto il paniere")
|
|
print("=" * 100)
|
|
panel = build_panel(assets, "1d")
|
|
print(f" Paniere {list(panel.columns)} {panel.shape[0]} barre {panel.index[0].date()}->{panel.index[-1].date()}")
|
|
print(f" {'fee RT':<10s}{'FULL%':>9s}{'OOS%':>9s}{'DD%':>6s}{'AnniP':>8s}")
|
|
for f in FEES:
|
|
full = simulate_rotation(panel, lookback=60, top_k=2, fee_rt=f)
|
|
oos = simulate_rotation(panel, lookback=60, top_k=2, fee_rt=f, oos_frac=0.30)
|
|
anni = str(full['pos_years']) + '/' + str(full['n_years'])
|
|
print(f" {f*100:>5.2f}%RT {full['ret']:>+9.0f}{oos['ret']:>+9.0f}{full['dd']:>6.0f}{anni:>8s}")
|
|
# per-anno alla fee reale
|
|
full = simulate_rotation(panel, lookback=60, top_k=2, fee_rt=0.001)
|
|
print(" per-anno (fee 0.10%): " + " ".join(f"{y}:{v:+.0f}%" for y, v in sorted(full["yearly"].items())))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
assets = available_assets()
|
|
print(f"VALIDAZIONE FINALE — asset disponibili: {assets}")
|
|
validate_dip(assets)
|
|
validate_trend(assets)
|
|
validate_rot(assets)
|