纯突破策略回测系统目录
🏗️ 回测系统架构
1. 系统整体架构
纯突破策略回测系统
├── 数据层 (Data Layer)
│ ├── 历史数据加载 (HistoricalDataLoader)
│ ├── 实时数据模拟 (RealtimeDataSimulator)
│ └── 数据质量验证 (DataQualityValidator)
├── 策略层 (Strategy Layer)
│ ├── 突破信号检测 (BreakoutSignalDetector)
│ ├── 买卖规则执行 (TradingRuleExecutor)
│ ├── 仓位管理 (PositionManager)
│ └── 风险控制 (RiskController)
├── 执行层 (Execution Layer)
│ ├── 交易模拟 (TradeSimulator)
│ ├── 成本计算 (CostCalculator)
│ └── 滑点模拟 (SlippageSimulator)
├── 分析层 (Analysis Layer)
│ ├── 绩效分析 (PerformanceAnalyzer)
│ ├── 风险分析 (RiskAnalyzer)
│ ├── 交易分析 (TradeAnalyzer)
│ └── 归因分析 (AttributionAnalyzer)
└── 报告层 (Report Layer)
├── 报告生成 (ReportGenerator)
├── 可视化 (Visualization)
└── 结果存储 (ResultStorage)
2. 回测引擎实现
2.1 回测引擎基类
# backtest/engine/base_backtester.py
from abc import ABC, abstractmethod
import pandas as pd
from typing import Dict, List, Optional
from dataclasses import dataclass
@dataclass
class TradeRecord:
"""交易记录"""
date: str
stock_code: str
action: str # 'buy' or 'sell'
price: float
quantity: int
amount: float
commission: float
reason: str # 交易原因
@dataclass
class Position:
"""持仓信息"""
stock_code: str
buy_date: str
buy_price: float
quantity: int
current_price: float
highest_price: float # 用于移动止损
holding_days: int
class BaseBacktester(ABC):
"""回测引擎基类"""
def __init__(self, config: Dict):
self.config = config
self.initial_capital = config.get('initial_capital', 10000000)
self.current_capital = self.initial_capital
self.positions: Dict[str, Position] = {}
self.trade_records: List[TradeRecord] = []
self.portfolio_values: List[Dict] = []
self.daily_returns: List[float] = []
@abstractmethod
def run(self) -> Dict:
"""运行回测"""
pass
@abstractmethod
def detect_signals(self, date: str) -> List[Dict]:
"""检测交易信号"""
pass
@abstractmethod
def execute_trades(self, date: str, signals: List[Dict]) -> None:
"""执行交易"""
pass
def calculate_portfolio_value(self, date: str) -> float:
"""计算组合价值"""
stock_value = sum(
position.quantity * position.current_price
for position in self.positions.values()
)
return self.current_capital + stock_value
def record_portfolio_value(self, date: str) -> None:
"""记录组合价值"""
portfolio_value = self.calculate_portfolio_value(date)
self.portfolio_values.append({
'date': date,
'value': portfolio_value,
'cash': self.current_capital,
'stock_value': portfolio_value - self.current_capital,
'positions_count': len(self.positions)
})
2.2 突破策略回测器
# backtest/engine/breakout_backtester.py
from .base_backtester import BaseBacktester, TradeRecord, Position
import pandas as pd
class BreakoutBacktester(BaseBacktester):
"""突破策略回测器"""
def __init__(self, config: Dict):
super().__init__(config)
self.breakout_detector = BreakoutDetector(config['breakout'])
self.buy_rules = BuyRules(config['buy'])
self.sell_rules = SellRules(config['sell'])
self.position_rules = PositionRules(config['position'])
def run(self) -> Dict:
"""运行突破策略回测"""
results = {
'portfolio_values': [],
'trade_records': [],
'performance_metrics': {},
'risk_metrics': {},
'trade_statistics': {}
}
# 获取回测日期范围
dates = self.get_trading_dates(
self.config['start_date'],
self.config['end_date']
)
print(f"开始回测: {self.config['start_date']} 至 {self.config['end_date']}")
print(f"回测天数: {len(dates)}")
print(f"初始资金: {self.initial_capital:,.0f}元")
for i, date in enumerate(dates):
if i % 100 == 0:
print(f"进度: {i}/{len(dates)} ({i/len(dates)*100:.1f}%)")
# 1. 更新持仓价格
self._update_positions(date)
# 2. 检测买入信号
buy_signals = self._detect_buy_signals(date)
# 3. 执行买入交易
if buy_signals:
self._execute_buy_trades(date, buy_signals)
# 4. 检测卖出信号
sell_signals = self._detect_sell_signals(date)
# 5. 执行卖出交易
if sell_signals:
self._execute_sell_trades(date, sell_signals)
# 6. 记录组合价值
self.record_portfolio_value(date)
# 7. 计算绩效指标
results = self._calculate_performance()
return results
def _detect_buy_signals(self, date: str) -> List[Dict]:
"""检测买入信号"""
buy_signals = []
# 获取当日市场数据
market_data = self.data_loader.get_market_data(date)
for stock_data in market_data:
# 检测突破信号
breakout_signal = self.breakout_detector.detect(stock_data)
if breakout_signal:
# 生成买入信号
buy_signal = self.buy_rules.generate_signal(
breakout_signal, stock_data
)
if buy_signal:
# 检查仓位限制
if self.position_rules.can_open_position(
self.positions, buy_signal
):
buy_signals.append(buy_signal)
return buy_signals
def _detect_sell_signals(self, date: str) -> List[Dict]:
"""检测卖出信号"""
sell_signals = []
for stock_code, position in self.positions.items():
# 获取当前价格
current_price = self.data_loader.get_current_price(stock_code, date)
# 检查止盈条件
if self.sell_rules.check_take_profit(position, current_price):
sell_signals.append({
'stock_code': stock_code,
'action': 'sell',
'reason': 'take_profit',
'price': current_price
})
# 检查止损条件
elif self.sell_rules.check_stop_loss(position, current_price):
sell_signals.append({
'stock_code': stock_code,
'action': 'sell',
'reason': 'stop_loss',
'price': current_price
})
# 检查强制卖出条件
elif self.sell_rules.check_force_sell(position, current_price, date):
sell_signals.append({
'stock_code': stock_code,
'action': 'sell',
'reason': 'force_sell',
'price': current_price
})
return sell_signals
def _execute_buy_trades(self, date: str, buy_signals: List[Dict]) -> None:
"""执行买入交易"""
for signal in buy_signals:
# 计算买入金额
buy_amount = self._calculate_buy_amount(signal)
if buy_amount <= self.current_capital:
# 计算买入价格(考虑滑点)
buy_price = self._calculate_buy_price(signal)
# 计算交易数量
quantity = int(buy_amount / buy_price / 100) * 100 # 整手交易
# 计算交易成本
commission = self.cost_calculator.calculate_commission(
buy_amount, 'buy'
)
# 执行买入
self._execute_buy(
date=date,
stock_code=signal['stock_code'],
price=buy_price,
quantity=quantity,
commission=commission,
reason=signal['reason']
)
def _execute_sell_trades(self, date: str, sell_signals: List[Dict]) -> None:
"""执行卖出交易"""
for signal in sell_signals:
stock_code = signal['stock_code']
if stock_code in self.positions:
position = self.positions[stock_code]
# 计算卖出价格(考虑滑点)
sell_price = self._calculate_sell_price(signal)
# 计算交易成本(包括印花税)
sell_amount = position.quantity * sell_price
commission = self.cost_calculator.calculate_commission(
sell_amount, 'sell'
)
# 执行卖出
self._execute_sell(
date=date,
stock_code=stock_code,
price=sell_price,
quantity=position.quantity,
commission=commission,
reason=signal['reason']
)
def _calculate_performance(self) -> Dict:
"""计算绩效指标"""
performance_analyzer = PerformanceAnalyzer()
# 计算收益指标
returns = performance_analyzer.calculate_returns(self.portfolio_values)
# 计算风险指标
risk = performance_analyzer.calculate_risk(self.portfolio_values)
# 计算交易统计
trade_stats = performance_analyzer.analyze_trades(self.trade_records)
# 计算基准对比
benchmark_comparison = performance_analyzer.compare_with_benchmark(
self.portfolio_values, self.config['benchmark']
)
return {
'returns': returns,
'risk': risk,
'trade_statistics': trade_stats,
'benchmark_comparison': benchmark_comparison
}
3. 绩效分析模块
3.1 绩效计算器
# backtest/analysis/performance_analyzer.py
import numpy as np
import pandas as pd
from typing import List, Dict
class PerformanceAnalyzer:
"""绩效分析器"""
def calculate_returns(self, portfolio_values: List[Dict]) -> Dict:
"""计算收益指标"""
values = [pv['value'] for pv in portfolio_values]
dates = [pv['date'] for pv in portfolio_values]
# 计算收益率序列
returns = []
for i in range(1, len(values)):
daily_return = (values[i] - values[i-1]) / values[i-1]
returns.append(daily_return)
# 计算累计收益率
cumulative_return = (values[-1] - values[0]) / values[0]
# 计算年化收益率
total_days = len(dates)
years = total_days / 252 # 假设一年252个交易日
annual_return = (1 + cumulative_return) ** (1/years) - 1
# 计算日收益率统计
daily_returns = pd.Series(returns)
return {
'cumulative_return': cumulative_return,
'annual_return': annual_return,
'daily_returns_mean': daily_returns.mean(),
'daily_returns_std': daily_returns.std(),
'positive_days': (daily_returns > 0).sum(),
'negative_days': (daily_returns < 0).sum(),
'max_single_day_gain': daily_returns.max(),
'max_single_day_loss': daily_returns.min()
}
def calculate_risk(self, portfolio_values: List[Dict]) -> Dict:
"""计算风险指标"""
values = [pv['value'] for pv in portfolio_values]
# 计算最大回撤
max_drawdown = self._calculate_max_drawdown(values)
# 计算波动率
returns = []
for i in range(1, len(values)):
daily_return = (values[i] - values[i-1]) / values[i-1]
returns.append(daily_return)
annual_volatility = np.std(returns) * np.sqrt(252)
# 计算夏普比率(假设无风险利率3%)
risk_free_rate = 0.03
annual_return = self.calculate_returns(portfolio_values)['annual_return']
sharpe_ratio = (annual_return - risk_free_rate) / annual_volatility
# 计算Calmar比率
calmar_ratio = annual_return / max_drawdown if max_drawdown > 0 else 0
return {
'max_drawdown': max_drawdown,
'annual_volatility': annual_volatility,
'sharpe_ratio': sharpe_ratio,
'calmar_ratio': calmar_ratio,
'value_at_risk_95': np.percentile(returns, 5),
'expected_shortfall_95': np.mean([r for r in returns if r <= np.percentile(returns, 5)])
}
def _calculate_max_drawdown(self, values: List[float]) -> float:
"""计算最大回撤"""
peak = values[0]
max_dd = 0
for value in values:
if value > peak:
peak = value
dd = (peak - value) / peak
if dd > max_dd:
max_dd = dd
return max_dd
def analyze_trades(self, trade_records: List[TradeRecord]) -> Dict:
"""分析交易统计"""
if not trade_records:
return {}
# 分离买入和卖出记录
buy_trades = [t for t in trade_records if t.action == 'buy']
sell_trades = [t for t in trade_records if t.action == 'sell']
# 计算交易统计
total_trades = len(buy_trades) + len(sell_trades)
# 计算胜率
winning_trades = sum(1 for t in sell_trades if t.amount > 0)
win_rate = winning_trades / len(sell_trades) if sell_trades else 0
# 计算平均盈亏
if sell_trades:
avg_profit = np.mean([t.amount for t in sell_trades])
avg_win = np.mean([t.amount for t in sell_trades if t.amount > 0])
avg_loss = np.mean([t.amount for t in sell_trades if t.amount < 0])
else:
avg_profit = avg_win = avg_loss = 0
# 计算盈亏比
profit_factor = abs(avg_win / avg_loss) if avg_loss != 0 else 0
return {
'total_trades': total_trades,
'buy_trades': len(buy_trades),
'sell_trades': len(sell_trades),
'win_rate': win_rate,
'avg_profit_per_trade': avg_profit,
'avg_winning_trade': avg_win,
'avg_losing_trade': avg_loss,
'profit_factor': profit_factor,
'max_consecutive_wins': self._calculate_max_consecutive_wins(sell_trades),
'max_consecutive_losses': self._calculate_max_consecutive_losses(sell_trades)
}
def compare_with_benchmark(self, portfolio_values: List[Dict],
benchmark_config: Dict) -> Dict:
"""与基准对比"""
# 获取基准数据
benchmark_values = self._get_benchmark_values(benchmark_config)
# 计算超额收益
portfolio_returns = self._calculate_portfolio_returns(portfolio_values)
benchmark_returns = self._calculate_benchmark_returns(benchmark_values)
# 计算信息比率
excess_returns = portfolio_returns - benchmark_returns
information_ratio = np.mean(excess_returns) / np.std(excess_returns)
# 计算跟踪误差
tracking_error = np.std(excess_returns)
# 计算Beta系数
covariance = np.cov(portfolio_returns, benchmark_returns)[0, 1]
benchmark_variance = np.var(benchmark_returns)
beta = covariance / benchmark_variance if benchmark_variance > 0 else 0
# 计算Alpha
alpha = np.mean(portfolio_returns) - beta * np.mean(benchmark_returns)
return {
'information_ratio': information_ratio,
'tracking_error': tracking_error,
'beta': beta,
'alpha': alpha,
'excess_return': np.mean(excess_returns),
'up_capture': self._calculate_up_capture(portfolio_returns, benchmark_returns),
'down_capture': self._calculate_down_capture(portfolio_returns, benchmark_returns)
}
4. 可视化模块
4.1 绩效可视化
# backtest/visualization/performance_charts.py
import matplotlib.pyplot as plt
import seaborn as sns
from typing import Dict, List
class PerformanceCharts:
"""绩效图表"""
def plot_equity_curve(self, portfolio_values: List[Dict],
benchmark_values: List[Dict] = None) -> None:
"""绘制净值曲线"""
dates = [pv['date'] for pv in portfolio_values]
values = [pv['value'] for pv in portfolio_values]
plt.figure(figsize=(12, 6))
plt.plot(dates, values, label='策略净值', linewidth=2)
if benchmark_values:
benchmark_dates = [bv['date'] for bv in benchmark_values]
benchmark_vals = [bv['value'] for bv in benchmark_values]
plt.plot(benchmark_dates, benchmark_vals, label='基准净值',
linewidth=1, alpha=0.7)
plt.title('净值曲线对比')
plt.xlabel('日期')
plt.ylabel('净值')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
def plot_drawdown(self, portfolio_values: List[Dict]) -> None:
"""绘制回撤曲线"""
values = [pv['value'] for pv in portfolio_values]
dates = [pv['date'] for pv in portfolio_values]
# 计算回撤
drawdowns = []
peak = values[0]
for value in values:
if value > peak:
peak = value
drawdown = (peak - value) / peak
drawdowns.append(drawdown)
plt.figure(figsize=(12, 4))
plt.fill_between(dates, 0, drawdowns, color='red', alpha=0.3)
plt.plot(dates, drawdowns, color='red', linewidth=1)
plt.title('回撤曲线')
plt.xlabel('日期')
plt.ylabel('回撤')
plt.grid(True, alpha=0.3)
plt.show()
def plot_monthly_returns(self, portfolio_values: List[Dict]) -> None:
"""绘制月度收益热力图"""
# 计算月度收益
monthly_returns = self._calculate_monthly_returns(portfolio_values)
# 创建热力图数据
years = sorted(set([mr['year'] for mr in monthly_returns]))
months = list(range(1, 13))
heatmap_data = []
for year in years:
year_data = []
for month in months:
monthly_return = next(
(mr['return'] for mr in monthly_returns
if mr['year'] == year and mr['month'] == month), 0
)
year_data.append(monthly_return)
heatmap_data.append(year_data)
# 绘制热力图
plt.figure(figsize=(10, 6))
sns.heatmap(heatmap_data, annot=True, fmt='.1%',
xticklabels=['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
yticklabels=years, cmap='RdYlGn', center=0)
plt.title('月度收益热力图 (%)')
plt.show()
5. 目录结构
backtest/
├── README.md # 本说明文件
├── engine/ # 回测引擎
│ ├── __init__.py
│ ├── base_backtester.py # 回测基类
│ ├── breakout_backtester.py # 突破策略回测器
│ ├── signal_detector.py # 信号检测器
│ └── trade_executor.py # 交易执行器
├── analysis/ # 绩效分析
│ ├── __init__.py
│ ├── performance_analyzer.py # 绩效分析器
│ ├── risk_analyzer.py # 风险分析器
│ ├── trade_analyzer.py # 交易分析器
│ └── attribution_analyzer.py # 归因分析器
├── visualization/ # 可视化
│ ├── __init__.py
│ ├── performance_charts.py # 绩效图表
│ ├── risk_charts.py # 风险图表
│ └── trade_charts.py # 交易图表
├── data/ # 回测数据
│ ├── market_data/
│ ├── benchmark_data/
│ └── processed_data/
├── configs/ # 回测配置
│ ├── backtest_config.yaml # 回测参数配置
│ └── strategy_config.yaml # 策略参数配置
├── results/ # 回测结果
│ ├── performance_reports/
│ ├── trade_records/
│ └── visualization_results/
└── tests/ # 测试
├── test_backtester.py
├── test_analyzer.py
└── test_visualization.py
6. 使用指南
6.1 运行回测
# backtest/run_backtest.py
from backtest.engine.breakout_backtester import BreakoutBacktester
from backtest.analysis.performance_analyzer import PerformanceAnalyzer
from backtest.visualization.performance_charts import PerformanceCharts
# 1. 加载配置
import yaml
with open('configs/backtest_config.yaml', 'r') as f:
config = yaml.safe_load(f)
# 2. 创建回测器
backtester = BreakoutBacktester(config)
# 3. 运行回测
results = backtester.run()
# 4. 分析绩效
analyzer = PerformanceAnalyzer()
performance_report = analyzer.analyze_results(results)
# 5. 可视化结果
charts = PerformanceCharts()
charts.plot_equity_curve(results['portfolio_values'])
charts.plot_drawdown(results['portfolio_values'])
charts.plot_monthly_returns(results['portfolio_values'])
# 6. 保存结果
backtester.save_results(results, 'results/backtest_results.json')
6.2 参数优化
# backtest/optimization/parameter_optimizer.py
class ParameterOptimizer:
"""参数优化器"""
def optimize_breakout_period(self, config: Dict) -> Dict:
"""优化突破周期参数"""
# 定义参数空间
period_range = range(20, 121, 5) # 20-120天,步长5天
best_period = None
best_sharpe = -float('inf')
for period in period_range:
# 更新配置
config['breakout']['period'] = period
# 运行回测
backtester = BreakoutBacktester(config)
results = backtester.run()
# 评估绩效
sharpe = results['performance_metrics']['sharpe_ratio']
if sharpe > best_sharpe:
best_sharpe = sharpe
best_period = period
return {
'best_period': best_period,
'best_sharpe': best_sharpe
}
7. 当前状态
✅ 已完成
- 回测系统架构设计
- 回测引擎框架实现
- 绩效分析模块设计
🔄 进行中
- 突破信号检测器实现
- 交易执行模块开发
- 可视化模块开发
⏳ 待开始
- 参数优化工具开发
- 多进程回测支持
- 实时监控系统
8. 联系人
回测开发负责人: 待定(开发团队)
数据支持: 赵云(数据工程将军)
策略验证: 待定(量化研究团队)
开发状态: 回测系统设计完成,开始技术实现
预计完成: 基础回测框架本周内完成