【vn.py】源码解析之 Dual Thrust 策略

2023-05-16

文章目录

        • Dual Thrust 策略
        • Dual Thrust 策略源码分析
          • 1、完整源码
          • 2、策略参数与变量
          • 3、策略执行逻辑

Dual Thrust 策略

Dual Thrust 策略是 Michael Chalek 在80 年代开发的一种通道突破策略,曾经被Future Thruth杂志评为最赚钱的策略之一, Dual Thrust常常用于日内CTA策略或者日间CTA策略。到目前为止,Dual Thrust 仍然在自动化交易系统中排名第二左右。由于其对于多种品种的普适性,所以也被广泛应用于各种股票、期货、货币等市场。

Dual Thrust 策略在做日内策略时的思想很简单,以当日的开盘价为基准设定上下通道进行突破,上下通道是以当日开盘价加减一定比例的范围Range而确定,然后日内突破上轨平空开多,突破下轨平多开空。这个范围Range由前N日的四个价位来确定 Range = Max(HH-LC, HC-LL),其中HH是前N日的High的最高价,LC是前N日的Close的最低价,HC是前N日Close的最高价,LL是前N日Low的最低价,在计算这个范围指标时有点类似平均真实波动指标(Average True Range,ATR),但是还是有很大区别的毕竟平均真实波动是 J. Welles Wilder 在1978提出的技术指标。

引入了多日的价格可以使得一定时期的范围Range相对稳定,当然也有直接以昨日的范围Range作为今日突破的基准。另外,Dual Thrust策略没有单独的止损,是一个趋势反转系统,也就说当出现反向信号时也就意味着平仓。

所以总结上面所说,上通道是 Open + K1 * Range;下通道是 Open + K2 * Range。需要设定的参数除了N之外,还有作为范围Range比例的系数K1和K2,通常这两个系数并不是一样的,这样也就导致了做多和做空的的触发条件是非对称的。

Dual Thrust 策略源码分析

由于前面已经写过关于CTA策略执行的流程和代码的架构,所以下面将主要围绕策略的参数与变量以及策略的主要执行逻辑进行分析。

1、完整源码
from datetime import time
from vnpy.app.cta_strategy import (
    CtaTemplate,
    StopOrder,
    TickData,
    BarData,
    TradeData,
    OrderData,
    BarGenerator,
    ArrayManager,
)


class DualThrustStrategy(CtaTemplate):
    """"""

    author = "用Python的交易员"

    fixed_size = 1
    k1 = 0.4
    k2 = 0.6

    bars = []

    day_open = 0
    day_high = 0
    day_low = 0

    range = 0
    long_entry = 0
    short_entry = 0
    exit_time = time(hour=14, minute=55)

    long_entered = False
    short_entered = False

    parameters = ["k1", "k2", "fixed_size"]
    variables = ["range", "long_entry", "short_entry", "exit_time"]

    def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
        """"""
        super(DualThrustStrategy, self).__init__(
            cta_engine, strategy_name, vt_symbol, setting
        )

        self.bg = BarGenerator(self.on_bar)
        self.am = ArrayManager()
        self.bars = []

    def on_init(self):
        """
        Callback when strategy is inited.
        """
        self.write_log("策略初始化")
        self.load_bar(10)

    def on_start(self):
        """
        Callback when strategy is started.
        """
        self.write_log("策略启动")

    def on_stop(self):
        """
        Callback when strategy is stopped.
        """
        self.write_log("策略停止")

    def on_tick(self, tick: TickData):
        """
        Callback of new tick data update.
        """
        self.bg.update_tick(tick)

    def on_bar(self, bar: BarData):
        """
        Callback of new bar data update.
        """
        self.cancel_all()

        self.bars.append(bar)
        if len(self.bars) <= 2:
            return
        else:
            self.bars.pop(0)
        last_bar = self.bars[-2]

        if last_bar.datetime.date() != bar.datetime.date():
            if self.day_high:
                self.range = self.day_high - self.day_low
                self.long_entry = bar.open_price + self.k1 * self.range
                self.short_entry = bar.open_price - self.k2 * self.range

            self.day_open = bar.open_price
            self.day_high = bar.high_price
            self.day_low = bar.low_price

            self.long_entered = False
            self.short_entered = False
        else:
            self.day_high = max(self.day_high, bar.high_price)
            self.day_low = min(self.day_low, bar.low_price)

        if not self.range:
            return

        if bar.datetime.time() < self.exit_time:
            if self.pos == 0:
                if bar.close_price > self.day_open:
                    if not self.long_entered:
                        self.buy(self.long_entry, self.fixed_size, stop=True)
                else:
                    if not self.short_entered:
                        self.short(self.short_entry,
                                   self.fixed_size, stop=True)

            elif self.pos > 0:
                self.long_entered = True

                self.sell(self.short_entry, self.fixed_size, stop=True)

                if not self.short_entered:
                    self.short(self.short_entry, self.fixed_size, stop=True)

            elif self.pos < 0:
                self.short_entered = True

                self.cover(self.long_entry, self.fixed_size, stop=True)

                if not self.long_entered:
                    self.buy(self.long_entry, self.fixed_size, stop=True)

        else:
            if self.pos > 0:
                self.sell(bar.close_price * 0.99, abs(self.pos))
            elif self.pos < 0:
                self.cover(bar.close_price * 1.01, abs(self.pos))

        self.put_event()

    def on_order(self, order: OrderData):
        """
        Callback of new order data update.
        """
        pass

    def on_trade(self, trade: TradeData):
        """
        Callback of new trade data update.
        """
        self.put_event()

    def on_stop_order(self, stop_order: StopOrder):
        """
        Callback of stop order update.
        """
        pass

