Dual Thrust(期货)

admin
2024-03-27 / 0 评论 / 36 阅读 / 正在检测是否收录...

Dual Thrust

1. 原理

由Michael Chalek在20世纪80年代开发的Dual Thrust策略是一个趋势跟踪策略。

其核心思想是定义一个区间,区间的上界和下界分别为支撑线和阻力线。当价格超过上界时,如果持有空仓,先平再开多;如果没有仓位,直接开多。当价格跌破下界时,如果持有多仓,则先平仓,再开空仓;如果没有仓位,直接开空仓。

上下界的设定是交易策略的核心部分。在计算上下界时共用到:最高价、最低价、收盘价、开盘价四个参数。

公式如下:

                  Range = Max(HH-LC,HC-LL)

上限:Open + K1 Range
下限:Open + k2 Range

K1 和 K2一般根据自己经验以及回测结果进行优化。

2. 策略逻辑

第一步:设置参数N、k1、k2
第二步:计算HH、LC、HC、LL
第三步:计算range
第四步:设定做多和做空信号

回测标的:SHFE.rb2010
回测期:2020-02-07 15:00:00 到 2020-04-15 15:00:00
回测初始资金:3万

注意:若修改回测期,需要修改对应的回测标的。

3. 策略代码

# coding=utf-8
from __future__ import print_function, absolute_import
from gm.api import *
"""
Dual Thrust是一个趋势跟踪系统
计算前N天的最高价-收盘价和收盘价-最低价。然后取这2N个价差的最大值,乘以k值。把结果称为触发值。
在今天的开盘,记录开盘价,然后在价格超过上轨(开盘+触发值)时马上买入,或者价格低于下轨(开盘-触发值)时马上卖空。
没有明确止损。这个系统是反转系统,也就是说,如果在价格超过(开盘+触发值)时手头有空单,则平空开多。
同理,如果在价格低于(开盘-触发值)时手上有多单,则平多开空。
选用了SHFE的rb2010 在2020-02-07 15:00:00 到 2020-04-15 15:00:00' 进行回测。
注意: 
1:为回测方便,本策略使用了on_bar的一分钟来计算,实盘中可能需要使用on_tick。
2:实盘中,如果在收盘的那一根bar或tick触发交易信号,需要自行处理,实盘可能不会成交
"""
# 策略中必须有init方法
def init(context):
    # 设置要进行回测的合约(可以在掘金终端的仿真交易中查询标的代码)
    context.symbol = 'SHFE.rb2010'  # 订阅&交易标的, 此处订阅的是上期所的螺纹钢 2010
    # 设置参数
    context.N = 5
    context.k1 = 0.2
    context.k2 = 0.2
    # 获取当前时间
    time = context.now.strftime('%H:%M:%S')
    # 如果策略执行时间点是交易时间段,则直接执行algo定义buy_line和sell_line,以防直接进入on_bar()导致context.buy_line和context.sell_line未定义
    if '09:00:00' < time < '15:00:00' or '21:00:00' < time < '23:00:00':
        algo(context)
    # 如果是交易时间段,等到开盘时间确保进入algo()
    schedule(schedule_func = algo, date_rule = '1d', time_rule = '09:00:00')
    schedule(schedule_func = algo, date_rule = '1d', time_rule = '21:00:00')
    # 只需要最新价,所以只需要订阅一个, 如果用tick,次数太多,用一分钟线代替
    subscribe(symbols=context.symbol, frequency='60s', count = 1)
def algo(context):
    # 取历史数据
    data = history_n(symbol=context.symbol, frequency='1d', end_time=context.now,
                     fields='symbol,open,high,low,close', count=context.N + 1, df=True)
    # 取开盘价
    # 回测模式下,开盘价可以直接用history_n取到
    if context.mode == 2:
        # 获取当天的开盘价
        current_open = data['open'].loc[context.N]
        # 去掉当天的实时数据
        data.drop(context.N, inplace = True)
    # 如果是实时模式,开盘价需要用current取到
    else:
        # 获取当天的开盘价
        current_open = current(context.symbol)[0]['open']
    # 计算Dual Thrust 的上下轨
    HH = data['high'].max()
    HC = data['close'].max()
    LC = data['close'].min()
    LL = data['low'].min()
    range = max(HH - LC, HC - LL)
    context.buy_line = current_open + range * context.k1  # 上轨
    context.sell_line = current_open - range * context.k2  # 下轨
