量化交易听起来高大上,其实核心就三件事:拿数据、生信号、模拟跑。
本文用 Python 从零实现一个简单但完整的回测引擎,包括:策略信号生成、资金管理、交易模拟、绩效评估。代码可以直接跑。
整体架构
市场数据(yfinance) → 信号生成(策略) → 回测引擎(模拟交易) → 绩效报告
我们要实现的东西:
- 数据获取:用 yfinance 拉免费行情
- 策略信号:均线交叉(最经典的入门策略)
- 回测引擎:模拟交易,算收益
- 绩效评估:Sharpe、最大回撤、胜率
Step 1:获取市场数据
import yfinance as yf
import pandas as pd
def fetch_data(ticker: str, period: str = "2y") -> pd.DataFrame:
"""获取股票历史数据"""
df = yf.download(ticker, period=period, auto_adjust=True)
df.columns = [c.lower() for c in df.columns]
return df[['open', 'high', 'low', 'close', 'volume']]
df = fetch_data("AAPL")
print(f"数据量: {len(df)} 天")
print(df.tail())
yfinance 是免费的,不需要任何 API key。
Step 2:均线交叉策略
最经典的趋势跟踪策略——快线上穿慢线买入,下穿卖出:
import numpy as np
def moving_average_crossover(df, fast=10, slow=30):
df = df.copy()
df['ma_fast'] = df['close'].rolling(fast).mean()
df['ma_slow'] = df['close'].rolling(slow).mean()
df['signal'] = np.where(df['ma_fast'] > df['ma_slow'], 1, 0)
df['position'] = df['signal'].diff()
return df.dropna()
df = moving_average_crossover(df)
buy_count = (df['position'] == 1).sum()
sell_count = (df['position'] == -1).sum()
print(f"买入: {buy_count} 次, 卖出: {sell_count} 次")
为什么从均线交叉开始? 因为它足够简单,逻辑清晰,而且是很多复杂策略的基础。
Step 3:回测引擎
核心部分——模拟真实交易:
class Backtest:
def __init__(self, df, initial_capital=100000, commission=0.001):
self.df = df.copy()
self.initial_capital = initial_capital
self.commission = commission
def run(self):
capital = self.initial_capital
shares = 0
trades = []
equity_curve = []
for i, row in self.df.iterrows():
if row['position'] == 1 and shares == 0:
shares = int(capital * 0.95 / row['close'])
cost = shares * row['close'] * (1 + self.commission)
capital -= cost
trades.append({'date': i, 'action': 'BUY', 'price': row['close'], 'shares': shares})
elif row['position'] == -1 and shares > 0:
revenue = shares * row['close'] * (1 - self.commission)
capital += revenue
trades.append({'date': i, 'action': 'SELL', 'price': row['close'], 'shares': shares})
shares = 0
total = capital + shares * row['close']
equity_curve.append({'date': i, 'equity': total})
self.trades = pd.DataFrame(trades)
self.equity = pd.DataFrame(equity_curve).set_index('date')
return self
bt = Backtest(df).run()
print(f"交易次数: {len(bt.trades)}")
print(f"最终资金: ${bt.equity['equity'].iloc[-1]:,.0f}")
print(f"收益率: {(bt.equity['equity'].iloc[-1] / 100000 - 1) * 100:.1f}%")
几个关键细节:
- 手续费:不算手续费的回测都是自欺欺人
- 95% 仓位:留现金避免买入时资金不够
- 记录净值曲线:后面算回撤要用
Step 4:绩效评估
def calculate_metrics(equity_df):
returns = equity_df['equity'].pct_change().dropna()
total_return = equity_df['equity'].iloc[-1] / equity_df['equity'].iloc[0] - 1
days = len(equity_df)
annual_return = (1 + total_return) ** (252 / days) - 1
excess_returns = returns - 0.04 / 252
sharpe = np.sqrt(252) * excess_returns.mean() / returns.std()
peak = equity_df['equity'].expanding().max()
drawdown = (equity_df['equity'] - peak) / peak
max_drawdown = drawdown.min()
return {
'total_return': f"{total_return:.1%}",
'annual_return': f"{annual_return:.1%}",
'sharpe_ratio': f"{sharpe:.2f}",
'max_drawdown': f"{max_drawdown:.1%}",
'trading_days': days
}
metrics = calculate_metrics(bt.equity)
for k, v in metrics.items():
print(f"{k}: {v}")
Sharpe > 1 还不错,> 2 很好,< 0 亏钱。最大回撤反映你要承受的最大痛苦。
接下来可以做什么?
这个 200 行引擎是最小可用版本。要做得更专业:
- 更多策略:RSI 动量、布林带均值回归、多因子组合
- 止损模块:固定止损、移动止损、ATR 波动率止损
- 仓位管理:Kelly 公式、波动率目标法
- 多标的:同时回测多只股票
如果你不想从零写这些,我整理了一个完整的 Python Quant Starter Kit,包含 3 个策略 + 风控模块 + 详细教程:
Q: yfinance 数据准确吗? 个人学习够用,生产环境用付费数据源。
Q: 这个策略能赚钱吗? 均线交叉在趋势市好,震荡市差。没有圣杯,关键是风控。
Q: 能交易加密货币吗? 可以,yfinance 支持 BTC-USD。
觉得有帮助点个赞。量化问题欢迎评论区交流。