vn.py source interpretation (IX policy class code analysis)

      When it comes to the most important class of. The class that white is the implementation strategy. And the vast majority of back-tested frameworks, strategic thinking is a kind of abstract, general will inherit a base class template, an instance of each run a real idea of ​​strategy is the strategy classes. Okay, a little around. Let's look at the code.

      In vn.py, each policy category is no accident began following this:

1, class variables and class definitions

class TRStrategy(CtaTemplate):
    """学习版本"""
    className = 'TRStrategy'
    author = u'lyx'

      First of all, this strategy inherits the parent class called CtaTemplate, and then set the name and author of the policy. Here, in the framework of nvpy, className the class name and policy to the same policy.

      Examples vnpy the authors, like to use the class variable to set the parameters of some policies, personally I feel that is not very appropriate. Vnpy the author's demo variables have generally directly behind the strategy:

    atrLength = 22          # 计算ATR指标的窗口数   
    atrMaLength = 10        # 计算ATR均线的窗口数
    rsiLength = 5           # 计算RSI的窗口数
    rsiEntry = 16           # RSI的开仓信号

      That these variables is a class variable rather than an instance variable. If the foundation is not everyone's python is good. . . The so-called class variables is this variable holds in the class of storage space, as long as there is a place has changed, all instances of this class will change; but the instance of the class is part of the memory space instance, examples 1 and instance variables of Example 2 was not half dime, but if you change a class variable for the instance, then the value of another example of a class variable is bound to change, because, stored in the variable space class! This is why individuals feel that these parameters in the initialization function more appropriate reasons, or else there may be unexpected to happen. So, we look at the initialization function of it.

2. The class initialization function

    def __init__(self, ctaEngine, setting):
        """Constructor"""
        super(TRStrategy, self).__init__(ctaEngine, setting)
        
        # 创建K线合成器对象
        self.bg = BarGenerator(self.onBar)
        self.bg30 = BarGenerator(self.onBar, 30, self.on30minBar)
        self.am = ArrayManager()

      Initialization function more interesting, would do three things, first is to obtain a ctaEngine, cta engine is put into them, this can be measured back can also be a firm offer. Here, we put this back-tested engine bindings come in, and passed to the parent class. But this is actually a problem, so it is best not to display the pass. It is constructed behind the line k and k synthesizer line manager. Here, k is the heart of the back line manager strategy.

3, policy-initialization function

      Then it is initialized, not the class initialization, but initialization function strategy. This is well understood, initialize a strategy that is very simple to initialization parameters, while the need to initialize the data read out. After the function call onBar noticed that here for initialization data required by loadBar function. Then the back-tested logic is relatively easy to understand, logical firm offer it? Firm when the database would go inside to find historical market data required length, so when a firm offer, in fact, is a database of thing can not be separated. (Concern here about the detail later)

