"""S2-05: Gap fade + overnight reversal. Crypto non ha gap di apertura classici, ma ha "gap di sessione": - Asia open (00 UTC): tende a continuare il trend USA precedente - EU open (07 UTC): spesso corregge eccessi notturni - USA open (13-14 UTC): alta volatilità, breakout o reversal Strategia: fai fade dell'overextension al cambio sessione. Se il prezzo ha fatto >1.5% nella sessione precedente, aspettati reversal. """ from __future__ import annotations import sys sys.path.insert(0, ".") import numpy as np import pandas as pd from src.data.downloader import load_data FEE = 0.001 INITIAL = 1000 LEVERAGE = 3 def run_gap_fade(asset, tf="1h"): print(f"\n{'#'*60}") print(f" {asset} {tf} — GAP FADE / SESSION REVERSAL") print(f"{'#'*60}") df = load_data(asset, tf) close = df["close"].values high = df["high"].values low = df["low"].values n = len(close) split = int(n * 0.7) timestamps = pd.to_datetime(df["timestamp"], unit="ms", utc=True) hours = timestamps.dt.hour.values session_opens = { "asia": 0, "eu": 7, "usa": 14, } configs = [ # (session_name, lookback_hours, entry_thr, hold_hours, stop_pct, name) ("eu", 7, 0.015, 4, 0.012, "eu_fade_1.5pct"), ("eu", 7, 0.02, 4, 0.015, "eu_fade_2pct"), ("eu", 7, 0.01, 6, 0.01, "eu_fade_1pct_6h"), ("usa", 7, 0.015, 4, 0.012, "usa_fade_1.5pct"), ("usa", 7, 0.02, 4, 0.015, "usa_fade_2pct"), ("asia", 8, 0.02, 6, 0.015, "asia_fade_2pct"), ("eu", 7, 0.025, 3, 0.015, "eu_fade_2.5pct_fast"), ("usa", 6, 0.015, 3, 0.01, "usa_fade_fast"), ] for session, lookback, entry_thr, hold, stop, name in configs: capital = float(INITIAL) correct = 0 total = 0 daily_trades = {} session_hour = session_opens[session] for i in range(max(split, lookback + 1), n - hold): if hours[i] != session_hour: continue day = timestamps.iloc[i].strftime("%Y-%m-%d") if daily_trades.get(day, 0) >= 1: continue prev_ret = (close[i] - close[i - lookback]) / close[i - lookback] direction = None if prev_ret > entry_thr: direction = "short" # fade the rally elif prev_ret < -entry_thr: direction = "long" # fade the dump if direction is None: continue entry = close[i] exit_price = close[min(i + hold, n - 1)] for j in range(i + 1, min(i + hold + 1, n)): if direction == "long": if (close[j] - entry) / entry >= stop * 2: exit_price = close[j] break if (entry - close[j]) / entry >= stop: exit_price = close[j] break else: if (entry - close[j]) / entry >= stop * 2: exit_price = close[j] break if (close[j] - entry) / entry >= stop: exit_price = close[j] break exit_price = close[j] if direction == "long": trade_ret = (exit_price - entry) / entry else: trade_ret = (entry - exit_price) / entry net = trade_ret * LEVERAGE - FEE * 2 * LEVERAGE capital += capital * 0.2 * net capital = max(capital, 0) total += 1 if trade_ret > 0: correct += 1 daily_trades[day] = daily_trades.get(day, 0) + 1 if total < 15: continue acc = correct / total * 100 ret = (capital - INITIAL) / INITIAL * 100 test_days = (n - split) / 24 test_years = test_days / 365.25 ann = ((capital / INITIAL) ** (1 / test_years) - 1) * 100 if test_years > 0 and capital > 0 else -100 dpnl = (capital - INITIAL) / test_days if test_days > 0 else 0 days_active = len(daily_trades) tag = "✅" if acc >= 58 and ann >= 30 else "" print(f" {name:25s}: trades={total:4d} acc={acc:.1f}% ret={ret:+.1f}% ann={ann:+.1f}% €/day={dpnl:.2f} active={days_active} {tag}") for asset in ["ETH", "BTC"]: run_gap_fade(asset)