"""Strategia 3: Fourier decomposition e proiezione. Ispirata al paper Pythagoras Trading Prediction. Idea: scomponi il prezzo in componenti sinusoidali via FFT, ricostruisci con le N componenti più forti, proietta nel futuro. """ 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 from src.backtest.engine import run_backtest print("=" * 60) print(" STRATEGIA 3: FOURIER PROJECTION — BTC 1H") print("=" * 60) df = load_data("BTC", "1h") close = df["close"].values n_total = len(close) WINDOW = 588 # dal paper: 588 candele per l'indicatore H-C N_COMPONENTS = 25 # dal paper: 25 linee verticali LOOKAHEAD = 6 STEP = 6 split_idx = int(n_total * 0.7) def fourier_project(series: np.ndarray, n_components: int, ahead: int) -> np.ndarray: """Ricostruisci serie con top-N componenti Fourier e proietta avanti.""" n = len(series) detrended = series - np.linspace(series[0], series[-1], n) fft_vals = np.fft.fft(detrended) freqs = np.fft.fftfreq(n) magnitudes = np.abs(fft_vals) magnitudes[0] = 0 top_indices = np.argsort(magnitudes)[-n_components * 2:] fft_filtered = np.zeros_like(fft_vals) fft_filtered[top_indices] = fft_vals[top_indices] t_extended = np.arange(n + ahead) reconstruction = np.zeros(n + ahead) for idx in top_indices: amp = np.abs(fft_vals[idx]) / n phase = np.angle(fft_vals[idx]) freq = freqs[idx] reconstruction += amp * np.cos(2 * np.pi * freq * t_extended / 1 + phase) trend_slope = (series[-1] - series[0]) / n trend_extended = series[0] + trend_slope * t_extended reconstruction += trend_extended return reconstruction print(f"\nParametri: window={WINDOW}, components={N_COMPONENTS}, lookahead={LOOKAHEAD}") print(f"Train: 0→{split_idx}, Test: {split_idx}→{n_total}") signals = pd.Series(0, index=df.index) accuracies = [] test_range = range(max(split_idx, WINDOW), n_total - LOOKAHEAD, STEP) total_steps = len(list(test_range)) print(f"Valutazione: {total_steps} punti (step={STEP})...") for count, i in enumerate(test_range): if count % 500 == 0: print(f" Progresso: {count}/{total_steps} ({count/total_steps*100:.0f}%)") window_data = close[i - WINDOW : i] projected = fourier_project(window_data, N_COMPONENTS, LOOKAHEAD) current_price = close[i - 1] projected_price = projected[-1] change_pct = (projected_price - current_price) / current_price if change_pct > 0.005: signals.iloc[i] = 1 elif change_pct < -0.005: signals.iloc[i] = -1 actual_ret = (close[i + LOOKAHEAD - 1] - current_price) / current_price if signals.iloc[i] == 1: accuracies.append(1 if actual_ret > 0 else 0) elif signals.iloc[i] == -1: accuracies.append(1 if actual_ret < 0 else 0) print(f"\nSegnali generati: {(signals != 0).sum()}") print(f" Long: {(signals == 1).sum()}, Short: {(signals == -1).sum()}") if accuracies: print(f"Accuratezza direzione: {np.mean(accuracies)*100:.1f}% su {len(accuracies)} segnali") test_df = df.iloc[split_idx:].reset_index(drop=True) test_signals = signals.iloc[split_idx:].reset_index(drop=True) result = run_backtest(test_df, test_signals, initial_capital=1000, fee_pct=0.001, max_hold_candles=LOOKAHEAD) print("\nRISULTATI TEST:") for k, v in result.summary().items(): print(f" {k}: {v}") # Varianti con parametri diversi print("\n\n--- VARIANTI PARAMETRI ---") for n_comp in [5, 10, 15, 25, 50]: for window in [144, 288, 588]: sigs = pd.Series(0, index=df.index) accs = [] test_r = range(max(split_idx, window), n_total - LOOKAHEAD, STEP) for i in test_r: w = close[i - window : i] proj = fourier_project(w, n_comp, LOOKAHEAD) cp = close[i - 1] pp = proj[-1] ch = (pp - cp) / cp if ch > 0.005: sigs.iloc[i] = 1 elif ch < -0.005: sigs.iloc[i] = -1 ar = (close[i + LOOKAHEAD - 1] - cp) / cp if sigs.iloc[i] == 1: accs.append(1 if ar > 0 else 0) elif sigs.iloc[i] == -1: accs.append(1 if ar < 0 else 0) if not accs: continue t_sigs = sigs.iloc[split_idx:].reset_index(drop=True) res = run_backtest(test_df, t_sigs, initial_capital=1000, fee_pct=0.001, max_hold_candles=LOOKAHEAD) acc = np.mean(accs) * 100 print(f" W={window:3d} N={n_comp:2d} → acc={acc:.1f}% trades={res.total_trades} ret={res.total_return*100:+.1f}% sharpe={res.sharpe_ratio:.2f}")