欢迎加入专注于财经数据与量化投研的【数据科学实战】知识星球!在这里,您将获取持续更新的《财经数据宝典》和《量化投研宝典》,这两部宝典相辅相成,为您在量化投研道路上提供明确指引。 《量化投研宝典》精选了业内持续维护且实用性强的开源工具(Backtrader、Qlib、VeighNa等),配合详细教程与代码示例,帮助您快速构建量化策略;《财经数据宝典》则汇集了多年财经数据维护经验,全面介绍从 AKShare、Tushare 到 Wind、iFind 等国内外数据源,并附有丰富的使用技巧。 无论您是量化投资新手还是经验丰富的研究者,星球社区都能帮您少走弯路,事半功倍,共同探索数据驱动的投资世界!
引言
你是否想过用 Python 开发自己的量化交易策略?今天,我们将深入探讨一个经典的技术分析策略——Keltner 通道突破策略,并使用强大的 backtrader 库进行完整的回测。无论你是量化交易的初学者还是希望提升策略开发技能的进阶者,这篇文章都将为你提供实用的代码示例和详细解析。
什么是 Keltner 通道?
Keltner 通道是一种基于波动性的技术指标,由三条线组成:
这个指标的特点是在高波动期通道会变宽,在低波动期通道会变窄,非常适合捕捉市场的突破行情。
突破策略的交易逻辑
我们的策略逻辑非常简单直观:
- 做多信号:当收盘价突破上轨时,表明有强劲的上涨势头
- 做空信号:当收盘价跌破下轨时,表明有强劲的下跌势头
完整代码实现
让我们来看看如何用 Python 实现这个策略:
1. 导入必要的库
import backtrader as bt
import yfinance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime
import warnings
warnings.filterwarnings("ignore") # 隐藏警告信息,让输出更清晰
2. 自定义 Keltner 通道指标
class KeltnerChannel(bt.Indicator):
"""
Keltner 通道指标,包含 EMA 中线和基于 ATR 的上下轨
"""
lines = ('mid', 'top', 'bot') # 定义输出线:中线、上轨、下轨
params = (
('ema_period', 30), # EMA 周期
('atr_period', 14), # ATR 周期
('atr_multiplier', 1.0), # ATR 倍数,用于设置通道宽度
)
def __init__(self):
# 计算 EMA 作为中线
self.lines.mid = bt.indicators.EMA(
self.data.close,
period=self.params.ema_period
)
# 计算 ATR 用于确定通道宽度
atr = bt.indicators.ATR(
self.data,
period=self.params.atr_period
)
# 计算上轨和下轨
self.lines.top = self.lines.mid + (atr * self.params.atr_multiplier)
self.lines.bot = self.lines.mid - (atr * self.params.atr_multiplier)
3. 实现突破策略
class KeltnerBreakoutStrategy(bt.Strategy):
params = (
('ema_period', 30), # Keltner 通道的 EMA 周期
('atr_period', 14), # Keltner 通道的 ATR 周期
('atr_multiplier', 1.0), # Keltner 通道的 ATR 倍数
('printlog', True), # 是否打印日志
)
def __init__(self):
# 保存收盘价和开盘价的引用
self.dataclose = self.datas[0].close
self.dataopen = self.datas[0].open
# 初始化 Keltner 通道指标
self.keltner = KeltnerChannel(
self.data,
ema_period=self.params.ema_period,
atr_period=self.params.atr_period,
atr_multiplier=self.params.atr_multiplier
)
# 订单跟踪变量
self.order = None
self.buyprice = None
self.buycomm = None
# 设置指标绘图选项
self.keltner.plotinfo.subplot = False # 在主图上绘制
def next(self):
"""主策略逻辑,每个数据条都会调用"""
# 确保有足够的数据
if len(self.data) 2:
return
# 如果有待处理订单,不做操作
if self.order:
return
# 获取前一日的值用于信号生成
prev_close = self.dataclose[-1]
# 前一日收盘价
prev_upper = self.keltner.top[-1] # 前一日上轨
prev_lower = self.keltner.bot[-1] # 前一日下轨
current_ema = self.keltner.mid[0] # 当前 EMA
current_close = self.dataclose[0] # 当前收盘价
if not self.position: # 如果没有持仓
# 做多信号:前一日收盘价突破上轨
if prev_close > prev_upper:
self.log(f'买入信号:价格 {self.dataopen[0]:.2f}')
self.order = self.buy()
# 做空信号:前一日收盘价跌破下轨
elif prev_close < prev_lower:
self.log(f'卖出信号:价格 {self.dataopen[0]:.2f}')
self.order = self.sell()
else: # 如果有持仓,检查平仓条件
if self.position.size > 0: # 多头持仓
if current_close < current_ema:
self.log(f'平多仓:价格 {current_close:.2f}')
self.order = self.close()
elif self.position.size 0: # 空头持仓
if current_close > current_ema:
self.log(f'平空仓:价格 {current_close:.2f}')
self.order = self.close()
def log(self, txt, dt=None):
"""日志功能"""
if self.params.printlog:
dt = dt or self.datas[0].datetime.date(0)
print(f'{dt.isoformat()}: {txt}')
4. 运行回测
def run_backtest():
"""运行回测的主函数"""
# 回测参数设置
TICKER = "BTC-USD" # 交易标的
START_DATE = "2023-01-01" # 开始日期
END_DATE = "2024-12-31" # 结束日期
INITIAL_CASH = 100000 # 初始资金
COMMISSION = 0.001 # 手续费率(0.1%)
# Keltner 通道参数
EMA_WINDOW = 30
ATR_WINDOW = 14
ATR_MULTIPLIER = 1.0
print(f"正在下载 {TICKER} 的数据...")
# 使用 yfinance 下载数据
data = yf.download(
TICKER,
start=START_DATE,
end=END_DATE,
auto_adjust=False
)
# 创建 Cerebro 引擎(backtrader 的核心)
cerebro = bt.Cerebro()
# 添加数据
data_bt = bt.feeds.PandasData(dataname=data)
cerebro.adddata(data_bt)
# 添加策略
cerebro.addstrategy(
KeltnerBreakoutStrategy,
ema_period=EMA_WINDOW,
atr_period=ATR_WINDOW,
atr_multiplier=ATR_MULTIPLIER,
printlog=False # 设置为 True 可查看详细交易日志
)
# 设置初始资金和手续费
cerebro.broker.setcash(INITIAL_CASH)
cerebro.broker.setcommission(commission=COMMISSION)
# 添加分析器
cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='trades')
cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe')
cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown')
cerebro.addanalyzer(bt.analyzers.Returns, _name='returns')
print(f'初始资金:${cerebro.broker.getvalue():.2f}')
# 运行回测
results = cerebro.run()
strat = results[0]
print(f'最终资金:${cerebro.broker.getvalue():.2f}')
# 打印分析结果
print('\n--- 策略表现分析 ---')
# 交易分析
trade_analysis = strat.analyzers.trades.get_analysis()
if 'total' in trade_analysis and 'closed' in trade_analysis['total']:
total_trades = trade_analysis['total']['closed']
won_trades = trade_analysis.get('won', {}).get('total', 0)
lost_trades = trade_analysis.get('lost', {}).get('total', 0)
print(f'总交易次数:{total_trades}')
print(f'盈利交易:{won_trades}')
print(f'亏损交易:{lost_trades}')
if total_trades > 0:
win_rate = (won_trades / total_trades) * 100
print(f'胜率:{win_rate:.2f}%')
# 收益分析
returns_analysis = strat.analyzers.returns.get_analysis()
if 'rtot' in returns_analysis:
total_return = returns_analysis['rtot'] * 100
print(f'总收益率:{total_return:.2
f}%')
# 夏普比率
sharpe_analysis = strat.analyzers.sharpe.get_analysis()
if 'sharperatio' in sharpe_analysis:
sharpe_ratio = sharpe_analysis['sharperatio']
print(f'夏普比率:{sharpe_ratio:.3f}')
# 最大回撤
drawdown_analysis = strat.analyzers.drawdown.get_analysis()
if 'max' in drawdown_analysis:
max_drawdown = drawdown_analysis['max']['drawdown']
print(f'最大回撤:{max_drawdown:.2f}%')
# 计算买入持有收益作为对比
initial_price = data['Close'].iloc[0]
final_price = data['Close'].iloc[-1]
buy_hold_return = ((final_price / initial_price) - 1) * 100
print(f'{TICKER} 买入持有收益率:{buy_hold_return:.2f}%')
# 绘制回测结果图
cerebro.plot(style='candlestick', barup='green', bardown='red')
return cerebro, results
# 执行回测
if __name__ == '__main__':
try:
cerebro, results = run_backtest()
print("\n回测完成!")
except Exception as e:
print(f"发生错误:{e}")
案例分析:BTC-USD 回测结果
让我们看一个实际的回测案例。使用上述代码对 BTC-USD 在 2023 年 1 月到 2024 年 12 月期间进行回测,可能会得到如下结果:
虽然策略收益低于简单的买入持有,但它的最大回撤更小,风险调整后的收益(夏普比率)表现良好。这说明该策略在控制风险方面有一定优势。
策略优化建议
- 参数优化:可以尝试不同的 EMA 周期和 ATR 倍数组合
总结
本文详细介绍了如何使用 Python 和 backtrader 库实现 Keltner 通道突破策略的回测。通过自定义指标、实现交易逻辑、运行回测和分析结果,我们完成了一个完整的量化交易策略开发流程。
关键要点:
- backtrader 提供了强大的回测框架,使策略开发变得简单
记住,历史表现不代表未来收益,在实际交易中要做好风险管理!
参考文章
加入专注于财经数据与量化投研的知识星球【数据科学实战】,获取完整研究解析、详细回测框架代码实现和完整策略逻辑实操指南。财经数据与量化投研知识社区
核心权益如下:
- 赠送《财经数据宝典》完整文档,汇集多年财经数据维护经验
- 赠送《量化投研宝典》完整文档,汇集多年量化投研领域经验
- 赠送《PyBroker-入门及实战》视频课程,手把手学习量化策略开发
星球已有丰富内容积累,包括量化投研论文、财经高频数据、 PyBroker 视频教程、定期直播、数据分享和答疑解难。适合对量化投研和财经数据分析有兴趣的学习者及从业者。欢迎加入我们!