class AtrSignal(CtaSignal):
    """Atr信号"""

    # ----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        super(AtrSignal, self).__init__()
        self.atrWindow = 30
        self.bg = BarGenerator(self.onBar, 15, self.on15Bar)
        self.am = ArrayManager()
        self.atrValue = 0.0
        # ----------------------------------------------------------------------

    def onTick(self, tick):
        """Tick更新"""
        self.bg.updateTick(tick)

    # ----------------------------------------------------------------------
    def onBar(self, bar):
        """K线更新"""
        self.bg.updateBar(bar)

    # ----------------------------------------------------------------------
    def on15Bar(self, bar):
        """15分钟K线更新"""
        self.am.updateBar(bar)

        print bar.datetime
        print "@atrSignal"
        print "atr inited: ", self.am.inited

        if not self.am.inited:
            self.setSignalPos(0)
            return

        atrArray = self.am.atr(self.atrWindow, array=True)
        self.atrValue = atrArray[-1]
        atrMa = atrArray[-self.atrWindow:].mean()

        print "atrValue: ", self.atrValue

        # 趋势增强
        if self.atrValue > atrMa:
            self.setSignalPos(1)
        else:
            self.setSignalPos(0)
Example #2
0
class Turtle01Strategy(CtaTemplate):
    """Turtle交易策略"""
    className = 'Turtle01Strategy'
    author = u'Leon Zhao'

    # 策略参数
    initDays = 35
    fixedSize = 2
    longDays = 20
    shortDays = 20
    longExitDays = 10
    shortExitDays = 10
    atrDays = 20
    exitAtr = 2

    # 策略变量
    barList = []                # K线对象的列表

    newTradeDay = False
    lastLongEntry = 0
    lastLongTime = 0
    lastShortEntry = 0
    lastShortTime = 0
    upperChannel = 0
    lowerChannel = 0
    longEntry = 0
    shortEntry = 0
    entryPrice = 0
    entryDirection = 0 
    entryUsage = 'Turtle'
    entryUnitNo = 0
    longExit = 0
    shortExit = 0
    longAtrExit = 0
    shortAtrExit = 0
    longChannelExit = 0
    shortChannelExit = 0
    atrValue = 0
    rangeLow = 0
    exitTime = time(hour=15, minute=20) #will not cover position when day close

    longEntered = False
    shortEntered = False

    # 参数列表,保存了参数的名称
    paramList = ['name',
                 'className',
                 'author',
                 'vtSymbol',
                 'longDays',
                 'shortDays']    

    # 变量列表,保存了变量的名称
    varList = ['inited',
               'trading',
               'pos',
               'longEntry',
               'shortEntry',
               'exitTime'] 
    
    # 同步列表,保存了需要保存到数据库的变量名称
    syncList = ['pos','entryPrice','entryDirection','entryUsage','entryUnitNo']    

    #----------------------------------------------------------------------
    def __init__(self, ctaEngine, setting):
        """Constructor"""
        super(Turtle01Strategy, self).__init__(ctaEngine, setting) 
        
        self.bg = BarGenerator(self.onBar,onDayBar = self.ondayBar)
        self.am = ArrayManager(max(self.longDays,self.shortDays,self.atrDays)+1)
        self.barList = []

    #----------------------------------------------------------------------
    def onInit(self):
        """初始化策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略初始化' %self.name)
    
        # 载入历史数据,并采用回放计算的方式初始化策略数值
        initData = self.loadBar(self.initDays)
        for bar in initData:
            self.onBar(bar)

        self.putEvent()

    #----------------------------------------------------------------------
    def onStart(self):
        """启动策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略启动' %self.name)
        # No need to add calculation of the Turtle Channel on Start, it may not change over days.
        #self.calcPrepare()
        self.putEvent()

    def readLastTrade(self,):
        #read last trade data from database
        pass
    def calcPrepare(self):
        #calculate initials of the strategy
        barLength = 0 
        barLength = max(self.longDays,self.shortDays,self.atrDays) 
        if self.am.count < barLength + 1:
            return   
        #self.atrValue = talib.ATR(self.am.high, self.am.low, self.am.close,self.atrDays)[-1] 
        self.atrValue = self.am.atr(self.atrDays,False)
        # = atrLine[-1]
       
        self.upperChannel = talib.MAX(self.am.high,self.longDays)[-1]
        self.lowerChannel = talib.MIN(self.am.low,self.shortDays)[-1]
        self.longChannelExit = talib.MIN(self.am.low,self.longExitDays)[-1]
        self.shortChannelExit = talib.MAX(self.am.high,self.shortExitDays)[-1]

    def calcKPI(self):    
        if self.pos>0:
                self.longAtrExit = int(self.lastLongEntry - 2*self.atrValue)
                if self.longAtrExit > self.longChannelExit:
                    self.longExit = self.longAtrExit
                else:
                    self.longExit = self.longChannelExit
                                 
                if self.entryUnitNo == 1:
                    self.longEntry = int(self.lastLongEntry + self.atrValue)
                elif self.entryUnitNo == 2:
                    self.longEntry = int(self.lastLongEntry +0.5*self.atrValue)
                else:
                    self.longEntry = 0
        elif self.pos == 0:
                self.longEntry = self.upperChannel
                self.shortEntry = self.lowerChannel
        else:
            self.shortAtrExit = int(self.lastShortEntry + 2*self.atrValue)
            if self.shortAtrExit < self.shortChannelExit:
                self.shortExit = self.shortAtrExit
            else:
                self.shortExit = self.shortChannelExit
            if self.entryUnitNo == 1:
                self.shortEntry = int(self.lastShortEntry-self.atrValue)
            elif self.entryUnitNo == 2:
                self.shortEntry = int(self.lastShortEntry-0.5*self.atrValue)
            else:
                self.shortEntry = 0
             
          
    #----------------------------------------------------------------------
    def onStop(self):
        """停止策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略停止' %self.name)
        self.putEvent()

    #----------------------------------------------------------------------
    def onTick(self, tick):
        """收到行情TICK推送(必须由用户继承实现)"""
        self.bg.updateTick(tick)          
        
    #----------------------------------------------------------------------
    def onBar(self, bar):
        """收到Bar推送(必须由用户继承实现)"""
        # 撤销之前发出的尚未成交的委托(包括限价单和停止单)
        self.cancelAll()

        self.bg.updateBar(bar)
              
        barLength = 0 
        barLength = max(self.longDays,self.shortDays,self.atrDays) 

        if self.am.count < barLength + 1:
            return              
        # 计算指标数值
        self.barList.append(bar)
        
        if len(self.barList) <= 2:
            return
        else:
            self.barList.pop(0)
        lastBar = self.barList[-2]
        
        # 新的一天
        if (lastBar.datetime.hour == 15 or (lastBar.datetime.hour==14 and lastBar.datetime.minute==59)) and ((bar.datetime.hour == 21) or (bar.datetime.hour == 9)):
            # 如果已经初始化
            if not self.upperChannel :
                #do things:
                self.calcPrepare()
            else:
                pass
            
        if self.pos == 0:
            self.lastLongTime = 0
            self.lastShortTime = 0
            self.entryUnitNo = 0
        self.calcKPI()
        
        if self.pos > 0:
            #self.sell(self.longExit,self.fixedSize,stop)
            if  bar.close < self.longExit:
                self.sell(bar.close-2,self.pos)
            elif bar.close > self.longEntry and self.longEntry > 0 :
                self.buy(bar.close+2,self.fixedSize)
            else:
                pass
        elif self.pos == 0:
            #self.entryUnitNo = 0
            if bar.close > self.longEntry and self.longEntry > 0 :
                self.buy(bar.close+2,self.fixedSize)
            elif bar.close < self.shortEntry and self.shortEntry > 0:
                self.short(bar.close -2, self.fixedSize)
            else:
                pass
        else:
            if bar.close < self.shortEntry and self.shortEntry > 0 :
                self.short(bar.close-2,self.fixedSize)
            elif bar.close > self.shortExit:
                self.cover(bar.close+2,abs(self.pos))
            else:
                pass                        

        # 发出状态更新事件
        self.putEvent()
    #update day chart
    def ondayBar(self, dayBar):
        """收到日线推送(必须由用户继承实现)"""
        self.am.updateBar(dayBar)
        self.calcPrepare()
        # 发出状态更新事件
        self.putEvent() 
    #----------------------------------------------------------------------
    def onOrder(self, order):
        """收到委托变化推送(必须由用户继承实现)"""
        #if order.status == STATUS_ALLTRADED and self.pos != 0:
            # How do I know the last trade is open or exit?
        #    self.entryUnitNo = self.entryUnitNo + 1

    #----------------------------------------------------------------------
    def onTrade(self, trade):
        # 发出状态更新事件
        if trade.direction == DIRECTION_LONG and trade.offset == OFFSET_OPEN:
            self.entryUnitNo = self.entryUnitNo + 1
            self.lastLongEntry = trade.price
            self.entryPrice = trade.price
            self.entryDirection = DIRECTION_LONG            
        elif trade.direction == DIRECTION_SHORT and trade.offset == OFFSET_OPEN:
            self.entryUnitNo = self.entryUnitNo + 1
            self.lastShortEntry = trade.price 
            self.entryPrice = trade.price
            self.entryDirection = DIRECTION_SHORT                         
        elif (trade.offset == OFFSET_CLOSE or trade.offset == OFFSET_CLOSETODAY ):
            #print(self.pos)
            self.entryUnitNo = 0
            self.lastLongEntry = 0
            self.lastShortEntry = 0
            self.entryPrice = 0
            self.entryDirection = OFFSET_CLOSE            
        else:
            pass
        self.putEvent() 

    #----------------------------------------------------------------------
    def onStopOrder(self, so):
        """停止单推送"""
        pass
Example #3
0
class TurtleTradingStrategy(CtaTemplate):
    """海龟交易策略"""
    className = 'TurtleTradingStrategy'
    author = u'用Python的交易员'

    # 策略参数
    entryWindow = 55                    # 入场通道窗口
    exitWindow = 20                     # 出场通道窗口
    atrWindow = 20                      # 计算ATR波动率的窗口
    initDays = 10                       # 初始化数据所用的天数
    fixedSize = 1                       # 每次交易的数量

    # 策略变量
    entryUp = 0                         # 入场通道上轨
    entryDown = 0                       # 入场通道下轨
    exitUp = 0                          # 出场通道上轨
    exitDown = 0                        # 出场通道下轨
    atrVolatility = 0                   # ATR波动率
    
    longEntry = 0                       # 多头入场价格
    shortEntry = 0                      # 空头入场价格
    longStop = 0                        # 多头止损价格
    shortStop = 0                       # 空头止损价格
    
    # 参数列表,保存了参数的名称
    paramList = ['name',
                 'className',
                 'author',
                 'vtSymbol',
                 'entryWindow',
                 'exitWindow',
                 'atrWindow',
                 'initDays',
                 'fixedSize']    

    # 变量列表,保存了变量的名称
    varList = ['inited',
               'trading',
               'pos',
               'entryUp',
               'entryDown',
               'exitUp',
               'exitDown',
               'longEntry',
               'shortEntry',
               'longStop',
               'shortStop']  
    
    # 同步列表,保存了需要保存到数据库的变量名称
    syncList = ['pos']

    #----------------------------------------------------------------------
    def __init__(self, ctaEngine, setting):
        """Constructor"""
        super(TurtleTradingStrategy, self).__init__(ctaEngine, setting)
        
        self.bg = BarGenerator(self.onBar)
        self.am = ArrayManager()
        
    #----------------------------------------------------------------------
    def onInit(self):
        """初始化策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略初始化' %self.name)
        
        # 载入历史数据,并采用回放计算的方式初始化策略数值
        initData = self.loadBar(self.initDays)
        for bar in initData:
            self.onBar(bar)

        self.putEvent()

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

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

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

    #----------------------------------------------------------------------
    def onBar(self, bar):
        """收到Bar推送(必须由用户继承实现)"""
        self.cancelAll()
    
        # 保存K线数据
        self.am.updateBar(bar)
        if not self.am.inited:
            return
        
        # 计算指标数值
        self.entryUp, self.entryDown = self.am.donchian(self.entryWindow)
        self.exitUp, self.exitDown = self.am.donchian(self.exitWindow)
        
        if not self.pos:
            self.atrVolatility = self.am.atr(self.atrWindow)
        
        # 判断是否要进行交易
        if self.pos == 0:
            self.longEntry = 0
            self.shortEntry = 0
            self.longStop = 0
            self.shortStop = 0
            
            self.sendBuyOrders(self.entryUp)
            self.sendShortOrders(self.entryDown)
    
        elif self.pos > 0:
            # 加仓逻辑
            self.sendBuyOrders(self.longEntry)
            
            # 止损逻辑
            sellPrice = max(self.longStop, self.exitDown)
            self.sell(sellPrice, abs(self.pos), True)
    
        elif self.pos < 0:
            # 加仓逻辑
            self.sendShortOrders(self.shortEntry)
            
            # 止损逻辑
            coverPrice = min(self.shortStop, self.exitUp)
            self.cover(coverPrice, abs(self.pos), True)
        
        # 同步数据到数据库
        self.saveSyncData()        
    
        # 发出状态更新事件
        self.putEvent()        

    #----------------------------------------------------------------------
    def onOrder(self, order):
        """收到委托变化推送(必须由用户继承实现)"""
        pass

    #----------------------------------------------------------------------
    def onTrade(self, trade):
        """成交推送"""
        if trade.direction == DIRECTION_LONG:
            self.longEntry = trade.price
            self.longStop = self.longEntry - self.atrVolatility * 2
        else:
            self.shortEntry = trade.price
            self.shortStop = self.shortEntry + self.atrVolatility * 2
        
        # 发出状态更新事件
        self.putEvent()

    #----------------------------------------------------------------------
    def onStopOrder(self, so):
        """停止单推送"""
        pass
    
    #----------------------------------------------------------------------
    def sendBuyOrders(self, price):
        """发出一系列的买入停止单"""
        t = self.pos / self.fixedSize
        
        if t < 1:
            self.buy(price, self.fixedSize, True)

        if t < 2:
            self.buy(price + self.atrVolatility*0.5, self.fixedSize, True)
                
        if t < 3:
            self.buy(price + self.atrVolatility, self.fixedSize, True)

        if t < 4:
            self.buy(price + self.atrVolatility*1.5, self.fixedSize, True)    
    
    #----------------------------------------------------------------------
    def sendShortOrders(self, price):
        """"""
        t = self.pos / self.fixedSize
        
        if t > -1:
            self.short(price, self.fixedSize, True)
        
        if t > -2:
            self.short(price - self.atrVolatility*0.5, self.fixedSize, True)
    
        if t > -3:
            self.short(price - self.atrVolatility, self.fixedSize, True)
    
        if t > -4:
            self.short(price - self.atrVolatility*1.5, self.fixedSize, True)            
