VeighNa量化社区
你的开源社区量化交易平台
Member
avatar
加入于:
帖子: 187
声望: 55

TradeBlazer交易开拓者(简称TB),可能是许多投资者开始接触量化时的第一根拐杖,也是国内用户量最大的量化平台之一。

 

但随着时间过去,国内量化投资者编程水平的逐渐提高,越来越多的人开始转向Python这样的开源生态体系。
 

在转换平台的过程中,由于编程语法、数据结构、驱动机制等方面的区别,不少人遇到了各种困难,掉在某些坑里可能几周都爬不出来。

 

本篇文章中我们就来通过一个的经典趋势跟踪策略AtrRsiStrategy,来详细讲解如何一步步将TB策略代码移植到vn.py上的过程。

 

 

ATR-RSI策略

 

完整的ATR-RSI策略逻辑如下:

 

  1. 开仓过滤:当前波动率(ATR)大于历史平均波动率(ATR均值)时,我们认为后续走出趋势的机会变大,只有此时才考虑开仓交易;
  2. 多头开仓:当RSI指标进入超买区域(比如RSI > 66),说明多头力量已取得上风,此时选择立即做多开仓,为了保证能够立刻成交,使用超价的限价委托来下单。
  3. 多头平仓:采用固定百分比的移动止损,在持有多头仓位情况下,跟踪价格曾经到达的最高点,当价格从最高点回落到固定百分比(比如0.8%)的一瞬间立刻平掉多头仓位。
  4. 空头开仓:当RSI指标进入超卖区域(比如RSI < 34),说明空头已取得上风,此时选择立即做空开仓,同样使用超价限价单保证立刻成交。
  5. 空头平仓:同样采用固定百分比的移动止损,当价格当价格从最低点反弹超过固定百分比(比如0.8%)的一瞬间立刻平掉空头仓位。

 

注意点:我们总是假设在当前K线走完计算信号并且发出委托,成交永远发生在下一根K线。即T时刻计算信号,发出委托;最快也要T+1时刻该委托才能成交。这也是下面停止单和限价单撮合的充分条件。

 
 

TB中的策略实现

 

创建RSI指标函数

 

  1. 打开TB,在【TB公式】->【公式管理器】->【公式应用】里面找到RSI指标

description

  1. 打开RSI指标公式应用,复制里面的代码。
  2. 同样在【TB公式】->【新建用户函数】里面创建新的RSI指标函数,这类命名为rsirsi,把代码粘贴到新的函数里面。
  3. 修改函数代码:变量输出类型修改成NumericSeries,删除最后4行的画图函数。
  4. 编译保存后,退出。

 
 
创建ATR-RSI策略

 

  • 在【TB公式】->【新建公式应用】打开新的策略模板。
  • 定义变量输出类型统一为NumericSeries。由于很多信号是基于T计算,在T+1时间成交的,故我们需要取前一个时刻的数据,比如前一个时刻的RSI指标,即rsi_value[1]。
  • 计算当前ris指标,rsi_value = rsi_array[1]

 

Params
  Numeric rsi_length(5);
  Numeric rsi_entry(16);
Vars
  NumericSeries rsi_array(0);
  NumericSeries rsi_value(0);
  NumericSeries rsi_buy(0);
  NumericSeries rsi_sell(0);

Begin
  // Calculate Rsi Value
  rsi_buy = 50 + rsi_entry;
  rsi_sell = 50 - rsi_entry;
  rsi_array = rsirsi(rsi_length); 
  rsi_value = rsi_array[1];

 

计算当前ATR指标,atr_value = atr_array[1];以及当前ATR均值,atr_ma= atr_ma_array[1]

 

Params
    Numeric atr_length(22);
  Numeric atr_ma_length(10);

Vars
  NumericSeries atr_value(0);
  NumericSeries atr_ma(0);
  NumericSeries atr_arry(0);
  NumericSeries atr_ma_array(0);

Begin
  // Calculate Atr Value and Atr Ma
  atr_arry = AvgTrueRange(atr_length);
  atr_ma_array = Average(atr_arry[atr_ma_length], atr_ma_length);  

  atr_value = atr_arry[1]; // last bar for atr_value  
  atr_ma = atr_ma_array[1];  // last bar for atr_ma_value

 