2、策略参数与变量

从下面的代码中可以看出超参有三个,范围Range比例的系数 K1、K2 以及仓位数 fixed_size。需要维护的变量有范围 range、上下通道 long_entry、short_entry。由于是日内交易,所以策略中还设定了一个每天的退场时间 exit_time 为14:55。另外还需要注意一下,就是vnpy中的Dual Thrust策略和上面介绍的还是有些区别的,主要在于vnpy中range是由昨日的最高价和最低价确定的,这个后面策略逻辑处就可以看出。总而言之,Dual Thrust 策略的参数还是较少的。

    fixed_size = 1
    k1 = 0.4
    k2 = 0.6

    bars = []

    day_open = 0
    day_high = 0
    day_low = 0

    range = 0
    long_entry = 0
    short_entry = 0
    exit_time = time(hour=14, minute=55)

    long_entered = False
    short_entered = False

    parameters = ["k1", "k2", "fixed_size"]
    variables = ["range", "long_entry", "short_entry", "exit_time"]
3、策略执行逻辑

同前面的Double MA一样,在对策略进行变量初始化时,也需要加载10天的1Min级别数据。然后主要的逻辑也是在on_bar()函数中,也就是说这个策略也是Bar驱动的。
首先,需要先清空所有未成交的委托单,然后记录下上一个bar和当前的bar。

        self.cancel_all()

        self.bars.append(bar)
        if len(self.bars) <= 2:
            return
        else:
            self.bars.pop(0)
        last_bar = self.bars[-2]

如果上一个bar的日期和当前bar的日期不同,则说明当前bar是今天的第一个bar,上一个bar是昨日的最后一个bar,然后通过昨日的最高价和最低价计算range;否则的话,就通过 max(self.day_high, bar.high_price)和min(self.day_low, bar.low_price) 记录每日的bar的最高价和最低价,从而确定range。然后,以K1和K2与range来设置上下通道long_entry和short_entry的大小。

        if last_bar.datetime.date() != bar.datetime.date():
            if self.day_high:
                self.range = self.day_high - self.day_low
                self.long_entry = bar.open_price + self.k1 * self.range
                self.short_entry = bar.open_price - self.k2 * self.range

            self.day_open = bar.open_price
            self.day_high = bar.high_price
            self.day_low = bar.low_price

            self.long_entered = False
            self.short_entered = False
        else:
            self.day_high = max(self.day_high, bar.high_price)
            self.day_low = min(self.day_low, bar.low_price)

        if not self.range:
            return

在得到昨日的范围range之后就可以判断是否满足开仓条件了。如果当前bar的时间在退场时间之前就进行判断是否满足开平仓条件,否则的话就直接以限价单进行平今。