def onInit(self):
        """初始化策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略初始化' % self.name)
        # 载入历史数据,并采用回放计算的方式初始化策略数值
        initData = self.loadBar(self.initDays)  # 这里的这个数据就是之前的数据,也就是从数据库或者本地获取的初始化的数据,
        for bar in initData:
            self.onBar(bar)

        self.putEvent()  # 回测中忽略这一方法

4, the policy began in the end of the callback function 

    def onStart(self):
        """启动策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略启动' % self.name)
        self.putEvent()

    #----------------------------------------------------------------------
    def onStop(self):
        """停止策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略停止' %self.name)
        self.putEvent()

      Above two callback functions to see that the policy start and end time will call again, nothing special.

5, tick drive and drive bar

      We look onTick function. This function is clear is that when there is tick data will conduct a callback function. However, our strategy is to bar level, not tick level. If a tick level, it can achieve policy logic here, because it is not, so we need to call bargenerator, which is updateTick. Here, when the tick data sufficient to synthesize a bar and they will call onBar function.

def onTick(self, tick):
        """收到行情TICK推送(必须由用户继承实现)"""
        self.bg.updateTick(tick)

def onBar(self, bar):
        """收到Bar推送(必须由用户继承实现)"""
        am = self.am        # 不明白这里为什么要这样
        am.updateBar(bar)
        if not am.inited:
            return
        
        # 计算快慢均线
        fastMa = am.sma(self.fastWindow, array=True)
        self.fastMa0 = fastMa[-1]
        self.fastMa1 = fastMa[-2]
        
        slowMa = am.sma(self.slowWindow, array=True)
        self.slowMa0 = slowMa[-1]
        self.slowMa1 = slowMa[-2]

        # 判断买卖
        crossOver = self.fastMa0>self.slowMa0 and self.fastMa1<self.slowMa1     # 金叉上穿
        crossBelow = self.fastMa0<self.slowMa0 and self.fastMa1>self.slowMa1    # 死叉下穿
        
        # 金叉和死叉的条件是互斥
        # 所有的委托均以K线收盘价委托(这里有一个实盘中无法成交的风险,考虑添加对模拟市价单类型的支持)
        if crossOver:
            # 如果金叉时手头没有持仓,则直接做多
            if self.pos == 0:
                self.buy(bar.close, 1)
            # 如果有空头持仓,则先平空,再做多
            elif self.pos < 0:
                self.cover(bar.close, 1)
                self.buy(bar.close, 1)
        # 死叉和金叉相反
        elif crossBelow:
            if self.pos == 0:
                self.short(bar.close, 1)
            elif self.pos > 0:
                self.sell(bar.close, 1)
                self.short(bar.close, 1)
                
        # 发出状态更新事件
        self.putEvent()

      Examples here are vnpy inside the source code is not written by the author, oh. onBar function to a good understanding, it is the core logic of the whole process, and is a concept backtrader the onbar, wire bar to a trigger time, which also simultaneously transmit Open unwinding signal.

6 onOrder 和 onTrader

      These two functions, is seen on the beginning that this is some kind of happened after a callback function. Order is the exchange got your list, it will be called once; onTrader is the sum of the completion of the match will be called Trader. So, ideally, both should be the same, but if there are withdrawals, there will be onOrder without calling onTrader. In addition, some arbitrage instruction, there will only be a onOrder, and can have two onTrader. In general, we will put logic in onOrder, there are special circumstances will be to use onTrader.

7, the mobile stop order

      Our strategy will always be to track trailing stop, in vnpy, the trailing stop single reason is a little copy of his implementation mechanism is constantly updated local stop-loss orders. Let's look at the example of the code vnpy:

if self.pos == 0:
            self.intraTradeHigh = bar.high
            self.intraTradeLow = bar.low

            # ATR数值上穿其移动平均线,说明行情短期内波动加大
            # 即处于趋势的概率较大,适合CTA开仓,也就是说,认为大波段来了!
            if self.atrValue > self.atrMa:
                # 使用RSI指标的趋势行情时,会在超买超卖区钝化特征,作为开仓信号
                if self.rsiValue > self.rsiBuy:
                    # 这里为了保证成交,选择超价5个整指数点下单
                    self.buy(bar.close+5, self.fixedSize)

                elif self.rsiValue < self.rsiSell:
                    self.short(bar.close-5, self.fixedSize)

        # 持有多头仓位
        elif self.pos > 0:
            # 计算多头持有期内的最高价,以及重置最低价
            self.intraTradeHigh = max(self.intraTradeHigh, bar.high)
            self.intraTradeLow = bar.low
            
            # 计算多头移动止损
            longStop = self.intraTradeHigh * (1-self.trailingPercent/100)

            # 发出本地止损委托
            self.sell(longStop, abs(self.pos), stop=True)

        # 持有空头仓位
        elif self.pos < 0:
            self.intraTradeLow = min(self.intraTradeLow, bar.low)
            self.intraTradeHigh = bar.high

            shortStop = self.intraTradeLow * (1+self.trailingPercent/100)
            self.cover(shortStop, abs(self.pos), stop=True)

      This is the source AtrRsi strategy given the author, we look at this structure:

if self.pos == 0:
#记录这一次bar的最高最低价,万一成交了,这个就是移动止损点的开始的位置。
            self.intraTradeHigh = bar.high
            self.intraTradeLow = bar.low

           #判断指标做相应的买卖操作。
elif self.pos > 0:
如果当前持仓是多头头寸,那么就更新移动止损的本地止损单位置。
# 计算多头持有期内的最高价,以及重置最低价
            self.intraTradeHigh = max(self.intraTradeHigh, bar.high)
            self.intraTradeLow = bar.low
            
            # 计算多头移动止损
            longStop = self.intraTradeHigh * (1-self.trailingPercent/100)

            # 发出本地止损委托
            self.sell(longStop, abs(self.pos), stop=True)

      Here still looks very simple. But personally I feel, whether to join the single track stop, in fact, a better approach is to start at a single time to complete the set trailing stops.

      There, we found, was issued a stop to True the list. This list can be called one-stop, also called stop-loss orders. Do some processing, that is, continue to receive the highest price you can implement a trailing stop orders.

      Here we should pay attention, stop orders, that is, stop-loss orders are locally maintained, in other words, stop sending will not be issued to a single exchange, but preserved locally, when there is market data, you first judgment about whether to trigger stop single market. Do a simple test is indeed the case, after the highs specific proportions, triggering stop loss orders automatically. And this proportion can set their own trailing stops in every policy, which is self.trailingPercent

8, the other line length k

      Our firm offer during all tick data to a bar, if the policy level a minute bar level, then implement strategies like direct onBar inside, but if our strategy is more timeframe or 10 minutes, 15 minutes such a strategy?

      Vnpy may be implemented by a plurality bargenerator. We remember that, you have to be on how many time dimensions, how many bargenerator need. The minutes tick data generated default level data.

      Suppose now that we need an hour bar, which is 60 minutes, then you can write:

self.bg = BarGenerator(self.onBar, 60, self.on60m)

      We note that this bg, which is bargenerator more than two parameters, one is 60, in fact, how many root-minute bar we need the bar, this is a 60-minute bar, which is generated after hours bar, which is a callback function ? Our strategy is to write the function inside. Here is on60m, we simply print bar time.

    def on60m(self, bar):

        print bar.datetime

This is not the end of it? No, we also need to call bg of updateBar in onBar inside:

    def onBar(self, bar):

        """收到Bar推送(必须由用户继承实现)"""

        self.bg.updateBar(bar)

      Simple comb, bg, which is bargenerator is a k-line sequence Ying generator, then initialize the generator when you need to have onbar this by a tick to call functions and minute level corresponding to the bar, and this level bar line corresponding to the callback function.

      In addition, in one example vnpy, we see that if we have two sequences of k-line, that is, there are two bg, then tick this place just call one of the bg of updateTick it.

    def onTick(self, tick):

        """收到行情TICK推送(必须由用户继承实现)"""

        # 只需要要在一个BarGenerator中合成1分钟K线

        self.bg5.updateTick(tick)

因为只有一个onBar,而在onBar中,可以更新两个bg的k线:

    def onBar(self, bar):

        """收到Bar推送(必须由用户继承实现)"""

        # 基于15分钟判断趋势过滤,因此先更新

        self.bg15.updateBar(bar)
        # 基于5分钟判断

        self.bg5.updateBar(bar)

   Another point to note is that putevent and synchronize data on these transactions are local, not necessarily the place onBar.

 

 

Published 205 original articles · won praise 236 · views 980 000 +

Guess you like

Origin blog.csdn.net/qtlyx/article/details/89045855