def on_bar(context, bars):
    # 取出订阅的这一分钟的bar
    bar = bars[0]
    # 取出买卖线
    buy_line =  context.buy_line
    sell_line = context.sell_line
    # 获取现有持仓
    position_long = context.account().position(symbol=context.symbol, side=PositionSide_Long)
    position_short = context.account().position(symbol=context.symbol, side=PositionSide_Short)
    # 交易逻辑部分
    # 如果超过range的上界
    if bar.close > buy_line:
        if position_long:  # 已经持有多仓,直接返回
            return
        elif position_short:  # 已经持有空仓,平仓再做多。
            order_volume(symbol=context.symbol, volume=1, side=OrderSide_Buy,
                         order_type=OrderType_Market, position_effect=PositionEffect_Close)
            print('市价单平空仓', context.symbol)
            order_volume(symbol=context.symbol, volume=1, side=OrderSide_Buy,
                         order_type=OrderType_Market, position_effect=PositionEffect_Open)
            print('市价单开多仓', context.symbol)
        else:  # 没有持仓时,市价开多。
            order_volume(symbol=context.symbol, volume=1, side=OrderSide_Buy,
                         order_type=OrderType_Market, position_effect=PositionEffect_Open)
            print('市价单开多仓', context.symbol)
    # 如果低于range的下界
    elif bar.close < sell_line:
        if position_long:  # 已经持有多仓, 平多再开空。
            order_volume(symbol=context.symbol, volume=1, side=OrderSide_Sell,
                         order_type=OrderType_Market, position_effect=PositionEffect_Close)
            print('市价单平多仓', context.symbol)
            order_volume(symbol=context.symbol, volume=1, side=OrderSide_Sell,
                         order_type=OrderType_Market, position_effect=PositionEffect_Open)
            print('市价单开空仓', context.symbol)
        elif position_short:  # 已经持有空仓,直接返回。
            return
        else:  # 没有持仓,直接开空
            order_volume(symbol=context.symbol, volume=1, side=OrderSide_Sell,
                         order_type=OrderType_Market, position_effect=PositionEffect_Open)
            print('市价单开空仓', context.symbol)
if __name__ == '__main__':
    '''
        strategy_id策略ID,由系统生成
        filename文件名,请与本文件名保持一致
        mode实时模式:MODE_LIVE回测模式:MODE_BACKTEST
        token绑定计算机的ID,可在系统设置-密钥管理中生成
        backtest_start_time回测开始时间
        backtest_end_time回测结束时间
        backtest_adjust股票复权方式不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST
        backtest_initial_cash回测初始资金
        backtest_commission_ratio回测佣金比例
        backtest_slippage_ratio回测滑点比例
    '''
    run(strategy_id='strategy_id',
        filename='main.py',
        mode=MODE_BACKTEST,
        token='token_id',
        backtest_start_time='2020-02-07 15:00:00',
        backtest_end_time='2020-04-15 15:00:00',
        backtest_initial_cash= 30000,
        backtest_commission_ratio=0.0001,
        backtest_slippage_ratio=0.0001)

4. 回测结果与稳健性分析

设定初始资金3万,手续费率为0.01%,滑点比率为0.01%,N=5,K1=0.2,K2=0.2,回测期为2020-02-07 到 2020-04-15时,回测结果如下图所示。
lu9v12l2.png
回测期策略累计收益率为9.88%,年化收益率为52.29%。同期沪深300指数收益率为-2.63%,策略跑赢沪深300指数。最大回撤为5.20%,夏普比率为2.63。

为了检验策略的稳健性,设置不同的k1、k2参数,并且设置不同的回测期,检验回测结果。回测结果如下表所示。
lu9v432w.png
可以发现,在2020年2月-2020年4月,不论k1、k2如何设置,收益率均维持为正,回撤比较稳定。在其他月份,回测结果较差。说明回测参数设置对于回测结果的影响较小,回测期对于回测结果的影响较大。

0

评论 (0)

取消