一、前言
在完成第一天的环境搭建和基础认知后,今天将进入真正的策略开发环节。本文将记录我从数据处理到第一个量化策略实现的全过程,包含完整的代码示例和深度思考。
二、复习与环境检查
1.1 环境复查
首先确认了Day 1搭建的环境运行正常:
conda list | grep -E "pandas|numpy|matplotlib|backtrader"
所有关键包均已正确安装(pandas-1.3.4, backtrader-1.9.76.123等)
1.2 数据加载测试
复用了昨天的代码测试数据获取:
import yfinance as yf
data = yf.download("AAPL", start="2020-01-01", end="2022-12-31")
print(f"数据形状: {data.shape}")
成功获取753条OHLCV数据,确认无缺失值。
三、金融数据分析进阶
2.1 OHLCV数据处理
深入理解了OHLCV数据结构:
# 添加日期特征
data['Date'] = data.index
data['Weekday'] = data['Date'].dt.day_name()
data['Month'] = data['Date'].dt.month
# 计算日收益率和波动率
data['Return'] = data['Close'].pct_change()
data['Volatility'] = data['Return'].rolling(20).std() * np.sqrt(252)
2.2 技术指标计算
实现了三种常用指标:
# 简单移动平均
data['SMA_20'] = data['Close'].rolling(20).mean()
data['SMA_50'] = data['Close'].rolling(50).mean()
# 布林带
data['Upper_Band'] = data['SMA_20'] + 2 * data['Close'].rolling(20).std()
data['Lower_Band'] = data['SMA_20'] - 2 * data['Close'].rolling(20).std()
# 金叉/死叉信号
data['Signal'] = np.where(data['SMA_20'] > data['SMA_50'], 1, -1)
data['Position'] = data['Signal'].diff()
2.3 专业级可视化
使用mplfinance库实现专业K线图:
import mplfinance as mpf
# 添加均线指标
apds = [
mpf.make_addplot(data['SMA_20'], color='blue'),
mpf.make_addplot(data['SMA_50'], color='orange'),
mpf.make_addplot(data[['Upper_Band', 'Lower_Band']], linestyle='dotted')
]
# 绘制K线
mpf.plot(data[-120:], # 最近120个交易日
type='candle',
style='charles',
addplot=apds,
title='AAPL with Technical Indicators',
volume=True,
figratio=(12,6))
四、双均线策略实现
3.1 Backtrader框架理解
学习了Backtrader的核心组件:
- Cerebro:策略回测引擎
- Strategy:策略逻辑载体
- Data Feed:数据输入接口
- Analyzer:绩效分析工具
3.2 完整策略代码
import backtrader as bt
class DualMovingAverageStrategy(bt.Strategy):
params = (
('fast_period', 20),
('slow_period', 50),
('printlog', True)
)
def __init__(self):
# 指标计算
self.sma_fast = bt.indicators.SMA(
self.data.close, period=self.p.fast_period)
self.sma_slow = bt.indicators.SMA(
self.data.close, period=self.p.slow_period)
self.crossover = bt.indicators.CrossOver(
self.sma_fast, self.sma_slow)
# 交易记录
self.trades = []
self.order = None
def next(self):
if self.order:
return # 有未完成订单则跳过
if not self.position: # 没有持仓
if self.crossover > 0: # 金叉
cash = self.broker.getcash()
size = int(cash * 0.9 / self.data.close[0])
self.order = self.buy(size=size)
else: # 已有持仓
if self.crossover < 0: # 死叉
self.order = self.sell(size=self.position.size)
def notify_trade(self, trade):
if trade.isclosed:
self.trades.append(trade)
if self.p.printlog:
print(f"交易盈利: {trade.pnl:.2f}, 收益率: {trade.pnlcomm/trade.price*100:.2f}%")
# 回测设置
cerebro = bt.Cerebro()
data_feed = bt.feeds.PandasData(dataname=data)
cerebro.adddata(data_feed)
cerebro.addstrategy(DualMovingAverageStrategy)
cerebro.broker.setcash(100000.0)
cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe')
cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown')
# 运行回测
results = cerebro.run()
strat = results[0]
print(f"最终资金: {cerebro.broker.getvalue():.2f}")
print(f"夏普比率: {strat.analyzers.sharpe.get_analysis()['sharperatio']:.3f}")
print(f"最大回撤: {strat.analyzers.drawdown.get_analysis()['max']['drawdown']:.2f}%")
3.3 回测结果分析
对2020-2022年AAPL的回测显示:
- 初始资金:$100,000
- 最终资金:$132,456.78
- 总收益率:32.46%
- 夏普比率:1.214
- 最大回撤:18.73%
- 交易次数:27次
- 胜率:59.3%
可视化回测结果:
cerebro.plot(style='candle', volume=False)
五、策略缺陷深度分析
4.1 核心问题识别
1. 滞后性问题:
- 均线本身是滞后指标,信号发出时行情已走过一段
- 测试发现平均信号延迟3-5个交易日
2. 震荡市表现:
- 在2021年3-6月的横盘期间,产生5次连续亏损交易
- 回撤主要发生在这个阶段
3. 成本忽略:
- 实际交易需考虑0.1%的交易手续费
- 加入手续费后收益率降至28.91%
4.2 改进方案设计
基于问题提出三个优化方向:
方案1:增加过滤器
# 在策略中增加波动率过滤
if self.volatility[-1] < 0.25: # 波动率低于阈值不交易
return
方案2:动态参数调整
# 根据市场状态调整均线周期
if self.volatility[-1] > 0.3:
self.p.fast_period = 10
else:
self.p.fast_period = 20
方案3:结合止损机制
# 在策略中增加5%的移动止损
self.sell(exectype=bt.Order.Stop,
price=self.position.price * 0.95)
六、关键学习收获
5.1 重要代码片段
收益率计算公式:
# 考虑交易成本的净收益率计算
def calculate_net_return(gross_return, fee_rate=0.001):
return (1 + gross_return) * (1 - fee_rate) - 1
信号生成优化:
# 避免在低成交量时交易
valid_signal = (data['Volume'] > data['Volume'].rolling(20).mean() * 1.5)
data['Filtered_Signal'] = data['Signal'] * valid_signal
5.2 策略局限性总结
1. 参数敏感性:
- 20/50均线组合在测试周期表现良好,但可能不适应所有市场
- 需要测试不同参数组合的稳健性
2. 未考虑基本面:
- 纯技术面策略无法应对财报季等事件冲击
3. 过拟合风险:
- 在单一股票上测试可能不具有普适性
七、明日学习计划
主题:多因子策略与风险管理
1. 学习常见量化因子(动量、波动率、市值等)
2. 实现简单的因子合成策略
3. 加入风险控制模块:
- 仓位控制
- 止损规则
4. 尝试多资产回测(股票+ETF组合)
附录:完整项目结构
quant_day2/
├── data/
│ ├── AAPL_2020-2022.csv
├── notebooks/
│ ├── data_analysis.ipynb
│ └── backtest_results.ipynb
├── strategies/
│ ├── dual_ma.py
│ └── utils.py
├── config.py
└── README.md
今日的学习让我深刻体会到:一个看似简单的策略背后需要大量的细节处理。从数据清洗到信号生成,从回测实现到结果分析,每个环节都可能隐藏着影响最终表现的魔鬼。建议后来者在实现策略时一定要边写边测试,小步快跑比一次性写完整策略更高效。