发布于vn.py社区公众号【vnpy-community】
 
原文作者:Bili | 发布时间:2021-09-07
 

description

 

前情回顾

 

在前面的文章当中,我们对RSJ指标有了初步的了解,并且在RSJ指标样本内外实验中也取得一个不错的回测结果。接下来,我们将继续根据陶勤英博士的财通证券研究所的研报中所指出的将RSJ指标与其他技术指标组合使用来进行进一步的实验研究观察回测后的效果。

 

测试原理

 

首先,我们根据文章选取了另外三个技术指标,分别是DMI指标、RSI指标和ROC指标分别与RSJ指标进行叠加使用进行回测。所以接下来我们需要了解各个指标的基本原理以及他们的信号逻辑:

 

DMI指标

 

基本原理:DMI指标基本原理是在于寻找股票价格涨跌过程中,股价藉以创新高价或新低价的功能,研判多空力量,进而寻求买卖双方的均衡点及股价在双方互动下波动的循环过程。与大多数技术指标相比DMI指标把每日的高低波动的幅度因素计算在内,从而更加准确的反应行情的走势及更好的预测行情未来的发展变化。

信号逻辑:趋向技术指标 DMI 包括多空指标 PDI、MDI 以及趋向指标 ADX。我们针对 DMI 指标建立相应的择时策略,即若昨日的 ADX 大于前一日的 ADX,则今日在收盘的时候看多;若昨日的 ADX、PDI、MDI 均小于前一日的相对应的指标值,则今日在收盘的时候看空(其中,ADX 值若不在[20,60]之内,则不进行交易)。

 

RSI指标

 

基本原理:RSI的原理简单来说是以数字计算的方法求出买卖双方的力量对比,譬如有100个人面对一-件商品, 如果50个人以上要买,竞相抬价,商品价格必涨。相反,如果50个人以上争着卖出,价格自然下跌。

信号逻辑:对于相对强弱指标 RSI,两种力量的对比决定了个股及大盘所处的状态:强势或弱势。从 RSI 值的变动范围来看:当 20 < RSI <50 时,弱势,卖出,空仓;当 50 < RSI <80 时,强势,买入,持仓。因此,若昨日的 RSI 值在(50,80]内,则今日在收盘的时候看多;若昨日的RSI 值在[20,50)内,则今日在收盘的时候看空。

 

ROC指标

 

基本原理:变动率指标(ROC),是以当日的收盘价和N天前的收盘价比较,通过计算股价某一段时间内收盘价变动的比例,应用价格的移动比较来测量价位动量,达到事先探测股价买卖供需力量的强弱,进而分析股价的趋势及其是否有转势的意愿,属于反趋势指标之一。

信号逻辑:ROC指标测量股价动量,可以用来监视常态性和极端性两种行情,对买卖股票提供强有力的参考。ROC 变大,代表未来市场仍会在短时间内上涨,反之则代表市场可能下跌。因此,若昨日的 ROC 大于前一日的 ROC,则今日在收盘的时候看多;反之,发出看空信号。

 

代码分析

 

接下来,我们将正式进入我们的实验阶段。为了避免文章内容的重复,我们仅以RSJ+DMI为例,为大家进行代码分析:

在本策略当中,我们需要使用到两种K线——5分钟K线和日K线,所以我们还需创建日K线生成器DailyBarGenerator:

class DailyBarGenerator:

    def __init__(self, on_daily_bar: Callable) -> None:
        """日K线合成器"""        
        self.on_daily_bar: Callable = on_daily_bar        
        self.daily_bar: BarData = None        
        self.last_bar: BarData = None

接下来构造update_bar函数用于更新K线到容器中,缓存K线本身的OHLCV数据:

 def update_bar(self, bar: BarData) -> None:        
     """更新分钟K线"""        
     if not self.daily_bar:            
         dt = bar.datetime.replace(                
             hour=0,                
             minute=0,                
             second=0,                
             microsecond=0
        )

        self.daily_bar = BarData(
            symbol=bar.symbol,
            exchange=bar.exchange,
            datetime=dt,
            gateway_name=bar.gateway_name,
            open_price=bar.open_price,
            high_price=bar.high_price,
            low_price=bar.low_price
        )        
    # Otherwise, update high/low price into window bar        
    else:            
        self.daily_bar.high_price = max(
            self.daily_bar.high_price,
            bar.high_price
        )            
        self.daily_bar.low_price = min(
            self.daily_bar.low_price,
            bar.low_price
        )

        # Update close price/volume/turnover into window bar   
        self.daily_bar.close_price = bar.close_price      
        self.daily_bar.volume += bar.volume  
        self.daily_bar.open_interest = bar.open_interest

        # Check if window bar completed       
        if (    
            self.last_bar       
            and self.last_bar.datetime.date() != bar.datetime.date()        
        ):            
            self.on_daily_bar(self.daily_bar)       
            self.daily_bar = None

        # Cache last bar object      
        self.last_bar = bar