在退场时间之前判断是否满足开平仓条件时:
1、如果不持仓:
并且当前bar的close大于当日开盘价并且没有多头入场,就以上轨道long_entry的价位开一笔停止单。
并且当前bar的close小于当日开盘价并且没有空头入场,就以下轨道short_entry的价位开一笔停止单。
2、如果持有多头,就以下轨道short_entry的价位开一笔停止单来平仓,并且如果没有空头入场的话再以下轨道short_entry的价位开一笔停止单来开仓;如果持有空头,就以上轨道long_entry的价位开一笔停止单来平仓,并且如果没有多头入场的话再以上轨道long_entry的价位开一笔停止单来开仓。

注意上面开出的委托单都是以停止单的形式开出的,停止单也就意味着是需要在bar的内部进行交易撮合,在bar内部价格突破时开仓。vnpy中的Dual Thrust策略有些类似于反复试探的思想,每个bar内部进行撮合交易,发出订单,没有撮合成功则在新的bar接收时撤掉之前发出的订单。毕竟是日内策略,如果还是以1-min的bar基础上进行交易未免交易机会也确实会有些少,而通过停止单的形式则正好可以满足其日内突破的要求。

        if bar.datetime.time() < self.exit_time:
            if self.pos == 0:
                if bar.close_price > self.day_open:
                    if not self.long_entered:
                        self.buy(self.long_entry, self.fixed_size, stop=True)
                else:
                    if not self.short_entered:
                        self.short(self.short_entry,
                                   self.fixed_size, stop=True)
            elif self.pos > 0:
                self.long_entered = True
                self.sell(self.short_entry, self.fixed_size, stop=True)
                if not self.short_entered:
                    self.short(self.short_entry, self.fixed_size, stop=True)
            elif self.pos < 0:
                self.short_entered = True
                self.cover(self.long_entry, self.fixed_size, stop=True)
                if not self.long_entered:
                    self.buy(self.long_entry, self.fixed_size, stop=True)
        else:
            if self.pos > 0:
                self.sell(bar.close_price * 0.99, abs(self.pos))
            elif self.pos < 0:
                self.cover(bar.close_price * 1.01, abs(self.pos))
        self.put_event()
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【vn.py】源码解析之 Dual Thrust 策略 的相关文章

