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)
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
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 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
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
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
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
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")
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
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
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