空仓情况下,发出限价单委托开仓:

  • 当波动率上涨并且RSI指标>66时,使用当前收盘价+5的限价单,超价买入保证成交;
  • 当波动率上涨并且RSI指标<34时候,使用当前收盘价-5的限价单,超价卖出保证成交。

 

  If(MarketPosition == 0)
  {
    intra_trade_low = Low[1];
    intra_trade_high = High[1];

    // 【Long condition】
    If(rsi_value > rsi_buy AND atr_value > atr_ma)
    {  
      long_limit = Close[1] + 5;
      If(long_limit>=Low)
      {
        Buy(fixed_size, Min(Open, long_limit));
      }  
    }

    // 【Short condition】
    Else If(rsi_value < rsi_sell AND atr_value > atr_ma)
    {
      short_limit = Close[1] - 5;
      If(short_limit <=High)
      {
        SellShort(fixed_size, Max(Open, short_limit));
      }   
    } 
  }

 

百分比移动止盈止损离场:

  • 多仓情况下,当价格从最高点回落0.8%的一瞬间触发条件单离场;
  • 空仓情况下,当价格从最低点回调0.8%的一瞬间触发条件单离场;

 

  // postition >0  
  Else If(MarketPosition >0)
  {
    intra_trade_high = Max(intra_trade_high, High[1]);
    intra_trade_low = Low[1];

    long_stop = intra_trade_high * (1 - trailing_percent / 100);

        If(Low <= long_stop)
        {
            Sell(MarketPosition, Min(Open, long_stop));
        }

  }

  // postiton < 0 
  Else If(MarketPosition <0)
  {  
    intra_trade_low = Min(intra_trade_low, Low[1]);
    intra_trade_high = High[1];

    short_stop = intra_trade_low *(1+ trailing_percent /100);

    If(High >= short_stop)
    {
            BuyToCover(-MarketPosition, Max(Open, short_stop));
    }
  }

 
 
策略回测结果

 

  • 数据:沪深300股指连续(IF888)
  • 时间区间:2019年1月~12月
  • K线周期:1分钟
  • 策略效果:资金曲线整体向上,平均盈亏比为2.08。

 
description

 
 

TB完整代码

 

Params
  Numeric atr_length(22);
  Numeric atr_ma_length(10);
  Numeric rsi_length(5);
  Numeric rsi_entry(16);
  Numeric trailing_percent(0.8);
  Numeric fixed_size(1);

Vars
  NumericSeries rsi_array(0);
  NumericSeries atr_value(0);
  NumericSeries atr_ma(0);
  NumericSeries rsi_value(0);
  NumericSeries rsi_buy(0);
  NumericSeries rsi_sell(0);
  NumericSeries intra_trade_high(0);
  NumericSeries intra_trade_low(0);
  NumericSeries atr_arry(0);
  NumericSeries atr_ma_array(0);
  NumericSeries long_stop(0);
  NumericSeries short_stop(0);
  NumericSeries long_limit(0);
  NumericSeries short_limit(0);
Begin

  // Calculate Rsi Value
  rsi_buy = 50 + rsi_entry;
  rsi_sell = 50 - rsi_entry;
  rsi_array = rsirsi(rsi_length); 
  rsi_value = rsi_array[1];

  // Calculate Atr Value and Atr Ma
  atr_arry = AvgTrueRange(atr_length);
  atr_ma_array = Average(atr_arry[atr_ma_length], atr_ma_length);


  atr_value = atr_arry[1]; // last bar for atr_value  
  atr_ma = atr_ma_array[1];  // last bar for atr_ma_value

  If(MarketPosition == 0)
  {
    intra_trade_low = Low[1];
    intra_trade_high = High[1];

    // 【Long condition】
    If(rsi_value > rsi_buy AND atr_value > atr_ma)
    {  
      long_limit = Close[1] + 5;
      If(long_limit>=Low)
      {
        Buy(fixed_size, Min(Open, long_limit));
      }  
    }

    // 【Short condition】
    Else If(rsi_value < rsi_sell AND atr_value > atr_ma)
    {
      short_limit = Close[1] - 5;
      If(short_limit <=High)
      {
        SellShort(fixed_size, Max(Open, short_limit));
      }      
    }
  }


  // postition >0  
  Else If(MarketPosition >0)
  {
    intra_trade_high = Max(intra_trade_high, High[1]);
    intra_trade_low = Low[1];

    long_stop = intra_trade_high * (1 - trailing_percent / 100);

        If(Low <= long_stop)
        {
            Sell(MarketPosition, Min(Open, long_stop));
        }

  }

  // postiton < 0 
  Else If(MarketPosition <0)
  {  
    intra_trade_low = Min(intra_trade_low, Low[1]);
    intra_trade_high = High[1];

    short_stop = intra_trade_low *(1+ trailing_percent /100);

    If(High >= short_stop)
    {
            BuyToCover(-MarketPosition, Max(Open, short_stop));
    }
  }
End

 

 

vn.py中的策略实现

 

