Dual Thrust策略

策略介绍

Dual Thrust是一个趋势跟踪系统,由Michael Chalek在20世纪80年代开发,曾被Future Thruth杂志评为最赚钱的策略之一。 Dual Trust是一个追涨杀跌的策略,原理并不复杂。是一个简单而又有效的短期趋势策略。

计算方法(以日为单位举例)

Dual Thrust策略利用前N日的最高价,最低价和收盘价,来确定一个合理的震荡区间Range。利用前一时间点的开盘价和Range,确定当前的上下双轨。如果当前价格向上/向下突破Range一定的比例,则认为一波上涨/下跌行情形成,产生买入/卖出信号。

计算方法如下(以天为单位举例):

(1)N日内每天的最高价HIGH(N), 最低价LOW(N), 和收盘价CLOSE(N)
(2)HH = N日HIGH的最高价 = MAX(HIGH(N))
        LC = N日CLOSE的最低价 = MIN(CLOSE(N))
        HC = N日CLOSE的最高价 = MAX(CLOSE(N))
        LL = N日LOW的最低价 = MIN(LOW(N))
(3)计算震荡区间Range
        Range = MAX(HH-LC, HC - LL)
(4)计算上下轨
        上轨 = 开盘价 + K1 * Range
        下轨 = 开盘价 - K2 * Range
        K1, K2为Range的系数,由用户自行设定。

使用方法

Dual Thrust策略是一个趋势突破策略。计算好上下轨之后,一旦价格突破上轨,我们就买入;当价格突破下轨,我们就卖出。所以,对于上下轨系数的设定十分关键。

K1和K2的值越小,越容易触发对应的轨道。而且,当K1<K2时,多头相对容易被触发;当K1>K2时,空头相对容易被触发。因此,投资者在使用该策略时,一方面可以参考历史数据测试的最优参数,另一方面,则可以根据自己对后市的判断,或从其他大周期的技术指标入手,阶段性地动态调整K1和K2的值。

优点

Dual Thrust系统具有简单易用、适用度广的特点,其思路简单、参数很少,配合不同的参数、止盈止损和仓位管理,可以为投资者带来长期稳定的收益,被投资者广泛应用于股票、货币、贵金属、债券、能源及股指期货市场等。

Dual Thrust在Range的设置上,引入前N日的四个价位,使得一定时期内的Range相对稳定,可以适用于日间的趋势跟踪。同时,Dual Thrust对于多头和空头的触发条件,考虑了非对称的幅度,可以通过参数K1和K2来确定。

代码

# !/usr/bin/env python
# -*- coding: utf-8 -*-

# 策略代码总共分为三大部分,1)PARAMS变量 2)initialize函数 3)handle_data函数
# 请根据指示阅读。或者直接点击运行回测按钮,进行测试,查看策略效果。

# 策略名称:Dual Thrust策略
# 策略详细介绍:https://wequant.io/study/strategy.dual_thrust.html
# 关键词:追涨杀跌、价格通道、止损。
# 方法:
# 1)根据一段时间内的最高价,最低价和收盘价,计算出一个价格上下限;
# 2)当前价格突破上限时,全仓买入;当价格突破下线时,全仓卖出;
# 3)加入了止盈止损机制。

import numpy as np

# 阅读1,首次阅读可跳过:
# PARAMS用于设定程序参数,回测的起始时间、结束时间、滑点误差、初始资金和持仓。
# 可以仿照格式修改,基本都能运行。如果想了解详情请参考新手学堂的API文档。
PARAMS = {
    "start_time": "2017-02-01 00:00:00",  # 回测起始时间
    "end_time": "2017-08-01 00:00:00",  # 回测结束时间
    "slippage": 0.003,  # 此处"slippage"包含佣金(千二)+交易滑点(千一)
    "account_initial": {"huobi_cny_cash": 100000,
                      "huobi_cny_btc": 0},  # 设置账户初始状态
}