Example #4
0
class EMAC_IntraDayCommonStrategy(CtaTemplate):
    """DualThrust交易策略"""
    className = 'EMAC_IntraDayCommonStrategy'
    author = u'Leon Zhao'

    # 策略参数
    fixedSize = 1
    fast1 = 5
    slow1 = 21

    fast2 = 8
    slow2 = 34

    fast3 = 13
    slow3 = 55
    dbpath = "./sr.csv"
    cumrange = 0
    shreshhold = 0.3
    atrDays = 20
    atrValue = 0
    initDays = 35
    # 策略变量
    barList = []  # K线对象的列表

    longEntry = 0
    shortEntry = 0
    exitTime = time(hour=15,
                    minute=20)  #will not cover position when day close

    longEntered = False
    shortEntered = False

    # 参数列表,保存了参数的名称
    paramList = ['name', 'className', 'author', 'vtSymbol']

    # 变量列表,保存了变量的名称
    varList = [
        'inited', 'trading', 'pos', 'cumrange', 'longEntry', 'shortEntry',
        'exitTime'
    ]
    range = 0
    longEntry1 = 0
    shortEntry1 = 0
    # 同步列表,保存了需要保存到数据库的变量名称
    syncList = ['pos', 'range', 'longEntry1', 'shortEntry1']

    #----------------------------------------------------------------------
    def __init__(self, ctaEngine, setting):
        """Constructor"""
        super(EMAC_IntraDayCommonStrategy, self).__init__(ctaEngine, setting)

        self.bg = BarGenerator(self.onBar,
                               onDayBar=self.ondayBar,
                               vtSymbol=self.vtSymbol)
        self.am = ArrayManager()
        self.indexam = ArrayManager()
        self.barList = []
        self.longEntry1 = 0
        self.shortEntry1 = 0
        # Read Parameters from Setting files
        if 'strParams' in setting:
            self.params = setting['strParams']
            if len(self.params) >= 3:
                for p in self.params:
                    if p[0] == 'unit':
                        self.fixedSize = p[1]
                    if p[0] == 'p1':
                        self.fast1 = p[1]
                    if p[0] == 'p2':
                        self.slow1 = p[1]
                    if p[0] == 'p3':
                        self.fast2 = p[1]
                    if p[0] == 'p4':
                        self.slow2 = p[1]
                    if p[0] == 'p5':
                        self.fast3 = p[1]
                    if p[0] == 'p6':
                        self.slow3 = p[1]
                    if p[0] == 'p7':
                        self.dbpath = p[1]
        else:
            # 策略参数
            self.fast1 = 5
            self.slow1 = 21

            self.fast2 = 8
            self.slow2 = 34

            self.fast3 = 13
            self.slow3 = 55
            self.dbpath = "./sr.csv"
        #print(self.fixedSize,self.k1,self.k2,self.rangeDays,self.initDays)
        self.cumrange = 0
        self.shreshhold = 0.3
        self.atrDays = 20
        self.atrValue = 0
        self.initDays = 100
        self.longEntry = 0
        self.shortEntry = 0
        self.exitTime = time(
            hour=15, minute=20)  #will not cover position when day close
        self.longEntered = False
        self.shortEntered = False
        self.emac_kpi = 0
        self.emac1scalar = 7.5
        self.emac2scalar = 5.3
        self.emac3scalar = 3.7
        self.pcstd = 0
        self.weights = [0.35, 0.3, 0.35]

    def loadIndexBar(self, dbpath):
        csvfile = "../TdxData/bar_data/" + dbpath
        #print(csvfile)
        dfindex = pd.read_csv(csvfile, parse_dates=True, index_col=0)
        #print(dbpath)
        dfindex["pc"] = dfindex["close"] - dfindex["close"].shift(-1)
        #dfordered = dfindex.sort_index( ascending=False)
        daybar = VtBarData()

        dt = datetime.now()
        for idx, indexbar in dfindex.iterrows():
            #print(idx)
            daybar.vtSymbol = self.vtSymbol
            daybar.symbol = self.vtSymbol
            daybar.exchange = ""

            daybar.open = indexbar["open"]
            daybar.high = indexbar["high"]
            daybar.low = indexbar["low"]
            daybar.close = indexbar["close"]
            #dt = datetime.strptime(str(indexbar["trade_date"]),"%Y%m%d")
            #change bar Date to next day if time is night
            #nextDay =
            daybar.datetime = idx  # 以第一根分钟K线的开始时间戳作为X分钟线的时间戳
            daybar.date = daybar.datetime.strftime('%Y%m%d')
            daybar.time = daybar.datetime.strftime('%H:%M:%S.%f')
            #print(daybar.datetime,daybar.close)
            self.indexam.updateBar(daybar)
        temp = dfindex["pc"].rolling(20, min_periods=20).std()
        temp = temp.dropna()
        self.pcstd = temp.iloc[-1]

    #----------------------------------------------------------------------
    def onInit(self):
        """初始化策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略初始化' % self.name)

        # 载入历史数据,并采用回放计算的方式初始化策略数值
        initData = self.loadBar(self.initDays)
        #dbpath = ""
        self.loadIndexBar(self.dbpath)
        for bar in initData:
            self.onBar(bar)

        self.putEvent()

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

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

    #----------------------------------------------------------------------
    def onTick(self, tick):
        """收到行情TICK推送(必须由用户继承实现)"""
        #ignore data before real open
        if (tick.datetime.hour == 8 or tick.datetime.hour == 20):
            return
        self.bg.updateTick(tick)

    def calcUnitNo(self, atr, fixSize):
        dtCap = 0.0
        defaultCap = 0.0
        unitNo = 0
        cust = []
        var_sizelist = CtaTemplate.vol_Size
        var_size = 0.0
        var_Symbol = ""
        if len(var_sizelist) == 0:
            return fixSize
        else:
            var_Symbol = var_Symbol.join(
                list(filter(lambda x: x.isalpha(), self.vtSymbol)))
            var_size = float(var_sizelist[var_Symbol][0])
            if var_size - 0 < 0.01:
                return fixSize

        var_temp = 0.0
        if len(CtaTemplate.cust_Setting) > 0:
            cust = CtaTemplate.cust_Setting
        for cs in cust:
            if cs["StrategyGroup"] == "DT" and cs["Status"] == 'True':
                dtCap = cs["CaptialAmt"]
                break
            if cs["StrategyGroup"] == "Default" and cs["Status"] == 'True':
                defaultCap = cs["CaptialAmt"]
        if dtCap > 0:
            self.capConfig = float(dtCap)
        elif defaultCap > 0:
            self.capConfig = float(defaultCap)
        else:
            self.capConfig = 0.0

        unitNo = 0
        if self.capConfig - 0 < 0.0001:
            unitNo = fixSize
        elif var_size - 0 < 0.001:
            unitNo = fixSize
        else:
            unitNo = int(self.capConfig * 0.0088 / (atr * var_size))
        if unitNo < 1:
            unitNo = 1

        return unitNo

    #---------calcuate range for the last several days
    def getUnitNo(self):
        if self.am.count >= self.atrDays + 1:
            self.atrValue = self.am.atr(self.atrDays, False)
            if self.atrValue > 0:
                self.fixedSize = self.calcUnitNo(self.atrValue, self.fixedSize)
        else:
            pass

    def CalcKPI(self):
        #pass
        emafast1 = self.indexam.ema(self.fast1)
        emaslow1 = self.indexam.ema(self.slow1)
        emafast2 = self.indexam.ema(self.fast2)
        emaslow2 = self.indexam.ema(self.slow2)
        emafast3 = self.indexam.ema(self.fast3)
        emaslow3 = self.indexam.ema(self.slow3)

        kpi = self.emac1scalar * (
            emafast1 -
            emaslow1) * self.weights[0] / self.pcstd + self.emac2scalar * (
                emafast2 -
                emaslow2) * self.weights[1] / self.pcstd + self.emac3scalar * (
                    emafast3 - emaslow3) * self.weights[2] / self.pcstd
        return kpi
        #indexvol = self.indexam

    #----------------------------------------------------------------------
    def onBar(self, bar):
        """收到Bar推送(必须由用户继承实现)"""

        if self.reduceCountdown() > 0:
            return
        # 撤销之前发出的尚未成交的委托(包括限价单和停止单)

        self.cancelAll()

        self.bg.updateBar(bar)
        barLength = 0
        barLength = self.atrDays + 1
        if self.am.count < barLength:
            return
        # 计算指标数值
        self.barList.append(bar)

        if len(self.barList) <= 2:
            return
        else:
            self.barList.pop(0)
        lastBar = self.barList[-2]

        self.getUnitNo()
        self.emac_kpi = self.CalcKPI()
        #print(self.emac_kpi,bar.close)
        pos_multiple = 1
        if abs(self.emac_kpi) > 30:
            pos_multiple = 2
        else:
            pos_multiple = 1
        #print(self.emac_kpi)
        if True:  # Trade Time, no matter when, just send signal
            if self.pos == 0:
                self.longEntered = False
                self.shortEntered = False
                if self.emac_kpi > 1:
                    self.buy(bar.close, self.fixedSize * pos_multiple)
                elif self.emac_kpi < -1:
                    self.short(bar.close, self.fixedSize * pos_multiple)
                else:
                    pass

            # 持有多头仓位
            elif self.pos > 0:
                self.longEntered = True
                self.shortEntered = False
                # 多头止损单
                if self.emac_kpi < 1 and self.emac_kpi > -1:
                    #self.sell(self.shortEntry -2 , self.fixedSize)
                    self.sell(bar.close, abs(self.pos))
                    # 空头开仓单
                elif self.emac_kpi < -1:
                    self.sell(bar.close,
                              abs(self.pos))  # close first then open new
                    if not self.shortEntered:
                        #self.short(self.shortEntry -2 , self.fixedSize)
                        self.short(bar.close, self.fixedSize * pos_multiple)
            # 持有空头仓位
            elif self.pos < 0:
                self.shortEntered = True
                self.longEntered = False
                # 空头止损单
                if self.emac_kpi > -1 and self.emac_kpi < 1:
                    #self.cover(self.longEntry + 2, self.fixedSize)
                    self.cover(bar.close, abs(self.pos))
                    # 多头开仓单

                elif self.emac_kpi > 1:
                    self.cover(bar.close,
                               abs(self.pos))  # close first then open new
                    if not self.longEntered:
                        #self.buy(self.longEntry + 2, self.fixedSize)
                        self.buy(bar.close, self.fixedSize)
        # 收盘平仓 This will not execute
        else:
            if self.pos > 0:
                self.sell(bar.close * 0.99, abs(self.pos))
            elif self.pos < 0:
                self.cover(bar.close * 1.01, abs(self.pos))

        # 发出状态更新事件
        self.putEvent()

    #update day chart
    def ondayBar(self, dayBar):
        """收到日线推送(必须由用户继承实现)"""
        self.am.updateBar(dayBar)
        self.range = None
        self.dayOpen = 0
        # 发出状态更新事件
        self.putEvent()

    #----------------------------------------------------------------------
    def onOrder(self, order):
        """收到委托变化推送(必须由用户继承实现)"""
        pass

    #----------------------------------------------------------------------
    def onTrade(self, trade):
        # 发出状态更新事件
        persisttrade(self.vtSymbol, self.className, trade)
        self.putEvent()

    #----------------------------------------------------------------------
    def onStopOrder(self, so):
        """停止单推送"""
        pass
Example #5
0
class DT_ChengfaStrategy(CtaTemplate):
    """DualThrust交易策略"""
    className = 'DT_ChengfaStrategy'
    author = u'Leon Zhao'

    # 策略参数
    fixedSize = 1
    k1 = 0.4
    k2 = 0.4
    
    rangeDays = 4
    initDays = 30 # original value is 10
    atrDays = 20


    # 策略变量
    barList = []                # K线对象的列表

    dayOpen = 0
    rangeHigh = 0
    rangeLow = 0
    rangeHighClose = 0
    rangeLowClose = 0
    range1 = 0
    range2 = 0
    
    range = 0
    longEntry = 0
    shortEntry = 0
    exitTime = time(hour=15, minute=20) #will not cover position when day close

    longEntered = False
    shortEntered = False

    # 参数列表,保存了参数的名称
    paramList = ['name',
                 'className',
                 'author',
                 'vtSymbol',
                 'k1',
                 'k2']    

    # 变量列表,保存了变量的名称
    varList = ['inited',
               'trading',
               'pos',
               'range',
               'longEntry',
               'shortEntry',
               'exitTime'] 
    longEntry1 = 0
    shortEntry1 = 0
    # 同步列表,保存了需要保存到数据库的变量名称
    syncList = ['pos','range','longEntry1','shortEntry1']    

    #----------------------------------------------------------------------
    def __init__(self, ctaEngine, setting):
        """Constructor"""
        super(DT_ChengfaStrategy, self).__init__(ctaEngine, setting) 
        
        self.bg = BarGenerator(self.onBar,onDayBar = self.ondayBar,vtSymbol =self.vtSymbol)
        self.am = ArrayManager()
        self.barList = []
        self.longEntry1 = 0
        self.shortEntry1 = 0        
        # Read Parameters from Setting files
        if 'strParams' in setting:
            self.params = setting['strParams']
            if len(self.params)>=3:
                for p in self.params:
                    if p[0] == 'unit':
                        self.fixedSize = p[1]
                    if p[0] == 'p1':
                        self.k1 = p[1]
                    if p[0] == 'p2':
                        self.k2 = p[1]
                    if p[0] == 'p3':
                        self.rangeDays = p[1]
                    if p[0] == 'p4':
                        self.atrDays = p[1]  
                    if p[0] == 'p5':
                        self.initDays = p[1]                                                 

        else:
            # 策略参数
            self.fixedSize = 1
            self.k1 = 0.4
            self.k2 = 0.4
            
            self.rangeDays = 4
            self.atrDays = 20
            self.initDays = 55 # original value is 10     
        #print(self.fixedSize,self.k1,self.k2,self.rangeDays,self.initDays)             
        self.dayOpen = 0
        self.rangeHigh = 0
        self.rangeLow = 0
        self.rangeHighClose = 0
        self.rangeLowClose = 0
        self.range1 = 0
        self.range2 = 0
        self.atrValue = 0
        
        self.range = 0
        self.longEntry = 0
        self.shortEntry = 0
        self.exitTime = time(hour=15, minute=20) #will not cover position when day close
        self.longEntered = False
        self.shortEntered = False
        self.testflag = False
                
    #----------------------------------------------------------------------
    def onInit(self):
        """初始化策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略初始化' %self.name)
    
        # 载入历史数据,并采用回放计算的方式初始化策略数值
        initData = self.loadBar(self.initDays)
        for bar in initData:
            self.onBar(bar)

        self.putEvent()

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

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

    #----------------------------------------------------------------------
    def onTick(self, tick):
        """收到行情TICK推送(必须由用户继承实现)"""
        #ignore data before real open
        if (tick.datetime.hour == 8 or tick.datetime.hour ==20):
            return
        self.bg.updateTick(tick)
        
    def calcUnitNo(self,atr,fixSize):
        dtCap = 0.0
        defaultCap = 0.0
        unitNo = 0
        cust = []
        var_sizelist = CtaTemplate.vol_Size
        var_size = 0.0
        var_Symbol = ""
        if len(var_sizelist) == 0:
            return fixSize
        else:
            var_Symbol = var_Symbol.join(list(filter(lambda x: x.isalpha(),self.vtSymbol)))            
            var_size = float(var_sizelist[var_Symbol][0])
            if var_size -0 < 0.01:
                return fixSize
        
        var_temp = 0.0
        if len(CtaTemplate.cust_Setting) > 0:
            cust = CtaTemplate.cust_Setting
        for cs in cust:
            if cs["StrategyGroup"] == "DT" and cs["Status"] == 'True':
                dtCap = cs["CaptialAmt"]
                break
            if cs["StrategyGroup"] == "Default" and cs["Status"] == 'True':
                defaultCap = cs["CaptialAmt"]
        if dtCap > 0:
            self.capConfig = float(dtCap)
        elif defaultCap > 0 :
            self.capConfig = float(defaultCap)
        else:
            self.capConfig = 0.0
        
        unitNo = 0
        if self.capConfig -0 < 0.0001:
            unitNo = fixSize
        elif var_size - 0 < 0.001:
            unitNo = fixSize
        else:
            unitNo = int(self.capConfig * 0.0088 /(atr*var_size))
        if unitNo < 1:
            unitNo = 1
        return unitNo    
        
    #---------calcuate range for the last several days 
    def calcRange(self):
        if self.am.count >= self.atrDays + 1 :
            self.atrValue = self.am.atr(self.atrDays,False)
            if self.atrValue > 0 :
                self.fixedSize = self.calcUnitNo(self.atrValue, self.fixedSize)          
            self.rangeHigh = talib.MAX(self.am.high,self.rangeDays)[-1]
            self.rangeLow =  talib.MIN(self.am.low,self.rangeDays)[-1]
            self.rangeHighClose = talib.MAX(self.am.close,self.rangeDays)[-1]
            self.rangeLowClose  = talib.MIN(self.am.close,self.rangeDays)[-1]
            self.range1 = self.rangeHigh-self.rangeLowClose
            self.range2 = self.rangeHighClose -self.rangeLow
            
            #print(self.rangeHigh,self.rangeLow)
            if (self.range1 > self.range2) :
                calcRange = self.range1
            else:
                calcRange = self.range2
        else:
            calcRange = 0
        return calcRange            
        
    #----------------------------------------------------------------------
    def onBar(self, bar):
        """收到Bar推送(必须由用户继承实现)"""
        self.fixedSize = 1
        if self.reduceCountdown() > 0:
            return
        # 撤销之前发出的尚未成交的委托(包括限价单和停止单)
        self.cancelAll()

        self.bg.updateBar(bar)
        barLength = 0
     
        # 计算指标数值
        self.barList.append(bar)
        
        if len(self.barList) <= 2:
            return
        else:
            self.barList.pop(0)
        lastBar = self.barList[-2]
        print(bar.close)
        
        if self.testflag:
            return
        if self.pos == 0:
            self.buy(bar.close,self.fixedSize)
        elif self.pos > 0:

            self.sell(bar.close,abs(self.pos))
            if not self.shortEntered:
                #self.short(self.shortEntry -2 , self.fixedSize)
                self.short(bar.close,self.fixedSize)
            self.testflag = True
            # 持有空头仓位
        elif self.pos < 0:            
            self.cover(bar.close,abs(self.pos))
            if not self.longEntered:
                self.buy(bar.close,self.fixedSize) 
            self.testflag = True
        # 发出状态更新事件
        self.putEvent()
    #update day chart
    def ondayBar(self, dayBar):
        """收到日线推送(必须由用户继承实现)"""
        self.am.updateBar(dayBar)
        self.range = None
        self.dayOpen = 0
        # 发出状态更新事件
        self.putEvent() 
    #----------------------------------------------------------------------
    def onOrder(self, order):
        """收到委托变化推送(必须由用户继承实现)"""
        pass

    #----------------------------------------------------------------------
    def onTrade(self, trade):
        # 发出状态更新事件
        persisttrade(self.vtSymbol,self.className ,trade)
        self.putEvent()

    #----------------------------------------------------------------------
    def onStopOrder(self, so):
        """停止单推送"""
        pass
