refactor: riorganizzazione script — Strategy ABC, folder strategies/waste/analysis
- src/strategies/base.py: Strategy ABC con Signal, BacktestResult, YearlyStats - src/strategies/indicators.py: keltner_ratio, detect_squeezes, ema, atr, rv, corr - scripts/strategies/: SQ01-SQ04 (squeeze puro/filtri), ML01 (squeeze+GBM) - scripts/waste/: W01-W22 script scartati + REF originali - scripts/analysis/: compare, best_yearly, final_report, paper_status - CLAUDE.md aggiornato con nuova struttura e tabella strategie Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
"""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}")
|
||||
Reference in New Issue
Block a user