# 阅读2,遇到不明白的变量可以跳过,需要的时候回来查阅:
# initialize函数是两大核心函数之一(另一个是handle_data),用于初始化策略变量。
# 策略变量包含:必填变量,以及非必填(用户自己方便使用)的变量
def initialize(context):
    # 设置回测频率, 可选:"1m", "5m", "15m", "30m", "60m", "4h", "1d", "1w"
    context.frequency = "1d"
    # 设置回测基准, 比特币:"huobi_cny_btc", 莱特币:"huobi_cny_ltc", 以太坊:"huobi_cny_eth"
    context.benchmark = "huobi_cny_btc"
    # 设置回测标的, 比特币:"huobi_cny_btc", 莱特币:"huobi_cny_ltc", 以太坊:"huobi_cny_eth"
    context.security = "huobi_cny_btc"

    # 设置策略参数
    # 计算HH,HC,LC,LL所需的历史bar数目,用户自定义的变量,可以被handle_data使用;如果只需要看之前1根bar,则定义window_size=1
    context.user_data.window_size = 5
    # 用户自定义的变量,可以被handle_data使用,触发多头的range
    context.user_data.K1 = 0.2
    # 用户自定义的变量,可以被handle_data使用,触发空头的range.当K1<K2时,多头相对容易被触发,当K1>K2时,空头相对容易被触发
    context.user_data.K2 = 0.5
    # 止损线,用户自定义的变量,可以被handle_data使用
    context.user_data.portfolio_stop_loss = 0.75
    # 用户自定义变量,记录下是否已经触发止损
    context.user_data.stop_loss_triggered = False
    # 止盈线,用户自定义的变量,可以被handle_data使用
    context.user_data.portfolio_stop_win = 100000
    # 用户自定义变量,记录下是否已经触发止盈
    context.user_data.stop_win_triggered = False

# 阅读3,策略核心逻辑:
# handle_data函数定义了策略的执行逻辑,按照frequency生成的bar依次读取并执行策略逻辑,直至程序结束。
# handle_data和bar的详细说明,请参考新手学堂的解释文档。
def handle_data(context):
    # 若已触发止盈/止损线,不会有任何操作
    if context.user_data.stop_loss_triggered:
        context.log.warn("已触发止损线, 此bar不会有任何指令 ... ")
        return
    if context.user_data.stop_win_triggered:
        context.log.info("已触发止盈线, 此bar不会有任何指令 ... ")
        return

    # 检查是否到达止损线或者止盈线
    if context.account.huobi_cny_net < context.user_data.portfolio_stop_loss * context.account_initial.huobi_cny_net or context.account.huobi_cny_net > context.user_data.portfolio_stop_win * context.account_initial.huobi_cny_net:
        should_stopped = True
    else:
        should_stopped = False

    # 如果有止盈/止损信号,则强制平仓,并结束所有操作
    if should_stopped:
        # 低于止损线,需要止损
        if context.account.huobi_cny_net < context.user_data.portfolio_stop_loss * context.account_initial.huobi_cny_net:
            context.log.warn(
                "当前净资产:%.2f 位于止损线下方 (%f), 初始资产:%.2f, 触发止损动作" %
                (context.account.huobi_cny_net, context.user_data.portfolio_stop_loss,
                 context.account_initial.huobi_cny_net))
            context.user_data.stop_loss_triggered = True
        # 高于止盈线,需要止盈
        else:
            context.log.warn(
                "当前净资产:%.2f 位于止盈线上方 (%f), 初始资产:%.2f, 触发止盈动作" %
                (context.account.huobi_cny_net, context.user_data.portfolio_stop_win,
                 context.account_initial.huobi_cny_net))
            context.user_data.stop_win_triggered = True

        if context.user_data.stop_loss_triggered:
            context.log.info("设置 stop_loss_triggered(已触发止损信号)为真")
        else:
            context.log.info("设置 stop_win_triggered (已触发止损信号)为真")

        # 需要止盈/止损,卖出全部持仓
        if context.account.huobi_cny_btc >= HUOBI_CNY_BTC_MIN_ORDER_QUANTITY:
            # 卖出时,全仓清空
            context.log.info("正在卖出 %s" % context.security)
            context.order.sell(context.security, quantity=str(context.account.huobi_cny_btc))
        return

    # 获取历史数据, 取后window_size+1根bar
    hist = context.data.get_price(context.security, count=context.user_data.window_size + 1,
                                  frequency=context.frequency)
    # 判断读取数量是否正确
    if len(hist.index) < (context.user_data.window_size + 1):
        context.log.warn("bar的数量不足, 等待下一根bar...")
        return

    # 取得最近1 根 bar的close价格
    latest_close_price = context.data.get_current_price(context.security)

    # 开始计算N日最高价的最高价HH,N日收盘价的最高价HC,N日收盘价的最低价LC,N日最低价的最低价LL
    hh = np.max(hist["high"].iloc[-context.user_data.window_size-1:-1])
    hc = np.max(hist["close"].iloc[-context.user_data.window_size-1:-1])
    lc = np.min(hist["close"].iloc[-context.user_data.window_size-1:-1])
    ll = np.min(hist["low"].iloc[-context.user_data.window_size-1:-1])
    price_range = max(hh - lc, hc - ll)

    # 取得倒数第二根bar的close, 并计算上下界限
    up_bound = hist["open"].iloc[-1] + context.user_data.K1 * price_range
    low_bound = hist["open"].iloc[-1] - context.user_data.K2 * price_range

    context.log.info("当前 价格:%s, 上轨:%s, 下轨: %s" % (latest_close_price, up_bound, low_bound))

    # 产生买入卖出信号,并执行操作
    if latest_close_price > up_bound:
        context.log.info("价格突破上轨,产生买入信号")
        if context.account.huobi_cny_cash >= HUOBI_CNY_BTC_MIN_ORDER_CASH_AMOUNT:
            # 买入信号,且持有现金,则市价单全仓买入
            context.log.info("正在买入 %s" % context.security)
            context.log.info("下单金额为 %s 元" % context.account.huobi_cny_cash)
            context.order.buy(context.security, cash_amount=str(context.account.huobi_cny_cash))
        else:
            context.log.info("现金不足,无法下单")
    elif latest_close_price < low_bound:
        context.log.info("价格突破下轨,产生卖出信号")
        if context.account.huobi_cny_btc >= HUOBI_CNY_BTC_MIN_ORDER_QUANTITY:
            # 卖出信号,且持有仓位,则市价单全仓卖出
            context.log.info("正在卖出 %s" % context.security)
            context.log.info("卖出数量为 %s" % context.account.huobi_cny_btc)
            context.order.sell(context.security, quantity=str(context.account.huobi_cny_btc))
        else:
            context.log.info("仓位不足,无法卖出")
    else:
        context.log.info("无交易信号,进入下一根bar")