Example #6
0
class TestPriceStrategy(CtaTemplate):
    """Turtle交易策略"""
    className = 'TestPriceStrategy'
    author = u'Leon Zhao'

    # 策略参数

    fixedSize = 1
    longDays = 20
    shortDays = 20
    longExitDays = 10
    shortExitDays = 10
    initDays = 35
    atrDays = 20
    exitAtr = 2

    # 策略变量
    barList = []  # K线对象的列表

    newTradeDay = False
    lastLongEntry = 0
    lastLongTime = 0
    lastShortEntry = 0
    lastShortTime = 0
    upperChannel = 0
    lowerChannel = 0
    longEntry = 0
    shortEntry = 0
    entryPrice = 0
    entryDirection = 0
    entryUsage = 'Turtle'
    entryUnitNo = 0
    longExit = 0
    shortExit = 0
    longAtrExit = 0
    shortAtrExit = 0
    longChannelExit = 0
    shortChannelExit = 0
    atrValue = 0
    entryAtr = 0
    rangeLow = 0
    exitTime = time(hour=15,
                    minute=20)  #will not cover position when day close

    longEntered = False
    shortEntered = False

    capConfig = 0.0
    onTradeCnt = 0
    previousTrade = 0
    bookTime = datetime.now()

    # 参数列表,保存了参数的名称
    paramList = [
        'name', 'className', 'author', 'vtSymbol', 'longDays', 'shortDays'
    ]

    # 变量列表,保存了变量的名称
    varList = [
        'inited', 'trading', 'pos', 'longEntry', 'shortEntry', 'exitTime',
        'upperChannel', 'lowerChannel'
    ]

    # 同步列表,保存了需要保存到数据库的变量名称
    syncList = [
        'pos', 'entryPrice', 'entryDirection', 'entryUsage', 'entryUnitNo',
        'lastLongEntry', 'lastShortEntry', 'entryAtr'
    ]

    #----------------------------------------------------------------------
    def __init__(self, ctaEngine, setting):
        """Constructor"""
        super(TestPriceStrategy, self).__init__(ctaEngine, setting)

        self.bg = BarGenerator(self.onBar,
                               onDayBar=self.ondayBar,
                               vtSymbol=self.vtSymbol)
        self.am = ArrayManager(
            max(self.longDays, self.shortDays, self.atrDays) + 1)
        self.barList = []
        if 'strParams' in setting:
            self.params = setting['strParams']
            if len(self.params) >= 3:
                for p in self.params:
                    if p[0] == 'unit':
                        self.fixedSize = p[1]
                    if p[0] == 'p1':
                        self.longDays = p[1]
                    if p[0] == 'p2':
                        self.shortDays = p[1]
                    if p[0] == 'p3':
                        self.longExitDays = p[1]
                    if p[0] == 'p4':
                        self.shortExitDays = p[1]
                    if p[0] == 'p5':
                        self.initDays = p[1]
        else:
            # 策略参数
            self.fixedSize = 1
            self.longDays = 20
            self.shortDays = 20
            self.longExitDays = 10
            self.shortExitDays = 10
            self.initDays = 35

        #print("ma debug:",self.fixedSize,self.longDays,self.shortDays,self.longExitDays,self.shortExitDays,self.initDays)
        # Use class variant should be OK, however, to be save just instance them.
        self.newTradeDay = False
        self.lastLongEntry = 0
        self.lastLongTime = 0
        self.lastShortEntry = 0
        self.lastShortTime = 0
        self.upperChannel = 0
        self.lowerChannel = 0
        self.longEntry = 0
        self.shortEntry = 0
        self.entryPrice = 0
        self.entryDirection = 0
        self.entryUsage = 'Turtle'
        self.entryUnitNo = 0
        self.longExit = 0
        self.shortExit = 0
        self.longAtrExit = 0
        self.shortAtrExit = 0
        self.longChannelExit = 0
        self.shortChannelExit = 0
        self.atrValue = 0
        self.entryAtr = 0
        self.rangeLow = 0
        self.exitTime = time(
            hour=15, minute=20)  #will not cover position when day close

        self.longEntered = False
        self.shortEntered = False

        self.capConfig = 0.0
        self.onTradeCnt = 0
        self.bookTime = datetime.now()
        self.buyCnt = 0

    #----------------------------------------------------------------------
    def onInit(self):
        """初始化策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略初始化' % self.name)

        # 载入历史数据,并采用回放计算的方式初始化策略数值
        initData = self.loadBar(self.initDays)
        for bar in initData:
            self.onBar(bar)

        self.putEvent()

    #----------------------------------------------------------------------
    def onStart(self):
        """启动策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略启动' % self.name)
        # No need to add calculation of the Turtle Channel on Start, it may not change over days.
        #self.calcPrepare()
        self.readLastTrade()
        self.putEvent()

    def readLastTrade(self):
        #read last trade data from database
        #In fact no need after I persist those two values.
        if self.pos > 0:
            self.lastLongEntry = self.entryPrice
            self.lastShortEntry = 0
        elif self.pos < 0:
            self.lastShortEntry = self.entryPrice
            self.lastLongEntry = 0
        pass

    def calcUnitNo(self, atr, fixSize):
        turtleCap = 0.0
        defaultCap = 0.0
        unitNo = 0
        cust = []
        var_sizelist = CtaTemplate.vol_Size
        var_size = 0.0
        var_Symbol = ""
        if len(var_sizelist) == 0:
            return fixSize
        else:
            var_Symbol = var_Symbol.join(
                list(filter(lambda x: x.isalpha(), self.vtSymbol)))
            var_size = float(var_sizelist[var_Symbol][0])
            if var_size - 0 < 0.01:
                return fixSize

        var_temp = 0.0
        if len(CtaTemplate.cust_Setting) > 0:
            cust = CtaTemplate.cust_Setting
        for cs in cust:
            if cs["StrategyGroup"] == "Turtle" and cs["Status"] == 'True':
                turtleCap = cs["CaptialAmt"]
                break
            if cs["StrategyGroup"] == "Default" and cs["Status"] == 'True':
                defaultCap = cs["CaptialAmt"]
        if turtleCap > 0:
            self.capConfig = float(turtleCap)
        elif defaultCap > 0:
            self.capConfig = float(defaultCap)
        else:
            self.capConfig = 0.0

        unitNo = 0
        if self.capConfig - 0 < 0.0001:
            unitNo = fixSize
        elif var_size - 0 < 0.001:
            unitNo = fixSize
        else:
            unitNo = int(self.capConfig * 0.005 / (atr * var_size))

        if unitNo < 1:
            unitNo = 1
        return unitNo

    def calcPrepare(self):
        #calculate initials of the strategy
        barLength = 0
        barLength = max(self.longDays, self.shortDays, self.atrDays)
        if self.am.count < barLength + 1:
            return
        #self.atrValue = talib.ATR(self.am.high, self.am.low, self.am.close,self.atrDays)[-1]
        self.atrValue = self.am.atr(self.atrDays, False)
        # = atrLine[-1]
        if self.atrValue > 0:
            self.fixedSize = self.calcUnitNo(self.atrValue, self.fixedSize)
            #call method to calc unit
        self.upperChannel = talib.MAX(self.am.high, self.longDays)[-1]
        self.lowerChannel = talib.MIN(self.am.low, self.shortDays)[-1]
        self.longChannelExit = talib.MIN(self.am.low, self.longExitDays)[-1]
        self.shortChannelExit = talib.MAX(self.am.high, self.shortExitDays)[-1]

    def calcKPI(self):
        if self.pos > 0:
            self.longAtrExit = int(self.lastLongEntry - 2 * self.entryAtr)
            if self.longAtrExit > self.longChannelExit:
                self.longExit = self.longAtrExit
            else:
                self.longExit = self.longChannelExit

            if self.entryUnitNo == 1:
                self.longEntry = int(self.lastLongEntry + self.entryAtr)
            elif self.entryUnitNo == 2:
                self.longEntry = int(self.lastLongEntry + 0.5 * self.entryAtr)
            else:
                self.longEntry = 0
        elif self.pos == 0:
            self.longEntry = self.upperChannel
            self.shortEntry = self.lowerChannel
        else:
            self.shortAtrExit = int(self.lastShortEntry + 2 * self.entryAtr)
            if self.shortAtrExit < self.shortChannelExit:
                self.shortExit = self.shortAtrExit
            else:
                self.shortExit = self.shortChannelExit
            if self.entryUnitNo == 1:
                self.shortEntry = int(self.lastShortEntry - self.entryAtr)
            elif self.entryUnitNo == 2:
                self.shortEntry = int(self.lastShortEntry -
                                      0.5 * self.entryAtr)
            else:
                self.shortEntry = 0

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

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

    #----------------------------------------------------------------------
    def onBar(self, bar):
        """收到Bar推送(必须由用户继承实现)"""
        # 撤销之前发出的尚未成交的委托(包括限价单和停止单)
        #self.cancelAll()

        self.bg.updateBar(bar)
        print(self.buyCnt)
        self.fixedSize = 11
        if self.buyCnt == 1135:
            self.onTradeCnt = 0
            sendPrice = float(bar.close)
            self.short(sendPrice - 2, 11)

        if self.buyCnt == 1137:
            self.onTradeCnt = 0
            sendPrice = float(bar.close)
            self.short(sendPrice - 20, 11)
        self.buyCnt += 1
        return
        barLength = 0
        barLength = max(self.longDays, self.shortDays, self.atrDays)

        if self.am.count < barLength + 1:
            return
        # 计算指标数值
        self.barList.append(bar)

        if len(self.barList) <= 2:
            return
        else:
            self.barList.pop(0)
        lastBar = self.barList[-2]

        # 新的一天
        if (lastBar.datetime.hour == 15 or lastBar.datetime.hour == 14) and (
            (bar.datetime.hour == 21) or (bar.datetime.hour == 9)):
            # 如果已经初始化
            if not self.upperChannel:
                #do things:
                self.calcPrepare()
            else:
                pass

        if self.pos == 0:
            self.lastLongTime = 0
            self.lastShortTime = 0
            self.entryUnitNo = 0
            self.entryAtr = self.atrValue
        self.calcKPI()

        if self.pos > 0:
            #self.sell(self.longExit,self.fixedSize,stop)
            if bar.close < self.longExit:
                self.sell(bar.close - 2, abs(self.pos))
            elif bar.close > self.longEntry and self.longEntry > 0:
                self.buy(bar.close + 1, self.fixedSize)
                self.bookTime = datetime.now()
                self.onTradeCnt = 0
            else:
                pass
        elif self.pos == 0:
            #self.entryUnitNo = 0
            if bar.close > self.longEntry and self.longEntry > 0:
                self.buy(bar.close + 1, self.fixedSize)
                self.bookTime = datetime.now()
                self.onTradeCnt = 0
            elif bar.close < self.shortEntry and self.shortEntry > 0:
                self.short(bar.close - 1, self.fixedSize)
                self.bookTime = datetime.now()
                self.onTradeCnt = 0
            else:
                pass
        else:
            if bar.close < self.shortEntry and self.shortEntry > 0:
                self.short(bar.close - 1, self.fixedSize)
                self.bookTime = datetime.now()
                self.onTradeCnt = 0
            elif bar.close > self.shortExit:
                self.cover(bar.close + 1, abs(self.pos))
            else:
                pass

        # 发出状态更新事件
        self.putEvent()

    #update day chart
    def ondayBar(self, dayBar):
        """收到日线推送(必须由用户继承实现)"""
        self.am.updateBar(dayBar)
        self.calcPrepare()
        # 发出状态更新事件
        self.putEvent()

    #----------------------------------------------------------------------
    def onOrder(self, order):
        """收到委托变化推送(必须由用户继承实现)"""
        #if order.status == STATUS_ALLTRADED and self.pos != 0:
        # How do I know the last trade is open or exit?
        #    self.entryUnitNo = self.entryUnitNo + 1

    #----------------------------------------------------------------------
    def onTrade(self, trade):
        # 发出状态更新事件
        if trade.direction == DIRECTION_LONG and trade.offset == OFFSET_OPEN:
            if (trade.volume + self.onTradeCnt) == self.fixedSize:
                self.entryUnitNo = self.entryUnitNo + 1
            else:
                self.onTradeCnt = trade.volume
                self.writeCtaLog(u'%s: 部分成交, 进场次数未累加,注意!' % self.name)

            self.lastLongEntry = trade.price
            self.entryPrice = trade.price
            self.entryDirection = DIRECTION_LONG
            self.entryAtr = self.atrValue
        elif trade.direction == DIRECTION_SHORT and trade.offset == OFFSET_OPEN:
            if (trade.volume + self.onTradeCnt) == self.fixedSize:
                self.entryUnitNo = self.entryUnitNo + 1
            else:
                self.onTradeCnt = trade.volume + self.onTradeCnt
                self.writeCtaLog(u'%s: 部分成交, 进场次数未累加,注意!' % self.name)
            self.lastShortEntry = trade.price
            self.entryPrice = trade.price
            self.entryDirection = DIRECTION_SHORT
            self.entryAtr = self.atrValue
        elif (trade.offset == OFFSET_CLOSE
              or trade.offset == OFFSET_CLOSETODAY):
            #print(self.pos)
            self.entryUnitNo = 0
            self.lastLongEntry = 0
            self.lastShortEntry = 0
            self.entryPrice = 0
            self.entryDirection = OFFSET_CLOSE
        else:
            pass
        self.putEvent()

    #----------------------------------------------------------------------
    def onStopOrder(self, so):
        """停止单推送"""
        pass
Example #7
0
class TurtleTradingStrategy(CtaTemplate):
    """海龟交易策略"""
    className = 'TurtleTradingStrategy'
    author = u'用Python的交易员'

    # 策略参数
    entryWindow = 55                    # 入场通道窗口
    exitWindow = 20                     # 出场通道窗口
    atrWindow = 20                      # 计算ATR波动率的窗口
    initDays = 10                       # 初始化数据所用的天数
    fixedSize = 1                       # 每次交易的数量

    # 策略变量
    entryUp = 0                         # 入场通道上轨
    entryDown = 0                       # 入场通道下轨
    exitUp = 0                          # 出场通道上轨
    exitDown = 0                        # 出场通道下轨
    atrVolatility = 0                   # ATR波动率
    
    longEntry = 0                       # 多头入场价格
    shortEntry = 0                      # 空头入场价格
    longStop = 0                        # 多头止损价格
    shortStop = 0                       # 空头止损价格
    
    # 参数列表,保存了参数的名称
    paramList = ['name',
                 'className',
                 'author',
                 'vtSymbol',
                 'entryWindow',
                 'exitWindow',
                 'atrWindow',
                 'initDays',
                 'fixedSize']    

    # 变量列表,保存了变量的名称
    varList = ['inited',
               'trading',
               'pos',
               'entryUp',
               'entryDown',
               'exitUp',
               'exitDown',
               'longEntry',
               'shortEntry',
               'longStop',
               'shortStop']  
    
    # 同步列表,保存了需要保存到数据库的变量名称
    syncList = ['pos']

    #----------------------------------------------------------------------
    def __init__(self, ctaEngine, setting):
        """Constructor"""
        super(TurtleTradingStrategy, self).__init__(ctaEngine, setting)
        
        self.bg = BarGenerator(self.onBar)
        self.am = ArrayManager()
        
    #----------------------------------------------------------------------
    def onInit(self):
        """初始化策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略初始化' %self.name)
        
        # 载入历史数据,并采用回放计算的方式初始化策略数值
        initData = self.loadBar(self.initDays)
        for bar in initData:
            self.onBar(bar)

        self.putEvent()

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

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

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

    #----------------------------------------------------------------------
    def onBar(self, bar):
        """收到Bar推送(必须由用户继承实现)"""
        self.cancelAll()
    
        # 保存K线数据
        self.am.updateBar(bar)
        if not self.am.inited:
            return
        
        # 计算指标数值
        self.entryUp, self.entryDown = self.am.donchian(self.entryWindow)
        self.exitUp, self.exitDown = self.am.donchian(self.exitWindow)
        
        if not self.pos:
            self.atrVolatility = self.am.atr(self.atrWindow)
        
        # 判断是否要进行交易
        if self.pos == 0:
            self.longEntry = 0
            self.shortEntry = 0
            self.longStop = 0
            self.shortStop = 0
            
            self.sendBuyOrders(self.entryUp)
            self.sendShortOrders(self.entryDown)
    
        elif self.pos > 0:
            # 加仓逻辑
            self.sendBuyOrders(self.longEntry)
            
            # 止损逻辑
            sellPrice = max(self.longStop, self.exitDown)
            self.sell(sellPrice, abs(self.pos), True)
    
        elif self.pos < 0:
            # 加仓逻辑
            self.sendShortOrders(self.shortEntry)
            
            # 止损逻辑
            coverPrice = min(self.shortStop, self.exitUp)
            self.cover(coverPrice, abs(self.pos), True)
        
        # 同步数据到数据库
        self.saveSyncData()        
    
        # 发出状态更新事件
        self.putEvent()        

    #----------------------------------------------------------------------
    def onOrder(self, order):
        """收到委托变化推送(必须由用户继承实现)"""
        pass

    #----------------------------------------------------------------------
    def onTrade(self, trade):
        """成交推送"""
        if trade.direction == DIRECTION_LONG:
            self.longEntry = trade.price
            self.longStop = self.longEntry - self.atrVolatility * 2
        else:
            self.shortEntry = trade.price
            self.shortStop = self.shortEntry + self.atrVolatility * 2
        
        # 发出状态更新事件
        self.putEvent()

    #----------------------------------------------------------------------
    def onStopOrder(self, so):
        """停止单推送"""
        pass
    
    #----------------------------------------------------------------------
    def sendBuyOrders(self, price):
        """发出一系列的买入停止单"""
        t = self.pos / self.fixedSize
        
        if t < 1:
            self.buy(price, self.fixedSize, True)

        if t < 2:
            self.buy(price + self.atrVolatility*0.5, self.fixedSize, True)
                
        if t < 3:
            self.buy(price + self.atrVolatility, self.fixedSize, True)

        if t < 4:
            self.buy(price + self.atrVolatility*1.5, self.fixedSize, True)    
    
    #----------------------------------------------------------------------
    def sendShortOrders(self, price):
        """"""
        t = self.pos / self.fixedSize
        
        if t > -1:
            self.short(price, self.fixedSize, True)
        
        if t > -2:
            self.short(price - self.atrVolatility*0.5, self.fixedSize, True)
    
        if t > -3:
            self.short(price - self.atrVolatility, self.fixedSize, True)
    
        if t > -4:
            self.short(price - self.atrVolatility*1.5, self.fixedSize, True)            
class TripleMAStrategy03(CtaTemplate):
    """基于三均线的交易策略"""
    className = 'TripleMAStrategy'
    author = 'Y.Raul'

    # 策略参数
    # 三均线长度设置
    maWindow1 = 10
    maWindow2 = 20
    maWindow3 = 120
    maWindow4 = 5
    slMultiplier = 4  # 计算止损距离的乘数
    atrWindow = 30  # ATR窗口数
    initDays = 10  # 初始化数据所用的天数
    fixedSize = 1  # 每次交易的数量

    # 策略变量
    atrValue = 0  # ATR指标数值

    intraTradeHigh = 0  # 持仓期内的最高点
    intraTradeLow = 0  # 持仓期内的最低点
    longStop = 0  # 多头止损
    shortStop = 0  # 空头止损

    # ma次新值
    ma10 = 0
    ma20 = 0
    # ma最新值
    ma11 = 0
    ma21 = 0
    ma31 = 0

    longEntry = 0  # 多头开仓
    longExit = 0  # 多头平仓
    shortEntry = 0
    shortExit = 0

    orderList = []  # 保存委托代码的列表

    # 参数列表,保存了参数的名称
    paramList = ['name',
                 'className',
                 'author',
                 'vtSymbol',
                 'maWindow1',
                 'maWindow2',
                 'maWindow3',
                 'maWindow4'
                 'initDays',
                 'fixedSize']

    # 变量列表,保存了变量的名称
    varList = ['inited',
               'trading',
               'pos',
               'ma10',
               'ma11',
               'ma20',
               'ma21']
    # 同步列表
    syncList = ['pos']

    # ----------------------------------------------------------------------
    def __init__(self, ctaEngine, setting):
        """Constructor"""
        super(TripleMAStrategy03, self).__init__(ctaEngine, setting)

        self.bm = BarGenerator(self.onBar, 5, self.onFiveBar)
        # 由于maWindow3的长度是120,所以ArrayManager的size要增加至150
        self.am = ArrayManager(size=150)

    # ----------------------------------------------------------------------
    def onInit(self):
        """初始化策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略初始化' % self.name)

        # 载入历史数据,并采用回放计算的方式初始化策略数值
        initData = self.loadBar(self.initDays)
        for bar in initData:
            self.onBar(bar)

        self.putEvent()

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

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

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

    # ----------------------------------------------------------------------
    def onBar(self, bar):
        """收到Bar推送(必须由用户继承实现)"""
        self.bm.updateBar(bar)

    # ----------------------------------------------------------------------
    def onFiveBar(self, bar):
        """收到5分钟K线"""
        # 保存K线数据
        self.am.updateBar(bar)
        if not self.am.inited:
            return

            # 撤销之前发出的尚未成交的委托(包括限价单和停止单)
        self.cancelAll()

        import talib
        # 计算指标数值
        ma3Array = self.am.sma(self.maWindow3, True)
        self.ma30 = ma3Array[-2]
        self.ma31 = ma3Array[-1]
        ma3_ma5 = talib.SMA(ma3Array, self.maWindow4)[-1]
        # ma3_ma5 = ma3Array.rolling(window = self.maWindow4)[-1]

        ma1Array = self.am.sma(self.maWindow1, True)
        self.ma10 = ma1Array[-2]
        self.ma11 = ma1Array[-1]
        # ma1_ma5 = ma1Array.rolling(window = self.maWindow4)[-1]
        ma1_ma5 = talib.SMA(ma1Array, self.maWindow4)[-1]
        ma2Array = self.am.sma(self.maWindow2, True)
        self.ma20 = ma2Array[-2]
        self.ma21 = ma2Array[-1]

        self.atrValue = self.am.atr(self.atrWindow)
        # 判断是否要进行交易
        # 当前无仓位,发送OCO开仓委托
        if self.pos == 0:
            self.intraTradeHigh = bar.high
            self.intraTradeLow = bar.low
            # 开多, bar.close > MA120,MA10 > MA120,MA10 上穿MA20,MA10、MA120向上
            if bar.close > self.ma31 and self.ma11 > self.ma31 \
                    and self.ma10 < self.ma20 and self.ma11 > self.ma21\
                    and self.ma31 > ma3_ma5 and self.ma11 > ma1_ma5:
                self.longEntry = bar.close
                self.buy(self.longEntry, self.fixedSize, True)
            # 开空, bar.close < MA120,MA10 < MA120,MA10 下穿MA20, MA10,MA120向下
            elif bar.close < self.ma31 and self.ma11 < self.ma31 \
                    and self.ma10 > self.ma20 and self.ma11 < self.ma21\
                    and self.ma31 < ma3_ma5 and self.ma11 < ma1_ma5:
                self.shortEntry = bar.close
                self.short(self.shortEntry, self.fixedSize, True)
        else:
            # 跟随止损
            # 持有多头仓位
            if self.pos > 0:
                self.intraTradeHigh = max(self.intraTradeHigh, bar.high)
                self.intraTradeLow = bar.low
                self.longStop = self.intraTradeHigh - self.atrValue * self.slMultiplier
                if bar.close < self.longStop:
                    self.sell(bar.close, abs(self.pos), True)
            # 持有空头仓位
            if self.pos < 0:
                self.intraTradeHigh = bar.high
                self.intraTradeLow = min(self.intraTradeLow, bar.low)
                self.shortStop = self.intraTradeLow + self.atrValue * self.slMultiplier
                if bar.close > self.shortStop:
                    self.cover(bar.close, abs(self.pos), True)
        # 发出状态更新事件
        self.putEvent()

        # ----------------------------------------------------------------------

    def onOrder(self, order):
        """收到委托变化推送(必须由用户继承实现)"""
        pass

    # ----------------------------------------------------------------------
    def onTrade(self, trade):
        # 发出状态更新事件
        self.putEvent()

    # ----------------------------------------------------------------------
    def onStopOrder(self, so):
        """停止单推送"""
        pass