交易信号执行:

5分钟K线用于计算RSJ指标,我们需要先使用BarGenerator将1分钟K线合成为5分钟K线,日K线用于计算DMI指标:

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

    self.bg = BarGenerator(self.on_bar, 5, self.on_5min_bar)    
    self.am = NewArrayManager()

    self.daily_bg = DailyBarGenerator(self.on_daily_bar)   
    self.daily_am = NewArrayManager()

计算技术指标:

分别将RSJ、ADX、PDI以及MDI指标计算出来:

    # 计算技术指标        
    self.rsj_value = self.am.rsj(self.rsj_window)

    adx_array = self.daily_am.adx(self.dmi_window, array=True)   
    pdi_array = self.daily_am.plus_di(self.dmi_window, array=True)      
    mdi_array = self.daily_am.minus_di(self.dmi_window, array=True)

    adx_value = adx_array[-1]

判断交易信号:

当 RSJ 和 DMI指标同时发出看多信号,则在当日收盘的时候发出看多信号;若两个指标同时发出看空信号,则在当日收盘的时候发出看空信号;否则,不发出任何信号。

    # 判断交易信号      
    target_pos = 0

    if bar.datetime.time() == time(14, 50):  
        # 满足开仓交易的ADX要求           
        if 20 <= adx_value <= 60:   
            # 做多条件               
            if (                   
                self.rsj_value < 0 
                and adx_array[-1] > adx_array[-2]         
            ):                 
                target_pos = 1           
            # 做空条件           
            elif (             
                self.rsj_value > 0   
                and adx_array[-1] < adx_array[-2]     
                and pdi_array[-1] < pdi_array[-2] 
                and mdi_array[-1] < mdi_array[-2] 
            ):                   
                target_pos = -1

交易逻辑:

基于目标仓位和实际仓位,决定如何交易:

            trade_volume = target_pos - self.pos
            if trade_volume > 0:    
                 if self.pos >= 0:      
                     self.buy(bar.close_price + 5, trade_volume)    
                 else:     
                    self.cover(bar.close_price + 5, trade_volume)           
             elif trade_volume < 0: 
                if self.pos <= 0:   
                    self.short(bar.close_price - 5, abs(trade_volume))     
                else:       
                    self.sell(bar.close_price - 5, abs(trade_volume))

tips:

由于策略中涉及到多个技术指标,代码变得较为复杂。所以在这时将计算技术指标、判断交易信号和交易逻辑三个部分分开来写,会让我们的代码变得更加清晰从而增加代码的可读性。

 

回测结果

 

我们用 2017 年 7 月 23 日-2020 年 7 月 22 日的数据,对该日内指标进行回测。指标当日发出信号后,分别于当日收盘分别对上证 50、沪深 300、中证 500指数进行操作。

实验一:RSJ + DMI

其中 DMI 组合指标时间参数为14。

  • 本地代码:IF888.CFFEX、IH888.CFFEX、IC888.CFFEX
  • K线周期:1分钟
  • 开始日期:2017-7-23
  • 结束日期:2020-7-22
  • 手续费率:0.00003
  • 交易滑点:0.4
  • 合约乘数:300
  • 价格跳动:0.2
  • 回测资金:100W

description

资金曲线的形状和财通研报中的结果基本一致 ,可以认为比较正确的复现了策略逻辑:

description

实验二:RSJ + RSI

其中 RSI 指标的时间参数为 6。

  • 本地代码:IF888.CFFEX、IH888.CFFEX、IC888.CFFEX

  • K线周期:1分钟

  • 开始日期:2017-7-23

  • 结束日期:2020-7-22

  • 手续费率:0.00003

  • 交易滑点:0.4

  • 合约乘数:300

  • 价格跳动:0.2

  • 回测资金:100W

description

资金曲线的形状和财通研报中的结果基本一致 ,可以认为比较正确的复现了策略逻辑:

description

实验三:RSJ + ROC

其中 ROC 指标参数为 12。

  • 本地代码:IF888.CFFEX、IH888.CFFEX、IC888.CFFEX

  • K线周期:1分钟

  • 开始日期:2017-7-23

  • 结束日期:2020-7-22

  • 手续费率:0.00003

  • 交易滑点:0.4

  • 合约乘数:300

  • 价格跳动:0.2

  • 回测资金:100W

description

资金曲线的形状和财通研报中的结果基本一致 ,可以认为比较正确的复现了策略逻辑:

description

结论:

RSJ指标分别与DMI、RSI和ROC指标组合使用的回测效果整体上资金曲线都是呈上扬趋势,但是对于部分品种来说资金曲线不太稳键,还有较大的改进提升空间。

 

代码获取

 

本文中提到的三个策略代码较长,就不在这里贴出了。请关注本公众号(vnpy-community)后,回复内容【RSJ】获取源代码下载链接。