#!/usr/bin/env python3 """ Quick comparison: v8 "Sticky Trend" vs v9 "MA Gap" baseline performance. Runs both indicators with default parameters on the same dataset. """ from pathlib import Path import pandas as pd from backtester.data_loader import load_csv from backtester.simulator import simulate_money_line, TradeConfig, SimulationResult from backtester.indicators.money_line import money_line_signals, MoneyLineInputs from backtester.indicators.money_line_v8 import money_line_v8_signals, MoneyLineV8Inputs # Load data print("Loading SOLUSDT 5m data (Aug 1 - Nov 28, 2025)...") data_slice = load_csv(Path("data/solusdt_5m.csv"), "SOLUSDT", "5m") df = data_slice.data print(f"Loaded {len(df)} candles\n") # Run v9 baseline print("=" * 60) print("v9 'MA Gap' - Baseline (Default Parameters)") print("=" * 60) v9_inputs = MoneyLineInputs( flip_threshold_percent=0.6, cooldown_bars=2, ma_gap_threshold=0.35, momentum_min_adx=23.0, momentum_long_max_pos=70.0, momentum_short_min_pos=25.0, ) v9_result = simulate_money_line(df, "SOLUSDT", v9_inputs) v9_trades = v9_result.trades print(f"Generated {len(v9_trades)} trades") wins_v9 = sum(1 for t in v9_trades if t.realized_pnl > 0) losses_v9 = sum(1 for t in v9_trades if t.realized_pnl <= 0) gross_wins_v9 = sum(t.realized_pnl for t in v9_trades if t.realized_pnl > 0) gross_losses_v9 = abs(sum(t.realized_pnl for t in v9_trades if t.realized_pnl <= 0)) pf_v9 = gross_wins_v9 / gross_losses_v9 if gross_losses_v9 > 0 else 0.0 wr_v9 = (wins_v9 / len(v9_trades) * 100) if v9_trades else 0.0 avg_win_v9 = (gross_wins_v9 / wins_v9) if wins_v9 > 0 else 0.0 avg_loss_v9 = (gross_losses_v9 / losses_v9) if losses_v9 > 0 else 0.0 print(f"\nResults:") print(f" Total P&L: ${v9_result.total_pnl:.2f}") print(f" Total Trades: {len(v9_trades)}") print(f" Win Rate: {wr_v9:.2f}%") print(f" Profit Factor: {pf_v9:.3f}") print(f" Max Drawdown: ${v9_result.max_drawdown:.2f}") print(f" Avg Win: ${avg_win_v9:.2f}") print(f" Avg Loss: ${avg_loss_v9:.2f}") # Run v8 baseline print("\n" + "=" * 60) print("v8 'Sticky Trend' - Baseline (Default Parameters)") print("=" * 60) # Create custom simulator for v8 since it doesn't use MoneyLineInputs v8_signals = money_line_v8_signals(df, MoneyLineV8Inputs()) print(f"Generated {len(v8_signals)} signals") # Manually simulate v8 trades using same logic as v9 from backtester.simulator import _simulate_trade, TradeConfig v8_trades = [] config = TradeConfig() index_positions = {ts: idx for idx, ts in enumerate(df.index)} next_available = 0 for sig in v8_signals: if sig.timestamp not in index_positions: continue idx = index_positions[sig.timestamp] if idx < next_available: continue # Convert v8 signal to v9 signal format for simulator from backtester.indicators.money_line import MoneyLineSignal ml_sig = MoneyLineSignal( timestamp=sig.timestamp, direction=sig.direction, entry_price=sig.entry_price, adx=sig.adx, atr=sig.atr, rsi=sig.rsi, volume_ratio=sig.volume_ratio, price_position=sig.price_position, signal_type="primary" ) trade = _simulate_trade(df, idx, ml_sig, "SOLUSDT", config) if trade: v8_trades.append(trade) next_available = trade._exit_index v8_total_pnl = sum(t.realized_pnl for t in v8_trades) v8_max_dd = 0.0 equity = 0.0 peak = 0.0 for t in v8_trades: equity += t.realized_pnl peak = max(peak, equity) v8_max_dd = min(v8_max_dd, equity - peak) wins_v8 = sum(1 for t in v8_trades if t.realized_pnl > 0) losses_v8 = sum(1 for t in v8_trades if t.realized_pnl <= 0) gross_wins_v8 = sum(t.realized_pnl for t in v8_trades if t.realized_pnl > 0) gross_losses_v8 = abs(sum(t.realized_pnl for t in v8_trades if t.realized_pnl <= 0)) pf_v8 = gross_wins_v8 / gross_losses_v8 if gross_losses_v8 > 0 else 0.0 wr_v8 = (wins_v8 / len(v8_trades) * 100) if v8_trades else 0.0 avg_win_v8 = (gross_wins_v8 / wins_v8) if wins_v8 > 0 else 0.0 avg_loss_v8 = (gross_losses_v8 / losses_v8) if losses_v8 > 0 else 0.0 print(f"\nResults:") print(f" Total P&L: ${v8_total_pnl:.2f}") print(f" Total Trades: {len(v8_trades)}") print(f" Win Rate: {wr_v8:.2f}%") print(f" Profit Factor: {pf_v8:.3f}") print(f" Max Drawdown: ${v8_max_dd:.2f}") print(f" Avg Win: ${avg_win_v8:.2f}") print(f" Avg Loss: ${avg_loss_v8:.2f}") # Comparison print("\n" + "=" * 60) print("HEAD-TO-HEAD COMPARISON") print("=" * 60) pnl_diff = v9_result.total_pnl - v8_total_pnl pnl_diff_pct = (pnl_diff / abs(v8_total_pnl)) * 100 if v8_total_pnl != 0 else 0 trade_diff = len(v9_trades) - len(v8_trades) wr_diff = wr_v9 - wr_v8 print(f"P&L: v9 ${v9_result.total_pnl:.2f} vs v8 ${v8_total_pnl:.2f} (Δ ${pnl_diff:+.2f}, {pnl_diff_pct:+.1f}%)") print(f"Trades: v9 {len(v9_trades)} vs v8 {len(v8_trades)} (Δ {trade_diff:+d})") print(f"Win Rate: v9 {wr_v9:.2f}% vs v8 {wr_v8:.2f}% (Δ {wr_diff:+.2f}%)") print(f"Profit Factor: v9 {pf_v9:.3f} vs v8 {pf_v8:.3f}") print("\n" + "=" * 60) if pnl_diff > 0: print(f"WINNER: v9 'MA Gap' by ${pnl_diff:.2f} ({pnl_diff_pct:.1f}%)") print("v9's faster entries + MA gap context outperform v8's conservative approach") else: print(f"WINNER: v8 'Sticky Trend' by ${-pnl_diff:.2f} ({-pnl_diff_pct:.1f}%)") print("v8's conservative confirmation bars outperform v9's speed") print("=" * 60)