class TripleMAStrategy04(CtaTemplate):
    """基于三均线的交易策略"""
    className = 'TripleMAStrategy04'
    author = 'Y.Raul'

    # 策略参数
    initDays = 10  # 初始化数据所用的天数
    addPos = True  # 加仓开关
    windowCheck = True #交易窗口开关
    openWindowSize = 5 #开盘观察窗口,单位分钟
    closeWindowSize = 10 #收盘平仓窗口,单位分钟
    minDiff = 1 #最小变动单位

    # 策略变量
    # 仓位设置
    stepPos = 1  # 每次交易的数量
    maxPos = 4  # 仓位上限
    addPosRatio = 3
    # 均线设置
    maWindow1 = 10
    maWindow2 = 20
    maWindow3 = 120
    maWindow4 = 5
    atrWindow = 30  # ATR窗口数
    
    # 出场设置
    exitOnTrailingStop = 2  # Trailing Stop 距离
    exitOnLossStop = 3 # Loss Stop 距离
    
    # 价格相关变量
    intraTradeHigh = 0  # 持仓期内的最高点
    intraTradeLow = 0  # 持仓期内的最低点
    longStop = 0  # 多头止损
    shortStop = 0  # 空头止损
    longEntry = 0  # 多头开仓
    shortEntry = 0
    avgEntryPrice = 0
    
    # 指标相关变量
    # ma次新值
    ma10 = 0
    ma20 = 0
    ma30 = 0
    # ma最新值
    ma11 = 0
    ma21 = 0
    ma31 = 0
    atrValue = 0  # ATR指标数值

    orderList = []  # 保存委托代码的列表

    # 参数列表,保存了参数的名称
    paramList = ['name',
                 'className',
                 'author',
                 'vtSymbol',
                 'maWindow1',
                 'maWindow2',
                 'maWindow3',
                 'maWindow4'
                 'initDays',
                 'addPos',
                 'stepPos',
                 'maxPos',
                 'exitOnTrailingStop',
                 'exitOnLossStop',
                 ]

    # 变量列表,保存了变量的名称
    varList = ['inited',
               'trading',
               'pos',
               'ma10',
               'ma11',
               'ma20',
               'ma21',
               'ma30',
               'ma31',
               'atrValue',
               'avgPrice']
    # 同步列表
    syncList = ['pos']

    # ----------------------------------------------------------------------
    def __init__(self, ctaEngine, setting):
        """Constructor"""
        super(TripleMAStrategy04, self).__init__(ctaEngine, setting)
        self.EntryPriceList = []
        self.bm = BarGenerator(self.onBar, 5, self.onFiveBar)
        self.am = ArrayManager(size= self.maWindow3 + 30)
    # ----------------------------------------------------------------------
    def onInit(self):
        """初始化策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略初始化' % self.name)
        # 载入历史数据,并采用回放计算的方式初始化策略数值
        initData = self.loadBar(self.initDays)
        for bar in initData:
            self.onBar(bar)
        self.putEvent()

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

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

    # ----------------------------------------------------------------------
    def onTick(self, tick):
        """收到行情TICK推送(必须由用户继承实现)"""
        self.curDateTime = tick.datetime
        # 计算交易时间和平仓时间
        if self.windowCheck == True:
            self.__timeWindow(tick.datetime)
        else:
            self.tradeWindow = True

        self.bm.updateTick(tick)

    # ----------------------------------------------------------------------
    def onBar(self, bar):
        """收到Bar推送(必须由用户继承实现)"""
        # 更新策略执行的时间(用于回测时记录发生的时间)
        # 回测数据传送的bar.datetime,为bar的结束时间
        self.curDateTime = bar.datetime
        # 计算交易时间和平仓时间
        if self.windowCheck == True:
            self.__timeWindow(bar.datetime)
        else:
            self.tradeWindow = True

        self.bm.updateBar(bar)

    # ----------------------------------------------------------------------
    def onFiveBar(self, bar):
        """收到5分钟K线"""
        # 保存K线数据
        self.am.updateBar(bar)
        if not self.am.inited:
            return
        # print bar.datetime

        # 撤销之前发出的尚未成交的委托(包括限价单和停止单)
        self.cancelAll()

        import talib
        # 计算指标数值
        ma3Array = self.am.sma(self.maWindow3, True)
        self.ma30 = round(ma3Array[-2])
        self.ma31 = round(ma3Array[-1])
        ma3_ma5 = round(talib.SMA(ma3Array, self.maWindow4)[-1])

        ma1Array = self.am.sma(self.maWindow1, True)
        self.ma10 = round(ma1Array[-2])
        self.ma11 = round(ma1Array[-1])
        ma1_ma5 = talib.SMA(ma1Array, self.maWindow4)[-1]

        ma2Array = self.am.sma(self.maWindow2, True)
        self.ma20 = round(ma2Array[-2])
        self.ma21 = round(ma2Array[-1])

        self.atrValue = round(self.am.atr(self.atrWindow))

        # 判断是否要进行交易
        # 当前无仓位,发送OCO开仓委托
        if self.pos == 0 :
            self.intraTradeHigh = bar.high
            self.intraTradeLow = bar.low
            if self.tradeWindow:
                # 开多, bar.close > MA120,MA10 > MA120,MA10 上穿MA20,MA10、MA120向上
                if bar.close > self.ma31 and self.ma11 > self.ma31 \
                        and self.ma10 < self.ma20 and self.ma11 > self.ma21\
                        and self.ma31 > ma3_ma5 and self.ma11 > ma1_ma5:

                    self.longEntry = bar.close
                    self.buy(self.longEntry, self.stepPos, True)

                    # lastEntryPrice = self.longEntry
                    self.LossStopPrice = round(self.longEntry * (100.0 -self.exitOnLossStop)/100)
                    self.EntryPriceList.append(self.longEntry)

                    # 记录log
                    log = "\n Trading: {0}\n".format(self.trading)+\
                        "{0} Buy : bar.close: {1};\n".format(bar.datetime, bar.close) + \
                          " ma10:{0}; ma11:{1}; ma20:{2}; ma21:{3}; ma30:{4};ma31:{5}\n".format(self.ma10,self.ma11,self.ma20,self.ma21,self.ma30,self.ma31) + \
                        "ma1_ma5:{0}; ma3_ma5:{1}\n".format(ma1_ma5,ma3_ma5)+\
                        "LossStopPrice:{0}\n".format(self.LossStopPrice)
                    self.writeCtaLog(log)

                # 开空, bar.close < MA120,MA10 < MA120,MA10 下穿MA20, MA10,MA120向下
                elif bar.close < self.ma31 and self.ma11 < self.ma31 \
                        and self.ma10 > self.ma20 and self.ma11 < self.ma21\
                        and self.ma31 < ma3_ma5 and self.ma11 < ma1_ma5:

                    self.shortEntry = bar.close
                    self.short(self.shortEntry, self.stepPos, True)
                    # lastEntryPrice = self.shortEntry
                    self.LossStopPrice = round(self.shortEntry * (100.0  + self.exitOnLossStop)/100)
                    self.EntryPriceList.append(self.shortEntry)

                    # 记录log
                    log = "\n Trading: {0}\n".format(self.trading)+\
                        "{0} Short : bar.close: {1};\n".format(bar.datetime, bar.close) + \
                          " ma10:{0}; ma11:{1}; ma20:{2}; ma21:{3}; ma30:{4};ma31:{5}\n".format(self.ma10, self.ma11,
                                                                                                self.ma20, self.ma21,
                                                                                                self.ma30, self.ma31) + \
                          "ma1_ma5:{0}; ma3_ma5:{1}\n".format(ma1_ma5, ma3_ma5) + \
                          "LossStopPrice:{0}\n".format(self.LossStopPrice)
                    self.writeCtaLog(log)
            # return
        else:
            if self.tradeWindow:
                # Trailing Stop 跟随止损
                if self.exitOnTrailingStop:
                    # 持有多头仓位
                    if self.pos > 0 and self.tradeWindow:

                        self.intraTradeHigh = max(self.intraTradeHigh, bar.high)
                        self.intraTradeLow = bar.low
                        self.longStop = round(self.intraTradeHigh - self.atrValue * self.exitOnTrailingStop)

                        if bar.close < self.longStop:
                            self.sell(bar.close, abs(self.pos), True)

                            # 记录log
                            log = "\n{0} Sell(Trailing Stop) : bar.close: {1};\n".format(bar.datetime, bar.close) + \
                                "intraTradeHigh:{0}; atrValue:{1}; dev: {2}\n".format(self.intraTradeHigh,self.atrValue,self.exitOnTrailingStop)+\
                                  "LongStop:{0}\n".format(self.longStop)
                            self.writeCtaLog(log)
                            self.putEvent()

                            self.EntryPriceList = []
                            return
                    # 持有空头仓位
                    if self.pos < 0 and self.tradeWindow:

                        self.intraTradeHigh = bar.high
                        self.intraTradeLow = min(self.intraTradeLow, bar.low)
                        self.shortStop = round(self.intraTradeLow + self.atrValue * self.exitOnTrailingStop)

                        if bar.close > self.shortStop:
                            self.cover(bar.close, abs(self.pos), True)
                            # 记录log
                            log = "\n{0} Cover(Trailing Stop) : bar.close: {1};\n".format(bar.datetime, bar.close) + \
                                  "intraTradeLow:{0}; atrValue:{1}; dev: {2}\n".format(self.intraTradeLow, self.atrValue,
                                                                                        self.exitOnTrailingStop) + \
                                  "LongStop:{0}\n".format(self.longStop)
                            self.writeCtaLog(log)
                            self.putEvent()

                            self.EntryPriceList = []
                            return

                # Loss Stop 固定止损
                if self.exitOnLossStop:
                        # 持有多头仓位
                    if self.pos > 0 and bar.close < self.LossStopPrice:
                        # 记录log
                        log = "\n{0} Sell(Stop Loss) : bar.close: {1};\n".format(bar.datetime, bar.close) + \
                              "LossStopPrice:{0}\n".format(self.LossStopPrice) + \
                            "Ratio:{0}%\n".format((1 - bar.close/self.LossStopPrice)*100)
                        self.writeCtaLog(log)

                        self.sell(bar.close, abs(self.pos), True)
                        self.putEvent()

                        self.EntryPriceList = []
                        return
                        # 持有空头仓位
                    if self.pos < 0 and bar.close > self.LossStopPrice:
                        # 记录log
                        log = "\n{0} Cover(Stop Loss) : bar.close: {1};\n".format(bar.datetime, bar.close) + \
                              "LossStopPrice:{0}\n".format(self.LossStopPrice) +\
                        "Ratio:{0}%\n".format((1 - bar.close / self.LossStopPrice) * 100)
                        self.writeCtaLog(log)

                        self.cover(bar.close, abs(self.pos), True)
                        self.putEvent()

                        self.EntryPriceList = []
                        return

                # 加仓
                if self.addPos and (self.maxPos - abs(self.pos) > 0):
                    print self.pos, (self.maxPos - abs(self.pos) )
                    print self.EntryPriceList

                    lastEntryPrice = self.EntryPriceList[-1]
                    # 固定百分比加仓
                    addPosOnPips= round(lastEntryPrice* self.addPosRatio/100)

                    self.writeCtaLog(u'\n 加仓判断:{0},当前仓位:{1}'.format(bar.datetime, self.pos))
                    # 加多仓
                    if self.pos > 0 \
                            and bar.close >= lastEntryPrice + addPosOnPips* self.minDiff:
                        # 记录log
                        self.writeCtaLog(u'\n {0},加仓多单{1}手,价格:{2}'.format(bar.datetime, self.stepPos, bar.close))
                        self.buy(bar.close, self.stepPos, True)

                        # 更新开仓价格
                        lastEntryPrice = bar.close
                        self.EntryPriceList.append(lastEntryPrice)
                        self.avgEntryPrice = sum(self.EntryPriceList)/len(self.EntryPriceList)

                        # 更新固定止损价
                        self.LossStopPrice = round( self.avgEntryPrice* (100.0 - self.exitOnLossStop) / 100)
                        self.writeCtaLog(u'\n 更新固定止损价:{0},最新仓位:{1}'.format(self.LossStopPrice,self.pos))

                        return

                    # 加空仓
                    if self.pos < 0 \
                            and bar.close <= (lastEntryPrice + addPosOnPips*self.minDiff):

                        self.writeCtaLog(u'{0},加仓空单{1}手,价格:{2}'.format(bar.datetime, self.stepPos, bar.close))
                        self.short(bar.close, self.stepPos, True)

                        # 更新开仓价格
                        lastEntryPrice = bar.close
                        self.EntryPriceList.append(lastEntryPrice)
                        self.avgEntryPrice = (sum(self.EntryPriceList)) / len(self.EntryPriceList)

                        # 更新固定止损价
                        self.LossStopPrice = round(self.avgEntryPrice * (100.0 + self.exitOnLossStop) / 100)
                        self.writeCtaLog(u'\n 更新固定止损价:{0},最新仓位:{1}'.format(self.LossStopPrice, self.pos))
                        return

        # 执行收盘前平仓检查
        # self.__dailyCloseCheck(bar)
        # 发出状态更新事件
        self.putEvent()

        # ----------------------------------------------------------------------

    def onOrder(self, order):
        """收到委托变化推送(必须由用户继承实现)"""
        log = u'\n OnOrder()更新,orderID:{0},{1},totalVol:{2},tradedVol:{3},offset:{4},price:{5},direction:{6},status:{7},orderTime: {7}'\
                         .format(order.orderID, order.vtSymbol, order.totalVolume,order.tradedVolume,
                                 order.offset, order.price, order.direction, order.status, order.orderTime)
        self.writeCtaLog(log)
        self.putEvent()
    # ----------------------------------------------------------------------
    def onTrade(self, trade):

        log = u'\n OnTrade()更新,orderID:{0},{1},Vol:{2},price:{3},direction:{4},tradeTime:{5}' \
            .format(trade.orderID, trade.vtSymbol, trade.volume,trade.price, trade.direction,trade.tradeTime)
        self.writeCtaLog(log)
        # 发出状态更新事件
        self.putEvent()

    # ----------------------------------------------------------------------
    def onStopOrder(self, so):
        """停止单推送"""
        log = u'\n OnStopOrder()停止单更新,stopOrderID:{0},Vol:{1},price:{2},direction:{3},status:{4}' \
            .format(so.stopOrderID, so.volume,so.price, so.direction,so.status)
        self.writeCtaLog(log)

    def __timeWindow(self, dt):
        """交易与平仓窗口"""

        # 螺纹钢交易窗口 避开早盘和夜盘的前5分钟,防止隔夜跳空。
        # 日内平仓窗口
        self.closeWindow = False
        # 交易窗口
        self.tradeWindow = False
        # 开盘窗口
        self.openWindow = False

        # 开市期头5分钟波动较大
        if (dt.hour == 9 or dt.hour == 21) and dt.minute < self.openWindowSize:
            self.openWindow = False
            return

        # 日盘
        if dt.hour == 9 and dt.minute >= 0:
            self.tradeWindow = True
            return

        if dt.hour == 10:
            if dt.minute <= 15 or dt.minute >= 30:
                self.tradeWindow = True
                return

        if dt.hour == 11 and dt.minute <= 30:
            self.tradeWindow = True
            return

        if dt.hour == 13 and dt.minute >= 30:
            self.tradeWindow = True
            return

        if dt.hour == 14:

            if dt.minute < 60 - self.closeWindowSize:
                self.tradeWindow = True
                return
            else:
                self.closeWindow = True
                return

        # 夜盘

        if dt.hour == 21 and dt.minute >= 0:
            self.tradeWindow = True
            return

        if dt.hour == 22 and dt.minute < 60 - self.closeWindowSize:
            self.tradeWindow = True
            return
        else:
            self.closeWindow = True
            return

    def __dailyCloseCheck(self, bar):
        """每天收盘前检查,如果是亏损单,则平掉"""

        if self.pos == 0 :
            return False

        if not self.closeWindow:
            return False

        # 撤销未成交的订单
        self.cancelAll()
        log = u'{0},收盘前{1}分钟,撤单及平仓'.format(bar.datetime,self.closeWindowSize)
        self.writeCtaLog(log)
        self.avgEntryPrice = (sum(self.EntryPriceList)) / len(self.EntryPriceList)
        # 记录log
        log = "\n{0} __dailyCloseCheck : bar.close: {1};\n".format(bar.datetime, bar.close) + \
              "avgPrice:{0}\n".format(self.avgEntryPrice)+\
            "pos:{0}\n".format(self.pos)

        self.writeCtaLog(log)

        # 强制平仓
        if self.pos > 0 and bar.close < self.avgEntryPrice:
            self.writeCtaLog(u'强制日内平亏损多仓')

            # 降低两个滑点
            self.sell(bar.close-2*self.minDiff, abs(self.pos),True )
            # 记录log
            log = "\n{0} Sell(Force) : bar.close: {1};\n".format(bar.datetime, bar.close) + \
                  "entryPrice:{0}\n".format(bar.close - 2 * self.minDiff)
            self.writeCtaLog(log)

            return True

        if self.pos < 0 and bar.close > self.avgEntryPrice:
            self.writeCtaLog(u'强制日内平亏损空仓')

            self.cover(bar.close+2*self.minDiff, abs(self.pos),True )
            # 记录log
            log = "\n{0} Cover(Force) : bar.close: {1};\n".format(bar.datetime, bar.close) + \
                  "forcePrice:{0}\n".format(bar.close - 2 * self.minDiff)
            self.writeCtaLog(log)
            return True

        return True
Example #10
0
class HgStrategy(CtaTemplate):
    """Demo"""
    className = 'haigui'
    author = u'zhice'
    priceTpye = PRICETYPE_MARKETPRICE # 设置为市价单

    
    # 参数列表,保存了参数的名称
    paramList = ['name',
                 'className',
                 'author',
                 'vtSymbol',
                 'productID',
                 'shortWindow',
                 'middleWindow',
                 'longWindow',

                 # 交易的实例名信息,一个实例包含一组策略实例
                 'instanceName',
                 'instanceId',
                 'instanceAccount']

    # 保存了要用pickle恢复的参数列表
    pickleParamList = []
    
    # 同步列表,保存了需要保存到数据库的变量名称
    syncList = ['pos']



    #----------------------------------------------------------------------
    def __init__(self, ctaEngine, setting):
        """Constructor"""
        super(HgStrategy, self).__init__(ctaEngine, setting)

        self.GOON = False

        # 每次启动要重建的参数
        self.bg = BarGenerator(self.onBar)
        self.cacheDays = max(self.longWindow, (2 * self.middleWindow) + 1)
        self.am = ArrayManager(self.cacheDays)
        self.myDb = mydb  # 数据库引擎
        self.hgDbEngine = hgDbEngine(mydb)  # cta 数据库操作的一些封装
        self.hgReport = hgReport(self.hgDbEngine)
        self.monitor = {}  # 合约当天的 10日线高低、20日线高低、55日线和ART信息
        self.contracts = self.hgDbEngine.getAllContract()  # 最新的合约信息
        self.sessionID = None  # 本地交易
        self.frontID = None  # 本次交易的
        self.logLevel = LOG_INFO # 设置日志输出级别
        self.bGenImg = True # 是否生成图像标志位
        # self.sessionid = uuid.uuid1() # 本次唯一id

        # 关于生成图片与展示html的两个关键变量
        self.imgHtmlRootDir = ''  # 图片和展示html的根路径
        if os.path.exists('/home/ubuntu/vnpy/vnpy-1.8/'):
            self.imgHtmlRootDir = '/home/ubuntu/'
            print('sys.path.append - /home/ubuntu/vnpy/vnpy-1.8/')
        elif os.path.exists('/srv/vnpy18'):
            self.imgHtmlRootDir = '/srv/img_html/'
            print('sys.path.append - /srv/vnpy18')

        # 【重要】所有要pickle存储的数据都要记录在变量中
        #  True 代表用pickle存储,False代表用正常方式存储
        self.pickleItemDict = {"orderList": True,
                               "tradeList": True,
                               "hgCellList": True,
                               "plan_add_price": False,
                               "atr": False,
                               "cell_num": False,
                               "s_or_b": False,
                               "offsetProfit": False,
                               "floatProfit": False,
                               "max_cell_num": False,
                               "health": False,
                               "MaxInstanceTotalCellNum": False,
                               "totalRealUnit": False,
                               "vtSymbol": False,
                               "symbolName": False}

        # 每次启动要用pickle恢复的数据
        #self.hgPosition = {} # 持仓信息
        self.orderList = [] # 报单列表
        self.tradeList = [] # 成交列表
        self.hgCellList = []  # 持仓列表,元素为HgCell
        self.plan_add_price = -1  # 加仓价格
        self.atr = -1
        self.cell_num = 0  # 持仓量
        self.s_or_b = ''  # 买卖方向
        self.offsetProfit = -1  # 平仓盈亏
        self.floatProfit = -1  # 浮动盈亏
        self.max_cell_num = 3  # 最大持仓量
        self.health = True # 交易状态是否健康
        self.MaxInstanceTotalCellNum = 12 # 相同实例下单方向的总持仓上限
        self.totalRealUnit = 0 # 真实总持仓
        self.vtSymbol = ''
        self.symbolName = '' # 合约中文名字


        fileProductID = self.productID
        # TODO通过pickle进行数据恢复
        self.hgDbEngine.recoveryFromDb(self)

        # 数据库恢复的 productID 与 配置文件中的不一致,属于异常情况,停止交易
        if fileProductID <> self.productID:
            self.stopTrading()
            self.myPrint(LOG_ERROR, '__init__', '文件与数据库中productID不一致,停止交易。')


        # 海龟交易主力合约,配置时 symbol 配置的是品种名称,进行翻译。
        ret = self.hgDbEngine.getDominantByProductID(self.productID)

        # 判断是否需要进行手工移仓
        # TODO 目前出现移仓情况需要手动处理
        if ret is not None and self.vtSymbol != "" and self.vtSymbol != ret:
            self.stopTrading() # 需要进行手工移仓
            self.myPrint(LOG_ERROR, '__init__', '需要进行手工移仓。')


        if ret is not None and self.vtSymbol == "" :
            self.vtSymbol = ret

        if ret is None:
            self.stopTrading()
            self.myPrint(LOG_ERROR, '__init__', '获取主力合约失败。')

        self.symbolName = self.contracts[self.vtSymbol]['name'] # 获取合约中文名字

        # 只在第一个实例中发送报告
        if self.instanceId.endswith('_01'):
            self.myPrint(LOG_INFO, 'onInit', '发送报告: ' + self.instanceName)
            self.hgReport.sendReport(self.instanceName, self.pickleItemDict)

        if self.health:
            self.myPrint(LOG_INFO, '__init__', '初始化完成。')
        else:
            self.myPrint(LOG_ERROR, '__init__', '初始化失败。')


    def stopTrading(self, info = ""):
        self.myPrint(LOG_ERROR, 'stopTrading', info)
        self.health = False

    #----------------------------------------------------------------------
    def onInit(self):
        """初始化策略(必须由用户继承实现)"""
        self.myPrint(LOG_INFO, 'onInit', '海龟交易法则策略开始初始化。')

        # 初始化合约信息
        #self.contracts = self.hgDbEngine.getAllContract()
        initData = self.hgDbEngine.loadDayBar(self.vtSymbol, self.cacheDays)
        if len(initData) != self.cacheDays:
            self.myPrint(LOG_ERROR, 'onInit', u'【ERROR】【hg】%s 合约初始化数据不足,需要长度为%d ,实际长度为 %d' % (self.vtSymbol, self.longWindow, len(initData)))
            self.stopTrading()
            return

        for bar in initData:
            self.am.updateBar(bar)

        shortWindowHighBreak = self.am.high[-self.shortWindow:].max()
        shortWindowLowBreak = self.am.low[-self.shortWindow:].min()

        middleWindowHighBreak = self.am.high[-self.middleWindow:].max()
        middleWindowLowBreak = self.am.low[-self.middleWindow:].min()

        longWindowHighBreak = self.am.high[-self.longWindow:].max()
        longWindowLowBreak = self.am.low[-self.longWindow:].min()


        atr = self.am.atr(20, False)
        # 如果记录过atr,则使用开仓时候的 atr
        if self.atr != -1 and self.cell_num > 0:
            atr = self.atr



        unit =  int(self.instanceAccount * 0.01 / (atr * self.contracts[self.vtSymbol]['size']))
        self.monitor = {
            'shortWindowHighBreak': shortWindowHighBreak,
            'shortWindowLowBreak': shortWindowLowBreak,
            'middleWindowHighBreak': middleWindowHighBreak,
            'middleWindowLowBreak': middleWindowLowBreak,
            'longWindowHighBreak': longWindowHighBreak,
            'longWindowLowBreak': longWindowLowBreak,
            'atr': atr,
            'unit': unit
        }

        # 增加一个校验,但凡有一个为零,认为初始化不成功,停止交易
        if 0 in [shortWindowHighBreak, shortWindowLowBreak, middleWindowHighBreak
            , middleWindowLowBreak, longWindowHighBreak, longWindowLowBreak, atr, unit]:
            self.myPrint(LOG_ERROR, 'onInit', u'%s合约初始化失败,信息为%s' % (self.vtSymbol, self.monitor))
            self.stopTrading()

        self.myPrint(LOG_INFO, 'onInit', u'%s合约初始化,信息为%s' % (self.vtSymbol,self.monitor))

        # 报单查询测试
        gateway = self.ctaEngine.mainEngine.getGateway('CTP')

        # 拿到本次交易的 sessionID 和 frontID,可以抽象到上层
        self.sessionID = gateway.tdApi.sessionID  # 本地交易
        self.frontID = gateway.tdApi.frontID  # 本次交易的

        self.myPrint(LOG_INFO, 'onInit', u'初始化,sessionID = %s; frontID = %s' % (self.sessionID, self.frontID))
        self.myPrint(LOG_INFO, 'onInit', '海龟交易法则策略初始化完成。')
        # TODO 每次重新登录如果有历史报单,对历史报单的处理


        # 前几个函数测试使用
        #self.health = False

        #self.myPrint(LOG_INFO, 'onInit', '未测试,先关闭真正的交易。')
        #self.stopTrading()
        #gateway.tdApi.qryTest()

    # ----------------------------------------------------------------------
    # 生成图像的封装函数
    def genImg(self, size, closePrice):
        # 每月的图片放在一个文件夹
        # 文件用时间命名
        strTime = datetime.now().strftime('%Y%m%d-%H%M%S-%f')
        strMonth = strTime[0:6]
        filePath = self.imgHtmlRootDir + 'img/' + strMonth
        if not os.path.exists(filePath):
            os.makedirs(filePath)
        fileNamePath = filePath + '/' + strTime + '.jpg'
        title = self.vtSymbol + " " + strTime

        s_h, s_l = self.am.donchian(self.shortWindow, array=True)
        m_h, m_l = self.am.donchian(self.middleWindow, array=True)
        l_h, l_l = self.am.donchian(self.longWindow, array=True)

        s_h = s_h[-size:]
        s_l = s_l[-size:]
        m_h = m_h[-size:]
        m_l = m_l[-size:]
        l_h = l_h[-size:]
        l_l = l_l[-size:]
        close = self.am.close[-size:]

        s_h = np.hstack((s_h, s_h[-1]))
        s_l = np.hstack((s_l, s_l[-1]))
        m_h = np.hstack((m_h, m_h[-1]))
        m_l = np.hstack((m_l, m_l[-1]))
        l_h = np.hstack((l_h, l_h[-1]))
        l_l = np.hstack((l_l, l_l[-1]))
        close = np.hstack((close, closePrice))


        saveImg(self,fileNamePath, title,s_h, s_l, m_h, m_l, l_h, l_l, close, size+1)



    # ----------------------------------------------------------------------


    #----------------------------------------------------------------------
    def onStart(self):
        """启动策略(必须由用户继承实现)"""
        self.myPrint(LOG_INFO, 'onStart', u'海龟交易法则策略启动')
        self.putEvent()
    
    #----------------------------------------------------------------------
    def onStop(self):
        """停止策略(必须由用户继承实现)"""
        self.myPrint(LOG_INFO, 'onStop', u'海龟交易法则策略启动')
        self.putEvent()
        
    #----------------------------------------------------------------------
    def onTick(self, tick):
        """收到行情TICK推送(必须由用户继承实现)"""

        self.bg.updateTick(tick)
        # TODO 将来可以添加校验,校验是否订阅的合约都有信号
    #----------------------------------------------------------------------
    def onBar(self, bar):
        """收到Bar推送(必须由用户继承实现)"""
        # TODO 对涨跌停的处理
        # TODO 异常值的处理
        # TODO 如果委托了,一直不成交怎么办
        #strategy.trading = False
        #strategy.inited = False


        print(bar.__dict__)

        self.myPrint(LOG_DEBUG, 'onBar', '进入onBar.')
        self.myPrint(LOG_DEBUG, 'onBar', bar.__dict__)



        if not self.trading :
            self.myPrint(LOG_INFO, 'onBar', 'self.trading is false')
            return

        if not self.health:
            self.myPrint(LOG_ERROR, 'onBar', 'self.health is false')
            return

        # 是否生成图像处理
        if self.bGenImg:
            # 生成图像
            self.genImg(20, bar.close)
            # 生成展示html
            genHtmls(self.imgHtmlRootDir)
            self.bGenImg = False


        #self.buy(3750, 1)

        #return
        # 测试
        #self.monitor['middleWindowHighBreak'] = 3575
        # 测试结束

        vtSymbol = bar.vtSymbol

        # 如果发现有合约初始化未完成,直接返回
        if not self.am.inited:
            self.myPrint(LOG_ERROR, 'onBar', u'【ERROR】【hg】合约未能正常初始化' % (vtSymbol))
            return

        # 账户金额有限,如果加仓单位还不到1,则直接返回
        if self.monitor['unit'] == 0:
            self.myPrint(LOG_ERROR, 'onBar', u'self.monitor[unit] == 0')
            return



        # 5、如果存在不问稳定的订单状态直接返回
        if not self.is_all_cell_stable():
            # self.printCells("not self.is_all_cell_stable()")
            self.myPrint(LOG_INFO, 'onBar', "not self.is_all_cell_stable()")
            return

        # TODO 这里可以优化, hand_cell 和 saveIntoDB 重复了。
        # 6、如果真实持仓未达到目标状态,下单,更新数据库,并返回
        if not self.is_all_cell_get_target_unit():
            self.myPrint(LOG_IMPORTANT, 'onBar', "当前订单稳定了,但是 没有达到目标仓位,则继续交易。")

            for hgcell in self.hgCellList:
                hgcell.hand_cell(self, bar.close)

            # 记录在数据库中
            self.hgDbEngine.saveIntoDB(self)
            return


        if not self.calCellNumAndTotalRealUnit() and len(self.hgCellList) > 0:
            self.myPrint(LOG_ERROR, 'onBar', 'not self.calCellNumAndTotalRealUnit() and len(self.hgCellList) > 0')
            self.stopTrading()
            return



        if self.cell_num >= 1:
            # 更新加仓价格
            cell = self.hgCellList[self.cell_num - 1]  # 取最后一个持仓
            if cell.is_all_order_stable(self) and cell.real_unit == cell.target_unit:
                # 订单都稳定了,并且达到了目标持仓,更新加仓价格
                if cell.open_direction == 'b':
                    self.plan_add_price = cell.real_in_price + (cell.N / 2)
                if cell.open_direction == 's':
                    self.plan_add_price = cell.real_in_price - (cell.N / 2)
            else:
                self.myPrint(LOG_ERROR, 'onBar', '不应该出现这种情况,存在订单不稳定或者 目标真实持仓不一致的情况')
                self.stopTrading()
                return

            # 更新退出价格信息
            self.update_plan_stop_price()

            # 进行一次数据库写入
            self.hgDbEngine.saveIntoDB(self)


        # 当前持仓大于最大持仓要求
        if self.cell_num >= self.max_cell_num:
            self.myPrint(LOG_INFO, 'onBar', u'已达到最大持仓 %d / %d' % (self.cell_num, self.max_cell_num))
            return

        # 单方向是否达到了最大值
        tmpInstanceTotalCellNum = self.hgDbEngine.getInstanceTotalCellNum(self.instanceName, self.s_or_b)
        if self.s_or_b and tmpInstanceTotalCellNum >= self.MaxInstanceTotalCellNum:
            self.myPrint(LOG_INFO, 'onBar', "tmpInstanceTotalCellNum >= self.MaxInstanceTotalCellNum ,"
                                            "the value is %d / %d " % (
                tmpInstanceTotalCellNum, self.MaxInstanceTotalCellNum))
            return


        # TODO 当前持仓是否满足 6 规则
        # TODO 撤销所有的合约

        # cell 是否发生变化,如果有发生变化,就不再进行下面的逻辑
        isCellChange = False

        # 如果未持有合约,判断是否有突破
        if self.cell_num == 0:

            if bar.close > self.monitor['middleWindowHighBreak']:
                # 有向上突破
                isCellChange = True
                self.s_or_b = 'b'
                a_cell = HgCell(vtSymbol, self.s_or_b, self.monitor['unit'],
                                self.monitor['middleWindowHighBreak'], BREAK_MIDDLEWINDOW, self.monitor['atr'])

                self.myPrint(LOG_IMPORTANT, 'onBar', "发现向上突破,开仓信息如下"
                                                     "vtSymbol = %s, "
                                                     "s_or_b = %s, "
                                                     "unit = %d, "
                                                     "middleWindowHighBreak = %d, "
                                                     "type = %s, "
                                                     "atr = %d " % (vtSymbol, self.s_or_b, self.monitor['unit'],
                                                                    self.monitor['middleWindowHighBreak'], BREAK_MIDDLEWINDOW, self.monitor['atr']))
                self.addCell(a_cell)


            elif bar.close < self.monitor['middleWindowLowBreak']:
                # 有向下突破
                isCellChange = True
                self.s_or_b = 's'
                a_cell = HgCell(vtSymbol, self.s_or_b, self.monitor['unit'],
                                self.monitor['middleWindowLowBreak'], BREAK_MIDDLEWINDOW, self.monitor['atr'])
                self.myPrint(LOG_IMPORTANT, 'onBar', "发现向下突破,开仓信息如下"
                                                     "vtSymbol = %s, "
                                                     "s_or_b = %s, "
                                                     "unit = %d, "
                                                     "middleWindowLowBreak = %d, "
                                                     "type = %s, "
                                                     "atr = %d " % (vtSymbol, self.s_or_b, self.monitor['unit'],
                                                                    self.monitor['middleWindowLowBreak'],
                                                                    BREAK_MIDDLEWINDOW, self.monitor['atr']))
                self.addCell(a_cell)

            # 初始持仓为0,并出现成交,说明开仓了,记录开仓时候的art
            if isCellChange == True:
                self.atr = self.monitor['atr']
                self.myPrint(LOG_IMPORTANT, 'onBar', "开仓atr = %d" % (self.atr))
                #  TODO 清仓完毕后需要重置一些属性,尤其是ATR

        if self.cell_num == 0:
            # 下面的操作只有有持仓时才操作
            self.myPrint(LOG_DEBUG, 'onBar', "self.cell_num == 0")
            return


        if not isCellChange:
            # 如果持有合约,判断是否触及退出
            # 10日线退出法则, 多头头寸,价格低于最近10日最低点时退出
            if self.s_or_b == 'b' and bar.close < self.monitor['shortWindowLowBreak']:
                self.myPrint(LOG_IMPORTANT, 'onBar', "10日线退出法则, 多头头寸,价格低于最近10日最低点时退出。"
                                                     "s_or_b = %s, "
                                                     "bar.close = %d,"
                                                     "shortWindowLowBreak = %d " % (self.s_or_b, bar.close, self.monitor['shortWindowLowBreak']))
                self.quitAllOrders()
                isCellChange = True
            # 10日线退出法则, 空头头寸,价格高于最近10日最高点时退出
            if self.s_or_b == 's' and bar.close > self.monitor['shortWindowHighBreak']:
                self.myPrint(LOG_IMPORTANT, 'onBar', "10日线退出法则,空头头寸,价格高于最近10日最高点时退出。"
                                                     "s_or_b = %s, "
                                                     "bar.close = %d,"
                                                     "shortWindowHighBreak = %d " % (
                             self.s_or_b, bar.close, self.monitor['shortWindowHighBreak']))
                self.quitAllOrders()
                isCellChange = True


        # 如果持有合约,判断是否触及止损
        # TODO 涨跌停的处理
        if not isCellChange:
            isCellChange = self.check_stop_condition(bar.close)
            if isCellChange:
                self.myPrint(LOG_IMPORTANT, 'onBar', "触及止损。")

        # 如果持有合约,判断是否触及加仓,同时判断仓位是否超过限制
        if not isCellChange:
            isCellChange = self.check_add_condition(bar.close)
            if isCellChange:
                self.myPrint(LOG_IMPORTANT, 'onBar', "触及加仓。")

        # 处理每个cell
        if isCellChange:
            self.myPrint(LOG_IMPORTANT, 'onBar', "处理cell变动,并记录在数据库中。")

            for hgcell in self.hgCellList:
                hgcell.hand_cell(self, bar.close)

            # 记录在数据库中
            self.hgDbEngine.saveIntoDB(self)



    # 判断是否所有cell的订单都是稳定的
    def is_all_cell_stable(self):
        ret = True
        for hgcell in self.hgCellList:
            ret = hgcell.is_all_order_stable(self) and ret
        return ret

    # 判断是否所有cell都达到目标订单了
    def is_all_cell_get_target_unit(self):
        ret = True
        for hgcell in self.hgCellList:
            ret = (hgcell.target_unit == hgcell.real_unit) and ret
        return ret


    #----------------------------------------------------------------------
    def onOrder(self, order):
        """收到委托变化推送(必须由用户继承实现)"""
        # 对于无需做细粒度委托控制的策略,可以忽略onOrder
        self.myPrint(LOG_DEBUG, 'onOrder', 'IN')
        self.myPrint(LOG_INFO, 'onOrder', str(order.__dict__).decode('unicode-escape'))
        self.printCells("*" * 20 + " in onorder")

        self.orderList.append(order)

        # onOrder  -133888101.1.CTP.4
        # 更新 cell 中 in_orderId_dict out_orderId_dict 中的订单信息

        is_update = False
        for hgcell in self.hgCellList:
            is_update = (hgcell.updateOrder(order) or is_update)

        if is_update:
            self.myPrint(LOG_IMPORTANT, 'onOrder', '成功更新 cell orders。')
        else:
            self.myPrint(LOG_ERROR, 'onOrder', '更新 cell orders 失败。')
            self.stopTrading()

        # TODO 更新持仓数量信息


        # 记录在数据库中
        self.hgDbEngine.saveIntoDB(self)
        self.printCells("*" * 20 + " out onorder")


    
    #----------------------------------------------------------------------
    def onTrade(self, trade):
        """收到成交推送(必须由用户继承实现)"""
        # 对于无需做细粒度委托控制的策略,可以忽略onOrder
        # 打印过trader信息,里面没有session信息
        self.myPrint(LOG_DEBUG, 'onTrade', '')
        self.myPrint(LOG_INFO, 'onTrade', str(trade.__dict__).decode('unicode-escape'))
        self.tradeList.append(trade)

        self.printCells("*"*20 + " in onTrade")
        is_update = False
        orderid = trade.vtOrderID
        # 如果 sessionID 和 frontID 维护了
        if self.sessionID is not None and self.frontID is not None:
            orderid = self.sessionID + '.' + self.frontID + '.' + orderid

        # 把 Trade 更新到 cell 中, 更新完之后,会自动计算当前cell持仓 和 真实价格
        for hgcell in self.hgCellList:
            is_update = (hgcell.updateTrade(orderid, trade) or is_update)

        if is_update:
            self.myPrint(LOG_IMPORTANT, 'onTrade', '成功更新 cell trades。')
        else:
            self.myPrint(LOG_ERROR, 'onTrade', '更新 cell trades 失败。')
            self.stopTrading()

        # 接收到成交之后打印一下自己
        for hgcell in self.hgCellList:
            hgcell.print_self()
        # 记录在数据库中
        self.hgDbEngine.saveIntoDB(self)

        # TODO 发送下报告,这里报告中有些字段还没更新,其实不是最佳时机
        self.myPrint(LOG_INFO, 'onTrade', '发送报告: ' + self.instanceName)
        self.hgReport.sendReport(self.instanceName, self.pickleItemDict)

        self.printCells("*" * 20 + " out onTrade")
    
    #----------------------------------------------------------------------
    def onStopOrder(self, so):
        """停止单推送"""
        self.myPrint("onStopOrder", so.__dict__)
        pass

    # ----------------------------------------------------------------------
    def myPrint(self, funName, date):
        print("%s strategyHg funName = %s ,  date = %s " % (datetime.now(), funName, date))

    # 自定义日志级别输出函数
    def myPrint(self, level, funName, data):

        info = ""
        if level == LOG_INFO:
            info = '【INFO】'
        if level == LOG_DEBUG:
            info = '【DEBUG】'
        if level == LOG_IMPORTANT:
            info = '【IMPORTANT】'
        if level == LOG_ERROR:
            info = '【ERROR】'

        # 添加策略实例标识
        info = info + self.instanceName + ' ' + self.instanceId + ' '
        if level >= self.logLevel:
            info = info + " %s strategyHg funName = %s ,  data = %s " % (datetime.now(), funName, data)
            #print(info) # 输出在文件中
            self.writeCtaLog(info) # 输出在数据库中

    # 计算真实cell持仓,和 总单位持仓,调用前提是订单已经稳定,订单列表肯定不能为空
    def calCellNumAndTotalRealUnit(self):

        self.myPrint(LOG_DEBUG, 'calCellNumAndTotalRealUnit', 'IN')
        positionCellNum = 0 # 处于已持仓的cell数量
        totalRealUnit = 0

        ret = True # 返回默认为正常
        # 倒叙遍历cell
        for cell in list(reversed(self.hgCellList)):
            real = cell.real_unit
            target = cell.target_unit
            totalRealUnit = totalRealUnit + real

            # 进入此函数,cell 一定经过了执行 hand_cell in_orderId_dict 不可能为空
            if not cell.in_orderId_dict:
                self.myPrint(LOG_ERROR, 'calRealCellAndUnitNum', 'not cell.in_orderId_dict')
                self.stopTrading()
                ret = False

            # 订单处于稳定状态,只有一种情况 target == real
            if target > 0 and target == real:
                positionCellNum = positionCellNum + 1
            elif target == 0 and target == real:
                self.myPrint(LOG_IMPORTANT, 'calRealCellAndUnitNum', 'cell 清空完毕,将cell删除。')
                self.hgCellList.remove(cell)
            else:
                # 其他情况均为不正常状态
                self.myPrint(LOG_ERROR, 'calRealCellAndUnitNum', '其他情况均为不正常状态'
                                                                 'target = %d, real = %d ' % (target, real))
                self.stopTrading()
                ret = False

        self.cell_num = len(self.hgCellList) # cell数量
        self.totalRealUnit = totalRealUnit # 真实总持仓情况

        return ret





    # ----------------------------------------------------------------------
    def addCell(self, cell):
        # 增加一个持仓单位
        # 当前持仓大于最大持仓要求
        if self.cell_num >= self.max_cell_num:
            self.myPrint(LOG_IMPORTANT, 'addCell', u'已达到最大持仓 %d / %d' % (self.cell_num, self.max_cell_num))
            return

        # 单方向是否达到了最大值
        tmpInstanceTotalCellNum = self.hgDbEngine.getInstanceTotalCellNum(self.instanceName, self.s_or_b)
        if tmpInstanceTotalCellNum >= self.MaxInstanceTotalCellNum:
            self.myPrint(LOG_IMPORTANT, 'addCell', "tmpInstanceTotalCellNum >= self.MaxInstanceTotalCellNum ,"
                                            "the value is %d / %d " % (
                tmpInstanceTotalCellNum, self.MaxInstanceTotalCellNum))
            return

        self.cell_num = self.cell_num + 1 # 持仓计数加1
        self.hgCellList.append(cell) # 添加在持仓列表中


    # ----------------------------------------------------------------------
    def quitAllOrders(self):
        # 设定所有持仓的目标仓位为0
        for cell in self.hgCellList:
            cell.target_unit = 0

    def check_stop_condition(self, price):
        """ 检验是否触发止损条件"""
        ret = False # 默认返回True

        if self.s_or_b == 'b':
            # 多头持仓
            for cell in self.hgCellList:
                if cell.plan_stop_price is not None:
                    if price <= cell.plan_stop_price:
                        # 当前价格小于等于止损价格时,设定目标仓位为0
                        cell.target_unit = 0
                        ret = True
                else:
                    self.myPrint(LOG_ERROR, 'check_stop_condition', "check_stop_condition,cell.plan_stop_price is None")
        if self.s_or_b == 's':
            # 空头持仓
            for cell in self.hgCellList:
                if cell.plan_stop_price is not None:
                    if price >= cell.plan_stop_price:
                        # 当前价格大于等于止损价格时,设定目标仓位为0
                        cell.target_unit = 0
                        ret = True
                else:
                    self.myPrint(LOG_ERROR, 'check_stop_condition', "check_stop_condition,cell.plan_stop_price is None")

        return ret

    # ----------------------------------------------------------------------
    def check_add_condition(self, price):
        """检验是否触及加仓条件"""
        # 之前已有校验,能进入这个函数说明未达到最大持仓,单方向也满足要求
        ret = False
        cell = self.hgCellList[len(self.hgCellList) - 1] # 取最后一个持仓
        if self.s_or_b == 'b':
            # 多头持仓,并且当前价格大约加仓价
            if price >= self.plan_add_price:
                a_cell = HgCell(self.vtSymbol, self.s_or_b, self.monitor['unit'],
                                self.plan_add_price, HALF_N, cell.N)

                self.myPrint(LOG_IMPORTANT, 'check_add_condition', "触发加仓,信息如下"
                                                     "vtSymbol = %s, "
                                                     "s_or_b = %s, "
                                                     "unit = %d, "
                                                     "plan_add_price = %d, "
                                                     "type = %s, "
                                                     "N = %d " % (self.vtSymbol, self.s_or_b, self.monitor['unit'],
                                                                    self.plan_add_price,
                                                                  HALF_N, cell.N))


                self.addCell(a_cell)
                ret = True

        if self.s_or_b == 's':
            # 空头持仓,并且当前价格小于加仓价
            if price <= self.plan_add_price:
                a_cell = HgCell(self.vtSymbol, self.s_or_b, self.monitor['unit'],
                                self.plan_add_price, HALF_N, cell.N)

                self.myPrint(LOG_IMPORTANT, 'check_add_condition', "触发加仓,信息如下"
                                                                   "vtSymbol = %s, "
                                                                   "s_or_b = %s, "
                                                                   "unit = %d, "
                                                                   "plan_add_price = %d, "
                                                                   "type = %s, "
                                                                   "N = %d " % (
                             self.vtSymbol, self.s_or_b, self.monitor['unit'],
                             self.plan_add_price,
                             HALF_N, cell.N))

                self.addCell(a_cell)
                ret = True

        return ret



    # ----------------------------------------------------------------------
    # 更新 self.plan_stop_price
    # 从最后一个仓位开始,计算每个仓位的退出值
    def update_plan_stop_price(self):

        last_real_in_price = None # 记录上一个价格
        last_plan_stop_price = None # 记录上一个止损价格

        for cell in list(reversed(self.hgCellList)):

            self.real_unit = 0  # 真实持仓单位
            self.real_in_price = 0  # 平均入场价格

            if cell.real_unit == 0 or cell.real_in_price == 0:
                self.myPrint(LOG_ERROR, 'update_plan_stop_price', "cell.real_unit == 0 or cell.real_in_price == 0")
                self.stopTrading()
                break


            tmp_plan_stop_price = None
            if self.s_or_b == 'b':
                tmp_plan_stop_price = cell.real_in_price - 2 * cell.N
            elif self.s_or_b == 's':
                tmp_plan_stop_price = cell.real_in_price + 2 * cell.N

            # 如果处理的是最后一个cell,直接更新
            if last_real_in_price == None:
                cell.plan_stop_price = tmp_plan_stop_price
            elif 0.8 < float(last_real_in_price)/cell.real_in_price < 1.2:
                cell.plan_stop_price = last_plan_stop_price
            else:
                cell.plan_stop_price = tmp_plan_stop_price

            last_real_in_price = cell.real_in_price
            last_plan_stop_price = cell.plan_stop_price

    # 获取当前实例s_or_b 方向的总持仓数
    """
    def getInstanceTotalCellNum(self):

        TotalCellNum = 0
        d = [
            {'$match': {"instanceName": self.instanceName ,"s_or_b" : self.s_or_b}},
            {'$group': {'_id': "$instanceName", 'total': {'$sum': "$cell_num"}}}
        ]
        ret = mydb.dbAggregateSum(MAIN_DB_NAME, TB_HG_MAIN, d)

        for tmp in ret:
            TotalCellNum = int(tmp['total'])
            break

        print("instanceName:%s %s 方向的总持仓为: %d" % (self.instanceName, self.s_or_b, TotalCellNum))
        return TotalCellNum
    """

    def printCells(self,info=""):
        print(info)
        print("start printself")
        gt200 = {key: value for key, value in self.__dict__.items() if key not in ['contracts','orderList','tradeList','hgCellList']}
        print(str(gt200).decode('unicode-escape'))
        print("end printsefl")
        print("start printcells")
        for cell in self.hgCellList:
            cell.print_self()
        print("end printcells")
Example #11
0
class KeltnerCommonStrategy(CtaTemplate):
    """DualThrust交易策略"""
    className = 'KeltnerCommonStrategy'
    author = u'Leon Zhao'

    # 策略参数
    fixedSize = 1
    kUpper = 2
    kLower = 2

    maDays = 13
    atrDays = 20  # I may use the average of ATR to reduce the range
    initDays = 100  # original value is 10
    kExit = 0.5

    # 策略变量
    barList = []  # K线对象的列表

    atrAvg = 0
    maHigh = 0
    maLow = 0
    longEntry = 0
    shortEntry = 0
    longExit = 0
    shortExit = 0
    rsiconfig = 50
    rsilen = 21

    #exitTime = time(hour=15, minute=20) #will not cover position when day close

    longEntered = False
    shortEntered = False

    # 参数列表,保存了参数的名称
    paramList = [
        'name', 'className', 'author', 'vtSymbol', 'kUpper', 'kLower', 'maDays'
    ]

    # 变量列表,保存了变量的名称
    varList = ['inited', 'trading', 'pos', 'atrAvg', 'longEntry', 'shortEntry']

    # 同步列表,保存了需要保存到数据库的变量名称
    syncList = ['pos', 'atrAvg', 'longEntry', 'shortEntry']

    #----------------------------------------------------------------------
    def __init__(self, ctaEngine, setting):
        """Constructor"""
        super(KeltnerCommonStrategy, self).__init__(ctaEngine, setting)

        self.bg = BarGenerator(self.onBar,
                               onDayBar=self.ondayBar,
                               vtSymbol=self.vtSymbol)
        self.am = ArrayManager()
        self.barList = []
        # Read Parameters from Setting files
        if 'strParams' in setting:
            self.params = setting['strParams']
            if len(self.params) >= 3:
                for p in self.params:
                    if p[0] == 'unit':
                        self.fixedSize = p[1]
                    if p[0] == 'p1':
                        self.kUpper = p[1]
                    if p[0] == 'p2':
                        self.kLower = p[1]
                    if p[0] == 'p3':
                        self.maDays = p[1]
                    if p[0] == 'p4':
                        self.atrDays = p[1]
                    if p[0] == 'p5':
                        self.initDays = p[1]
                    if p[0] == 'p6':
                        self.rsilen = p[1]
                    if p[0] == 'p7':
                        self.rsiconfig = p[1]
        else:
            # 策略参数
            self.fixedSize = 1
            self.kUpper = 2
            self.kLower = 2

            self.maDays = 13
            self.atrDays = 20
            self.initDays = 55  # original value is 10
            self.rsiconfig = 50
            self.rsilen = 21
        #print(self.fixedSize,self.kUpper,self.kLower,self.maDays,self.initDays)
        self.atrAvg = 0
        self.maHigh = 0
        self.maLow = 0
        self.longEntry = 0
        self.shortEntry = 0
        self.longExit = 0
        self.shortExit = 0

        #exitTime = time(hour=15, minute=20) #will not cover position when day close

        self.longEntered = False
        self.shortEntered = False

        self.loginterval = 15
        self.logcountdown = 0

    #----------------------------------------------------------------------
    def onInit(self):
        """初始化策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略初始化' % self.name)

        # 载入历史数据,并采用回放计算的方式初始化策略数值
        initData = self.loadBar(self.initDays)
        for bar in initData:
            self.onBar(bar)

        self.putEvent()

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

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

    # ----------------------------------------------------------------------
    def writeKeyValue(self):
        """Update long short entry price(必须由用户继承实现)"""
        #print("write key")
        if self.logcountdown > self.loginterval:
            self.logcountdown = 0
            outstr = "Symbol(" + self.vtSymbol + ")Long Entry:"
            outstr = outstr + str(round(self.longEntry,
                                        2)) + ", Short Entry:" + str(
                                            round(self.shortEntry, 2))
            outstr = outstr + ",Exit:" + str(round(self.emamean, 2))
            self.writeCtaLog(u'%s' % outstr)
        self.logcountdown += 1
        self.putEvent()

    #----------------------------------------------------------------------
    def onTick(self, tick):
        """收到行情TICK推送(必须由用户继承实现)"""
        #ignore data before real open
        if (tick.datetime.hour == 8 or tick.datetime.hour == 20):
            return
        self.bg.updateTick(tick)

    def keltner(self, n, dev, ret_array=False):
        """肯特纳通道"""
        mid = self.sma(n, ret_array)
        atr = self.atr(n, ret_array)

        up = mid + atr * dev
        down = mid - atr * dev

        return up, down

    def calcUnitNo(self, atr, fixSize):
        keltnerCap = 0.0
        defaultCap = 0.0
        unitNo = 0
        cust = []
        var_sizelist = CtaTemplate.vol_Size
        var_size = 0.0
        var_Symbol = ""
        if len(var_sizelist) == 0:
            return fixSize
        else:
            var_Symbol = var_Symbol.join(
                list(filter(lambda x: x.isalpha(), self.vtSymbol)))
            var_size = float(var_sizelist[var_Symbol][0])
            if var_size - 0 < 0.01:
                return fixSize

        var_temp = 0.0
        if len(CtaTemplate.cust_Setting) > 0:
            cust = CtaTemplate.cust_Setting
        for cs in cust:
            if cs["StrategyGroup"] == "Keltner" and cs["Status"] == 'True':
                keltnerCap = cs["CaptialAmt"]
                break
            if cs["StrategyGroup"] == "Default" and cs["Status"] == 'True':
                defaultCap = cs["CaptialAmt"]
        if keltnerCap > 0:
            self.capConfig = float(keltnerCap)
        elif defaultCap > 0:
            self.capConfig = float(defaultCap)
        else:
            self.capConfig = 0.0

        unitNo = 0
        if self.capConfig - 0 < 0.0001:
            unitNo = fixSize
        elif var_size - 0 < 0.001:
            unitNo = fixSize
        else:
            unitNo = int(self.capConfig * 0.0066 / (atr * var_size))
        if unitNo < 1:
            unitNo = 1
        return unitNo

    #---------calcuate range for the last several days
    def calcKPI(self):
        if self.am.count >= self.maDays:
            self.atrAvg = self.am.atr(self.atrDays, False)
            if self.atrAvg > 0:
                self.fixedSize = self.calcUnitNo(self.atrAvg, self.fixedSize)
            hval = self.am.highArray
            lval = self.am.lowArray
            cval = self.am.closeArray
            meanval = [(h + l + c) / 3 for h, l, c in zip(hval, lval, cval)]
            sidx = len(meanval) - self.maDays
            self.emamean = np.mean(meanval[sidx:])
            #self.emamean = self.am.ema(meanval, array=False)

            tlen = len(self.am.closeArray) - self.rsilen - 2
            rsi1 = talib.RSI(self.am.closeArray[tlen:], timeperiod=self.rsilen)
            self.rsival = rsi1[-1]

            self.longEntry = self.emamean + self.atrAvg * self.kUpper
            self.shortEntry = self.emamean - self.atrAvg * self.kLower
            self.longExit = self.emamean
            self.shortExit = self.emamean

    #----------------------------------------------------------------------
    def onBar(self, bar):
        """收到Bar推送(必须由用户继承实现)"""
        if self.reduceCountdown() > 0:
            return
        # 撤销之前发出的尚未成交的委托(包括限价单和停止单)
        self.cancelAll()

        self.bg.updateBar(bar)

        barLength = 0
        barLength = max(self.atrDays, self.maDays) + 1
        if self.am.count < barLength:
            return
        # 计算指标数值
        self.barList.append(bar)

        if len(self.barList) <= 2:
            return
        else:
            self.barList.pop(0)
        lastBar = self.barList[-2]

        # 新的一天
        #for commodity trade at night 9 also need because some day night is canncel due to holiday
        if (lastBar.datetime.hour == 15 or lastBar.datetime.hour == 14) and (
            (bar.datetime.hour == 21 or bar.datetime.hour == 9)):
            #for commodity not trade at night:
            #if (lastBar.datetime.hour == 15 or lastBar.datetime.hour==14 and lastBar.datetime.minute==59) and ((bar.datetime.hour == 9)  ):
            # 如果已经初始化
            self.range = self.calcKPI()
            self.dayOpen = bar.open
            #self.longEntered = False
            #self.shortEntered = False
        else:
            pass

        # 尚未到收盘
        if self.maHigh < 1:
            self.calcKPI()
        if (self.longEntry < self.emamean) or (
                self.shortEntry > self.emamean) or (abs(self.rsival) > 100):
            #print(self.kUpper,self.kLower,self.range,"b",self.longEntry,"c",bar.open,bar.datetime)
            self.writeCtaLog(
                u'long Entry less than MA or vice vesa , or RSI wrong, need to check'
            )
            return

        if True:  # Trade Time, no matter when, just send signal
            #print("KK:", self.longEntry, self.shortEntry, self.rsival)
            if self.pos == 0:
                self.longEntered = False
                self.shortEntered = False
                if bar.close > self.longEntry and self.rsival >= self.rsiconfig:
                    #if not self.longEntered:
                    #self.buy(self.longEntry + 2, self.fixedSize)
                    self.buy(bar.close, self.fixedSize)
                elif bar.close < self.shortEntry and self.rsival <= self.rsiconfig:
                    #if not self.shortEntered:
                    #self.short(self.shortEntry - 2, self.fixedSize)
                    self.short(bar.close, self.fixedSize)
                else:
                    pass

            # 持有多头仓位
            elif self.pos > 0:
                self.longEntered = True
                self.shortEntered = False
                # 多头止损单
                if bar.close < self.longExit:
                    #self.sell(self.shortEntry -2 , self.fixedSize)
                    self.sell(bar.close, self.pos)
                    # 空头开仓单

            # 持有空头仓位
            elif self.pos < 0:
                self.shortEntered = True
                self.longEntered = False
                # 空头止损单
                if bar.close > self.shortExit:
                    #self.cover(self.longEntry + 2, self.fixedSize)
                    self.cover(bar.close, self.pos)

        # 收盘平仓 This will not execute
        else:
            if self.pos > 0:
                self.sell(bar.close * 0.99, abs(self.pos))
            elif self.pos < 0:
                self.cover(bar.close * 1.01, abs(self.pos))

        self.writeKeyValue()

        # 发出状态更新事件
        self.putEvent()

    #update day chart
    def ondayBar(self, dayBar):
        """收到日线推送(必须由用户继承实现)"""
        self.am.updateBar(dayBar)
        # 发出状态更新事件
        self.putEvent()

    #----------------------------------------------------------------------
    def onOrder(self, order):
        """收到委托变化推送(必须由用户继承实现)"""

        pass

    #----------------------------------------------------------------------
    def onTrade(self, trade):
        persisttrade(self.vtSymbol, self.className, trade)
        # 发出状态更新事件
        self.putEvent()

    #----------------------------------------------------------------------
    def onStopOrder(self, so):
        """停止单推送"""
        pass