TB策略的逻辑完全由行情驱动,即每次有行情变化(Tick更新、K线走完)时会完整执行代码中的所有逻辑。与之不同的是,vn.py内置的CTA策略模板,提供了诸多的事件驱动回调函数,如:Tick更新驱动(on_tick函数)、K线驱动(on_bar函数)、成交驱动(on_trade)、委托驱动(on_order)等。

 

要移植TB上的策略,只需在vn.py策略代码的on_bar回调函数中实现对应的策略逻辑即可:

 

  1. 调用cancel_all()函数撤销未成交委托,保证当前委托状态的干净和唯一性;
  2. 基于K线时间序列容器ArrayManager,来维护K线历史数据,计算需要的技术指标数据;
  3. 委托方式同样分为4种,下单时的可选参数中,stop=True意味着停止单,stop=False意味着限价单:
    • buy:买入开仓
    • sell:卖出平仓
    • short:卖出开仓
    • cover:买入平仓

 

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

    am = self.am
    am.update_bar(bar)
    if not am.inited:
        return

    atr_array = am.atr(self.atr_length, array=True)
    self.atr_value = atr_array[-1]
    self.atr_ma = atr_array[-self.atr_ma_length:].mean()
    self.rsi_value = am.rsi(self.rsi_length)

    if self.pos == 0:
        self.intra_trade_high = bar.high_price
        self.intra_trade_low = bar.low_price

        if self.atr_value > self.atr_ma:
            if self.rsi_value > self.rsi_buy:
                self.buy(bar.close_price + 5, self.fixed_size)
            elif self.rsi_value < self.rsi_sell:
                self.short(bar.close_price - 5, self.fixed_size)

    elif self.pos > 0:
        self.intra_trade_high = max(self.intra_trade_high, bar.high_price)
        self.intra_trade_low = bar.low_price

        long_stop = self.intra_trade_high * \
            (1 - self.trailing_percent / 100)
        self.sell(long_stop, abs(self.pos), stop=True)

    elif self.pos < 0:
        self.intra_trade_low = min(self.intra_trade_low, bar.low_price)
        self.intra_trade_high = bar.high_price

        short_stop = self.intra_trade_low * \
            (1 + self.trailing_percent / 100)
        self.cover(short_stop, abs(self.pos), stop=True)

    self.put_event()

 

完整的代码实现请参考Github仓库中的策略源代码。

 
 

策略回测结果
 

  • 数据:RQData的沪深300主力拼接合约(IF888)
  • 时间区间:2019年1月~12月
  • K线周期:1分钟
  • 策略效果:资金曲线整体向上,与TB的资金曲线几乎一致。

 

description

 

 

两个平台的对比总结

 
K线数据访问区别

 

TB

  • 默认访问的是当前最新时间点的数据,如使用Close函数访问的是当前最新一根尚未走完的K线数据;
  • 如果需要访问最近一根已经走完的K线收盘价,则必须使用Close[1];
  • 同样,对于最近一根已经走完K线的技术指标,以RSI指标举例,则必须使用rsi_array[1]。
     

vn.py

  • 默认访问的是最近一根已经走完的K线数据;
  • 当前最新一根尚未走完的K线数据,在策略内禁止访问,杜绝TB上的信号闪烁问题(未来函数)。

 
 

委托撮合逻辑区别

 
TB

  • 需要策略开发者在策略内,自行编写相对复杂的委托撮合逻辑,来尽量逼近真实交易情况;

vn.py

  • 内置了详尽的停止单、限价单撮合逻辑,在调用委托函数时,只需调整可选参数stop即可实现委托的转变。
     

 
策略回测结果区别

 

即使在策略逻辑层面已经做到一致,TB和vn.py的回测资金曲线图依旧可能存在某些细节方面的区别。主要原因是数据源方面的不同,TB使用的是自身提供的历史数据源,而vn.py默认推荐使用的是RQData数据服务。

 
 
了解更多知识,请关注vn.py社区公众号。
 

description

Member
avatar
加入于:
帖子: 2
声望: 0

期待来个股票策略落地vnpy的教程

Member
avatar
加入于:
帖子: 252
声望: 3

期待更多TB转vnpy策略讲解,万分感激

Member
avatar
加入于:
帖子: 19
声望: 0

实用!期待更多策略。。。

Member
avatar
加入于:
帖子: 2
声望: 0

on_order 和on_trade有更多的样例说明就好了,比如如果出100手 但只成交了 70手 怎么办

© 2015-2022 上海韦纳软件科技有限公司
备案服务号:沪ICP备18006526号

沪公网安备 31011502017034号

【用户协议】
【隐私政策】
【免责条款】