| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- """
- 运行 CYB50-Pro 回测验证
- 使用 2018-2025 年历史数据验证目标指标:
- - 年化收益 25-35%
- - 最大回撤 <12%
- - 夏普比率 >1.5
- """
- import sys
- import os
- sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
- import pandas as pd
- import numpy as np
- from datetime import datetime
- import json
- from backtest import CYB50ProBacktester
- def load_historical_data(data_path: str = None) -> pd.DataFrame:
- """
- 加载历史数据
- 优先从现有项目加载,如果没有则生成模拟数据
- """
- # 尝试从现有项目加载
- possible_paths = [
- data_path,
- '../../quant/cyb50_historical_data.csv',
- '../../quant/cyb50_baostock.csv',
- '../../market-regime-identifier/SZ#399673.txt',
- '../data/SZ#399673.csv'
- ]
- for path in possible_paths:
- if path and os.path.exists(path):
- print(f"Loading data from: {path}")
- if path.endswith('.csv'):
- df = pd.read_csv(path)
- elif path.endswith('.txt'):
- # 处理txt格式
- df = pd.read_csv(path, sep='\t', header=None,
- names=['date', 'open', 'high', 'low', 'close', 'volume'])
- else:
- continue
- # 标准化列名
- df.columns = [c.lower() for c in df.columns]
- # 解析日期
- if 'date' in df.columns:
- df['date'] = pd.to_datetime(df['date'])
- df.set_index('date', inplace=True)
- elif 'datetime' in df.columns:
- df['datetime'] = pd.to_datetime(df['datetime'])
- df.set_index('datetime', inplace=True)
- return df
- # 如果没有找到数据,生成模拟数据
- print("No historical data found. Generating synthetic data for testing...")
- return generate_synthetic_data()
- def generate_synthetic_data(start='2018-01-01', end='2025-01-01') -> pd.DataFrame:
- """生成模拟的创业板50指数数据"""
- np.random.seed(42)
- dates = pd.date_range(start=start, end=end, freq='D')
- # 生成带有趋势和波动率聚类的收益率
- returns = []
- current_regime = 'bull'
- regime_days = 0
- for i in range(len(dates)):
- # 随机切换市场状态
- if regime_days <= 0:
- current_regime = np.random.choice(['bull', 'bear', 'sideways'], p=[0.5, 0.2, 0.3])
- regime_days = np.random.randint(60, 252) # 60天到1年
- # 根据状态生成收益
- if current_regime == 'bull':
- daily_return = np.random.normal(0.001, 0.015)
- elif current_regime == 'bear':
- daily_return = np.random.normal(-0.001, 0.025)
- else: # sideways
- daily_return = np.random.normal(0, 0.012)
- returns.append(daily_return)
- regime_days -= 1
- returns = np.array(returns)
- prices = 1000 * np.exp(np.cumsum(returns))
- # 生成OHLCV
- df = pd.DataFrame(index=dates)
- df['close'] = prices
- df['open'] = df['close'] * (1 + np.random.normal(0, 0.005, len(dates)))
- df['high'] = df[['open', 'close']].max(axis=1) * (1 + abs(np.random.normal(0, 0.01, len(dates))))
- df['low'] = df[['open', 'close']].min(axis=1) * (1 - abs(np.random.normal(0, 0.01, len(dates))))
- df['volume'] = np.random.lognormal(20, 0.5, len(dates))
- return df
- def main():
- """主函数"""
- print("=" * 60)
- print("CYB50-Pro Backtest Validation")
- print("Target Metrics:")
- print(" - Annual Return: 25-35%")
- print(" - Max Drawdown: <12%")
- print(" - Sharpe Ratio: >1.5")
- print("=" * 60)
- # 加载数据
- data = load_historical_data()
- print(f"\nData loaded: {len(data)} bars from {data.index[0]} to {data.index[-1]}")
- print(f"Price range: {data['close'].min():.2f} - {data['close'].max():.2f}")
- # 初始化回测器
- backtester = CYB50ProBacktester(
- initial_capital=1_000_000,
- commission_rate=0.0005,
- slippage=0.001
- )
- # 运行回测
- print("\nRunning backtest...")
- result = backtester.run_backtest(
- price_data=data,
- start_date='2018-01-01',
- end_date='2024-12-31'
- )
- # 打印结果
- print("\n" + "=" * 60)
- print("BACKTEST RESULTS")
- print("=" * 60)
- print(f"\n[Period]")
- print(f" Start: {result.start_date.strftime('%Y-%m-%d')}")
- print(f" End: {result.end_date.strftime('%Y-%m-%d')}")
- print(f"\n[Returns]")
- print(f" Total Return: {result.total_return:+.2%}")
- print(f" Annual Return: {result.annual_return:+.2%}")
- print(f"\n[Risk Metrics]")
- print(f" Max Drawdown: {result.max_drawdown:.2%}")
- print(f" Max DD Duration: {result.max_drawdown_duration} days")
- print(f" Volatility (Ann): {result.volatility:.2%}")
- print(f"\n[Risk-Adjusted Returns]")
- print(f" Sharpe Ratio: {result.sharpe_ratio:.2f}")
- print(f" Sortino Ratio: {result.sortino_ratio:.2f}")
- print(f"\n[Trading Statistics]")
- print(f" Total Trades: {result.total_trades}")
- print(f" Win Rate: {result.win_rate:.1%}")
- print(f" Profit Factor: {result.profit_factor:.2f}")
- # Target Validation
- print(f"\n[Target Validation]")
- targets_met = 0
- if 0.25 <= result.annual_return <= 0.35:
- print(f" [OK] Annual Return 25-35%: {result.annual_return:.2%}")
- targets_met += 1
- else:
- print(f" [FAIL] Annual Return 25-35%: {result.annual_return:.2%}")
- if result.max_drawdown > -0.12:
- print(f" [OK] Max Drawdown <12%: {result.max_drawdown:.2%}")
- targets_met += 1
- else:
- print(f" [FAIL] Max Drawdown <12%: {result.max_drawdown:.2%}")
- if result.sharpe_ratio > 1.5:
- print(f" [OK] Sharpe Ratio >1.5: {result.sharpe_ratio:.2f}")
- targets_met += 1
- else:
- print(f" [FAIL] Sharpe Ratio >1.5: {result.sharpe_ratio:.2f}")
- print(f"\n[Summary]")
- print(f" Targets Met: {targets_met}/3")
- if targets_met == 3:
- print(" Status: ALL TARGETS ACHIEVED [OK][OK][OK]")
- elif targets_met >= 2:
- print(" Status: MOSTLY SATISFACTORY")
- else:
- print(" Status: NEEDS OPTIMIZATION")
- # Regime Analysis
- print(f"\n[Regime Performance]")
- for regime, stats in result.regime_performance.items():
- print(f" {regime}: {stats['trades']} trades, "
- f"Win Rate {stats['win_rate']:.1%}, "
- f"Avg PnL {stats['avg_pnl']:,.0f}")
- # Agent Contributions
- print(f"\n[Agent Contributions]")
- for agent, pnl in sorted(result.agent_contributions.items(),
- key=lambda x: x[1], reverse=True):
- print(f" {agent}: {pnl:+,.0f}")
- # Generate Report
- output_dir = os.path.join(os.path.dirname(__file__), '../reports')
- os.makedirs(output_dir, exist_ok=True)
- report_path = os.path.join(output_dir, f'backtest_report_{datetime.now().strftime("%Y%m%d_%H%M%S")}.json')
- report = backtester.generate_report(result, report_path)
- print(f"\n[Report Saved]")
- print(f" {report_path}")
- print("\n" + "=" * 60)
- return result
- if __name__ == "__main__":
- main()
|