Example #12
0
class TurtleSignal(object):
    """海龟信号"""

    # ----------------------------------------------------------------------
    def __init__(self,
                 portfolio,
                 vtSymbol,
                 entryWindow,
                 exitWindow,
                 atrWindow,
                 profitCheck=False):
        """Constructor"""
        self.portfolio = portfolio  # 投资组合

        self.vtSymbol = vtSymbol  # 合约代码
        self.entryWindow = entryWindow  # 入场通道周期数
        self.exitWindow = exitWindow  # 出场通道周期数
        self.atrWindow = atrWindow  # 计算ATR周期数
        self.profitCheck = profitCheck  # 是否检查上一笔盈利

        self.am = ArrayManager(60)  # K线容器

        self.atrVolatility = 0  # ATR波动率
        self.entryUp = 0  # 入场通道
        self.entryDown = 0
        self.exitUp = 0  # 出场通道
        self.exitDown = 0

        self.longEntry1 = 0  # 多头入场位
        self.longEntry2 = 0
        self.longEntry3 = 0
        self.longEntry4 = 0
        self.longStop = 0  # 多头止损位

        self.shortEntry1 = 0  # 空头入场位
        self.shortEntry2 = 0
        self.shortEntry3 = 0
        self.shortEntry4 = 0
        self.shortStop = 0  # 空头止损位

        self.unit = 0  # 信号持仓
        self.result = None  # 当前的交易
        self.resultList = []  # 交易列表
        self.bar = None  # 最新K线

    # ----------------------------------------------------------------------
    # def onBar(self, bar):
    #     """"""
    #     self.bar = bar
    #     self.am.updateBar(bar)
    #     if not self.am.inited:
    #         return
    #
    #     self.generateSignal(bar)
    #     self.calculateIndicator()

    def onBar(self, bar):
        """"""
        self.bar = bar
        self.am.updateBar(bar)
        while not self.am.inited:
            self.am.updateBar(bar)

        self.generateSignal(bar)
        self.calculateIndicator()

    # ----------------------------------------------------------------------
    def generateSignal(self, bar):
        """
        判断交易信号
        要注意在任何一个数据点:buy/sell/short/cover只允许执行一类动作
        """
        # 如果指标尚未初始化,则忽略
        if not self.longEntry1:
            return

        # 优先检查平仓
        if self.unit > 0:
            longExit = max(self.longStop, self.exitDown)

            if bar.low <= longExit:
                self.sell(longExit)
                return
        elif self.unit < 0:
            shortExit = min(self.shortStop, self.exitUp)
            if bar.high >= shortExit:
                self.cover(shortExit)
                return

        # 没有仓位或者持有多头仓位的时候,可以做多(加仓)
        if self.unit >= 0:
            trade = False

            if bar.high >= self.longEntry1 and self.unit < 1:
                self.buy(self.longEntry1, 1)
                trade = True

            if bar.high >= self.longEntry2 and self.unit < 2:
                self.buy(self.longEntry2, 1)
                trade = True

            if bar.high >= self.longEntry3 and self.unit < 3:
                self.buy(self.longEntry3, 1)
                trade = True

            if bar.high >= self.longEntry4 and self.unit < 4:
                self.buy(self.longEntry4, 1)
                trade = True

            if trade:
                return

        # 没有仓位或者持有空头仓位的时候,可以做空(加仓)
        if self.unit <= 0:
            if bar.low <= self.shortEntry1 and self.unit > -1:
                self.short(self.shortEntry1, 1)

            if bar.low <= self.shortEntry2 and self.unit > -2:
                self.short(self.shortEntry2, 1)

            if bar.low <= self.shortEntry3 and self.unit > -3:
                self.short(self.shortEntry3, 1)

            if bar.low <= self.shortEntry4 and self.unit > -4:
                self.short(self.shortEntry4, 1)

    # ----------------------------------------------------------------------
    def calculateIndicator(self):
        """计算技术指标"""
        self.entryUp, self.entryDown = self.am.donchian(self.entryWindow)
        self.exitUp, self.exitDown = self.am.donchian(self.exitWindow)

        # 有持仓后,ATR波动率和入场位等都不再变化
        if not self.unit:
            self.atrVolatility = self.am.atr(self.atrWindow)

            self.longEntry1 = self.entryUp
            self.longEntry2 = self.entryUp + self.atrVolatility * 0.5
            self.longEntry3 = self.entryUp + self.atrVolatility * 1
            self.longEntry4 = self.entryUp + self.atrVolatility * 1.5
            self.longStop = 0

            self.shortEntry1 = self.entryDown
            self.shortEntry2 = self.entryDown - self.atrVolatility * 0.5
            self.shortEntry3 = self.entryDown - self.atrVolatility * 1
            self.shortEntry4 = self.entryDown - self.atrVolatility * 1.5
            self.shortStop = 0

    # ----------------------------------------------------------------------
    def newSignal(self, direction, offset, price, volume):
        """"""
        self.portfolio.newSignal(self, direction, offset, price, volume)

    # ----------------------------------------------------------------------
    def buy(self, price, volume):
        """买入开仓"""
        price = self.calculateTradePrice(DIRECTION_LONG, price)

        self.open(price, volume)
        self.newSignal(DIRECTION_LONG, OFFSET_OPEN, price, volume)

        # 以最后一次加仓价格,加上两倍N计算止损
        self.longStop = price - self.atrVolatility * 2

    # ----------------------------------------------------------------------
    def sell(self, price):
        """卖出平仓"""
        price = self.calculateTradePrice(DIRECTION_SHORT, price)
        volume = abs(self.unit)

        self.close(price)
        self.newSignal(DIRECTION_SHORT, OFFSET_CLOSE, price, volume)

    # ----------------------------------------------------------------------
    def short(self, price, volume):
        """卖出开仓"""
        price = self.calculateTradePrice(DIRECTION_SHORT, price)

        self.open(price, -volume)
        self.newSignal(DIRECTION_SHORT, OFFSET_OPEN, price, volume)

        # 以最后一次加仓价格,加上两倍N计算止损
        self.shortStop = price + self.atrVolatility * 2

    # ----------------------------------------------------------------------
    def cover(self, price):
        """买入平仓"""
        price = self.calculateTradePrice(DIRECTION_LONG, price)
        volume = abs(self.unit)

        self.close(price)
        self.newSignal(DIRECTION_LONG, OFFSET_CLOSE, price, volume)

    # ----------------------------------------------------------------------
    def open(self, price, change):
        """开仓"""
        self.unit += change

        if not self.result:
            self.result = TurtleResult()
        self.result.open(price, change)

    # ----------------------------------------------------------------------
    def close(self, price):
        """平仓"""
        self.unit = 0

        self.result.close(price)
        self.resultList.append(self.result)
        self.result = None

    # ----------------------------------------------------------------------
    def getLastPnl(self):
        """获取上一笔交易的盈亏"""
        if not self.resultList:
            return 0

        result = self.resultList[-1]
        return result.pnl

    # ----------------------------------------------------------------------
    def calculateTradePrice(self, direction, price):
        """计算成交价格"""
        # 买入时,停止单成交的最优价格不能低于当前K线开盘价
        if direction == DIRECTION_LONG:
            tradePrice = max(self.bar.open, price)
        # 卖出时,停止单成交的最优价格不能高于当前K线开盘价
        else:
            tradePrice = min(self.bar.open, price)

        return tradePrice
