问题描述
解决方案
这是一个基于**唐奇安通道(Donchian Channel)**的趋势突破策略。
策略原理
唐奇安通道由理查德·唐奇安(Richard Donchian)提出,是海龟交易法则的核心技术指标之一。
- 上轨:过去 $N$ 天的最高价。
- 下轨:过去 $M$ 天的最低价。
- 买入信号:当当前价格突破过去 $N$ 天的最高价(上轨)时,做多。
- 卖出信号:当当前价格跌破过去 $M$ 天的最低价(下轨)时,平仓。
策略代码
以下代码可以直接在聚宽(JoinQuant)的回测环境中运行。为了演示方便,本策略选取沪深300ETF (510300.XSHG) 作为交易标的,采用经典的 20日突破买入,10日跌破卖出 的参数设置。
# -*- coding: utf-8 -*-
# 导入聚宽函数库
import jqdata
def initialize(context):
"""
初始化函数,设定基准、手续费、滑点、策略参数
"""
# 1. 设定基准(沪深300指数)
set_benchmark('000300.XSHG')
# 2. 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 3. 设置手续费:买入万三,卖出万三加千一印花税,最低5元
set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
# 4. 设定要操作的股票(这里以沪深300ETF为例,方便回测)
g.security = '510300.XSHG'
# 5. 唐奇安通道参数设置
# N日突破买入 (通常为20)
g.buy_window = 20
# M日跌破卖出 (通常为10,短于买入窗口以保护利润)
g.sell_window = 10
# 6. 每天开盘时运行
run_daily(market_open, time='09:30')
def market_open(context):
"""
每日交易逻辑
"""
security = g.security
# 获取股票的当前数据
current_data = get_current_data()
# 如果停牌,则跳过
if current_data[security].paused:
return
# 获取历史数据
# 我们需要过去 N 天的数据来计算上轨,过去 M 天的数据计算下轨
# 注意:attribute_history 获取的是不包含当前时刻的历史数据(昨天及之前)
# 这样可以避免"未来函数",即用今天的收盘价来决定今天是否开盘买入
# 获取买入窗口的历史最高价数据
buy_history = attribute_history(security, g.buy_window, '1d', ['high'])
# 获取卖出窗口的历史最低价数据
sell_history = attribute_history(security, g.sell_window, '1d', ['low'])
# 计算唐奇安通道上轨(过去N天的最高价)
donchian_upper = buy_history['high'].max()
# 计算唐奇安通道下轨(过去M天的最低价)
donchian_lower = sell_history['low'].min()
# 获取当前最新价格
current_price = current_data[security].last_price
# 获取当前账户持仓情况
position = context.portfolio.positions[security]
# --- 交易逻辑 ---
# 1. 买入逻辑:如果当前价格突破上轨,且空仓
if current_price > donchian_upper and position.closeable_amount == 0:
# 全仓买入
order_value(security, context.portfolio.available_cash)
log.info("价格突破上轨 (%.2f > %.2f),买入 %s" % (current_price, donchian_upper, security))
# 2. 卖出逻辑:如果当前价格跌破下轨,且持有仓位
elif current_price < donchian_lower and position.closeable_amount > 0:
# 清仓卖出
order_target(security, 0)
log.info("价格跌破下轨 (%.2f < %.2f),卖出 %s" % (current_price, donchian_lower, security))
# 3. 记录通道值以便在回测图表中查看
record(Upper_Band=donchian_upper, Lower_Band=donchian_lower, Price=current_price)
代码详解
-
初始化 (
initialize):- 设置了
g.buy_window = 20和g.sell_window = 10。这是经典的“海龟法则”短周期配置。意味着价格突破过去20天最高价买入,跌破过去10天最低价卖出。 - 使用了
510300.XSHG(ETF) 作为标的,因为指数本身不能交易,ETF更适合模拟真实交易。
- 设置了
-
数据获取 (
attribute_history):- 使用
attribute_history获取过去 N 天的数据。在market_open(09:30) 调用时,它返回的是截止到昨天收盘的数据。这是非常重要的,因为唐奇安通道的突破标准必须基于过去的数据,否则会产生未来函数。
- 使用
-
交易逻辑:
- 买入:
current_price > donchian_upper。当今天的实时价格超过了过去20天的最高价,视为趋势确立,执行买入。 - 卖出:
current_price < donchian_lower。当今天的实时价格低于了过去10天的最低价,视为趋势反转或回调过深,执行止损/止盈。
- 买入:
-
可视化 (
record):- 使用
record函数将上轨、下轨和当前价格画在回测收益图的下方,方便直观判断策略是否按预期执行。
- 使用
常见问题 (Q&A)
Q1: 为什么卖出窗口(10天)比买入窗口(20天)短?
A: 这是一个非对称的通道设计。较长的买入窗口是为了过滤掉市场的短期噪音,确认趋势真正形成后再进场;较短的卖出窗口是为了在趋势反转时能够更快地离场,保住利润,即“让利润奔跑,快速截断亏损”。
Q2: 这个策略适合什么类型的市场?
A: 唐奇安通道是典型的趋势跟踪策略。它在单边上涨或下跌的大趋势行情中表现最好。在横盘震荡市中,由于价格频繁上下穿越通道,会导致频繁止损,产生连续的小额亏损。
Q3: 如何扩展到多只股票?
A: 可以将 g.security 修改为一个列表(例如 get_index_stocks('000300.XSHG')),然后在 market_open 中使用 for 循环遍历列表,对每一只股票单独计算通道和信号。同时需要注意资金分配(例如每只股票分配总资金的1/10)。
Q4: 回测时发现买入价格比上轨高很多怎么办?
A: 这是滑点或跳空高开造成的。如果开盘价直接跳空高开突破上轨,策略会以开盘价买入。可以通过 set_slippage 设置滑点模型来模拟真实的交易成本。