商品期货策略-ATR通道突破策略

策略分享
商品期货
标签: #<Tag:0x00007f5c003ca0d8> #<Tag:0x00007f5c003c9e58>

(iQuant) #1

导语:商品期货交易上线啦!听闻这个消息的小编当然坐不住了,决定立刻商品期货走一波!本文选择实现的是经典的ATR通道突破策略(也被称为波动性突破策略,曾多年获得实盘前10策略殊荣),让我们来看看ATR通道突破策略在商品期货中的应用吧!

背景知识

真实波动幅度均值(ATR)是由威尔斯·威尔德(J. Welles Wilder)在其1978年所著的《技术分析中的新概念》('New concepts in Technical Trading Systems” 1978, ISBN 0-89459-027-8)一书中首先提出的,这一指标主要用来衡量证券价格的波动。因此,这一技术指标并不能直接反映价格走向及其趋势稳定性,而只是表明价格波动的程度。他观察到随着趋势的发展,市场参与者的情绪反应更加强烈,日波幅逐渐增大。同样地,方向不明,在一定的范围盘整时,平均真实波幅最终向上突破通常也指示了价格的突破。

真实波动幅度均值(ATR)是优秀的交易系统设计者的一个不可缺少的工具,它称得上是技术指标中的一匹真正的劲马。每一位系统交易者都应当熟悉ATR及其具有的许多有用功能。其众多应用包括:参数设置,入市,止损,止盈等,甚至是资金管理中的一个非常有价值的辅助工具。本文只做入市的应用介绍。

真实波幅(TR)

话不多说先上公式!

image

其中:High是指当日最高价,Low为当日最低价,pre_close是指前一日收盘价。
公式看上去很复杂,其实它要表达的就是昨日收盘以后标的的最大波幅,让我们来看看K线图里真实波幅具体指哪一部分。

从图片中我们可以很容易的看出,真实波幅就是昨天收盘后股票的最大振幅,也就是图片中最长的那一根箭头所表示的位置。

平均真实波幅(ATR)

平均真实波幅其实就是真实波幅的一个移动平均值,话不多说,直接看公式:
image

或者用滑动平均的方法:
image

其中:days是取平均的天数,比如我们要取真实波幅20日的平均,days就取20;TrueRange是真实波幅。
从公式可以看出,ATR值其实就是标的(证券或者期货)days日内的平均真实波幅,当这个值大的时候,就说明这段时间标的每一天的波动率都很大,当这个值小的时候,就说明这段时间每一天的波动率都很小。

策略实现

指标计算:

  • 中轨:收盘价的25日移动平均值
  • 通道宽度:平均真实波幅*2
  • 上轨:中轨+通道宽度
  • 下轨:中轨-通道宽度

交易逻辑:

  • 当价格突破上轨,进场买入,建多仓
  • 当价格突破下轨,进场卖出,建空仓
  • 一个完整的交易系统需要包括仓位管理和资金配置,这里不做介绍,小编多年的期货产品管理经验(😢😢😢)认为,期货交易系统只要具备出场逻辑(这样的系统也称为正反手系统),那么无需再加止盈止损模块。

策略信号如下,当价格突破上轨开多,当价格突破下轨,开空,只要标的具备波动性,那么策略是可以盈利的。


下面是商品期货策略样例代码,如果要模拟交易正常运行的话,需要单独修改下日期参数,我会在下文给出样例。欢迎大家 克隆研究

克隆策略

ATR突破择时策略

ATR是Average True Range平均真实范围的简称,利用这个指标,可以构建一个基于价格波动的通道。

当价格往上突破通道上轨时,买入股票

当价格往下突破通道下轨时,卖出股票

1. 策略参数

In [1]:
# 1. 策略基本参数
def prepare(context):
    # 策略比较参考标准,以沪深300为例
    benchmark = '000300.INDX'

2. 策略主体函数

In [2]:
# 初始化虚拟账户状态,只在第一个交易日运行
def initialize(context):
    # 设置是否是结算模式
    context.set_need_settle(False)
    # 设置手续费,PerContract 说明是每手手续费多少,分别是开仓、平昨、平今
    context.set_commission(equities_commission=None, futures_commission=None) # 手续费按系统默认手续费设置
    # 设置最大杠杆
    context.set_max_leverage(0.6, 'fill_amap') # 这里能够设置最大杠杆
    context.observation = 30
    context.width = 2
    context.ma_length = 25
    
def handle_data(context, data):
    
    if context.trading_day_index < context.observation: # 在30个交易日以后才开始真正运行
        return
    today = data.current_dt.strftime('%Y-%m-%d') # 当前交易日期
    instrument = context.future_symbol(context.instruments[0]) # 交易标的
    curr_po=context.portfolio.positions[instrument] # 持仓
    curr_position = curr_po.amount  # 持仓数量
     
    price = data.current(instrument, 'price')  # 当前价格
    high_price= np.array(data.history(instrument, 'high', context.observation, '1d')) # high
    low_price= np.array(data.history(instrument, 'low', context.observation, '1d')) # low
    close_price= np.array(data.history(instrument, 'close', context.observation, '1d')) # close_price

    # 判断获取的数据中多少天是缺失数据,如果全是缺失数据,是不能计算ATR指标的
    nan_num = [k for k in [np.isnan(i) for i in close_price] if k == True]
    if len(nan_num) == context.observation:
        return

    # 创建ATR买卖信号,包括最高价,最低价,收盘价和参数timeperiod
    # 注意:ATR函数使用的price必须是narray
    import talib
    atr = talib.ATR(high_price,low_price,close_price, timeperiod=context.ma_length)[-1]
    ma = data.history(instrument, 'high', context.ma_length, '1d').mean()
    high_line = ma + context.width * atr # 上轨
    low_line = ma - context.width * atr # 下轨
 
    # 交易逻辑
    if price >= high_line and curr_position <=0 and data.can_trade(instrument): # 开多
        order_target(instrument,  5) # order_target的含义是本次下单使得最终的仓位为目标仓位,如果是正数,那么就是多头仓位
        print(today, '买入开仓')

    elif price <= low_line and curr_position >= 0 and data.can_trade(instrument): # 开空
        order_target(instrument,  -5) # 下单后使得仓位为5手空单
        print(today, '卖出开仓')

3. 回测接口

In [3]:
# 3. 启动回测
# 策略回测接口: https://v2.bigquant.com/docs/module_trade.html
m = M.trade.v4(
    instruments= ['RU1809.SHF'],
    start_date='2017-11-03',
    end_date='2018-07-01',
    prepare=prepare,
    initialize=initialize,
    handle_data=handle_data,
    # 买入订单以开盘价成交
    order_price_field_buy='open',
    # 卖出订单以开盘价成交
    order_price_field_sell='open',
    capital_base=1000000,
    benchmark='000300.SHA',
    product_type = 'future',
    m_deps=np.random.rand()
)
[2018-09-10 11:51:22.988519] INFO: bigquant: backtest.v8 开始运行..
[2018-09-10 11:51:22.992723] INFO: bigquant: biglearning backtest:V8.0.8
[2018-09-10 11:51:22.993462] INFO: bigquant: product_type:future by specified
[2018-09-10 11:51:25.616801] INFO: algo: TradingAlgorithm V1.2.8
2018-01-23 卖出开仓
[2018-09-10 11:51:26.472659] INFO: Performance: Simulated 160 trading days out of 160.
[2018-09-10 11:51:26.473782] INFO: Performance: first open: 2017-11-02 21:00:00+00:00
[2018-09-10 11:51:26.474583] INFO: Performance: last close: 2018-06-29 15:00:00+00:00
  • 收益率16.97%
  • 年化收益率28.01%
  • 基准收益率-12.16%
  • 阿尔法0.17
  • 贝塔-0.26
  • 夏普比率1.99
  • 胜率1.0
  • 盈亏比0.0
  • 收益波动率11.25%
  • 信息比率0.11
  • 最大回撤4.98%
[2018-09-10 11:51:27.038501] INFO: bigquant: backtest.v8 运行完成[4.050003s].

(youke) #4

看交易逻辑,是不是有未来函数?当天K线没走完的话,如何知道最高最低价?

import talib
atr = talib.ATR(high_price,low_price,close_price, timeperiod=context.ma_length)[-1]
ma = data.history(instrument, 'high', context.ma_length, '1d').mean()
high_line = ma + context.width * atr # 上轨
low_line = ma - context.width * atr # 下轨

# 交易逻辑
if price >= high_line and curr_position <=0 and data.can_trade(instrument): # 开多
    order_target(instrument,  5) # order_target的含义是本次下单使得最终的仓位为目标仓位,如果是正数,那么就是多头仓位
    print(today, '买入开仓')

elif price <= low_line and curr_position >= 0 and data.can_trade(instrument): # 开空
    order_target(instrument,  -5) # 下单后使得仓位为5手空单
    print(today, '卖出开仓')

(chad) #5

为了规避未来函数,BigQuant回测平台采取的是当根Bar出信号,下根Bar成交的机制。
这个例子是日线的例子,不会出现你说的这个问题。