好的,我们来深度解析一下这份渤海证券的《日内交易策略及趋势体系更新——金融工程 CTA 策略专题报告之六》研究报告,并提供一个基于其日内策略思路的 Python (Backtrader) 策略示例。
报告来源: 渤海证券《日内交易策略及趋势体系更新——金融工程 CTA 策略专题报告之六》(2017年7月10日)
分析师: 崔健 CFA
SAC NO: S1150511010016
目录
- 报告核心内容概述
- 第一部分:日内波段交易策略详解
- 2.1 策略思路与品种选择
- 2.1.1 波动幅度与最小跳点的重要性
- 2.1.2 趋势与震荡对日内策略的影响
- 2.2 交易信号逻辑
- 2.2.1 滤网 (趋势判断)
- 2.2.2 进场信号 (突破)
- 2.2.3 止损信号
- 2.2.4 止盈信号 (条件触发)
- 2.2.5 收盘平仓
- 2.2.6 时间过滤
- 2.3 策略参数与回测设置
- 2.4 回测结果 (以锡、镍为例)
- 2.5 策略风险分析
- 2.5.1 尾盘无法平仓风险
- 2.5.2 同质化风险
- 2.6 日内策略总结
- 2.1 策略思路与品种选择
- 第二部分:趋势跟踪体系完善
- 3.1 仓位管理优化 (增加波动率因子)
- 3.2 逃顶/离场指标修改 (引入 KD 指标)
- 3.3 增加交易品种与周期 (多品种、多周期)
- 3.4 策略回撤创新高处理 (暂停策略)
- 3.5 组合回测结果与分析
- 3.5.1 组合参数与设置
- 3.5.2 组合绩效指标
- 3.5.3 结果分析与对比
- 未来研究方向
- 基于报告的 Python (Backtrader) 日内策略示例
- 5.1 策略逻辑重述 (Python 视角)
- 5.2 Python 代码示例
- 5.3 代码说明与注意事项
1. 报告核心内容概述
这份报告分为两大核心部分:
- 日内交易策略探讨: 报告设计并测试了一个日内波段交易策略,重点关注交易逻辑、品种选择、风险以及回测表现。该策略主要应用于波动性相对最小跳点较大的品种(如锡、镍)。
- 趋势跟踪体系更新: 对之前报告中提出的趋势跟踪策略体系进行了多方面的完善和优化,包括仓位管理、离场信号、品种/周期扩展以及风险控制处理,并进行了更新后的组合回测。
核心观点总结:
- 日内策略:
- 信号: 使用日线唐奇安通道判断大趋势方向(滤网),结合当日动态高低点与N日高低点的突破作为进场信号,以突破时的反向极值点作为初始止损,盈利一定幅度后启动止盈机制,日内未触发止损止盈则收盘前强制平仓。
- 风险: 主要风险在于尾盘行情剧烈波动可能导致无法按计划平仓,被迫隔夜持仓。
- 容量: 适用于资金容量较小的场景(估计锡、镍合计约100-200万)。
- 趋势体系更新:
- 改进: 仓位管理加入波动率因子(ATRCoff),降低波动大时的仓位;修改逃顶指标;增加交易品种和周期(日线、周线、小时线);对回撤创新高的子策略进行风险处理(暂停)。
- 组合表现: 更新后的组合年化收益约20.22%,最大回撤9.55%,夏普比率1.59(5000万资金规模,35%最大保证金占用)。
2. 第一部分:日内波段交易策略详解
2.1 策略思路与品种选择
-
2.1.1 波动幅度与最小跳点的重要性:
- 报告强调,日内策略(尤其是突破策略)的盈利空间很大程度上取决于日均波幅相对于最小变动价位(跳点)的倍数。
- 如果这个倍数太小(如玉米),那么交易成本和滑点(报告中按开平各2跳,合计4跳压力测试)会吃掉大部分潜在利润空间,策略难以有效。
- 因此,报告筛选出锡(沪锡 Sn)和镍(沪镍 Ni)这两个比率最高的品种进行策略设计和测试,因为它们能更好地承受交易摩擦。
-
2.1.2 趋势与震荡对日内策略的影响:
- 报告认为日内策略本质上也是动量策略,依赖于日内出现较为流畅的趋势性行情。
- 在窄幅震荡、缺乏明确方向的市场中,日内策略表现会很差,甚至不如隔夜趋势策略。
- 为了过滤掉部分震荡行情,引入了基于日线级别唐奇安通道的趋势滤网。
2.2 交易信号逻辑
报告构建的日内策略信号流程如下:
-
2.2.1 滤网 (趋势判断):
- 使用日线级别的唐奇安通道 (Donchian Channel, 参数 DCLen)。
- 若昨日收盘价突破唐奇安通道上轨,趋势状态设为 1 (多头)。
- 若昨日收盘价跌破唐奇安通道下轨,趋势状态设为 -1 (空头)。
- 引入长期均线 (LMALen) 作为反转过滤:状态为 1 时,若收盘价跌破长期均线,状态重置为 0 (观望);状态为 -1 时,若收盘价突破长期均线,状态重置为 0。
- 状态改变的当天不进行交易。
- 只有在状态为 1 时才考虑做多,状态为 -1 时才考虑做空。
-
2.2.2 进场信号 (突破):
- 做多 (趋势状态=1): 当价格突破
max(HT, HN)
时进场。HT
: 当天开盘后前 T 分钟内的最高价。 (T = Slen * Knum)HN
: 过去 N 个交易日的最高价。 (N = Slen)
- 做空 (趋势状态=-1): 当价格跌破
min(LT, LN)
时进场。LT
: 当天开盘后前 T 分钟内的最低价。LN
: 过去 N 个交易日的最低价。
- 做多 (趋势状态=1): 当价格突破
-
2.2.3 止损信号:
- 采用突破时的当日反向极值作为止损点。
- 做多进场后,止损点设为从开盘到进场时刻这段时间内的最低价。
- 做空进场后,止损点设为从开盘到进场时刻这段时间内的最高价。
- 价格触及止损点则立即平仓。
-
2.2.4 止盈信号 (条件触发):
- 进场后,若价格向有利方向移动 M 个最小跳点。
- 则设置一个固定止盈点,距离进场价位 R * M 个最小跳点。
- 参数 R 可取 0.4 或 0.6。 (这意味着止盈幅度小于触发止盈的幅度,可能是一种追踪止损的简化表达或固定目标)
- 价格触及该止盈点则平仓。
-
2.2.5 收盘平仓:
- 如果在收盘前,既没有触发止损,也没有触发止盈。
- 则在接近收盘时强制平仓 (End of Day, EOD)。回测中使用 14:55 的价格代替,实盘建议在 14:55 到 14:59 之间操作。
-
2.2.6 时间过滤 (TimeFilter):
- 如果进场信号发生在接近收盘的某个时间点之后 (例如 14:30 之后),则忽略该信号,不开仓。
- 理由是尾盘产生的信号,持仓时间过短,且容易受收盘效应影响,历史回测表现不佳。
2.3 策略参数与回测设置
- 品种: 沪锡 (Sn)、沪镍 (Ni)
- 周期: 5分钟 K线进行回测 (计算 T 分钟高低点)
- 参数 (Table 2):
- 镍: DCLen=4, MALen=32 (滤网用), Slen=1 (N=1), Knum=18 (T=18根5分钟线=90分钟), M=1200, R=未明确给出(假设按0.6), TimeFilter=14:30
- 锡: DCLen=8, MALen=24 (滤网用), Slen=3 (N=3), Knum=18 (T=54根5分钟线=270分钟), M=1200, R=未明确给出(假设按0.6), TimeFilter=14:30
- 回测设置:
- 初始资金: 100万
- 保证金比例: 10%
- 手续费: 0.0035% (双边)
- 滑点: 开仓 5 跳,平仓 3 跳 (非常严格的压力测试)
- 单笔最大保证金占用: 10% (低杠杆)
- 测试时间: 品种上市 ~ 2017年6月30日
2.4 回测结果 (以锡、镍为例)
- 锡 (Sn):
- 胜率: 67.92%
- 盈亏比: 1.26
- CAGR: 7.91%
- 夏普比率: 2.93
- 最大回撤: 7.2%
- 镍 (Ni):
- 胜率: 56.32%
- 盈亏比: 1.54
- CAGR: 10.20%
- 夏普比率: 1.08
- 最大回撤: 4.35%
结论: 在考虑了较高的滑点和手续费后,策略在锡和镍上表现尚可,尤其是在低杠杆(最大10%保证金占用)的情况下。锡的夏普比率很高,镍的年化收益更高。
2.5 策略风险分析
- 2.5.1 尾盘无法平仓风险:
- 这是日内策略特有的核心风险。如果临近收盘时(如14:59)价格出现剧烈单向波动,可能导致程序化交易的平仓指令连续撤单、追价,最终无法成交,被迫持有隔夜仓位。报告中给出了镍的实例图 (图3)。
- 2.5.2 同质化风险: 量化策略的普遍风险,如果市场结构发生变化或策略被广泛使用,效果可能下降。
2.6 日内策略总结
报告认为该日内策略相对初级,未深入研究订单簿等微观结构。其优势在于锡、镍合约设计(高波动/跳点比)使得策略能通过较严格的滑点压力测试。策略资金容量有限,主要作为低风险、低容量的补充。
3. 第二部分:趋势跟踪体系完善
报告对前期(报告五)的趋势跟踪体系进行了优化:
3.1 仓位管理优化 (增加波动率因子 ATRCoff)
- 原仓位公式:
min(Hands1, Hands2)
Hands1 = Asset * MaxMR / (ATR * TN * ContractNum)
(基于ATR波动率)Hands2 = Asset * MaxMR / (Close * TN * ContractNum)
(基于名义价值,防单品种过度暴露)
- 改进: 在 Hands1 中加入波动率调节系数
ATRCoff
(设为 10)。Hands1 = Asset * MaxMR / (ATRCoff * ATR * TN * ContractNum)
- 目的: 提高波动率在仓位决定中的权重。
ATRCoff
越大,同等 ATR 下分配的仓位越小。 - 效果 (Table 5): 与 ATRCoff=1 相比,ATRCoff=10 时:
- 年化收益率下降 (28.5% -> 19.7%)。
- 最大回撤显著降低 (16.3% -> 9.6%)。
- 平均持仓保证金占比降低 (10.6% -> 7.6%)。
- 盈亏比提高 (2.09 -> 2.30)。
- 夏普比率略降 (1.55 -> 1.54)。
- 结论: 牺牲部分收益,换取更低的回撤和更稳健的风险调整后收益。
3.2 逃顶/离场指标修改 (引入 KD 指标)
- 原指标: 长期均线乖离率 + 单日大幅下跌。缺点是对“十字星”等顶部形态不敏感。
- 新指标: 结合 KD 指标 和 均线乖离率 (Bias)。
- 计算 стандарт KD 指标 (默认参数 n=9, m=3)。
- 计算收盘价相对 60 周期均线的乖离率
Bias = abs(C - MA60) / MA60
。 - 当
Bias > 0.8
(表示价格偏离均线较远) 并且 KD 指标发生死叉 (K 线从上向下穿过 D 线) 时,视为顶部/离场信号。
3.3 增加交易品种与周期 (多品种、多周期)
- 扩展: 认识到仅依赖日线周期不足,增加了更多交易品种,并将策略应用扩展到周线、日线、小时线等多个周期。
- 分类:
- 中期波段: 日线 MACD、小时线策略等。
- 长期趋势: 周线策略等。
- 考量: 中期策略稳定性稍差,但回测指导意义较强;长期策略更稳定,但回测结果对短期指导意义较弱。
- 组合列表 (Table 6 & 7): 给出了详细的品种-周期-策略-参数配置表,包含 DC-MA (均线过滤唐奇安), DC-BOLL (布林带过滤唐奇安), MACD 等多种策略类型。
3.4 策略回撤创新高处理 (暂停策略)
- 问题: 当市场波动特征发生剧变 (如2017年螺纹钢宽幅震荡,ATR远超历史),基于历史数据回测的策略可能失效,导致回撤突破历史最大值。
- 处理: 如果某个子策略(特定品种/周期/策略组合)的回撤创下历史新高,暂时停止该子策略的交易。
- 理由: 表明当前市场环境已超出策略设计的历史范畴,原有假设可能不再成立,需要暂停以控制风险。
3.5 组合回测结果与分析
- 回测设置:
- 初始资金: 5000万
- 最大组合保证金占用: 35%
- 资金分配: 每个子策略均分最大保证金比例 (即每个子策略分配 35% / 总策略数量 的保证金上限)
- 测试时间: 2004年6月1日 ~ 2017年3月15日
- 组合绩效 (Table 8):
- 胜率: 48.50%
- 盈亏比: 2.49
- CAGR: 20.22%
- 夏普比率: 1.60
- 最大回撤: 9.55% (发生在 2015年底-2016年初)
- 交易次数: 4565
- 结果分析:
- 与前期对比: 本次回测年化收益 (20.22%) 低于前期报告的组合 (31.49%)。
- 原因:
- 资金规模扩大 (500万 -> 5000万): 为了容纳更大资金,必须加入更多策略,特别是周线级别的长周期策略。
- 周线策略特性: 周线策略通常回撤较大,为了将组合整体回撤控制在可接受范围 (如 10% 左右),不得不降低整体仓位/杠杆。
- 权衡: 这是规模扩大后,在收益与风险之间进行权衡的结果。
4. 未来研究方向
报告指出的未来方向主要是深化多品种、多策略、多周期的理念:
- 增加更多策略类型: 特别是补充小时线级别的策略,丰富组合的收益来源和时间框架。
- 扩展交易品种: 对目前尚未纳入投资范围的期货品种进行测试和研究,看是否适合加入组合。
5. 基于报告的 Python (Backtrader) 日内策略示例
5.1 策略逻辑重述 (Python 视角)
我们将实现报告中描述的日内策略核心逻辑,以沪镍 (Ni) 的参数为例,并做一些简化以便于演示。
- 数据: 需要日线数据(用于趋势过滤)和分钟线数据(用于执行交易逻辑)。
- 过滤: 简化处理,假设我们能获取每日的趋势状态 (+1, -1, 0)。
- 信号:
- 跟踪 T 分钟高低点 (
HT
,LT
)。 - 获取 N 日高低点 (
HN
,LN
)。 - 在允许的时间段内 (
TimeFilter
),根据趋势状态,判断分钟线价格是否突破max(HT, HN)
或跌破min(LT, LN)
。
- 跟踪 T 分钟高低点 (
- 执行:
- 突破/跌破时下单。
- 记录入场价和入场时的反向极值(最低/最高价)作为止损价。
- 管理:
- 每根 K 线检查是否触及止损价。
- (简化:暂时省略 M 点触发 R*M 止盈逻辑,仅保留止损和 EOD 退出)
- 检查是否到达 EOD 时间,若是则平仓。
5.2 Python 代码示例
import backtrader as bt
import datetime
class IntradayBreakoutStrategy(bt.Strategy):
params = (
# 日内策略参数 (以报告中 Ni 为例,略作调整)
('N_days', 1), # HN/LN 的 N 值 (Slen)
('T_minutes_bars', 18), # HT/LT 的 T 值 (Knum, 假设5分钟线)
# ('M_ticks', 1200), # 止盈触发点数 (单位: 最小跳点)
# ('R_profit_factor', 0.6), # 止盈目标因子
('stop_loss_lookback', 1), # 止损基于入场K线本身 (1) 或更前
('time_filter_hour', 14),
('time_filter_minute', 30),
('eod_hour', 14),
('eod_minute', 55),
# 假设的每日趋势状态 (需要外部传入或计算)
# 实际应用中需要日线数据计算唐奇安通道等
('daily_trend', 0), # +1: Long bias, -1: Short bias, 0: Neutral
('debug', False),
)
def __init__(self):
# 引用数据 - 假设 self.data0 为分钟线数据
self.dataclose = self.data0.close
self.datahigh = self.data0.high
self.datalow = self.data0.low
self.dataopen = self.data0.open
# 跟踪订单和状态
self.order = None
self.entry_price = None
self.stop_price = None
self.bars_since_entry = 0
self.session_high = -float('inf')
self.session_low = float('inf')
self.bars_in_session = 0
# 获取 N 日高低点 (需要日线数据支持, 这里用占位符)
# 实际应用中需要加载日线数据并计算
# self.daily_high_N = bt.ind.Highest(self.data_daily.high, period=self.p.N_days)(-1) # -1 获取上一日的值
# self.daily_low_N = bt.ind.Lowest(self.data_daily.low, period=self.p.N_days)(-1)
# 简化: 假设 N=1, 即昨日高低点
# 这里需要一个方法来获取昨日的日线高低点 HN/LN
# 暂时用 hardcode 占位,表示这个值需要从外部获取
self.HN = 85000 # Placeholder - Yesterday's High
self.LN = 84000 # Placeholder - Yesterday's Low
# 跟踪当日 T 分钟高低点 (HT, LT)
self.intra_high_T = -float('inf')
self.intra_low_T = float('inf')
def log(self, txt, dt=None):
''' 日志记录 '''
if self.p.debug:
dt = dt or self.data0.datetime.datetime(0)
print(f'{dt.isoformat()} - {txt}')
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
return # 等待处理完成
if order.status in [order.Completed]:
if order.isbuy():
self.log(f'BUY EXECUTED, Price: {order.executed.price:.2f}, Cost: {order.executed.value:.2f}, Comm: {order.executed.comm:.2f}')
self.entry_price = order.executed.price
# 计算止损价: 入场 K 线之前的最低价
lookback_period = max(1, self.p.stop_loss_lookback)
self.stop_price = min(self.datalow.get(ago=-i, size=lookback_period))
self.log(f'Stop Loss set at: {self.stop_price:.2f}')
elif order.issell():
self.log(f'SELL EXECUTED, Price: {order.executed.price:.2f}, Cost: {order.executed.value:.2f}, Comm: {order.executed.comm:.2f}')
self.entry_price = order.executed.price
# 计算止损价: 入场 K 线之前的最高价
lookback_period = max(1, self.p.stop_loss_lookback)
self.stop_price = max(self.datahigh.get(ago=-i, size=lookback_period))
self.log(f'Stop Loss set at: {self.stop_price:.2f}')
self.bars_since_entry = 0
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log(f'Order Canceled/Margin/Rejected: Status {order.getstatusname()}')
self.order = None # 重置订单跟踪
def notify_trade(self, trade):
if not trade.isclosed:
return
self.log(f'TRADE PROFIT, GROSS {trade.pnl:.2f}, NET {trade.pnlcomm:.2f}')
self.stop_price = None
self.entry_price = None
def next(self):
current_time = self.data0.datetime.time(0)
current_dt = self.data0.datetime.datetime(0)
# 每日开盘重置会话跟踪变量
if current_time.hour == 9 and current_time.minute == 0: # 假设9点开盘
self.session_high = self.datahigh[0]
self.session_low = self.datalow[0]
self.bars_in_session = 1
self.intra_high_T = -float('inf')
self.intra_low_T = float('inf')
# 需要在这里更新 HN 和 LN (昨日高低点)
# self.HN = ...
# self.LN = ...
# 以及获取当天的日线趋势状态 self.p.daily_trend
# self.p.daily_trend = get_daily_trend_status(...)
else:
self.session_high = max(self.session_high, self.datahigh[0])
self.session_low = min(self.session_low, self.datalow[0])
self.bars_in_session += 1
# --- 检查是否持仓 ---
if self.position:
self.bars_since_entry += 1
# 检查 EOD 平仓时间
if current_time.hour == self.p.eod_hour and current_time.minute >= self.p.eod_minute:
self.log(f'EOD Closing position at {self.dataclose[0]:.2f}')
self.order = self.close()
return
# 检查止损
if self.position.size > 0: # 持有多单
if self.datalow[0] <= self.stop_price:
self.log(f'STOP LOSS HIT (Long) at {self.stop_price:.2f}. Current Low: {self.datalow[0]:.2f}')
self.order = self.close()
return
elif self.position.size < 0: # 持有空单
if self.datahigh[0] >= self.stop_price:
self.log(f'STOP LOSS HIT (Short) at {self.stop_price:.2f}. Current High: {self.datahigh[0]:.2f}')
self.order = self.close()
return
# (此处可添加 M 点触发止盈逻辑)
return # 如果持仓,本周期不再开仓
# --- 如果未持仓,判断是否开仓 ---
# 检查时间过滤
if current_time.hour > self.p.time_filter_hour or \
(current_time.hour == self.p.time_filter_hour and current_time.minute >= self.p.time_filter_minute):
return # 时间太晚,不开仓
# 更新 T 分钟高低点 (HT, LT)
if self.bars_in_session <= self.p.T_minutes_bars:
self.intra_high_T = max(self.intra_high_T, self.datahigh[0])
self.intra_low_T = min(self.intra_low_T, self.datalow[0])
# 如果还没到 T 分钟,不开仓
if self.bars_in_session < self.p.T_minutes_bars:
return
# 检查开仓条件
if self.p.daily_trend == 1: # 趋势看多
entry_threshold = max(self.intra_high_T, self.HN)
if self.dataclose[0] > entry_threshold:
self.log(f'LONG ENTRY SIGNAL: Close {self.dataclose[0]:.2f} > Threshold {entry_threshold:.2f} (max(HT={self.intra_high_T:.2f}, HN={self.HN:.2f}))')
self.order = self.buy()
elif self.p.daily_trend == -1: # 趋势看空
entry_threshold = min(self.intra_low_T, self.LN)
if self.dataclose[0] < entry_threshold:
self.log(f'SHORT ENTRY SIGNAL: Close {self.dataclose[0]:.2f} < Threshold {entry_threshold:.2f} (min(LT={self.intra_low_T:.2f}, LN={self.LN:.2f}))')
self.order = self.sell()
# --- Backtrader 执行部分 ---
if __name__ == '__main__':
cerebro = bt.Cerebro()
# 添加策略
cerebro.addstrategy(IntradayBreakoutStrategy, debug=True) # 打开日志
# 添加数据 (需要准备好分钟线数据)
# 例如: 从 CSV 加载数据
data = bt.feeds.GenericCSVData(
dataname='your_5minute_data.csv', # 替换为你的数据文件路径
dtformat=('%Y-%m-%d %H:%M:%S'), # 匹配你的日期时间格式
datetime=0,
open=1,
high=2,
low=3,
close=4,
volume=5,
openinterest=-1, # 如果没有,设为-1
timeframe=bt.TimeFrame.Minutes,
compression=5 # 假设是5分钟数据
)
cerebro.adddata(data)
# 设置初始资金
cerebro.broker.setcash(1000000.0)
# 设置手续费 (按报告中的百分比)
cerebro.broker.setcommission(commission=0.00035, mult=10) # mult是合约乘数,假设为10
# 设置滑点 (按报告中的跳数)
# Backtrader 的滑点设置比较复杂,这里用一个简化的固定滑点模拟
# 假设最小跳点为 10
tick_size = 10.0
entry_slippage_points = 5 * tick_size
exit_slippage_points = 3 * tick_size
# 注意: Backtrader 内建滑点主要基于百分比或固定值,模拟跳数滑点需要自定义
# 这里仅作示意,实际滑点模型可能更复杂
# cerebro.broker.set_slippage_fixed(entry_slippage_points, slip_open=True) # 开仓滑点
# cerebro.broker.set_slippage_fixed(exit_slippage_points, slip_out=True) # 平仓滑点
# 更简单的方式是用 SlippagePercent
# cerebro.broker.set_slippage_perc(perc=0.001) # 0.1% 滑点
# 设置保证金 (按报告中的比例) - Backtrader 默认不检查保证金,需开启
# cerebro.broker.setmargin(margin=0.10) # 10% 保证金
# 按报告设置,单笔最大保证金占用10%,这里通过设置sizer实现近似效果
# 目标是单笔开仓的名义价值不超过总资产的某个比例 (因为保证金是10%, 那么名义价值约等于保证金的10倍)
# 10%保证金占用,相当于名义价值占总资产 100% ? (报告说法有点歧义,是指单笔交易使用的保证金不超过总资产的10%?)
# 如果是后者,那么Sizer这样设置:
cerebro.addsizer(bt.sizers.PercentSizer, percents=100) # 错误理解,这会让单笔名义价值等于全部现金
# 正确理解:单次交易使用的保证金不超过总资金的 10%
# 假设保证金率固定 10%,那么单笔名义价值 = (总资金 * 10%) / 10% = 总资金 * 100% ? 仍然不对
# 另一种理解:单笔分配的名义价值,使其保证金占用为总资金的 10%
# Size = (TotalValue * 10%) / (Price * MarginRate * Multiplier)
# Backtrader Sizer 不直接支持按保证金占用比例,需要自定义 Sizer
# 简化:暂时使用固定手数或简单比例
cerebro.addsizer(bt.sizers.FixedSize, stake=1) # 固定每次交易1手
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
# 运行回测
results = cerebro.run()
strat = results[0] # 获取策略实例
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
# 可以添加分析指标
# cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='mysharpe')
# cerebro.addanalyzer(bt.analyzers.DrawDown, _name='mydrawdown')
# ... 运行后提取分析结果 ...
# print('Sharpe Ratio:', strat.analyzers.mysharpe.get_analysis())
# print('Drawdown:', strat.analyzers.mydrawdown.get_analysis())
# 绘制结果图
# cerebro.plot() # 需要 matplotlib 安装
5.3 代码说明与注意事项
- 数据依赖: 上述代码需要分钟线数据 (
your_5minute_data.csv
)。关键: 它还需要获取每日的趋势状态 (daily_trend
) 和昨日的日线高低点 (HN
,LN
)。这通常需要额外加载日线数据,并在策略的__init__
或start
方法中计算好,或者在next
方法中每日开盘时更新。示例代码中用占位符表示了这些需要外部获取的值。 - 唐奇安通道滤网: 完整的日线唐奇安通道过滤逻辑(包括均线反转重置)没有在示例代码中完全实现,需要结合日线数据进行计算。
daily_trend
参数代表了最终的过滤结果。 - 止盈逻辑简化: 示例代码省略了 M 点触发 R*M 止盈的逻辑,只保留了止损和 EOD 平仓。实现该逻辑需要在
next
方法的持仓部分添加判断:计算当前盈利点数是否超过 M,若超过则设置或更新止盈订单。 - 参数: 代码中的参数 (
N_days
,T_minutes_bars
等) 是根据报告中沪镍的设置给出的。HN
,LN
的占位符值需要根据实际数据动态获取。 - 滑点与保证金: Backtrader 中模拟跳数滑点和按保证金比例确定头寸相对复杂,需要自定义滑点模型和 Sizer。示例代码中使用了简化的手续费和固定手数设置。实际应用需要精确实现。
- 合约乘数: 手续费设置中
mult
需要根据交易品种的实际合约乘数设置。 - 回测细节: 报告中的回测设置非常具体(如开5平3滑点),要在 Backtrader 中精确复制需要较多定制工作。
- 健壮性: 这只是一个基础框架,实际策略还需要考虑更多细节,如异常处理、交易流动性、隔夜风险管理(如果被迫持仓)等。
希望这个详细的解析和代码示例对您理解这份报告和实践其策略思路有所帮助!