回测

Dual Thrust策略既可以用于追踪长期趋势,也可以用于抓取短期波动。

回测一(短期):

  • 参数设置如下:
时间段 2016-10-01至2016-10-10
回测频率(context.frequency) 1m
K1 0.25
K2 0.25
回看时间窗口 1

因为是按照1分钟的频率回测,策略对时间的敏感度很高,所以我们将回看窗口设置的很小,使得其可以很快的对市场变化做出反应,防止对暴涨暴跌做不出及时的调整。同时,K1和K2设置的也相对较小,买卖都容易触碰,做到快进快出。抓住市场短时间的波动带来的收益。

  • 回测结果如下:

红色的收益曲线几乎是在直线上升,最大回撤只有0.4%,都远远好于基准。在基准的几次较快的下跌中,都能较为及时的退出。

回测二(长期):

  • 参数设置如下:
时间段 2015-01-01至2016-10-01
回测频率(context.frequency) 1d
K1 0.2
K2 0.5
回看时间窗口 5

因为现在是要用天来回测,我们将回测窗口设置的长了一些,降低假性突破的干扰。同时可以注意到,与之前不同,现在的K1和K2并不相等,这就是我们之前说的非对称。如果长期看好比特币,就将K1设置的小一些,让多头更容易触发,“宽进严出”,防止在价格震荡时被洗出局。

  • 回测结果如下:

几次大的上涨行情全部抓住,而且在回调的时候也能较为及时的退出。回撤稍大,但相对于基准来说还是可以接受。在15年上半年的震荡行情中也有一定表现。

总结

Dual Thrust策略是一种简单而又行之有效的追涨杀跌策略,短期和长期都可以使用,重点是对参数的调节。调小参数做短期抓震荡,调大参数做长期抓趋势, 看多调小K1,看空调小K2。只有灵活掌握参数设置的精髓,才能从容面对各种行情。