class TripleMAStrategy05(CtaTemplate):
    """基于三均线的交易策略"""
    className = 'TripleMAStrategy05'
    author = 'Y.Raul'

    # 策略参数
    initDays = 10  # 初始化数据所用的天数
    windowCheck = True  #交易窗口开关
    openWindowSize = 5  #开盘观察窗口,单位分钟
    closeWindowSize = 10  #收盘平仓窗口,单位分钟
    minDiff = 1  #最小变动单位

    # 策略变量
    # 仓位设置
    stepPos = 1  # 每次交易的数量
    maxPos = 4  # 仓位上限
    addPosRatio = 3

    # 均线设置
    maWindow1 = 10
    maWindow2 = 20
    maWindow3 = 120
    maWindow4 = 5
    atrWindow = 30  # ATR窗口数

    # 分级出场设置
    trailingStart1 = 50
    trailingStart2 = 80
    exitOnTrailingStop1 = 30  # Trailing Stop 距离
    exitOnTrailingStop2 = 20  # Trailing Stop 距离
    exitOnLossStop = 5  # Loss Stop 距离

    # 价格相关变量
    intraTradeHigh = 0  # 持仓期内的最高点
    intraTradeLow = 0  # 持仓期内的最低点
    longExit = 0  # 多头止损
    shortExit = 0  # 空头止损
    longEntry = 0  # 多头开仓
    shortEntry = 0
    avgEntryPrice = 0

    # 指标相关变量
    # ma次新值
    ma10 = 0
    ma20 = 0
    ma30 = 0
    # ma最新值
    ma11 = 0
    ma21 = 0
    ma31 = 0
    atrValue = 0  # ATR指标数值

    orderList = []  # 保存委托代码的列表

    # 参数列表,保存了参数的名称
    paramList = [
        'name', 'className', 'author', 'vtSymbol', 'maWindow1', 'maWindow2',
        'maWindow3', 'maWindow4', 'initDays', 'addPos', 'stepPos', 'maxPos',
        'exitOnTrailingStop', 'exitOnLossStop'
    ]

    # 变量列表,保存了变量的名称
    varList = [
        'inited', 'trading', 'pos', 'ma10', 'ma11', 'ma20', 'ma21', 'ma30',
        'ma31', 'atrValue', 'avgEntryPrice'
    ]
    # 同步列表
    syncList = ['pos']

    # ----------------------------------------------------------------------
    def __init__(self, ctaEngine, setting):
        """Constructor"""
        super(TripleMAStrategy05, self).__init__(ctaEngine, setting)
        self.entryPriceList = []
        self.bm = BarGenerator(self.onBar, 5, self.onFiveBar)
        self.am = ArrayManager(size=150)

        self.backTesting = True
        # 策略信号
        self.buySig = False
        self.shortSig = False
        self.sellSig = False
        self.coverSig = False

    # ----------------------------------------------------------------------
    def onInit(self):
        """初始化策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略初始化' % self.name)

        if self.backTesting:
            # 载入历史数据,并采用回放计算的方式初始化策略数值
            self.writeCtaLog(u"回测模式,载入历史数据,并采用回放计算的方式初始化策略数值")
            initData = self.loadBar(self.initDays)
            for bar in initData:
                self.onBar(bar)
        else:
            # self.trading = True
            self.inited = True
            self.writeCtaLog(u"实盘模式")
        self.putEvent()

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

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

    # ----------------------------------------------------------------------
    def onTick(self, tick):
        """收到行情TICK推送(必须由用户继承实现)"""
        self.curDateTime = tick.datetime
        # 计算交易时间和平仓时间
        if self.windowCheck == True:
            self.__timeWindow(tick.datetime)
        else:
            self.tradeWindow = True

        self.bm.updateTick(tick)

    # ----------------------------------------------------------------------
    def onBar(self, bar):
        """收到Bar推送(必须由用户继承实现)"""
        # 回测数据传送的bar.datetime,为bar的开始时间

        self.curDateTime = bar.datetime
        # 计算交易时间和平仓时间
        if self.windowCheck == True:
            self.__timeWindow(bar.datetime)
        else:
            self.tradeWindow = True

        self.bm.updateBar(bar)

        if self.pos == 0:
            self.intraTradeLow = bar.low
            self.intraTradeHigh = bar.high
        else:
            self.intraTradeHigh = max(bar.high, self.intraTradeHigh)
            self.intraTradeLow = min(bar.low, self.intraTradeLow)

        print "-----" * 10
        print "@onBar"
        print "bar.datetime: {0}; pos: {1} ".format(bar.datetime, self.pos)
        print "buySig: {0}; shortSig: {1}".format(self.buySig, self.shortSig)
        print "sellSig: {0}; coverSig: {1}".format(self.sellSig, self.coverSig)
        print "intraTradeHigh: {0}".format(self.intraTradeHigh)
        print "intraTradeLow: {0}".format(self.intraTradeLow)

        #检查交易信号
        if self.buySig:

            self.longEntry = max(self.longEntry, bar.close)
            self.buy(self.longEntry, self.stepPos, True)

            # self.LossStopPrice = round(self.longEntry * (100.0 - self.exitOnLossStop) / 100)
            self.entryPriceList.append(self.longEntry)
            self.avgEntryPrice = sum(self.entryPriceList) / len(
                self.entryPriceList)
            self.LossStopPrice = round(self.avgEntryPrice *
                                       (100.0 + self.exitOnLossStop) / 100)

            self.intraTradeHigh = max(bar.high, self.avgEntryPrice)
            self.intraTradeLow = min(bar.low, self.avgEntryPrice)

            # 记录log
            log = "\n Trading: {0}\n".format(self.trading) + \
                  "{0} Buy : longEntry: {1};\n".format(bar.datetime, self.longEntry) + \
                  " ma10:{0}; ma11:{1}; ma20:{2}; ma21:{3}; ma30:{4};ma31:{5}\n".format(self.ma10, self.ma11, self.ma20,
                                                                                        self.ma21, self.ma30,
                                                                                        self.ma31) + \
                  "ma1_ma5:{0}; ma3_ma5:{1}\n".format(self.ma1_ma5, self.ma3_ma5)
            # "LossStopPrice:{0}\n".format(self.LossStopPrice)
            self.writeCtaLog(log)

            self.buySig = False
            return

        if self.shortSig:

            self.shortEntry = min(self.shortEntry, bar.close)
            self.short(self.shortEntry, self.stepPos, True)

            # self.LossStopPrice = round(self.shortEntry * (100.0 + self.exitOnLossStop) / 100)
            self.entryPriceList.append(self.shortEntry)
            self.avgEntryPrice = sum(self.entryPriceList) / len(
                self.entryPriceList)
            self.LossStopPrice = round(self.avgEntryPrice *
                                       (100.0 + self.exitOnLossStop) / 100)

            self.intraTradeHigh = max(bar.high, self.avgEntryPrice)
            self.intraTradeLow = min(bar.low, self.avgEntryPrice)

            # 记录log
            log = "\n Trading: {0}\n".format(self.trading) + \
                  "{0} Short : shortEntry: {1};\n".format(bar.datetime, self.shortEntry) + \
                  " ma10:{0}; ma11:{1}; ma20:{2}; ma21:{3}; ma30:{4};ma31:{5}\n".format(self.ma10, self.ma11,
                                                                                        self.ma20, self.ma21,
                                                                                        self.ma30, self.ma31) + \
                  "ma1_ma5:{0}; ma3_ma5:{1}\n".format(self.ma1_ma5, self.ma3_ma5)
            # "LossStopPrice:{0}\n".format(self.LossStopPrice)
            self.writeCtaLog(log)

            self.shortSig = False
            return

        if self.sellSig:

            self.longExit = min(bar.close, self.longExit)
            self.sell(self.longExit, abs(self.pos), True)

            # 记录log
            # log = "\n{0} Sell(Trailing Stop) : longExit: {1};\n".format(bar.datetime, self.longExit) + \
            #       "intraTradeHigh:{0}; atrValue:{1}; \n".format(self.intraTradeHigh, self.atrValue) + \
            #       "LongExit:{0}\n".format(self.longExit)
            # self.writeCtaLog(log)

            self.entryPriceList = []
            self.sellSig = False
            return

        if self.coverSig:

            self.shortExit = max(bar.close, self.shortExit)
            self.cover(self.shortExit, abs(self.pos), True)

            # 记录log
            # log = "\n{0} Cover(Trailing Stop) : bar.close: {1};\n".format(bar.datetime, bar.close) + \
            #       "intraTradeLow:{0}; atrValue:{1};\n".format(self.intraTradeLow, self.atrValue) + \
            #       "shortExit:{0}\n".format(self.shortExit)
            # self.writeCtaLog(log)

            self.entryPriceList = []
            self.coverSig = False
            return

    # ----------------------------------------------------------------------
    def onFiveBar(self, bar):
        """收到5分钟K线"""

        print "-----" * 10
        print "@onFiveBar"
        print "bar.datetime: ", bar.datetime
        print "ma11: %f, ma10 %f" % (self.ma11, self.ma10)
        print "ma21: %f, ma20 %f" % (self.ma21, self.ma20)
        print "ma31: %f, ma30 %f" % (self.ma31, self.ma30)

        # 保存K线数据
        self.am.updateBar(bar)
        print "bar count: ", self.am.count
        print "am.inited: ", self.am.inited
        if not self.am.inited:
            return

        # 撤销之前发出的尚未成交的委托(包括限价单和停止单)
        # 等于策略信号的生存期只有5分钟
        self.cancelAll()

        import talib

        # 计算指标数值
        ma3Array = self.am.sma(self.maWindow3, True)
        self.ma30 = round(ma3Array[-2])
        self.ma31 = round(ma3Array[-1])
        self.ma3_ma5 = round(talib.SMA(ma3Array, self.maWindow4)[-1])

        ma1Array = self.am.sma(self.maWindow1, True)
        self.ma10 = round(ma1Array[-2])
        self.ma11 = round(ma1Array[-1])
        self.ma1_ma5 = talib.SMA(ma1Array, self.maWindow4)[-1]

        ma2Array = self.am.sma(self.maWindow2, True)
        self.ma20 = round(ma2Array[-2])
        self.ma21 = round(ma2Array[-1])

        self.atrValue = round(self.am.atr(self.atrWindow))

        # 当前为空仓
        if self.pos == 0:

            self.intraTradeHigh = bar.high
            self.intraTradeLow = bar.low
            self.avgEntryPrice = 0
            self.entryPriceList = []

            if self.tradeWindow:
                # 开多, bar.close > MA120,MA10 > MA120,MA10 上穿MA20,MA10、MA120向上
                if bar.close > self.ma31 and self.ma11 > self.ma31 \
                        and self.ma10 < self.ma20 and self.ma11 > self.ma21\
                        and self.ma31 > self.ma3_ma5 and self.ma11 > self.ma1_ma5:

                    self.buySig = True
                    self.longEntry = bar.close

                # 开空, bar.close < MA120,MA10 < MA120,MA10 下穿MA20, MA10,MA120向下
                elif bar.close < self.ma31 and self.ma11 < self.ma31 \
                        and self.ma10 > self.ma20 and self.ma11 < self.ma21\
                        and self.ma31 < self.ma3_ma5 and self.ma11 < self.ma1_ma5:

                    self.shortSig = True
                    self.shortEntry = bar.close
        if self.pos != 0:
            self.intraTradeHigh = max(self.intraTradeHigh, bar.high)
            self.intraTradeLow = min(self.intraTradeLow, bar.low)

            if self.tradeWindow:
                if self.pos > 0:
                    # 二级止赢判断 盈利80跳
                    if self.intraTradeHigh >= self.avgEntryPrice + self.trailingStart2 * self.minDiff:
                        # 回撤20跳
                        if (bar.close <= self.intraTradeHigh -
                                self.exitOnTrailingStop2 * self.minDiff):
                            self.longExit = self.intraTradeHigh - self.exitOnTrailingStop2 * self.minDiff
                            self.sellSig = True
                            if bar.close < self.longExit:
                                self.longExit = bar.close
                            # 记录log
                            log = "\n{0} Sell(Trailing Stop2)\n".format(bar.datetime) + \
                                'bar.close: {0}; bar.low: {1}; longExit: {2}'.format(bar.close,bar.low, self.longExit)+ \
                                'intraTradeHigh: {0}; avgEntryPrice: {1}; bar.open: {2}'.format(self.intraTradeHigh,self.avgEntryPrice, bar.open)
                            self.writeCtaLog(log)
                    # 一级止赢判断,盈利50跳
                    elif self.intraTradeHigh >= self.avgEntryPrice + self.trailingStart1 * self.minDiff:
                        # 回撤20跳
                        if (bar.close <= self.intraTradeHigh -
                                self.exitOnTrailingStop1 * self.minDiff):
                            self.longExit = self.intraTradeHigh - self.exitOnTrailingStop1 * self.minDiff
                            self.sellSig = True
                            if bar.close < self.longExit:
                                self.longExit = bar.close
                            # 记录log
                            log = "\n{0} Sell(Trailing Stop1)\n".format(bar.datetime) + \
                                  'bar.close: {0}; bar.low: {1}; longExit: {2}'.format(bar.close, bar.low,
                                                                                       self.longExit)+ \
                                  'intraTradeHigh: {0}; avgEntryPrice: {1}; bar.open: {2}'.format(self.intraTradeHigh,self.avgEntryPrice, bar.open)
                            self.writeCtaLog(log)
                    # 止损,回撤20跳
                    elif (bar.close <= self.avgEntryPrice -
                          self.exitOnLossStop * self.minDiff):
                        self.longExit = self.avgEntryPrice - self.exitOnLossStop * self.minDiff
                        self.sellSig = True
                        if bar.close < self.longExit:
                            self.longExit = bar.close
                        # 记录log
                        log = "\n{0} Sell(Loss Stop)\n".format(bar.datetime) + \
                              'bar.close: {0}; bar.low: {1}; longExit: {2}'.format(bar.close, bar.low,
                                                                                   self.longExit)+ \
                              'intraTradeHigh: {0}; avgEntryPrice: {1}; bar.open: {2}'.format(self.intraTradeHigh,
                                                                                              self.avgEntryPrice,
                                                                                              bar.open)
                        self.writeCtaLog(log)

            elif self.pos < 0:
                # 二级止赢判断 盈利80跳
                if self.intraTradeLow <= self.avgEntryPrice - self.trailingStart2 * self.minDiff:
                    # 回撤20跳
                    if (bar.close >= self.intraTradeLow +
                            self.exitOnTrailingStop2 * self.minDiff):
                        self.shortExit = self.intraTradeLow + self.exitOnTrailingStop2 * self.minDiff
                        self.coverSig = True
                        if bar.close > self.shortExit:
                            self.shortExit = bar.close
                        # 记录log
                        log = "\n{0} Cover(Trailing Stop1)\n".format(bar.datetime) + \
                              'bar.close: {0}; bar.low: {1}; shortExit: {2}'.format(bar.close, bar.low,
                                                                                   self.shortExit)+ \
                              'intraTradeLow: {0}; avgEntryPrice: {1}; bar.open: {2}'.format(self.intraTradeLow,
                                                                                              self.avgEntryPrice,
                                                                                              bar.open)
                        self.writeCtaLog(log)
                # 一级止赢判断,盈利50跳
                elif self.intraTradeLow <= self.avgEntryPrice - self.trailingStart1 * self.minDiff:
                    # 回撤20跳
                    if (bar.close >= self.intraTradeLow +
                            self.exitOnTrailingStop1 * self.minDiff):
                        self.shortExit = self.intraTradeLow + self.exitOnTrailingStop1 * self.minDiff
                        self.coverSig = True
                        if bar.close > self.shortExit:
                            self.shortExit = bar.close
                        # 记录log
                        log = "\n{0} Cover(Trailing Stop2)\n".format(bar.datetime) + \
                              'bar.close: {0}; bar.low: {1}; shortExit: {2}'.format(bar.close, bar.low,
                                                                                   self.shortExit)+ \
                              'intraTradeLow: {0}; avgEntryPrice: {1}; bar.open: {2}'.format(self.intraTradeLow,
                                                                                             self.avgEntryPrice,
                                                                                             bar.open)
                        self.writeCtaLog(log)
                # 止损,回撤20跳
                elif (bar.close >=
                      self.avgEntryPrice + self.exitOnLossStop * self.minDiff):
                    self.shortExit = self.avgEntryPrice + self.exitOnLossStop * self.minDiff
                    self.coverSig = True
                    if bar.close > self.shortExit:
                        self.shortExit = bar.close
                    # 记录log
                    log = "\n{0} Cover(Loss Stop)\n".format(bar.datetime) + \
                          'bar.close: {0}; bar.low: {1}; shortExit: {2}'.format(bar.close, bar.low,
                                                                                self.shortExit)+ \
                          'intraTradeLow: {0}; avgEntryPrice: {1}; bar.open: {2}'.format(self.intraTradeLow,
                                                                                         self.avgEntryPrice,
                                                                                         bar.open)
                    self.writeCtaLog(log)

                # # 加仓
                # if self.addPos and (self.maxPos - abs(self.pos) > 0):
                #     # print self.pos, (self.maxPos - abs(self.pos) )
                #     # print self.entryPriceList
                #
                #     lastEntryPrice = self.entryPriceList[-1]
                #     # 固定百分比加仓
                #     addPosOnPips= round(lastEntryPrice* self.addPosRatio/100)
                #
                #     self.writeCtaLog(u'\n 加仓判断:{0},当前仓位:{1}'.format(bar.datetime, self.pos))
                #     # 加多仓
                #     if self.pos > 0 \
                #             and bar.close >= (lastEntryPrice + addPosOnPips* self.minDiff):
                #
                #         self.buySig = True
                #         self.longEntry = bar.close
                #
                #         # 记录log
                #         self.writeCtaLog(u'\n {0},加仓多单{1}手,价格:{2}'.format(bar.datetime, self.stepPos, self.longEntry))
                #
                #         return
                #
                #     # 加空仓
                #     if self.pos < 0 \
                #             and bar.close <= (lastEntryPrice + addPosOnPips*self.minDiff):
                #
                #         self.shortSig = True
                #         self.shortEntry = bar.close
                #         # 记录log
                #         self.writeCtaLog(u'{0},加仓空单{1}手,价格:{2}'.format(bar.datetime, self.stepPos, self.shortEntry))

        # 执行收盘前平仓检查
        # self.__dailyCloseCheck(bar)
        # 发出状态更新事件
        self.putEvent()

        # ----------------------------------------------------------------------

    def onOrder(self, order):
        """收到委托变化推送(必须由用户继承实现)"""
        log = u'\n OnOrder()更新,orderID:{0},{1},totalVol:{2},tradedVol:{3},offset:{4},price:{5},direction:{6},status:{7},orderTime: {7}'\
                         .format(order.orderID, order.vtSymbol, order.totalVolume,order.tradedVolume,
                                 order.offset, order.price, order.direction, order.status, order.orderTime)
        # self.writeCtaLog(log)
        self.putEvent()

    # ----------------------------------------------------------------------
    def onTrade(self, trade):

        log = u'\n OnTrade()更新,orderID:{0},{1},Vol:{2},price:{3},direction:{4},tradeTime:{5}' \
            .format(trade.orderID, trade.vtSymbol, trade.volume,trade.price, trade.direction,trade.tradeTime)
        # self.writeCtaLog(log)
        # 发出状态更新事件
        self.putEvent()

    # ----------------------------------------------------------------------
    def onStopOrder(self, so):
        """停止单推送"""
        log = u'\n OnStopOrder()停止单更新,stopOrderID:{0},Vol:{1},price:{2},direction:{3},status:{4}' \
            .format(so.stopOrderID, so.volume,so.price, so.direction,so.status)
        # self.writeCtaLog(log)

    def __timeWindow(self, dt):
        """交易与平仓窗口"""

        # 螺纹钢交易窗口 避开早盘和夜盘的前5分钟,防止隔夜跳空。
        # 日内平仓窗口
        self.closeWindow = False
        # 交易窗口
        self.tradeWindow = False
        # 开盘窗口
        self.openWindow = False

        # 开市期头5分钟波动较大
        if (dt.hour == 9 or dt.hour == 21) and dt.minute < self.openWindowSize:
            self.openWindow = False
            return

        # 日盘
        if dt.hour == 9 and dt.minute >= 0:
            self.tradeWindow = True
            return

        if dt.hour == 10:
            if dt.minute <= 15 or dt.minute >= 30:
                self.tradeWindow = True
                return

        if dt.hour == 11 and dt.minute <= 30:
            self.tradeWindow = True
            return

        if dt.hour == 13 and dt.minute >= 30:
            self.tradeWindow = True
            return

        if dt.hour == 14:

            if dt.minute < 60 - self.closeWindowSize:
                self.tradeWindow = True
                return
            else:
                self.closeWindow = True
                return

        # 夜盘

        if dt.hour == 21 and dt.minute >= 0:
            self.tradeWindow = True
            return

        if dt.hour == 22 and dt.minute < 60 - self.closeWindowSize:
            self.tradeWindow = True
            return
        else:
            self.closeWindow = True
            return

    def __dailyCloseCheck(self, bar):
        """每天收盘前检查,如果是亏损单,则平掉"""

        if self.pos == 0:
            return False

        if not self.closeWindow:
            return False

        # 撤销未成交的订单
        self.cancelAll()
        log = u'{0},收盘前{1}分钟,撤单及平仓'.format(bar.datetime, self.closeWindowSize)
        self.writeCtaLog(log)
        self.avgEntryPrice = (sum(self.entryPriceList)) / len(
            self.entryPriceList)
        # 记录log
        log = "\n{0} __dailyCloseCheck : bar.close: {1};\n".format(bar.datetime, bar.close) + \
              "avgPrice:{0}\n".format(self.avgEntryPrice)+\
            "pos:{0}\n".format(self.pos)

        self.writeCtaLog(log)

        # 强制平仓
        if self.pos > 0 and bar.close < self.avgEntryPrice:
            self.writeCtaLog(u'强制日内平亏损多仓')

            # 降低两个滑点
            self.sell(bar.close - 2 * self.minDiff, abs(self.pos), True)
            # 记录log
            log = "\n{0} Sell(Force) : bar.close: {1};\n".format(bar.datetime, bar.close) + \
                  "entryPrice:{0}\n".format(bar.close - 2 * self.minDiff)
            self.writeCtaLog(log)

            return True

        if self.pos < 0 and bar.close > self.avgEntryPrice:
            self.writeCtaLog(u'强制日内平亏损空仓')

            self.cover(bar.close + 2 * self.minDiff, abs(self.pos), True)
            # 记录log
            log = "\n{0} Cover(Force) : bar.close: {1};\n".format(bar.datetime, bar.close) + \
                  "forcePrice:{0}\n".format(bar.close - 2 * self.minDiff)
            self.writeCtaLog(log)
            return True

        return True
Example #14
0
class DT_IntraDayLongStrategy(CtaTemplate):
    """DualThrust交易策略"""
    className = 'DT_IntraDayLongStrategy'
    author = u'Leon Zhao'

    # 策略参数
    fixedSize = 1
    k1 = 0.4
    k2 = 0.4

    rangeDays = 4
    initDays = 30  # original value is 10
    atrDays = 20

    # 策略变量
    barList = []  # K线对象的列表

    dayOpen = 0
    rangeHigh = 0
    rangeLow = 0
    rangeHighClose = 0
    rangeLowClose = 0
    range1 = 0
    range2 = 0
    rsiconfig = 50
    rsilen = 21

    range = 0
    longEntry = 0
    shortEntry = 0
    exitTime = time(hour=15,
                    minute=20)  # will not cover position when day close

    longEntered = False
    shortEntered = False

    # 参数列表,保存了参数的名称
    paramList = ['name', 'className', 'author', 'vtSymbol', 'k1', 'k2']

    # 变量列表,保存了变量的名称
    varList = [
        'inited', 'trading', 'pos', 'range', 'longEntry', 'shortEntry',
        'exitTime'
    ]
    longEntry1 = 0
    shortEntry1 = 0
    # 同步列表,保存了需要保存到数据库的变量名称
    syncList = ['pos', 'range', 'longEntry1', 'shortEntry1']

    # ----------------------------------------------------------------------
    def __init__(self, ctaEngine, setting):
        """Constructor"""
        super(DT_IntraDayLongStrategy, self).__init__(ctaEngine, setting)

        self.bg = BarGenerator(self.onBar,
                               onDayBar=self.ondayBar,
                               vtSymbol=self.vtSymbol)
        self.am = ArrayManager()
        self.barList = []
        self.longEntry1 = 0
        self.shortEntry1 = 0
        # Read Parameters from Setting files
        if 'strParams' in setting:
            self.params = setting['strParams']
            if len(self.params) >= 3:
                for p in self.params:
                    if p[0] == 'unit':
                        self.fixedSize = p[1]
                    if p[0] == 'p1':
                        self.k1 = p[1]
                    if p[0] == 'p2':
                        self.k2 = p[1]
                    if p[0] == 'p3':
                        self.rangeDays = p[1]
                    if p[0] == 'p4':
                        self.atrDays = p[1]
                    if p[0] == 'p5':
                        self.initDays = p[1]
                    if p[0] == 'p6':
                        self.rsilen = p[1]
                    if p[0] == 'p7':
                        self.rsiconfig = p[1]

        else:
            # 策略参数
            self.fixedSize = 1
            self.k1 = 0.4
            self.k2 = 0.4

            self.rangeDays = 4
            self.atrDays = 20
            self.initDays = 55  # original value is 10
            self.rsiconfig = 50
            self.rsilen = 21
        # print(self.fixedSize,self.k1,self.k2,self.rangeDays,self.initDays)
        self.dayOpen = 0
        self.rangeHigh = 0
        self.rangeLow = 0
        self.rangeHighClose = 0
        self.rangeLowClose = 0
        self.range1 = 0
        self.range2 = 0
        self.atrValue = 0

        self.range = 0
        self.longEntry = 0
        self.shortEntry = 0
        self.exitTime = time(
            hour=15, minute=20)  # will not cover position when day close
        self.longEntered = False
        self.shortEntered = False
        self.rsival = 1000

        self.loginterval = 15
        self.logcountdown = 0

    # ----------------------------------------------------------------------
    def onInit(self):
        """初始化策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略初始化' % self.name)

        # 载入历史数据,并采用回放计算的方式初始化策略数值
        initData = self.loadBar(self.initDays)
        for bar in initData:
            self.onBar(bar)

        self.putEvent()

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

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

    # ----------------------------------------------------------------------
    def writeKeyValue(self):
        """Update long short entry price(必须由用户继承实现)"""
        #print("write key")
        if self.logcountdown > self.loginterval:
            self.logcountdown = 0
            outstr = "Symbol(" + self.vtSymbol + ")Long Entry:"
            outstr = outstr + str(round(self.longEntry,
                                        2)) + ", Short Entry:" + str(
                                            round(self.shortEntry, 2))
            self.writeCtaLog(u'%s' % outstr)
        self.logcountdown += 1
        self.putEvent()

    # ----------------------------------------------------------------------
    def onTick(self, tick):
        """收到行情TICK推送(必须由用户继承实现)"""
        # ignore data before real open
        if (tick.datetime.hour == 8 or tick.datetime.hour == 20):
            return
        self.bg.updateTick(tick)

    def calcUnitNo(self, atr, fixSize):
        dtCap = 0.0
        defaultCap = 0.0
        unitNo = 0
        cust = []
        var_sizelist = CtaTemplate.vol_Size
        var_size = 0.0
        var_Symbol = ""
        if len(var_sizelist) == 0:
            return fixSize
        else:
            var_Symbol = var_Symbol.join(
                list(filter(lambda x: x.isalpha(), self.vtSymbol)))
            var_size = float(var_sizelist[var_Symbol][0])
            if var_size - 0 < 0.01:
                return fixSize

        var_temp = 0.0
        if len(CtaTemplate.cust_Setting) > 0:
            cust = CtaTemplate.cust_Setting
        for cs in cust:
            if cs["StrategyGroup"] == "DT" and cs["Status"] == 'True':
                dtCap = cs["CaptialAmt"]
                break
            if cs["StrategyGroup"] == "Default" and cs["Status"] == 'True':
                defaultCap = cs["CaptialAmt"]
        if dtCap > 0:
            self.capConfig = float(dtCap)
        elif defaultCap > 0:
            self.capConfig = float(defaultCap)
        else:
            self.capConfig = 0.0

        unitNo = 0
        if self.capConfig - 0 < 0.0001:
            unitNo = fixSize
        elif var_size - 0 < 0.001:
            unitNo = fixSize
        else:
            unitNo = int(self.capConfig * 0.0088 / (atr * var_size))
        if unitNo < 1:
            unitNo = 1
        return unitNo

        # ---------calcuate range for the last several days

    def calcRange(self):
        if self.am.count >= self.atrDays + 1:
            self.atrValue = self.am.atr(self.atrDays, False)
            if self.atrValue > 0:
                self.fixedSize = self.calcUnitNo(self.atrValue, self.fixedSize)
            self.rangeHigh = talib.MAX(self.am.high, self.rangeDays)[-1]
            self.rangeLow = talib.MIN(self.am.low, self.rangeDays)[-1]
            self.rangeHighClose = talib.MAX(self.am.close, self.rangeDays)[-1]
            self.rangeLowClose = talib.MIN(self.am.close, self.rangeDays)[-1]
            self.range1 = self.rangeHigh - self.rangeLowClose
            self.range2 = self.rangeHighClose - self.rangeLow
            #self.rsival = self.am.rsi(self.rsilen, array=False)

            tlen = len(self.am.closeArray) - self.rsilen - 2
            rsi1 = talib.RSI(self.am.closeArray[tlen:], timeperiod=self.rsilen)
            self.rsival = rsi1[-1]
            #print("rsi array", rsi1)

            #print("\n\rthe rsi is:", rsi1[-1])

            # print(self.rangeHigh,self.rangeLow)
            if (self.range1 > self.range2):
                calcRange = self.range1
            else:
                calcRange = self.range2
        else:
            calcRange = 0
        return calcRange

        # ----------------------------------------------------------------------

    def onBar(self, bar):
        """收到Bar推送(必须由用户继承实现)"""
        if self.reduceCountdown() > 0:
            return
        # 撤销之前发出的尚未成交的委托(包括限价单和停止单)
        self.cancelAll()

        self.bg.updateBar(bar)
        barLength = 0
        barLength = max(self.atrDays, self.rangeDays) + 1
        if self.am.count < barLength:
            return
            # 计算指标数值
        self.barList.append(bar)

        if len(self.barList) <= 2:
            return
        else:
            self.barList.pop(0)
        lastBar = self.barList[-2]

        # 新的一天
        # for commodity trade at night 9 also need because some day night is canncel due to holiday
        if (lastBar.datetime.hour == 15 or lastBar.datetime.hour == 14) and (
            (bar.datetime.hour == 21 or bar.datetime.hour == 9)):
            # for commodity not trade at night:
            # if (lastBar.datetime.hour == 15 or lastBar.datetime.hour==14 and lastBar.datetime.minute==59) and ((bar.datetime.hour == 9)  ):
            # 如果已经初始化
            self.range = self.calcRange()
            # old logic, use current open
            #self.dayOpen = bar.open
            # New logic, use yesterday close, since I skip first two minutes, cause differ from back test
            self.dayOpen = self.am.close
            if self.range:
                self.longEntry = bar.open + self.k1 * self.range
                self.shortEntry = bar.open - self.k2 * self.range

                # self.longEntered = False
            # self.shortEntered = False
        else:
            pass

        # 尚未到收盘
        if not self.range:
            self.range = self.calcRange()
            self.dayOpen = bar.open
            if self.range:
                self.longEntry = bar.open + self.k1 * self.range
                self.shortEntry = bar.open - self.k2 * self.range

        if (self.range == 0):
            # print(self.k1,self.k2,self.range,"b",self.longEntry,"c",bar.open,bar.datetime)
            self.writeCtaLog(u'Range eq 0 , need to check')
        if abs(self.rsival) > 100:
            self.writeCtaLog(u'RSI large than 100, need to check')

        if True:  # Trade Time, no matter when, just send signal
            #print("DT Long:", self.longEntry, self.shortEntry, self.rsival)
            if self.pos == 0:
                self.longEntered = False
                self.shortEntered = False
                if bar.close > self.longEntry and self.rsival >= self.rsiconfig:
                    # if not self.longEntered:
                    # self.buy(self.longEntry + 2, self.fixedSize)
                    self.buy(bar.close, self.fixedSize)
                else:
                    pass
            # 持有多头仓位
            elif self.pos > 0:
                self.longEntered = True
                self.shortEntered = False
                # 多头止损单
                if bar.close < self.shortEntry:
                    # self.sell(self.shortEntry -2 , self.fixedSize)
                    self.sell(bar.close, abs(self.pos))

            # 持有空头仓位
            elif self.pos < 0:
                print("somethong wrong, there should have no short position!")
            else:
                pass

        # 收盘平仓 This will not execute
        else:
            pass

        self.writeKeyValue()

        # 发出状态更新事件
        self.putEvent()

    # update day chart
    def ondayBar(self, dayBar):
        """收到日线推送(必须由用户继承实现)"""
        self.am.updateBar(dayBar)
        self.range = None
        self.dayOpen = 0
        # 发出状态更新事件
        self.putEvent()
        # ----------------------------------------------------------------------

    def onOrder(self, order):
        """收到委托变化推送(必须由用户继承实现)"""
        pass

    # ----------------------------------------------------------------------
    def onTrade(self, trade):
        # 发出状态更新事件
        persisttrade(self.vtSymbol, self.className, trade)
        self.putEvent()

    # ----------------------------------------------------------------------
    def onStopOrder(self, so):
        """停止单推送"""
        pass