随机推荐

  • aruco marker使用笔记

    在英伟达Jetson Xaiver开发板上配置 SDK环境 opencv 4 1 1 CUDA 10 2 1 git clone https github com pal robotics aruco ros 2 复制到catkin ws
  • catkin_make命令

    catkin make是在catkin工作区中构建代码的便捷工具 catkin make遵循catkin工作区的标准布局 xff0c 如REP 128中所述 用法 假设您的catkin工作区位于 catkin ws中 xff0c 则应始终在
  • docker容器中运行界面程序

    Docker比较常用的场景是 运行无界面的后台服务 或者 运行Web服务 不过有时出于个人的喜好或特定的需求 xff0c 我们会希望在Docker中运行带图形界面的应用程序 将容器中的图形界面展示到外部的一般性思路 xff1a 目前Unix
  • linux录屏

    Linux下好用的录屏软件是kazam录屏后视频处理软件kdenlive根据剪辑好的视频撰写解说词 xff0c 使用讯飞配音app将解说词文字转换为语音mp3将语音与视频通过kdenlive软件合成在一起 xff0c 完美的演示视频诞生了
  • 【python】conda和pip安装库之间的区别

    conda 首先 xff0c conda是一个通用的包管理器 xff0c 意思是什么语言的包都可以用其进行管理 xff0c 自然也就包括Python了 在安装Anaconda或者Miniconda时 xff0c 会对conda进行一同安装
  • OpenHarmony-Overview_zh

    OpenHarmony开源项目 项目介绍 OpenHarmony是开放原子开源基金会 xff08 OpenAtom Foundation xff09 旗下开源项目 xff0c 定位是一款面向全场景的开源分布式操作系统 OpenHarmony
  • 【python量化】用时间卷积神经网络(TCN)进行股价预测

    写在前面 下面这篇文章首先主要简单介绍了目前较为先进的时间序列预测方法 时间卷积神经网络 xff08 TCN xff09 的基本原理 xff0c 然后基于TCN的开源代码 xff0c 手把手教你如何通过时间卷积神经网络来进行股价预测 xff
  • 【python量化】将Transformer模型用于股票价格预测

    写在前面 下面的这篇文章主要教大家如何搭建一个基于Transformer的简单预测模型 xff0c 并将其用于股票价格预测当中 原代码在文末进行获取 1 Transformer模型 Transformer 是 Google 的团队在 201
  • 解读:基于GCN的股票预测模型

    前言 xff1a 自ICLR2017首次提出图卷积神经网络 xff08 GCN xff09 的概念 xff0c 该模型在节点分类 边预测等任务上表现出了出色的性能 在传统因子选股模型中 xff0c 常常将股票视为独立的个体 xff0c 但事
  • 【python量化】基于backtrader的深度学习模型量化回测框架

    写在前面 在本文中 xff0c 我们将介绍使用PyTorch构建一个深度学习模型 xff0c 并将其集成到backtrader回测框架中 具体地 xff0c 我们将使用PyTorch来实现一个长短期记忆神经网络 xff08 LSTM xff
  • 【量化交易】股票价格前复权与后复权的区别以及注意事项

    时不时就会看到到底是用股票前复权还是后复权价格的讨论 xff0c 比如下面就是一个很经典的问法 xff1a 我用前复权价格计算指标的时候 xff0c 发现会出现负价格 xff0c 就没法取log了 xff0c 应该是分红太多导致的 xff0
  • skfuzzy.cmeans与sklearn.KMeans聚类效果对比以及使用方法

    因为实验中要用到聚类效果的对比 xff0c 没有时间自己来实现算法 xff0c 所以Kmeans就用到了sklearn中的Kmeans类 xff0c FCM用到了skfuzzy cmeans 几个概念 1 Kmeans Kmeans是聚类算
  • 对论文中模型进行编程实现时的注意要求和总结

    看论文时 xff0c 如果论文中有对自己研究方向有帮助或者具有实际用处的模型时 xff0c 不免通过编程对其进行实现 如果是一个简单的模型 xff0c 用个caffe tensorflow之类的框架跑跑就出来的那就无所谓了 xff0c 但是
  • 机器学习里面的Ground Truth是什么意思

    在看英文文献的时候 xff0c 经常会看到Ground Truth这个词汇 xff0c 翻译的意思是地面实况 xff0c 放到机器学习里面 xff0c 再抽象点可以把它理解为真值 真实的有效值或者是标准的答案 维基百科对Ground Tru
  • python 字符串(str)与列表(list)以及数组(array)之间的转换方法详细整理

    前提 xff1a list以及array是python中经常会用到的数据类型 xff0c 当需要对list以及array进行文件的读写操作的时候 xff0c 由于write函数参数需要的是一个str xff0c 所以这时就需要对list或者
  • RMSE(均方根误差)、MSE(均方误差)、MAE(平均绝对误差)、SD(标准差)

    RMSE xff08 Root Mean Square Error xff09 均方根误差 衡量观测值与真实值之间的偏差 常用来作为机器学习模型预测结果衡量的标准 MSE xff08 Mean Square Error xff09 均方误差
  • LINUX——FIREWALLD防火墙基础(FIREWALLD-CMD命令操作+FIREWALLD-CONFIG图形管理工具)

    LINUX FIREWALLD防火墙基础 xff08 FIREWALLD CMD命令操作 43 FIREWALLD CONFIG图形管理工具 xff09 前言一 FIREWALLD概述1 1 FIREWALLD1 2 FIREWALLD和I
  • 时间序列分析之ADF检验

    ADF检验 在使用很多时间序列模型的时候 xff0c 如 ARMA ARIMA xff0c 都会要求时间序列是平稳的 xff0c 所以一般在研究一段时间序列的时候 xff0c 第一步都需要进行平稳性检验 xff0c 除了用肉眼检测的方法 x
  • pandas之分组groupby()的使用整理与总结

    文章目录 前言准备基本操作可视化操作REF 前言 在使用pandas的时候 xff0c 有些场景需要对数据内部进行分组处理 xff0c 如一组全校学生成绩的数据 xff0c 我们想通过班级进行分组 xff0c 或者再对班级分组后的性别进行分
  • 【vn.py】源码解析之 Dual Thrust 策略

    文章目录 Dual Thrust 策略Dual Thrust 策略源码分析1 完整源码2 策略参数与变量3 策略执行逻辑 Dual Thrust 策略 Dual Thrust 策略是 Michael Chalek 在80 年代开发的一种通道