编写策略

使用Wt编写策略前,首先需要确定使用的引擎,为了应对不同的交易情景,WT提供了多种交易引擎供大家选择

  • 适用于中低频,多策略,多账户,多品种交易的CTA引擎
  • 适用于高频,自己管理交易执行的HFT引擎
  • 适用于多因子选股的SEL引擎
  • 适用于超高频,低延迟但需要自己处理所有细节的UFT引擎

根据自己的需求选择合适的引擎后,便可以开始编写策略了。CTA引擎是WT中最具有特色的引擎,也是有别于其他交易系统的引擎,后续的讲解主要以CTA引擎为例。

同大多数的量化框架一样,WT中的策略也采用事件驱动的方式。所谓事件驱动的意思是,策略的中逻辑的执行,是由一个个事件推动的,当一个事件发生时,策略便执行对应事件的回调函数,从而推动策略运行。

WT通过各种事件驱动策略执行,而策略则通过上下文对象主动与WT交互,不同的交易引擎提供的事件以及上下文有所不同,具体可以查阅对应交易引擎部分

以最经典的唐奇安通道策略为例:

from wtpy import BaseCtaStrategy
from wtpy import CtaContext
import numpy as np

class StraDualThrust(BaseCtaStrategy):
    
    def __init__(self, name:str, code:str, barCnt:int, period:str, days:int, k1:float, k2:float, isForStk:bool = False):
        BaseCtaStrategy.__init__(self, name)

        self.__days__ = days
        self.__k1__ = k1
        self.__k2__ = k2

        self.__period__ = period
        self.__bar_cnt__ = barCnt
        self.__code__ = code

        self.__is_stk__ = isForStk

    def on_init(self, context:CtaContext):
        code = self.__code__    #品种代码
        if self.__is_stk__:
            code = code + "Q"

        #这里演示了品种信息获取的接口
        #pInfo = context.stra_get_comminfo(code)
        #print(pInfo)

        context.stra_get_bars(code, self.__period__, self.__bar_cnt__, isMain = True)
        context.stra_log_text("DualThrust inited")

        #读取存储的数据
        self.xxx = context.user_load_data('xxx',1)

    
    def on_calculate(self, context:CtaContext):
        code = self.__code__    #品种代码

        trdUnit = 1
        if self.__is_stk__:
            trdUnit = 100

        #读取最近50条1分钟线(dataframe对象)
        theCode = code
        if self.__is_stk__:
            theCode = theCode + "Q"
        df_bars = context.stra_get_bars(theCode, self.__period__, self.__bar_cnt__, isMain = True)

        #把策略参数读进来,作为临时变量,方便引用
        days = self.__days__
        k1 = self.__k1__
        k2 = self.__k2__

        #平仓价序列、最高价序列、最低价序列
        closes = df_bars.closes
        highs = df_bars.highs
        lows = df_bars.lows

        #读取days天之前到上一个交易日位置的数据
        hh = np.amax(highs[-days:-1])
        hc = np.amax(closes[-days:-1])
        ll = np.amin(lows[-days:-1])
        lc = np.amin(closes[-days:-1])

        #读取今天的开盘价、最高价和最低价
        # lastBar = df_bars.get_last_bar()
        openpx = df_bars.opens[-1]
        highpx = df_bars.highs[-1]
        lowpx = df_bars.lows[-1]

        lastBar = df_bars.get_bar(-1)
        timePx = lastBar["bartime"]

        '''
        !!!!!这里是重点
        1、首先根据最后一条K线的时间,计算当前的日期
        2、根据当前的日期,对日线进行切片,并截取所需条数
        3、最后在最终切片内计算所需数据
        '''

        #确定上轨和下轨
        upper_bound = openpx + k1* max(hh-lc,hc-ll)
        lower_bound = openpx - k2* max(hh-lc,hc-ll)

        #读取当前仓位
        curPos = context.stra_get_position(code)/trdUnit
        now = context.stra_get_date()*10000 + timePx%10000
        
        # 向外输出指标
        context.write_indicator(tag=self.__period__, time=int(now), data={
            "highpx":highpx,
            "lowpx": lowpx,
            "upper_bound":upper_bound,
            "lower_bound":lower_bound,
            "current_position": curPos
        })

        if curPos == 0:
            if highpx >= upper_bound:
                context.stra_enter_long(code, 1*trdUnit, 'enterlong')
                context.stra_log_text("向上突破%.2f>=%.2f,多仓进场" % (highpx, upper_bound))
                return

            if lowpx <= lower_bound and not self.__is_stk__:
                context.stra_enter_short(code, 1*trdUnit, 'entershort')
                context.stra_log_text("向下突破%.2f<=%.2f,空仓进场" % (lowpx, lower_bound))
                return
        elif curPos > 0:
            if lowpx <= lower_bound:
                context.stra_exit_long(code, 1*trdUnit, 'exitlong')
                # context.stra_log_text("向下突破%.2f<=%.2f,多仓出场" % (lowpx, lower_bound))
                #raise Exception("except on purpose")
                return
        else:
            if highpx >= upper_bound and not self.__is_stk__:
                context.stra_exit_short(code, 1*trdUnit, 'exitshort')
                context.stra_log_text("向上突破%.2f>=%.2f,空仓出场" % (highpx, upper_bound))
                return


    def on_tick(self, context:CtaContext, stdCode:str, newTick:dict):
        #context.stra_log_text ("on tick fired")
        return

策略继承自基础策略类BaseCtaStrategy,通过重写基类中的各个回调函数实现,具体流程如下:

  1. __init__用于在回测或实盘初始化策略的时候,接受外部传入的参数。
  2. 当框架准备好数据后,调用on_init函数订阅数据
  3. 最后由on_tick,on_bar,on_calculate不断推动策略执行。