BitFinTrainer / scripts /multi_backtest.py
luohoa97's picture
Deploy BitNet-Transformer Trainer
d5b7ee9 verified
#!/usr/bin/env python3
"""
Multi-stock backtesting script for strategy evolution.
Tests one or more strategies across multiple symbols and timeframes.
"""
import sys
import os
from datetime import datetime, timedelta
import pandas as pd
import numpy as np
import logging
# Add project root to path
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from trading_cli.backtest.engine import BacktestEngine
from trading_cli.strategy.strategy_factory import create_trading_strategy, available_strategies
from trading_cli.data.market import fetch_ohlcv_yfinance
# Configure logging
logging.basicConfig(level=logging.WARNING)
logger = logging.getLogger(__name__)
DEFAULT_SYMBOLS = ["AAPL", "MSFT", "GOOGL", "AMZN", "TSLA", "NVDA", "META", "AMD", "COIN", "MARA"]
DEFAULT_DAYS = 365
def run_multi_backtest(symbols, strategy_ids, days=DEFAULT_DAYS, config=None):
if config is None:
config = {
"signal_buy_threshold": 0.2,
"signal_sell_threshold": -0.15,
"risk_pct": 0.02,
"stop_loss_pct": 0.05,
}
results = []
print(f"{'Symbol':<8} | {'Strategy':<15} | {'Return %':>10} | {'Sharpe':>8} | {'Win%':>6} | {'Trades':>6}")
print("-" * 70)
for symbol in symbols:
# Fetch data once per symbol
ohlcv = fetch_ohlcv_yfinance(symbol, days=days)
if ohlcv.empty:
print(f"Failed to fetch data for {symbol}")
continue
for strategy_id in strategy_ids:
# Create strategy
strat_config = config.copy()
strat_config["strategy_id"] = strategy_id
strategy = create_trading_strategy(strat_config)
# Run backtest
engine = BacktestEngine(
config=strat_config,
use_sentiment=False, # Skip sentiment for pure technical baseline
strategy=strategy
)
res = engine.run(symbol, ohlcv, initial_capital=100_000.0)
print(f"{symbol:<8} | {strategy_id:<15} | {res.total_return_pct:>9.2f}% | {res.sharpe_ratio:>8.2f} | {res.win_rate:>5.1f}% | {res.total_trades:>6}")
results.append({
"symbol": symbol,
"strategy": strategy_id,
"return_pct": res.total_return_pct,
"sharpe": res.sharpe_ratio,
"win_rate": res.win_rate,
"trades": res.total_trades,
"max_drawdown": res.max_drawdown_pct
})
# Aggregate results by strategy
df = pd.DataFrame(results)
if not df.empty:
summary = df.groupby("strategy").agg({
"return_pct": ["mean", "std"],
"sharpe": "mean",
"win_rate": "mean",
"trades": "sum"
})
print("\n--- Summary ---")
print(summary)
return df
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--symbols", nargs="+", default=DEFAULT_SYMBOLS)
parser.add_argument("--strategies", nargs="+", default=["hybrid", "mean_reversion", "momentum", "trend_following"])
parser.add_argument("--days", type=int, default=DEFAULT_DAYS)
args = parser.parse_args()
run_multi_backtest(args.symbols, args.strategies, args.days)