好的,这份东方证券的报告详细测试了国内商品期货常用的几种日内CTA策略。以下将根据报告内容,为您提供一份包含目录、深度解析、总结和策略Python示例代码的中文摘要。
报告摘要与解析:《国内商品期货常用日内 CTA 策略测试》
发布机构: 东方证券
发布日期: 2016年10月13日
核心研究员: 朱剑涛
目录
- 报告概述与核心结论
- 常用日内 CTA 策略介绍
- 2.1 Dual Thrust 策略
- 2.2 ATR 策略
- 2.3 R-Breaker 策略
- 2.4 菲阿里四价策略
- 2.5 空中花园策略
- 策略回测的准备与设置
- 3.1 数据准备
- 3.2 回测框架与参数设置
- 3.3 一般性过滤条件
- 主要 CTA 策略实证研究
- 4.1 Dual Thrust 策略实证
- 参数选择与敏感性分析
- ATR 过滤优化
- 止盈止损优化
- 跨品种表现与推进分析
- 4.2 ATR 策略实证
- 4.3 R-Breaker 策略实证
- 4.4 菲阿里四价策略实证
- 4.5 空中花园策略实证
- 4.1 Dual Thrust 策略实证
- 策略相关性分析与组合构建
- 总结
- 风险提示
- 策略 Python 示例代码 (根据报告描述)
- 8.1 Dual Thrust
- 8.2 ATR 通道突破
- 8.3 R-Breaker
- 8.4 菲阿里四价
- 8.5 空中花园
1. 报告概述与核心结论
本报告旨在测试国内商品期货市场上几种常见的日内CTA(Commodity Trading Advisor,商品交易顾问)策略的实际效果。研究选取了10个成交活跃的商品期货品种,基于1分钟K线数据,使用Python构建了统一的回测平台,对包括Dual Thrust、ATR通道、R-Breaker、菲阿里四价和空中花园在内的策略进行了回溯测试,并在原始策略基础上增加了止盈、止损、信号过滤、交易次数限制等优化措施。
核心结论:
- 整体表现: 商品期货的日内CTA策略表现普遍逊色于股指期货,单品种单策略的夏普比率(Sharpe Ratio)很难达到2,收益回撤比很难超过3。
- 策略对比:
- 菲阿里四价和空中花园策略在大部分品种上表现不佳。仅加入止盈止损后的空中花园策略在橡胶上有一定表现。
- Dual Thrust、ATR通道和R-Breaker策略表现相对较好,但主要集中在螺纹钢、橡胶、白糖等少数几个品种上。
- 优化效果: ATR波动率过滤、动态止盈和固定止损等优化手段,能一定程度上改善策略表现,提高夏普比率和收益风险比,降低回撤。特别是ATR过滤能有效剔除部分震荡行情下的无效信号。
- 组合价值: 国内商品期货品种丰富,是其相对于股指期货的最大优势。通过构建多品种、多策略的投资组合,利用策略和品种间的低相关性,可以有效分散风险,获得更稳健的收益。例如,报告中展示了结合ATR白糖和R-Breaker橡胶的低相关性组合(相关性0.02),在30%保证金下可获得年化28.5%收益,最大回撤7.6%,夏普比率达到2.3。
- 参数敏感性: 策略参数(如Dual Thrust的K1/K2,ATR通道的N/M,R-Breaker的参数)对结果有显著影响,需要针对不同品种进行优化。
2. 常用日内 CTA 策略介绍
报告中主要测试了以下五种基于趋势突破逻辑的日内交易策略:
- 2.1 Dual Thrust 策略:
- 核心思想: 利用前N日的最高价(HH)、最低价(LL)、收盘价最高值(HC)、收盘价最低值(LC)计算出一个价格范围(Range),Range = Max(HH-LC, HC-LL)。当日开盘价加上/减去Range乘以一个系数(K1/K2)得到上下轨。价格突破上轨做多,突破下轨做空。
- 特点: K1和K2可以设置不同,允许非对称突破,思路简单,适用性较广。
- 2.2 ATR 策略:
- 核心思想: 利用平均真实波幅(ATR)来衡量市场波动性,并构建通道。通道上轨 = 当根K线开盘价 + N周期ATR * M,下轨 = 当根K线开盘价 - N周期ATR * M。价格突破上轨做多,跌穿下轨做空。
- 特点: 通道宽度随波动率动态调整。ATR也可用于设置止损。
- 2.3 R-Breaker 策略:
- 核心思想: 结合了趋势和反转交易。根据前一日的最高价、最低价和收盘价计算出六个关键价位:突破买入价、观察卖出价、反转卖出价、反转买入价、观察买入价、突破卖出价。
- 交易逻辑:
- 反转: 持多单时,价格跌破反转卖出价则反手做空;持空单时,价格升破反转买入价则反手做多。
- 趋势: 空仓时,价格突破突破买入价做多;价格跌破突破卖出价做空。
- 特点: 机制较为复杂,试图捕捉趋势和转折点。报告发现去除反转部分、只做趋势突破时,部分品种表现反而更好。
- 2.4 菲阿里四价策略 (Aberration / Pivot Point Variation):
- 核心思想: 一种极简的突破策略。上轨 = 昨日最高价,下轨 = 昨日最低价。
- 交易逻辑: 价格突破上轨(昨日高点)做多;价格跌穿下轨(昨日低点)做空。
- 特点: 逻辑最简单,但过于依赖昨日极值点,实测效果不佳。
- 2.5 空中花园策略 (Opening Range Breakout with Gap):
- 核心思想: 基于开盘跳空的突破策略。首先判断当日开盘价是否相对昨日收盘价大幅跳空(报告中设定为超过1%)。若满足跳空条件,则以上/下轨 = 开盘后第一根K线的最高/最低价。
- 交易逻辑: 满足跳空条件后,价格突破第一根K线最高价做多;跌破第一根K线最低价做空。
- 特点: 试图捕捉由隔夜消息引发的大幅跳空后的日内趋势延续。交易次数较少,胜率依赖于跳空后的趋势强度。
3. 策略回测的准备与设置
- 3.1 数据准备:
- 使用国内三大商品交易所(上期所、大商所、郑商所)自2003年以来的主要商品期货品种的1分钟K线数据(包含OHLC、成交量、持仓量)。
- 主力合约定义: 采用持仓量最大的合约作为主力合约,每日收盘后判断,连续5日持仓量最大且与前主力不同时才进行切换,以减少频繁切换和跳空。
- 测试品种: 选取了10个近两年活跃度高、关注度高的品种进行测试(螺纹钢、焦炭、沪铜、黄金、塑料、橡胶、白糖、棉花、豆粕、棕榈油)。
- 3.2 回测框架与参数设置:
- 平台: 使用免费开源的 Python 语言搭建回测框架。
- 保证金与杠杆: 固定保证金比例10%,最大可用仓位30%(即实际杠杆最高3倍)。
- 交易成本: 冲击成本设为万分之二,手续费设为万分之零点三。
- 3.3 一般性过滤条件:
- 开盘/收盘过滤: 过滤掉开盘后半小时(9:00-9:30)和收盘前10-15分钟的交易信号,规避开盘剧烈波动和收盘前的不确定性。
- 下午开仓过滤: 过滤掉下午发出的开仓信号,避免持仓时间过短。
- 交易次数限制: 限制每日交易次数,降低高频交易成本和过度交易风险。
4. 主要 CTA 策略实证研究
报告对每种策略都进行了详细的实证分析,以Dual Thrust为例,展示了主要研究步骤:
- 4.1 Dual Thrust 策略实证:
- 参数选择与敏感性: 通过绘制参数(k1, k2)与评价指标(年化收益、最大回撤、收益风险比、夏普比率、胜率、盈亏比)的热力图,分析参数敏感性,并找出全样本“最优”参数。发现k1(多头参数)的敏感性低于k2(空头参数)。
- ATR 过滤优化: 引入ATR指标过滤低波动行情。计算ATR与当日收益率的关系,发现在低ATR(如<0.02)时,收益随机性强,平均收益接近零。通过设定ATR阈值,过滤掉低波动日的交易信号,能有效提升策略表现(提高夏普比率和胜率)。
- 止盈止损优化: 引入动态止盈(按持仓期最高价回撤一定比例p止盈)和固定止损(按开仓价回撤一定比例q止损)。通过参数(p, q)热力图寻找最优止盈止损比例。发现加入合理的止盈止损能显著降低最大回撤,提升收益风险比和夏普比率。
- 跨品种表现: 将优化后的策略(带ATR过滤和止盈止损)应用于10个品种,寻找各品种的“最优”参数组合。结果显示螺纹钢、橡胶、白糖表现最好。
- 推进分析 (Walk-Forward Analysis): 为了模拟真实交易,采用滚动窗口(如每年、每两年、每三年)优化参数,并将优化得到的参数应用于下一个周期进行回测。结果显示,推进分析得到的表现虽然劣于样本内最优结果,但仍能获得较为稳定正收益,证明策略有一定鲁棒性。
- 4.2 - 4.5 其他策略实证:
- ATR策略: 表现与Dual Thrust类似,在橡胶、白糖上较好。经过优化后有所提升。
- R-Breaker策略: 原始策略表现一般,优化(止盈止损)后有所改善。有趣的是,报告发现去除反转交易逻辑,仅保留趋势突破部分,在某些品种上表现反而更好,暗示商品期货日内反转信号可能噪音较大。
- 菲阿里四价策略: 表现最差,即使加入止盈止损优化,大部分品种仍为负收益。
- 空中花园策略: 原始策略表现不佳。加入止盈止损优化后,橡胶品种表现突出,其他品种改善有限。
5. 策略相关性分析与组合构建
- 相关性分析: 计算了表现较好(优化后Sharpe > 1.5, 收益风险比 > 2)的策略/品种收益序列之间的相关系数。
- 同策略跨品种: 相关性普遍较低,说明品种间走势独立性较强。
- 同品种跨策略: ATR和Dual Thrust策略在同一品种上相关性较高(如橡胶0.56,白糖0.75),因其逻辑相似。其他策略间相关性较低。
- 组合构建:
- 低相关组合: 选取相关性极低(0.02)的ATR白糖和R-Breaker橡胶构建组合。
- 高相关组合: 选取相关性高(0.75)的ATR白糖和Dual Thrust白糖构建组合。
- 构建方法:
- 固定初始仓位比例1:1,之后独立运行。
- 每日收盘后按1:1比例重新分配资金(再平衡)。
- 结果: 低相关组合(ATR白糖+R-Breaker橡胶)无论采用哪种构建方法,其夏普比率(2.2-2.3)、收益风险比(3.7-3.8)和最大回撤(约-7.5%)均显著优于单个策略,也优于高相关组合。这证明了通过低相关策略/品种进行组合是提升CTA策略表现的有效途径。
6. 总结
- 国内商品期货日内CTA策略的直接应用效果普遍不如股指期货,需要精细化优化。
- 菲阿里四价和空中花园策略(未优化)基本失效。Dual Thrust, ATR, R-Breaker相对较好,尤其在螺纹钢、橡胶、白糖上。
- ATR波动率过滤和止盈止损是有效的优化手段,能提升策略稳定性。
- 商品期货品种丰富、相关性低是构建稳健投资组合的基础。选择低相关的策略和品种进行组合,能显著改善整体风险收益特征。
- 日内交易策略的利润来源于市场波动和一定程度的无效性(信息传递时滞、行为偏差等)。
7. 风险提示
- 量化模型失效风险: 基于历史数据优化的模型可能在未来市场环境中失效。
- 市场极端环境冲击: 极端行情(如流动性枯竭、大幅跳空、政策突变)可能导致模型表现剧烈恶化,产生超预期亏损。
8. 策略 Python 示例代码 (根据报告描述)
注意: 以下代码仅为策略核心逻辑的简单示意,并非完整的可运行回测代码。需要结合具体的回测框架(如vnpy, backtrader等)和数据进行实现。假设已有pandas DataFrame df
包含 datetime
, open
, high
, low
, close
列,以及预先计算好的所需指标(如ATR, 前N日极值等)。
import pandas as pd
import numpy as np
# 假设 df 是包含1分钟K线数据的DataFrame
# 假设 pre_day_data 包含前一日的 OHLC 数据
# 假设 atr_value 是当前计算好的ATR值
# 假设 params 是一个包含策略参数的字典
def dual_thrust_signal(df, current_index, params, pre_n_data):
"""
Dual Thrust 信号生成示例
params: {'N': N天周期, 'K1': K1系数, 'K2': K2系数}
pre_n_data: 包含过去N天OHLC数据的DataFrame
"""
if current_index < 1: return 0 # 需要前一根K线
N = params['N']
K1 = params['K1']
K2 = params['K2']
# 计算 N 日 HH, LL, HC, LC (需要 pre_n_data 数据)
# 这里仅为示意,实际需要从 pre_n_data 计算
HH = pre_n_data['high'].rolling(N).max().iloc[-1]
LC = pre_n_data['close'].rolling(N).min().iloc[-1]
HC = pre_n_data['close'].rolling(N).max().iloc[-1]
LL = pre_n_data['low'].rolling(N).min().iloc[-1]
range_val = max(HH - LC, HC - LL)
current_open = df['open'].iloc[current_index]
buy_line = current_open + K1 * range_val
sell_line = current_open - K2 * range_val
current_high = df['high'].iloc[current_index]
current_low = df['low'].iloc[current_index]
signal = 0
if current_high > buy_line:
signal = 1 # 做多信号
elif current_low < sell_line:
signal = -1 # 做空信号
return signal
def atr_channel_signal(df, current_index, params, atr_value):
"""
ATR 通道突破信号生成示例
params: {'M': ATR倍数}
atr_value: 当前周期的ATR值 (需预先计算好)
"""
if current_index < 1 or atr_value is None or np.isnan(atr_value): return 0
M = params['M']
current_open = df['open'].iloc[current_index]
upper_band = current_open + M * atr_value
lower_band = current_open - M * atr_value
current_high = df['high'].iloc[current_index]
current_low = df['low'].iloc[current_index]
signal = 0
if current_high > upper_band:
signal = 1 # 做多信号
elif current_low < lower_band:
signal = -1 # 做空信号
return signal
def r_breaker_signal(df, current_index, params, pre_day_data):
"""
R-Breaker 信号生成示例 (简化逻辑)
params: 通常是固定系数,这里用字典占位 {'f1': 0.35, 'f2': 1.07, 'f3': 0.07, 'f4': 0.25}
pre_day_data: 包含前一日 OHLC 数据的 Series/dict
"""
if current_index < 1 or pre_day_data is None: return 0
prev_high = pre_day_data['high']
prev_low = pre_day_data['low']
prev_close = pre_day_data['close']
f1 = params.get('f1', 0.35)
f2 = params.get('f2', 1.07)
f3 = params.get('f3', 0.07)
f4 = params.get('f4', 0.25)
# 计算六个价位
setup_sell = prev_high + f1 * (prev_close - prev_low) # 观察卖出价 Ssetup
setup_buy = prev_low - f1 * (prev_high - prev_close) # 观察买入价 Bsetup
reverse_sell = f2 / 2 * (prev_high + prev_low) - f3 * prev_low # 反转卖出价 Senter
reverse_buy = f2 / 2 * (prev_high + prev_low) - f3 * prev_high # 反转买入价 Benter
breakout_buy = setup_sell + f4 * (setup_sell - setup_buy) # 突破买入价 Bbreak
breakout_sell = setup_buy - f4 * (setup_sell - setup_buy) # 突破卖出价 Sbreak
current_high = df['high'].iloc[current_index]
current_low = df['low'].iloc[current_index]
# 获取当前持仓状态 (需要回测引擎提供, 此处假设为0)
current_position = 0 # 0:空仓, 1:持多, -1:持空
signal = 0
# 简化信号逻辑:仅处理空仓时的突破信号
if current_position == 0:
if current_high > breakout_buy:
signal = 1 # 趋势做多
elif current_low < breakout_sell:
signal = -1 # 趋势做空
# 实际R-Breaker还需处理持仓时的反转逻辑 (更复杂)
# if current_position == 1: # 持多单
# if current_high > setup_sell and current_low < reverse_sell:
# signal = -2 # 反手做空信号 (需要区分首次开仓和平仓反手)
# elif current_position == -1: # 持空单
# if current_low < setup_buy and current_high > reverse_buy:
# signal = 2 # 反手做多信号
return signal # 简化只返回 1 或 -1
def aberration_signal(df, current_index, params, pre_day_data):
"""
菲阿里四价信号生成示例
params: {} (无特殊参数)
pre_day_data: 包含前一日 OHLC 数据的 Series/dict
"""
if current_index < 1 or pre_day_data is None: return 0
prev_high = pre_day_data['high']
prev_low = pre_day_data['low']
upper_rail = prev_high
lower_rail = prev_low
current_high = df['high'].iloc[current_index]
current_low = df['low'].iloc[current_index]
signal = 0
if current_high > upper_rail:
signal = 1 # 做多信号
elif current_low < lower_rail:
signal = -1 # 做空信号
return signal
def opening_range_breakout_signal(df, current_index, params, pre_day_data, first_bar_data):
"""
空中花园 (Opening Range Breakout with Gap) 信号生成示例
params: {'gap_ratio': 0.01}
pre_day_data: 包含前一日 OHLC 数据的 Series/dict
first_bar_data: 当日第一根K线的数据 Series/dict (需在开盘后第一个bar结束时获取)
"""
if current_index < 1 or pre_day_data is None or first_bar_data is None: return 0
gap_ratio = params.get('gap_ratio', 0.01)
current_open = df['open'].iloc[current_index]
prev_close = pre_day_data['close']
# 检查是否是开盘后的第一个bar之后
is_after_first_bar = (df['datetime'].iloc[current_index].time() > first_bar_data['datetime'].time())
# 检查跳空条件 (仅在开盘时检查一次)
gap_up = current_open >= prev_close * (1 + gap_ratio)
gap_down = current_open <= prev_close * (1 - gap_ratio)
gap_condition_met = gap_up or gap_down # 实际应用中可能需要保存这个状态
if not gap_condition_met or not is_after_first_bar:
return 0 # 不满足跳空或在第一个bar内,不产生信号
# 使用第一个bar的高低点作为轨道
orb_high = first_bar_data['high']
orb_low = first_bar_data['low']
current_high = df['high'].iloc[current_index]
current_low = df['low'].iloc[current_index]
signal = 0
if current_high > orb_high:
signal = 1 # 做多信号
elif current_low < orb_low:
signal = -1 # 做空信号
return signal
这份摘要和解析希望能帮助您理解东方证券这份关于国内商品期货日内CTA策略的研究报告。请注意,报告基于2016年的数据和市场环境,策略的有效性可能随时间变化。示例代码仅为策略逻辑示意。