WeQuant交易策略—Dual Thrust的更多相关文章

  1. WeQuant交易策略—网格交易

    网格交易策略(Grid Trading) 策略介绍 网格策略本质上是一种低吸高抛的策略.标的物价格越低,吸纳的头寸越多:标的物价格越高,卖出的头寸越多.网格策略巧妙地借鉴了日常生活中渔翁撒网扑鱼的思路 ...

  2. WeQuant交易策略—ATR

    ATR(真实波幅均值)策略 策略介绍 ATR(average true range,真实波幅均值),是用来衡量一段时间内价格的真实的平均波动范围,ATR不是一个领先指标,但是它测量最重要的市场参数之一 ...

  3. WeQuant交易策略—RSI

    RSI指标策略 策略介绍 RSI(相对强弱指标),是通过一段时期内的平均收盘上涨和下跌数,计算价格上涨所产生的波动占整个波动的百分比,来分析市场买卖盘的意向和实力. 计算公式(以日为单位举例) RSI ...

  4. WeQuant交易策略—BOLL

    BOLL(布林线指标)策略 简介 BOLL(布林线)指标是技术分析的常用工具之一,由美国股市分析家约翰•布林根据统计学中的标准差原理设计出来的一种非常简单实用的技术分析指标.一般而言,价格的运动总是围 ...

  5. WeQuant交易策略—KDJ

    KDJ随机指标策略策略介绍KDJ指标又叫随机指标,是一种相当新颖.实用的技术分析指标,它起先用于期货市场的分析,后被广泛用于股市的中短期趋势分析,是期货和股票市场上最常用的技术分析工具.随机指标KDJ ...

  6. WeQuant交易策略—MACD

    MACD(指数平滑异同平均线)策略简介MACD指标应该是大家最常见的技术指标,在很多股票.比特币的软件中都是默认显示的.MACD是从双指数移动平均线发展而来的.意义和双移动平均线基本相同,即由快.慢均 ...

  7. WeQuant交易策略—简单均线

    简单双均线策略(Simple Moving Average) 策略介绍简单双均线策略,通过一短一长(一快一慢)两个回看时间窗口收盘价的简单移动平均绘制两条均线,利用均线的交叉来跟踪价格的趋势.这里说的 ...

  8. WeQuant交易策略—EMV

    EMV指标策略 简介 EMV(Ease of Movement Value, 简易波动指标),它是由RichardW.ArmJr.根据等量图和压缩图的原理设计而成, 目的是将价格与成交量的变化结合成一 ...

  9. WeQuant交易策略—Chaikin A/D

    策略名称:AD指标策略 多空双方力量浮标- AD(Chaikin A/D线)策略关键词:ChaikinA/D线.多空对比.AD指标是一种非常流行的平横交易量指标, 用于估定一段时间内该证券累积的资金流 ...

随机推荐

  1. 狙杀ES6之开光篇

    前言 最近有很多小伙伴在后台留言说,闰土哥,是时候来一波干货了!(机智的你们似乎已经猜到我接下来要说什么了,哈哈-).没错,今天闰土为大家带来了久违的干货文章,而且是一个系列的哦!(文章系列较长,请自 ...

  2. 小型 Web 页项目打包优化方案

    背景   目前团队中新的 Web 项目基本都采用了 Vue 或 React ,加上 RN,这些都属于比较重量级的框架,然而对于小型 Web 页面,又显得过大.早期的一些项目则使用了较原始的 HTML ...

  3. python数据结构之队列

    队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表. 队列是一种先进先出的(First In First Out)的线性表,简称FIFO.允许插入的一端为队尾,允许删除的一端 ...

  4. org.apache.hadoop.security.AccessControlException: Permission denied: user=?, access=WRITE, inode="/":hadoop:supergroup:drwxr-xr-x 异常解决

    进行如下更改:vim /usr/local/hadoop/etc/hadoop/hdfs-site.xml[我的hadoop目录在/usr/local下,具体的是修改你的hadoop目录中的/etc/ ...

  5. 【linux相识相知】磁盘分区及文件系统管理详解

    磁盘,提供持久的数据存储,它不像我们的内存,如果突然断电了,在内存中的数据一般都会被丢掉了,内存中的数据在保存的时候,会被写到硬盘里面,磁盘也是一种I/O设备. 我们都知道磁盘分区完成之后,还要进行格 ...

  6. 点击<tr>表格行元素进行跳转

    意外发现table中利用<a>标签控制tr的行为无效. 尝试<a>标签在table中的使用,只有在<td><a href="">&l ...

  7. OpenCV探索之路(二十一)如何生成能在无opencv环境下运行的exe

    我们经常遇到这样的需求:我们在VS写好的程序,需要在一个没有装opencv甚至没有装vs的电脑下运行,跑出效果.比如,你在你的电脑用opencv+vs2015写出一个程序,然后老师叫你把程序发给他,他 ...

  8. android rss阅读器开发一点小技巧

    这几天一直在学习开发Rss阅读器,遇到一个很坑的问题,InputSource这里总是出错.弄了好久,终于让我找到一个解决方法----看代码: new Thread(){ @Override publi ...

  9. 蓝桥杯比赛javaB组练习《饮料换购》

    题目如下: 饮料换购 乐羊羊饮料厂正在举办一次促销优惠活动.乐羊羊C型饮料,凭3个瓶盖可以再换一瓶C型饮料,并且可以一直循环下去,但不允许赊账. 请你计算一下,如果小明不浪费瓶盖,尽量地参加活动,那么 ...

  10. 解决删除元素动画的bug

    效果说明 首先说明一下我需要做到的效果 其实很简单---点击删除按钮的时候,加入删除动画 删除动画是这样的,高度和宽度都会均匀的变小,内部的元素需要被隐藏(因为会有文字挤在一起):直到变为0结束,时长 ...