欢迎加入专注于财经数据与量化投研的【数据科学实战】知识星球!在这里,您将获取持续更新的《财经数据宝典》和《量化投研宝典》,这两部宝典相辅相成,为您在量化投研道路上提供明确指引。 《量化投研宝典》精选了业内持续维护且实用性强的开源工具(Backtrader、Qlib、VeighNa等),配合详细教程与代码示例,帮助您快速构建量化策略;《财经数据宝典》则汇集了多年财经数据维护经验,全面介绍从 AKShare、Tushare 到 Wind、iFind 等国内外数据源,并附有丰富的使用技巧。 无论您是量化投资新手还是经验丰富的研究者,星球社区都能帮您少走弯路,事半功倍,共同探索数据驱动的投资世界!
引言
在量化交易领域,突破策略一直是交易者关注的焦点。今天,我们将深入探讨一种基于分位数通道的突破策略,这种策略通过动态计算价格通道来识别交易机会。与传统的布林带等固定标准差通道不同,分位数通道能够更好地适应价格分布的特点,为我们提供更精准的交易信号。
本文将详细介绍如何使用 Python 和 backtrader 框架实现这一策略,包括策略核心逻辑、参数优化以及滚动回测等完整流程。
策略核心原理
分位数通道策略的核心思想是:
- 动态通道计算:基于历史价格数据的分位数(如 80% 和 20%)来确定上下通道边界
- 突破交易:当价格突破上通道时做多,突破下通道时做空
策略实现代码
import backtrader as bt
import numpy as np
class QuantileChannelStrategy(bt.Strategy):
params = (
('lookback_period', 60), # 回看周期
('upper_quantile', 0.8), # 上通道分位数(80%)
('lower_quantile', 0.2), # 下通道分位数(20%)
('breakout_threshold', 1.02), # 突破确认阈值(2%)
('stop_loss_pct', 0.08), # 止损百分比(8%)
('rebalance_period', 5), # 重新计算周期
('min_channel_width', 0.02), # 最小通道宽度(2%)
('order_percentage', 0.95), # 仓位比例
('printlog', False),
)
def __init__(self):
# 使用滚动窗口提高效率
self.price_window = []
# 通道水平
self.upper_channel = 0
self.lower_channel = 0
self.trend_line = 0
# 交易变量
self.rebalance_counter = 0
self.stop_price = 0
self.order = None
# 策略统计
self.trade_count = 0
self.breakout_entries = 0
self.stop_loss_exits = 0
def calculate_channels_fast(self):
"""快速计算分位数通道"""
if len(self.price_window) < self.params.lookback_period:
return False
# 使用 numpy 计算分位数
prices = np.array(self.price_window)
# 计算基本分位数
self.upper_channel = np.quantile(prices, self.params.upper_quantile)
self.lower_channel = np.quantile(prices, self.params.lower_quantile)
self.trend_line = np.quantile(prices, 0.5) # 中位数
# 确保最小通道宽度
channel_width = (self.upper_channel - self.lower_channel) / self.trend_line
if channel_width < self.params.min_channel_width:
half_width = self.trend_line * self.params.min_channel_width / 2
self.upper_channel = self.trend_line + half_width
self.lower_channel = self.trend_line - half_width
return True
def
detect_breakout_simple(self, current_price):
"""简化的突破检测"""
if current_price > self.upper_channel * self.params.breakout_threshold:
return 1 # 上方突破
elif current_price < self.lower_channel / self.params.breakout_threshold:
return -1 # 下方突破
else:
return 0 # 无突破
def next(self):
current_price = self.data.close[0]
# 维护滚动窗口
self.price_window.append(current_price)
if len(self.price_window) > self.params.lookback_period:
self.price_window.pop(0)
# 检查是否有待处理订单
if self.order:
return
# 定期重新计算通道
self.rebalance_counter += 1
if self.rebalance_counter >= self.params.rebalance_period:
if not self.calculate_channels_fast():
return
self.rebalance_counter = 0
# 检查止损
if self.position.size > 0 and current_price <= self.stop_price:
self.order = self.close()
self.stop_loss_exits += 1
return
# 检测突破
breakout = self.detect_breakout_simple(current_price)
# 交易逻辑
if breakout != 0 and self.position.size == 0:
cash = self.broker.get_cash()
size = (cash * self.params.order_percentage) / current_price
if breakout == 1: # 做多
self.order = self.buy(size=size)
self.breakout_entries += 1
elif breakout == -1: # 做空
self.order = self.sell(size=size)
self.breakout_entries += 1
# 均值回归退出
elif self.position.size != 0:
if abs(current_price - self.trend_line) / self.trend_line 0.01:
self.order = self.close()
参数优化
为了找到最佳的策略参数组合,我们需要进行系统的参数优化:
def optimize_quantile_channel_parameters():
"""运行参数优化"""
import yfinance as yf
import pandas as pd
# 获取数据
df = yf.download('BTC-USD', start='2020-01-01', end='2025-01-01')
# 设置回测环境
cerebro = bt.Cerebro()
data = bt.feeds.PandasData(dataname=df)
cerebro.adddata(data)
cerebro.broker.setcash(10000.0)
cerebro.broker.setcommission(commission=0.001)
# 添加分析器
cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe')
cerebro.addanalyzer(bt.analyzers.Returns, _name='returns')
cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='trades')
# 参数优化范围
cerebro.optstrategy(
QuantileChannelStrategy,
lookback_period=[30, 60, 90], # 回看周期
upper_quantile=[0.75, 0.8, 0.85], # 上分位数
lower_quantile=[0.15, 0.2, 0.25], # 下分位数
breakout_threshold=[1.015, 1.02, 1.03], # 突破阈值
stop_loss_pct=[0.05, 0.08, 0.10], # 止损百分比
rebalance_period=[3, 5, 7] # 重平衡周期
)
# 运行优化
results = cerebro.run()
# 收集结果
best_sharpe = -999
best_params = None
for result in results:
strategy = result[0]
sharpe = strategy.analyzers.sharpe.get_analysis()['sharperatio']
if sharpe > best_sharpe:
best_sharpe = sharpe
best_params = {
'lookback_period': strategy.p.lookback_period,
'upper_quantile': strategy.p.upper_quantile,
'lower_quantile': strategy.p.lower_quantile,
'breakout_threshold': strategy.p.breakout_threshold,
'stop_loss_pct': strategy.p.stop_loss_pct,
'rebalance_period': strategy.p.rebalance_period
}
return best_params
滚动回测
为了验证策略的稳健性,我们需要进行滚动回测:
def run_rolling_backtest(ticker, start, end, window_months, strategy_params):
"""滚动回测函数"""
import pandas as pd
import dateutil.relativedelta as rd
all_results = []
start_dt = pd.to_datetime(start)
end_dt = pd.to_datetime(end)
current_start = start_dt
while True:
current_end = current_start + rd.relativedelta(months=window_months)
if current_end > end_dt:
break
# 获取数据
data = yf.download(ticker, start=current_start, end=current_end)
if len(data) 90:
current_start += rd.relativedelta(months=window_months)
continue
# 计算买入持有收益
start_price = data['Close'].iloc[0]
end_price = data['Close'].iloc[-1]
benchmark_ret = (end_price - start_price) / start_price * 100
# 运行策略
cerebro = bt.Cerebro()
cerebro.addstrategy(QuantileChannelStrategy, **strategy_params)
cerebro.adddata(bt.feeds.PandasData(dataname=data))
cerebro.broker.setcash(100000)
cerebro.broker.setcommission(commission=0.001)
start_val = cerebro.broker.getvalue()
result = cerebro.run()
final_val = cerebro.broker.getvalue()
strategy_ret = (final_val - start_val) / start_val * 100
all_results.append({
'start': current_start.date(),
'end': current_end.date(),
'strategy_return': strategy_ret,
'benchmark_return': benchmark_ret,
'outperformance': strategy_ret - benchmark_ret
})
current_start += rd.relativedelta(months=window_months)
return pd.DataFrame(all_results)
实际应用案例
让我们看一个完整的应用案例:
# 1. 参数优化
best_params = optimize_quantile_channel_parameters()
print(f"最佳参数:{best_params}")
# 2. 滚动回测
results_df = run_rolling_backtest(
ticker='BTC-USD',
start='2018-01-01',
end='2025-01-01',
window_months=12,
strategy_params=best_params
)
# 3. 性能分析
avg_strategy_return = results_df['strategy_return'].mean()
avg_benchmark_return = results_df['benchmark_return'].mean()
win_rate = (results_df['outperformance'] > 0).mean()
print(f"策略平均收益:{avg_strategy_return:.2
f}%")
print(f"基准平均收益:{avg_benchmark_return:.2f}%")
print(f"胜率:{win_rate:.2%}")
# 4. 可视化结果
import matplotlib.pyplot as plt
plt.figure(figsize=(12, 6))
plt.plot(results_df.index, results_df['strategy_return'], label='策略收益', marker='o')
plt.plot(results_df.index, results_df['benchmark_return'], label='基准收益', marker='s')
plt.xlabel('回测期数')
plt.ylabel('收益率 (%)')
plt.title('分位数通道策略滚动回测结果')
plt.legend()
plt.grid(True)
plt.show()
策略优化建议
- 动态参数调整:根据市场波动率动态调整通道宽度和突破阈值
总结
分位数通道突破策略通过动态适应市场价格分布,提供了一种灵活有效的交易方法。本文详细介绍了策略的实现、优化和回测流程,展示了如何使用 Python 和 backtrader 框架构建完整的量化交易系统。
通过参数优化和滚动回测,我们可以找到适合特定市场的最佳参数组合,并验证策略的稳健性。需要注意的是,任何策略都需要根据实际市场情况进行调整,并做好充分的风险管理。
希望本文能够帮助你理解和实现分位数通道策略,在量化交易的道路上更进一步!
参考文章
加入专注于财经数据与量化投研的知识星球【数据科学实战】,获取完整研究解析、详细回测框架代码实现和完整策略逻辑实操指南。财经数据与量化投研知识社区
核心权益如下:
- 赠送《财经数据宝典》完整文档,汇集多年财经数据维护经验
- 赠送《量化投研宝典》完整文档,汇集多年量化投研领域经验
- 赠送《PyBroker-入门及实战》视频课程,手把手学习量化策略开发
星球已有丰富内容积累,包括量化投研论文、财经高频数据、 PyBroker 视频教程、定期直播、数据分享和答疑解难。适合对量化投研和财经数据分析有兴趣的学习者及从业者。欢迎加入我们!