class CciSignal(CtaSignal): """CCI信号""" # ---------------------------------------------------------------------- def __init__(self): """Constructor""" super(CciSignal, self).__init__() self.cciWindow = 10 self.cciLevel = 20 self.cciLong = self.cciLevel self.cciShort = -self.cciLevel self.cciValue = 0.0 self.bg = BarGenerator(self.onBar, 15, self.on15Bar) self.am = ArrayManager() # ---------------------------------------------------------------------- def onTick(self, tick): """Tick更新""" self.bg.updateTick(tick) # ---------------------------------------------------------------------- def onBar(self, bar): """K线更新""" self.bg.updateBar(bar) def on15Bar(self, bar): self.am.updateBar(bar) print bar.datetime print "@cciSignal" print "cci inited: ", self.am.inited if not self.am.inited: self.setSignalPos(0) return self.cciValue = self.am.cci(self.cciWindow) print "cciValue: ", self.cciValue if self.cciValue >= self.cciLong: self.setSignalPos(1) elif self.cciValue <= self.cciShort: self.setSignalPos(-1) else: self.setSignalPos(0)
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 MaSignal(CtaSignal): """双均线信号""" # ---------------------------------------------------------------------- def __init__(self): """Constructor""" super(MaSignal, self).__init__() self.fastWindow = 5 self.slowWindow = 20 self.bg = BarGenerator(self.onBar, 5, self.onFiveBar) self.am = ArrayManager() # ---------------------------------------------------------------------- def onTick(self, tick): """Tick更新""" self.bg.updateTick(tick) # ---------------------------------------------------------------------- def onBar(self, bar): """K线更新""" self.bg.updateBar(bar) # ---------------------------------------------------------------------- def onFiveBar(self, bar): """5分钟K线更新""" self.am.updateBar(bar) if not self.am.inited: self.setSignalPos(0) fastMa = self.am.sma(self.fastWindow) slowMa = self.am.sma(self.slowWindow) if fastMa > slowMa: self.setSignalPos(1) elif fastMa < slowMa: self.setSignalPos(-1) else: self.setSignalPos(0)
class MaSignal(CtaSignal): """双均线信号""" #---------------------------------------------------------------------- def __init__(self): """Constructor""" super(MaSignal, self).__init__() self.fastWindow = 5 self.slowWindow = 20 self.bg = BarGenerator(self.onBar, 5, self.onFiveBar) self.am = ArrayManager() #---------------------------------------------------------------------- def onTick(self, tick): """Tick更新""" self.bg.updateTick(tick) #---------------------------------------------------------------------- def onBar(self, bar): """K线更新""" self.bg.updateBar(bar) #---------------------------------------------------------------------- def onFiveBar(self, bar): """5分钟K线更新""" self.am.updateBar(bar) if not self.am.inited: self.setSignalPos(0) fastMa = self.am.sma(self.fastWindow) slowMa = self.am.sma(self.slowWindow) if fastMa > slowMa: self.setSignalPos(1) elif fastMa < slowMa: self.setSignalPos(-1) else: self.setSignalPos(0)
class BollSignal(CtaSignal): """布林带信号""" # ---------------------------------------------------------------------- def __init__(self): """Constructor""" super(BollSignal, self).__init__() self.bollWindow = 18 self.bollDev = 5 # self.bollUp, self.bollDown = 0.0,0.0 self.bg = BarGenerator(self.onBar, 15, self.on15Bar) self.am = ArrayManager() # ---------------------------------------------------------------------- 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) if not self.am.inited: self.setSignalPos(0) bollUp, bollDown = self.am.boll(self.bollWindow, self.bollDev) if bar.close >= bollUp: self.setSignalPos(1) elif bar.close <= bollDown: self.setSignalPos(-1) else: self.setSignalPos(0)
class MultiSignalStrategy(TargetPosTemplate): """CCI+ATR合成信号策略""" className = 'MultiSignalStrategy' author = 'Y.Raul' # 策略参数 initDays = 10 # 初始化数据所用的天数 fixedSize = 1 # 每次交易的数量 # 策略变量 signalPos = {} # 信号仓位 # 参数列表,保存了参数的名称 paramList = ['name', 'className', 'author', 'vtSymbol'] # 变量列表,保存了变量的名称 varList = ['inited', 'trading', 'pos', 'signalPos', 'targetPos'] # 同步列表,保存了需要保存到数据库的变量名称 syncList = ['pos'] #---------------------------------------------------------------------- def __init__(self, ctaEngine, setting): """Constructor""" super(MultiSignalStrategy, self).__init__(ctaEngine, setting) self.cciSignal = CciSignal() self.trailingStopSignal = TrailingStopSignal() self.atrSignal = AtrSignal() self.rsiSignal = RsiSignal() self.signalPos = {"cci": 0, "atr": 0, "rsi": 0} self.entryPriceList = [] self.avgEntryPrice = 0.0 self.bg = BarGenerator(self.onBar, 15, self.on15Bar) #---------------------------------------------------------------------- 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推送(必须由用户继承实现)""" super(MultiSignalStrategy, self).onTick(tick) self.atrSignal.onTick(tick) self.cciSignal.onTick(tick) self.rsiSignal.onTick(tick) if self.pos: self.trailingStopSignal.onTick(tick) #---------------------------------------------------------------------- def onBar(self, bar): """收到Bar推送(必须由用户继承实现)""" super(MultiSignalStrategy, self).onBar(bar) self.atrSignal.onBar(bar) self.cciSignal.onBar(bar) self.rsiSignal.onBar(bar) # 更新trailingStop中的ATR值 self.trailingStopSignal.atrValue = self.atrSignal.atrValue self.trailingStopSignal.onBar(bar) self.bg.updateBar(bar) def on15Bar(self, bar): self.signalPos['atr'] = self.atrSignal.getSignalPos() self.signalPos['cci'] = self.cciSignal.getSignalPos() self.signalPos['rsi'] = self.rsiSignal.getSignalPos() # 记录log log = "-----" * 10 + "\n@on15Bar\n" + \ "bar.datetime: {0}\n".format(bar.datetime) + \ "cci: {0}; atr: {1}; trailing: {2}\n".format(self.signalPos['cci'], self.signalPos['atr'],self.trailingStopSignal.signalPos) +\ "pos: {0}\n".format(self.pos)+\ "cciValue: {0}, atrValue: {1}, rsiValue: {2}\n".format(self.cciSignal.cciValue,self.atrSignal.atrValue, self.rsiSignal.rsiValue) self.writeCtaLog(log) self.caculatePos(bar) def caculatePos(self, bar): """ 根据信号合成计算仓位 """ # 记录log log = "-----" * 10 + "\n@caculatePos\n" + \ "bar.datetime: {0}\n".format(bar.datetime) + \ "cci: {0}; atr: {1}; trailing: {2}\n".format(self.signalPos['cci'], self.signalPos['atr'],self.trailingStopSignal.signalPos) +\ "pos: {0}\n".format(self.pos) self.writeCtaLog(log) # 开仓 if self.pos == 0 and self.signalPos['atr'] == 1: if (self.signalPos['cci'] == 1 and self.signalPos['rsi'] == 1): # 记录log log = '\n [Buy]\n' self.writeCtaLog(log) self.setTargetPos(self.fixedSize) if (self.signalPos['cci'] == -1 and self.signalPos['rsi'] == -1): # 记录log log = '\n [Short]\n' self.writeCtaLog(log) self.setTargetPos(-1 * self.fixedSize) # 平仓 if self.pos != 0 and self.trailingStopSignal.getSignalPos() == 0: # 记录log log = "-----" * 10 + "\n@trailingStop\n" + \ "bar.datetime: {0}\n".format(bar.datetime) + \ "atr: {0}; intraHigh: {1}; intraLow: {2}\n".format(self.trailingStopSignal.atrValue, self.trailingStopSignal.intraTradeHigh, self.trailingStopSignal.intraTradeLow) + \ "holdpos: {0}\n".format(self.trailingStopSignal.holdPos) +\ "longStop: {0}; shortStop: {1}\n".format(self.trailingStopSignal.longStop,self.trailingStopSignal.shortStop) +\ "close: {0}\n".format(bar.close) +\ "tailingStop: {0}\n".format(self.trailingStopSignal.getSignalPos()) self.writeCtaLog(log) if self.pos > 0: # 记录log log = '\n [Sell]\n' self.writeCtaLog(log) elif self.pos < 0: # 记录log log = '\n [Cover]\n' self.writeCtaLog(log) # 重置信号 self.atrSignal = AtrSignal() self.cciSignal = CciSignal() self.rsiSignal = RsiSignal() # 设置策略仓位 self.setTargetPos(0) #---------------------------------------------------------------------- def onOrder(self, order): """收到委托变化推送(必须由用户继承实现)""" super(MultiSignalStrategy, self).onOrder(order) if order.direction == DIRECTION_LONG and order.offset == OFFSET_OPEN: if order.totalVolume == order.tradedVolume: # 更新入场价列表,更新平均入场价 self.entryPriceList.append(order.price) self.avgEntryPrice = sum(self.entryPriceList) / len( self.entryPriceList) self.trailingStopSignal.intraTradeHigh = self.avgEntryPrice elif order.direction == DIRECTION_SHORT and order.offset == OFFSET_OPEN: if order.totalVolume == order.tradedVolume: # 更新入场价列表,更新平均入场价 self.entryPriceList.append(order.price) self.avgEntryPrice = sum(self.entryPriceList) / len( self.entryPriceList) self.trailingStopSignal.intraTradeLow = self.avgEntryPrice # 更新trailingStop中的仓位 self.trailingStopSignal.holdPos = self.pos self.putEvent() #---------------------------------------------------------------------- def onTrade(self, 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 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 MultiTimeframeStrategy(CtaTemplate): """跨时间周期交易策略""" className = 'MultiTimeframeStrategy' author = u'用Python的交易员' # 策略参数 rsiSignal = 20 # RSI信号阈值 rsiWindow = 14 # RSI窗口 fastWindow = 5 # 快速均线窗口 slowWindow = 20 # 慢速均线窗口 initDays = 10 # 初始化数据所用的天数 fixedSize = 1 # 每次交易的数量 # 策略变量 rsiValue = 0 # RSI指标的数值 rsiLong = 0 # RSI买开阈值 rsiShort = 0 # RSI卖开阈值 fastMa = 0 # 5分钟快速均线 slowMa = 0 # 5分钟慢速均线 maTrend = 0 # 均线趋势,多头1,空头-1 # 参数列表,保存了参数的名称 paramList = ['name', 'className', 'author', 'vtSymbol', 'rsiSignal', 'rsiWindow', 'fastWindow', 'slowWindow'] # 变量列表,保存了变量的名称 varList = ['inited', 'trading', 'pos', 'rsiValue', 'rsiLong', 'rsiShort', 'fastMa', 'slowMa', 'maTrend'] # 同步列表,保存了需要保存到数据库的变量名称 syncList = ['pos'] #---------------------------------------------------------------------- def __init__(self, ctaEngine, setting): """Constructor""" super(MultiTimeframeStrategy, self).__init__(ctaEngine, setting) self.rsiLong = 50 + self.rsiSignal self.rsiShort = 50 - self.rsiSignal # 创建K线合成器对象 self.bg5 = BarGenerator(self.onBar, 5, self.on5MinBar) self.am5 = ArrayManager() self.bg15 = BarGenerator(self.onBar, 15, self.on15MinBar) self.am15 = 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推送(必须由用户继承实现)""" # 只需要要在一个BarGenerator中合成1分钟K线 self.bg5.updateTick(tick) #---------------------------------------------------------------------- def onBar(self, bar): """收到Bar推送(必须由用户继承实现)""" # 基于15分钟判断趋势过滤,因此先更新 self.bg15.updateBar(bar) # 基于5分钟判断 self.bg5.updateBar(bar) #---------------------------------------------------------------------- def on5MinBar(self, bar): """5分钟K线""" self.cancelAll() # 保存K线数据 self.am5.updateBar(bar) if not self.am5.inited: return # 如果15分钟数据尚未初始化完毕,则直接返回 if not self.maTrend: return # 计算指标数值 self.rsiValue = self.am5.rsi(self.rsiWindow) # 判断是否要进行交易 # 当前无仓位 if self.pos == 0: if self.maTrend > 0 and self.rsiValue >= self.rsiLong: self.buy(bar.close+5, self.fixedSize) elif self.maTrend < 0 and self.rsiValue <= self.rsiShort: self.short(bar.close-5, self.fixedSize) # 持有多头仓位 elif self.pos > 0: if self.maTrend < 0 or self.rsiValue < 50: self.sell(bar.close-5, abs(self.pos)) # 持有空头仓位 elif self.pos < 0: if self.maTrend > 0 or self.rsiValue > 50: self.cover(bar.close+5, abs(self.pos)) # 发出状态更新事件 self.putEvent() #---------------------------------------------------------------------- def on15MinBar(self, bar): """15分钟K线推送""" self.am15.updateBar(bar) if not self.am15.inited: return # 计算均线并判断趋势 self.fastMa = self.am15.sma(self.fastWindow) self.slowMa = self.am15.sma(self.slowWindow) if self.fastMa > self.slowMa: self.maTrend = 1 else: self.maTrend = -1 #---------------------------------------------------------------------- def onOrder(self, order): """收到委托变化推送(必须由用户继承实现)""" pass #---------------------------------------------------------------------- def onTrade(self, trade): # 发出状态更新事件 self.putEvent() #---------------------------------------------------------------------- def onStopOrder(self, so): """停止单推送""" pass
class BollChannelStrategy(CtaTemplate): """基于布林通道的交易策略""" className = 'BollChannelStrategy' author = u'用Python的交易员' # 策略参数 bollWindow = 18 # 布林通道窗口数 bollDev = 3.4 # 布林通道的偏差 cciWindow = 10 # CCI窗口数 atrWindow = 30 # ATR窗口数 slMultiplier = 5.2 # 计算止损距离的乘数 initDays = 10 # 初始化数据所用的天数 fixedSize = 1 # 每次交易的数量 # 策略变量 bollUp = 0 # 布林通道上轨 bollDown = 0 # 布林通道下轨 cciValue = 0 # CCI指标数值 atrValue = 0 # ATR指标数值 intraTradeHigh = 0 # 持仓期内的最高点 intraTradeLow = 0 # 持仓期内的最低点 longStop = 0 # 多头止损 shortStop = 0 # 空头止损 # 参数列表,保存了参数的名称 paramList = ['name', 'className', 'author', 'vtSymbol', 'bollWindow', 'bollDev', 'cciWindow', 'atrWindow', 'slMultiplier', 'initDays', 'fixedSize'] # 变量列表,保存了变量的名称 varList = ['inited', 'trading', 'pos', 'bollUp', 'bollDown', 'cciValue', 'atrValue', 'intraTradeHigh', 'intraTradeLow', 'longStop', 'shortStop'] # 同步列表,保存了需要保存到数据库的变量名称 syncList = ['pos', 'intraTradeHigh', 'intraTradeLow'] #---------------------------------------------------------------------- def __init__(self, ctaEngine, setting): """Constructor""" super(BollChannelStrategy, self).__init__(ctaEngine, setting) self.bg = BarGenerator(self.onBar, 15, self.onXminBar) # 创建K线合成器对象 self.bg30 = BarGenerator(self.onBar, 30, self.on30minBar) self.am = ArrayManager() #---------------------------------------------------------------------- def on30minBar(self, bar): """""" #---------------------------------------------------------------------- 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.bg.updateBar(bar) #---------------------------------------------------------------------- def onXminBar(self, bar): """收到X分钟K线""" # 全撤之前发出的委托 self.cancelAll() # 保存K线数据 am = self.am am.updateBar(bar) if not am.inited: return # 计算指标数值 self.bollUp, self.bollDown = am.boll(self.bollWindow, self.bollDev) self.cciValue = am.cci(self.cciWindow) self.atrValue = am.atr(self.atrWindow) # 判断是否要进行交易 # 当前无仓位,发送开仓委托 if self.pos == 0: self.intraTradeHigh = bar.high self.intraTradeLow = bar.low if self.cciValue > 0: self.buy(self.bollUp, self.fixedSize, True) elif self.cciValue < 0: self.short(self.bollDown, self.fixedSize, True) # 持有多头仓位 elif self.pos > 0: self.intraTradeHigh = max(self.intraTradeHigh, bar.high) self.intraTradeLow = bar.low self.longStop = self.intraTradeHigh - self.atrValue * self.slMultiplier self.sell(self.longStop, abs(self.pos), True) # 持有空头仓位 elif self.pos < 0: self.intraTradeHigh = bar.high self.intraTradeLow = min(self.intraTradeLow, bar.low) self.shortStop = self.intraTradeLow + self.atrValue * self.slMultiplier self.cover(self.shortStop, abs(self.pos), True) # 同步数据到数据库 self.saveSyncData() # 发出状态更新事件 self.putEvent() #---------------------------------------------------------------------- def onOrder(self, order): """收到委托变化推送(必须由用户继承实现)""" pass #---------------------------------------------------------------------- def onTrade(self, trade): # 发出状态更新事件 self.putEvent() #---------------------------------------------------------------------- def onStopOrder(self, so): """停止单推送""" pass
class KkStrategy(CtaTemplate): """基于King Keltner通道的交易策略""" className = 'KkStrategy' author = u'用Python的交易员' # 策略参数 kkLength = 11 # 计算通道中值的窗口数 kkDev = 1.6 # 计算通道宽度的偏差 trailingPrcnt = 0.8 # 移动止损 initDays = 10 # 初始化数据所用的天数 fixedSize = 1 # 每次交易的数量 # 策略变量 kkUp = 0 # KK通道上轨 kkDown = 0 # KK通道下轨 intraTradeHigh = 0 # 持仓期内的最高点 intraTradeLow = 0 # 持仓期内的最低点 buyOrderIDList = [] # OCO委托买入开仓的委托号 shortOrderIDList = [] # OCO委托卖出开仓的委托号 orderList = [] # 保存委托代码的列表 # 参数列表,保存了参数的名称 paramList = ['name', 'className', 'author', 'vtSymbol', 'kkLength', 'kkDev'] # 变量列表,保存了变量的名称 varList = ['inited', 'trading', 'pos', 'kkUp', 'kkDown'] # 同步列表,保存了需要保存到数据库的变量名称 syncList = ['pos', 'intraTradeHigh', 'intraTradeLow'] #---------------------------------------------------------------------- def __init__(self, ctaEngine, setting): """Constructor""" super(KkStrategy, self).__init__(ctaEngine, setting) self.bg = BarGenerator(self.onBar, 5, self.onFiveBar) # 创建K线合成器对象 self.am = ArrayManager() self.buyOrderIDList = [] self.shortOrderIDList = [] self.orderList = [] #---------------------------------------------------------------------- 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.bg.updateBar(bar) #---------------------------------------------------------------------- def onFiveBar(self, bar): """收到5分钟K线""" # 撤销之前发出的尚未成交的委托(包括限价单和停止单) for orderID in self.orderList: self.cancelOrder(orderID) self.orderList = [] # 保存K线数据 am = self.am am.updateBar(bar) if not am.inited: return # 计算指标数值 self.kkUp, self.kkDown = am.keltner(self.kkLength, self.kkDev) # 判断是否要进行交易 # 当前无仓位,发送OCO开仓委托 if self.pos == 0: self.intraTradeHigh = bar.high self.intraTradeLow = bar.low self.sendOcoOrder(self.kkUp, self.kkDown, self.fixedSize) # 持有多头仓位 elif self.pos > 0: self.intraTradeHigh = max(self.intraTradeHigh, bar.high) self.intraTradeLow = bar.low l = self.sell(self.intraTradeHigh*(1-self.trailingPrcnt/100), abs(self.pos), True) self.orderList.extend(l) # 持有空头仓位 elif self.pos < 0: self.intraTradeHigh = bar.high self.intraTradeLow = min(self.intraTradeLow, bar.low) l = self.cover(self.intraTradeLow*(1+self.trailingPrcnt/100), abs(self.pos), True) self.orderList.extend(l) # 同步数据到数据库 self.saveSyncData() # 发出状态更新事件 self.putEvent() #---------------------------------------------------------------------- def onOrder(self, order): """收到委托变化推送(必须由用户继承实现)""" pass #---------------------------------------------------------------------- def onTrade(self, trade): if self.pos != 0: # 多头开仓成交后,撤消空头委托 if self.pos > 0: for shortOrderID in self.shortOrderIDList: self.cancelOrder(shortOrderID) # 反之同样 elif self.pos < 0: for buyOrderID in self.buyOrderIDList: self.cancelOrder(buyOrderID) # 移除委托号 for orderID in (self.buyOrderIDList + self.shortOrderIDList): if orderID in self.orderList: self.orderList.remove(orderID) # 发出状态更新事件 self.putEvent() #---------------------------------------------------------------------- def sendOcoOrder(self, buyPrice, shortPrice, volume): """ 发送OCO委托 OCO(One Cancel Other)委托: 1. 主要用于实现区间突破入场 2. 包含两个方向相反的停止单 3. 一个方向的停止单成交后会立即撤消另一个方向的 """ # 发送双边的停止单委托,并记录委托号 self.buyOrderIDList = self.buy(buyPrice, volume, True) self.shortOrderIDList = self.short(shortPrice, volume, True) # 将委托号记录到列表中 self.orderList.extend(self.buyOrderIDList) self.orderList.extend(self.shortOrderIDList) #---------------------------------------------------------------------- def onStopOrder(self, so): """停止单推送""" pass
class tempStrategy(CtaTemplate): """双指数均线策略Demo""" className = 'DoubleMaStrategy' author = u'用Python的交易员' # 策略参数 fastWindow = 12 # 快速均线参数 slowWindow = 26 # 慢速均线参数 initDays = 0 # 初始化数据所用的天数 # 策略变量 fastMa0 = EMPTY_FLOAT # 当前最新的快速EMA fastMa1 = EMPTY_FLOAT # 上一根的快速EMA slowMa0 = EMPTY_FLOAT slowMa1 = EMPTY_FLOAT exitTime = time(hour=15, minute=0) # 参数列表,保存了参数的名称 paramList = [ 'name', 'className', 'author', 'vtSymbol', 'fastWindow', 'slowWindow' ] # 变量列表,保存了变量的名称 varList = [ 'inited', 'trading', 'pos', 'fastMa0', 'fastMa1', 'slowMa0', 'slowMa1' ] # 同步列表,保存了需要保存到数据库的变量名称 syncList = ['pos'] #---------------------------------------------------------------------- def __init__(self, ctaEngine, setting, tickadd=5): """Constructor""" super(tempStrategy, self).__init__(ctaEngine, setting) self.bg = BarGenerator(self.onBar) self.am = zhibiao() self.tickadd = tickadd #策略相关 self.celve0 = zerocelve(self.tickadd) self.celve1 = lianmianKongcelve(self.tickadd) self.celve2 = lianmianduocelve(self.tickadd) self.celve3 = newzerocelve(self.tickadd, self.cancelAll) self.tickCelvezu = [ #self.celve0, # self.celve1 , self.celve3 ] self.barCelvezu = [ #self.celve0 , # ,self.celve2 self.celve3 ] self.tradingcelve = [ #self.celve0 , #,self.celve2 self.celve3 ] for ce in self.tradingcelve: ce.am = self.am #策略参数 #断网恢复变量 self.stopcount = None #交易时间和监控联网状态变量 self.yepan = True self.yepanhour = 23 self.yepanminute = 00 self.lastbardatetime = None #交易变量相关 self.tradetime = None #控制开仓和平仓稳定变量 self.tradecount = 0 # 注意策略类中的可变对象属性(通常是list和dict等),在策略初始化时需要重新创建, # 否则会出现多个策略实例之间数据共享的情况,有可能导致潜在的策略逻辑错误风险, # 策略类中的这些可变对象属性可以选择不写,全都放在__init__下面,写主要是为了阅读 # 策略时方便(更多是个编程习惯的选择) #---------------------------------------------------------------------- def onInit(self): """初始化策略(必须由用户继承实现)""" self.writeCtaLog(u'双EMA演示策略初始化') initData = self.loadBar(self.initDays) for bar in initData: pass # self.onBar(bar) self.am.inited = True self.putEvent() #---------------------------------------------------------------------- def onStart(self): """启动策略(必须由用户继承实现)""" self.writeCtaLog(u'双EMA演示策略启动') self.putEvent() #---------------------------------------------------------------------- def closeAllPosistion(self, price): self.cancelAll() print('--closeallpos--') if self.pos > 0: self.short(price - self.tickadd, abs(self.pos)) elif self.pos < 0: self.cover(price + self.tickadd, abs(self.pos)) # ---------------------------------------------------------------------- def datetimePlusMinute(self, datelatime, minute): newdatetime = datetime.now() if datelatime.minute + minute < 60: newdatetime.minute = datelatime.minute + minute else: newdatetime.hour = datelatime.hour + 1 newdatetime.minute = datelatime.minute + minute - 60 return newdatetime # ---------------------------------------------------------------------- def iscontinueTime(self, firstdatetime, seconddatetime): if (firstdatetime.hour == seconddatetime.hour and firstdatetime.minute + 1 == seconddatetime.minute) \ or ( firstdatetime.hour == seconddatetime.hour - 1 and firstdatetime.minute == 59 and seconddatetime.minute == 0): return True # ---------------------------------------------------------------------- def isTradeContinueTime(self, firstdatetime, seconddatetime): if self.iscontinueTime(firstdatetime, seconddatetime): return True elif firstdatetime.hour == 10 and ( firstdatetime.minute == 15 or firstdatetime.minute == 14 ) and seconddatetime.hour == 10 and seconddatetime.minute == 30: return True elif firstdatetime.hour == 11 and ( firstdatetime.minute == 29 or firstdatetime.minute == 30 ) and seconddatetime.hour == 13 and seconddatetime.minute == 30: return True elif self.yepan and (seconddatetime.hour == 9 or (seconddatetime.hour == 8 and seconddatetime.minute == 59)) and ( firstdatetime.hour == self.yepanhour or (firstdatetime.hour == self.yepanhour - 1 and firstdatetime.minute == 59)): return True elif (firstdatetime.hour == 15 or (firstdatetime.hour == 14 and firstdatetime.minute == 59)) and ( (seconddatetime.hour == 9 and seconddatetime.minute == 0) or (seconddatetime.hour == 8 and seconddatetime.minute == 59)): return True elif ((firstdatetime.hour == 14 and firstdatetime.minute == 59) or firstdatetime.hour == 15) and ( seconddatetime.hour == 21 or (seconddatetime.hour == 20 and seconddatetime.minute == 59)): return True else: print('dus conne', firstdatetime, seconddatetime) return False # ---------------------------------------------------------------------- def tickcelve(self, zhibiao, price, tick): for celve in self.tickCelvezu: xinhao = celve.celveOntick(zhibiao) if xinhao == 100: print(price, 'kaicangduo', tick.datetime) if xinhao == 50: print(price, 'pingcang', tick.datetime) if xinhao == 200: print(price, 'kongcang', tick.datetime) if xinhao == 250: print(price, 'pingkongcang', tick.datetime) self.chulikaipingcang(xinhao, price) # ---------------------------------------------------------------------- def barcelve(self, zhibiao, price): for celve in self.barCelvezu: xinhao = celve.celveOnbar(zhibiao) if xinhao == 100: print('kaicangduo,bar') if xinhao == 50: print('pingcang,bar') self.chulikaipingcang(xinhao, price) # ---------------------------------------------------------------------- def chulikaipingcang(self, celve, price): # if celve == 100: # self.buy(price,1) print('chuli', celve, self.celve3.cangwei) selfpos = 0 if celve != 0 and celve is not None: print('nowposis', self.pos, 'celveis', celve, 'andpriceis', price, self.am.datetime) if self.pos == 0 and celve == 100: if self.pos == 0: # self.weituopos = 1 self.buy(price + 1, 1) # 如果有空头持仓,则先平空,再做多 elif self.pos < selfpos: # self.weituopos = 1 self.cover(price, 1) self.buy(price, 1) elif self.pos == 0 and celve == paiduikong: self.short(price, 1) elif self.pos == -1 and celve == paiduikongping: self.cover(price, 1) elif self.pos == 1 and celve == 50: if self.pos > selfpos: # self.weituopos = 0 self.sell(price - 1, 1) elif self.pos == 0 and celve == 200: if self.pos == selfpos: # self.weituopos = -1 self.short(price - 1, 1) elif self.pos > selfpos: self.sell(price, 1) self.short(price, 1) # self.weituopos = -1 elif self.pos == 0 and celve == paiduiduo: self.buy(price, 1) elif self.pos > 0 and celve == paiduiduoping: self.sell(price, 1) elif self.pos == -1 and celve == 250: # self.weituopos += 1 self.cover(price + 1, 1) # ---------------------------------------------------------------------- def onStop(self): """停止策略(必须由用户继承实现)""" self.writeCtaLog(u'双EMA演示策略停止') self.putEvent() #---------------------------------------------------------------------- def onTick(self, tick): """收到行情TICK推送(必须由用户继承实现)""" self.bg.updateTick(tick) self.am.updateTick(tick) if not self.stopcount: if not self.tradecount: self.tickcelve(self.am, tick.lastPrice, tick) elif tick.datetime.second > 55: self.tickcelve(self.am, tick.lastPrice, tick) #---------------------------------------------------------------------- def onBar(self, bar): """收到Bar推送(必须由用户继承实现)""" print('begin') #过滤掉非交易时间的bar ntt = False if self.celve3.cancelorder is None and ntt: print('fuzhid') self.celve3.cancelorder = self.cancelOrder(self) else: ntt = True if self.notintradingTime(bar): return #数据合成和计算相关 am = self.am am.updateBar(bar) self.bg.updateBar(bar) #处理是否断网 self.checkIfConnecting(bar) # 处理bar上的刚开仓 self.handleTradeCount() #检查是否断网所需的上一根bar时间 self.lastbardatetime = bar.datetime # if not am.inited: # print('retr') # return # 计算快慢均线 #bar策略相关 for ce in self.tradingcelve: ce.nowtime = bar.datetime if not self.stopcount: self.barcelve(am, bar.close) # 金叉和死叉的条件是互斥 # 所有的委托均以K线收盘价委托(这里有一个实盘中无法成交的风险,考虑添加对模拟市价单类型的支持) am.endBar() # 发出状态更新事件 self.putEvent() #---------------------------------------------------------------------- def handleTradeCount(self): if self.tradecount > 0: self.tradecount -= 1 def onOrder(self, order): """收到委托变化推送(必须由用户继承实现)""" # 对于无需做细粒度委托控制的策略,可以忽略onOrder self.celve3.order = order.vtOrderID print 'order', order.price, order.direction, order.offset, order.vtOrderID pass #---------------------------------------------------------------------- def onTrade(self, trade): """收到成交推送(必须由用户继承实现)""" # 对于无需做细粒度委托控制的策略,可以忽略onOrder\ if trade.direction == u'多' and trade.offset == u'开仓': self.tradecount = 5 # print('kaiduo') if trade.direction == u'空' and trade.offset == u'开仓': self.tradecount = 5 # print('kaikong') if trade.direction == u'多' and trade.offset != u'开仓': self.cancelAll() print('slls', self.cancelAll() is None) self.celve2.cangwei = 0 # print('pingkong') if trade.direction == u'空' and trade.offset != u'开仓': self.cancelAll() self.celve2.cangwei = 0 #self.celve0.cangwei -= trade.volume # print('pingduo') print 'trade', trade.price, trade.direction, trade.offset, trade.tradeTime #这里有待修正 if trade.offset == u'开仓': for ce in self.tickCelvezu: print('set tradeprice') if ce.cangwei != 0: ce.tradePrice = trade.price if trade.offset != u'开仓': for ce in self.tickCelvezu: if ce.cangwei == 0: ce.tradePrice = None #---------------------------------------------------------------------- def onStopOrder(self, so): """停止单推送""" pass def handleDisConnected(self, price): print('DISCONNECTED', self.lastbardatetime, self.am.datetime) self.closeAllPosistion(price) self.stopcount = 15 def notintradingTime(self, bar): if bar.datetime.hour == 15 and bar.datetime.minute > 0: return True def closingTime(self, datetime): if datetime.hour == 14 and datetime.minute == 59: return True def checkIfConnecting(self, bar): if self.lastbardatetime is None: self.lastbardatetime = bar.datetime else: if not self.isTradeContinueTime(self.lastbardatetime, bar.datetime): #断网了,需要处理断网状态 self.handleDisConnected(bar.close) #没有断网 else: if self.stopcount > 0: self.stopcount -= 1
class TestLogOrderStrategy(CtaTemplate): """DualThrust交易策略""" className = 'TestLogOrderStrategy' author = u'Leon Zhao' # 策略参数 flipcoil = 0 timeinterval = 0 hasorder = False countdown = 0 # 策略变量 barList = [] # K线对象的列表 # 参数列表,保存了参数的名称 paramList = ['name', 'className', 'author', 'vtSymbol' ] # 变量列表,保存了变量的名称 varList = ['inited', 'trading', 'pos' ] # 同步列表,保存了需要保存到数据库的变量名称 syncList = ['pos'] #---------------------------------------------------------------------- def __init__(self, ctaEngine, setting): """Constructor""" super(TestLogOrderStrategy, self).__init__(ctaEngine, setting) self.bg = BarGenerator(self.onBar) self.am = ArrayManager() self.barList = [] self.flipcoil = 0 self.timeinterval = 3 self.hasorder = False self.countdown = 0 self.orderentred = False #---------------------------------------------------------------------- def onInit(self): """初始化策略(必须由用户继承实现)""" self.writeCtaLog(u'%s策略初始化' %self.name) # 载入历史数据,并采用回放计算的方式初始化策略数值 initData = self.loadBar(10) for bar in initData: self.onBar(bar) self.countdown = 0 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 onBar(self, bar): """收到Bar推送(必须由用户继承实现)""" # 撤销之前发出的尚未成交的委托(包括限价单和停止单) if (bar.datetime.hour < 14 or (bar.datetime.hour == 14 and bar.datetime.minute < 40)): if self.countdown > 0: self.countdown = self.countdown - 1 print(self.countdown) return self.cancelAll() print(self.countdown) self.bg.updateBar(bar) # 计算指标数值 self.barList.append(bar) if len(self.barList) <= 2: return else: self.barList.pop(0) lastBar = self.barList[-2] self.flipcoil = random.random() orderqty = [2,2,2] if self.pos == 0: if self.flipcoil > 0.5: self.buy(bar.close,random.choice(orderqty)) self.countdown = self.timeinterval else: self.short(bar.close,random.choice(orderqty)) self.countdown = self.timeinterval elif self.pos > 0: if self.flipcoil < 0.5: self.sell(bar.close,self.pos) self.countdown = self.timeinterval self.short(bar.close,2) else: if self.flipcoil > 0.5: self.cover(bar.close,abs(self.pos)) self.countdown = self.timeinterval self.buy(bar.close,2) print(self.countdown) # 发出状态更新事件 self.putEvent() #---------------------------------------------------------------------- def onOrder(self, order): """收到委托变化推送(必须由用户继承实现)""" pass #---------------------------------------------------------------------- def onTrade(self, trade): # 发出状态更新事件 #logtext = logtext + ","+trade.date #logtext = logtext + "," +trade.datetime logtext = "" logtext = logtext + "," + trade.offset logtext = logtext + "," + trade.direction logtext = logtext + "," + str(trade.volume) logtext = logtext + "," + str(trade.price) self.writeCtaLog(logtext) persisttrade(self.vtSymbol,self.className ,trade) self.putEvent() #---------------------------------------------------------------------- def onStopOrder(self, so): """停止单推送""" pass
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 GridStrategy(CtaTemplate): className = 'GridStrategy' author = u'BillyZhang' # 策略参数 historyBars = 200 # 历史数据大小,用来确定网格基准线 initDays = 20 # 初始化数据所用的天数,随着历史数据大小要改变 gridlines = 10 # 网格线数量,单边数量 ordersize = 10 # 最大持仓数量 order = 1 # 每次下单手数 barMins = 30 #bar的时间 frozenBars = 1 #平仓后,frozenBars个bar不再开反向单 atrWindow = 30 # ATR窗口数 slMultiplier = 5.0 # 计算止损距离的乘数 # 基本变量 upline = 0 #当前上线 bottomline = 0 #当前下线 frozen = 0 #当前是否冻结开反向单 intraTradeHigh = 0 intraTradeLow = 0 atrValue = 0 # 参数列表,保存了参数的名称 paramList = [ 'name', 'className', 'author', 'vtSymbol', 'historyBars', 'initDays', 'gridlines', 'barMins', 'order', 'ordersize', 'atrWindow', 'slMultiplier' ] # 变量列表,保存了变量的名称 varList = [ 'inited', 'trading', 'pos', 'frozen', 'upline', 'bottomline' 'atrValue' ] # 同步列表,保存了需要保存到数据库的变量名称 syncList = ['pos', 'frozen'] # ---------------------------------------------------------------------- def __init__(self, ctaEngine, setting): """Constructor""" super(GridStrategy, self).__init__(ctaEngine, setting) self.bg = BarGenerator(self.onBar, self.barMins, self.onXminBar) # 创建K线合成器对象 self.am = ArrayManager(self.historyBars + 50) # ---------------------------------------------------------------------- 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 onXminBar(self, bar): """收到X分钟K线""" # 全撤之前发出的委托 self.cancelAll() # 保存K线数据 am = self.am am.updateBar(bar) if not am.inited: return # 这里采用了均量交易法,就是每笔。 # 空仓时候,每次突破上线是开多单,突破下线是开空单; # 有多单时候,突破上线加多单,突破下线情况清空所有多单, # 有空单时候,突破下线加空单,突破上线清空所有空单, # 为防止在一个线上下波动,造成重复开平仓情况,如果突破平仓,比如平多单,后面n个bar不能再开多单,只能开空单;反之平空单后, # 后面n个bar只能开多单。 # 计算网格,返回通道队列, 再算出当前点位所在通道,0为最下通道,2*self.gridlines - 1为最上通道 baseline = self.am.sma(self.historyBars) # 过去300的标准差,按照顶一个gridlines取整做出一个队列 intervallist = baseline + np.array([ n * 1.00 / self.gridlines for n in range(-1 * self.gridlines, self.gridlines + 1) ]) * self.am.std(self.historyBars) griploc = pd.cut([bar.close], intervallist, labels=[nx for nx in range(0, 2 * self.gridlines)])[0] # 如果返回为nan,说明现在bar.close在标准差范围以外,如果没有仓位,先不处理;如果有,按照ATR波动移动止盈 if isnan(griploc): # 持有多头仓位 if self.pos > 0: self.intraTradeHigh = max(self.intraTradeHigh, bar.high) self.intraTradeLow = bar.low self.longStop = self.intraTradeHigh - self.atrValue * self.slMultiplier self.sell(self.longStop, abs(self.pos), True) # 持有空头仓位 elif self.pos < 0: self.intraTradeHigh = bar.high self.intraTradeLow = min(self.intraTradeLow, bar.low) self.shortStop = self.intraTradeLow + self.atrValue * self.slMultiplier self.cover(self.shortStop, abs(self.pos), True) return #返回上下线: self.upline = intervallist[griploc + 1] self.bottomline = intervallist[griploc] # 空仓时候,每次突破上线是开多单,突破下线是开空单; # 如果此时在最下一个通道,此时只挂往上的多单, 如果在最上面通道,此时只挂往下空单;如果在中间的,则同时开上下单 if self.pos == 0: if griploc == 0: self.buy(self.upline, self.order, True) elif griploc == 2 * self.gridlines - 1: self.short(self.bottomline, self.order, True) else: #此时如果frozen 为0, 直接开上下单: if self.frozen == 0: self.buy(self.upline, self.order, True) self.short(self.bottomline, self.order, True) #此时如果大于0,只能开空单,如果小于0,只能开多单 elif self.frozen > 0: self.frozen = self.frozen - 1 self.short(self.bottomline, self.order, True) elif self.frozen < 0: self.frozen = self.frozen + 1 self.buy(self.upline, self.order, True) #如果持有多仓时候,如果在中间通道,同时开上下单;如果最高点位不再开单,突破最大标准差高点, elif self.pos > 0: # 在最下通道不可能有多单,只用考量在中间段,pos 小于ordersize可以增多仓,否则只能向下平仓;和最高段情况,最高段设置往下平仓, if griploc == 2 * self.gridlines - 1: self.intraTradeHigh = bar.high self.sell(self.bottomline, abs(self.pos), True) else: if abs(self.pos) < self.ordersize: self.buy(self.upline, self.order, True) self.sell(self.bottomline, abs(self.pos), True) else: self.sell(self.bottomline, abs(self.pos), True) elif self.pos < 0: # 最上通道通道不可能有空单,只用考虑中间段,和最低档情况 if griploc == 0: self.intraTradeLow = bar.low self.cover(self.upline, abs(self.pos), True) else: if abs(self.pos) < self.ordersize: self.cover(self.upline, abs(self.pos), True) self.sell(self.bottomline, self.order, True) else: self.cover(self.upline, abs(self.pos), True) # ---------------------------------------------------------------------- def onTick(self, tick): """收到行情TICK推送(必须由用户继承实现)""" self.bg.updateTick(tick) # ---------------------------------------------------------------------- def onBar(self, bar): """收到Bar推送(必须由用户继承实现)""" self.bg.updateBar(bar) # ---------------------------------------------------------------------- def onOrder(self, order): """收到委托推送""" pass # ---------------------------------------------------------------------- def onTrade(self, trade): # 发出状态更新事件 # 如果收到成交,清空所有挂单 self.cancelAll() # 如果交易多头方向,且现在仓位为0,则应该是空头平仓,不再开空单 if trade.direction == DIRECTION_LONG and self.pos == 0: self.frozen = -1 * self.frozen # 如果交易空头方向,且现在仓位为0,则应该是多平仓,不再开多单 elif trade.direction == DIRECTION_SHORT and self.pos == 0: self.frozen = self.frozen self.putEvent() # ---------------------------------------------------------------------- def onStopOrder(self, so): """停止单推送""" pass
class BollChannelStrategy01(CtaTemplate): """基于布林通道的交易策略""" className = 'BollChannelStrategy01' author = 'Y.Raul' # 策略参数 bollWindow = 18 # 布林通道窗口数 bollDev = 5 # 布林通道的偏差 cciWindow = 10 # CCI窗口数 atrWindow = 30 # ATR窗口数 slMultiplier = 5 # 计算止损距离的乘数 initDays = 10 # 初始化数据所用的天数 fixedSize = 1 # 每次交易的数量 # 策略变量 bollUp = 0 # 布林通道上轨 bollDown = 0 # 布林通道下轨 cciValue = 0 # CCI指标数值 atrValue = 0 # ATR指标数值 filterTime = True #是否过滤9点开盘后头五分钟,15点收盘前五分钟 intraTradeHigh = 0 # 持仓期内的最高点 intraTradeLow = 0 # 持仓期内的最低点 longStop = 0 # 多头止损 shortStop = 0 # 空头止损 avgEntryPrice = 0 #平均入场价 avgExitPrice = 0 #平均出场价 buySig = False shortSig = False exitOnLossStop = 2 miniDiff = 1 # 参数列表,保存了参数的名称 paramList = [ 'name', 'className', 'author', 'vtSymbol', 'bollWindow', 'bollDev', 'cciWindow', 'atrWindow', 'slMultiplier', 'initDays', 'fixedSize' ] # 变量列表,保存了变量的名称 varList = [ 'inited', 'trading', 'pos', 'bollUp', 'bollDown', 'cciValue', 'atrValue', 'intraTradeHigh', 'intraTradeLow', 'longStop', 'shortStop' ] # 同步列表,保存了需要保存到数据库的变量名称 syncList = ['pos', 'intraTradeHigh', 'intraTradeLow'] #---------------------------------------------------------------------- def __init__(self, ctaEngine, setting): """Constructor""" super(BollChannelStrategy01, self).__init__(ctaEngine, setting) self.bm = BarGenerator(self.onBar, 15, self.on5minBar) # 创建K线合成器对象 self.am = ArrayManager() self.entryPriceList = [] self.orderList = [] #---------------------------------------------------------------------- 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): self.bm.updateBar(bar) if not self.trading: # 记录log log = "-----" * 10 + "\n@onBar\n" + \ "trading: {0}\n".format(self.trading)+\ "bar.datetime: {0}; pos: {1} \n".format(bar.datetime, self.pos) + \ "buySig: {0}; shortSig: {1}\n".format(self.buySig, self.shortSig) self.writeCtaLog(log) print log self.buySig = False self.shortSig = False return # 检查开仓信号 if self.buySig: res = self.buy(self.bollUp, self.fixedSize) self.orderList.extend(res) # 记录log log = "-----" * 10 + "\n@onBar\n" + \ "bar.datetime: {0}; pos: {1} \n".format(bar.datetime, self.pos) + \ "buySig: {0}; shortSig: {1}\n".format(self.buySig, self.shortSig) self.writeCtaLog(log) log = "\n Trading: {0}\n".format(self.trading) + \ "{0} Buy : bar.close: {1};\n".format(bar.datetime, bar.close) + \ " entryUp:{0}; cci:{1};\n".format(self.bollUp, self.cciValue) self.writeCtaLog(log) self.buySig = False print log if self.shortSig: res = self.short(self.bollDown, self.fixedSize) self.orderList.extend(res) # 记录log log = "-----" * 10 + "\n@onBar\n" + \ "bar.datetime: {0}; pos: {1} \n".format(bar.datetime, self.pos) + \ "buySig: {0}; shortSig: {1}\n".format(self.buySig, self.shortSig) self.writeCtaLog(log) log = "\n Trading: {0}\n".format(self.trading) + \ "{0} Short : bar.close: {1};\n".format(bar.datetime, bar.close) + \ " entryDown:{0}; cci:{1};\n".format(self.bollDown, self.cciValue) self.writeCtaLog(log) self.shortSig = False print log self.putEvent() #---------------------------------------------------------------------- def on5minBar(self, bar): """收到X分钟K线""" # 全撤之前发出的委托 self.cancelAll() # 保存K线数据 am = self.am am.updateBar(bar) if not am.inited: return # 计算指标数值 self.bollUp, self.bollDown = am.boll(self.bollWindow, self.bollDev) self.cciValue = am.cci(self.cciWindow) atrArray = am.atr(self.atrWindow, array=True) self.atrValue = atrArray[-1] self.atrMa = atrArray[-self.atrWindow:].mean() # 判断是否要进行交易 # 当前无仓位,发送开仓委托,限价单 if self.pos == 0: self.intraTradeHigh = bar.high self.intraTradeLow = bar.low self.entryPriceList = [] self.orderList = [] self.avgEntryPrice = [] import datetime timeWindow = True if self.filterTime: timeWindow = bar.datetime.time() > datetime.time( 9) and bar.datetime.time() < datetime.time(14, 55) if timeWindow: if self.cciValue > 0 and self.atrValue > self.atrMa: # if self.cciValue > 0 : self.buySig = True elif self.cciValue < 0 and self.atrValue < self.atrMa: # elif self.cciValue < 0: self.shortSig = True # 记录log log = "-----" * 10 + "\n@on5minBar\n" + \ "timeWidow: {0}\n".format(timeWindow) +\ "bar.datetime: {0}; pos: {1} ; close: {2}\n".format(bar.datetime, self.pos, bar.close) + \ "buySig: {0}; shortSig: {1}\n".format(self.buySig, self.shortSig) + \ "intraTradeHigh: {0}\n".format(self.intraTradeHigh) + \ "intraTradeLow: {0}\n".format(self.intraTradeLow) self.writeCtaLog(log) print log # 当前有仓位,以本地停止单止损 # 持有多头仓位 elif 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.stopExit: # 固定止损 # self.sell(bar.close, abs(self.pos), True) # else: # 跟随止损 # self.sell(self.longStop, abs(self.pos), True) self.sell(self.longStop, abs(self.pos), True) # 记录log log = "-----" * 10 + "\n@on5minBar\n" + \ "bar.datetime: {0}; pos: {1} ; close: {2}\n".format(bar.datetime, self.pos, bar.close) + \ "intraTradeHigh: {0}\n".format(self.intraTradeHigh) + \ "intraTradeLow: {0}\n".format(self.intraTradeLow) "avgEntryPrice: {0}\n".format(self.avgEntryPrice) + \ "longStop: {0}\n".format(self.longStop) + \ "stopExit:{0}\n".format(self.stopExit) self.writeCtaLog(log) print log # 持有空头仓位 elif 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.stopExit: # 固定止损 # self.cover(bar.close, abs(self.pos), True) # else: # 跟随止损 # self.cover(self.shortStop, abs(self.pos), True) self.cover(self.shortStop, abs(self.pos), True) # 记录log log = "-----" * 10 + "\n@on5minBar\n" + \ "bar.datetime: {0}; pos: {1} ; close: {2}\n".format(bar.datetime, self.pos, bar.close) + \ "intraTradeHigh: {0}\n".format(self.intraTradeHigh) + \ "intraTradeLow: {0}\n".format(self.intraTradeLow) +\ "avgEntryPrice: {0}\n".format(self.avgEntryPrice) + \ "shortStop: {0}\n".format(self.longStop) + \ "stopExit:{0}\n".format(self.stopExit) self.writeCtaLog(log) print log # 同步数据到数据库 self.saveSyncData() # 发出状态更新事件 self.putEvent() #---------------------------------------------------------------------- def onOrder(self, order): ''' 处理order更新 :param order: :return: ''' # 记录log log = "-----" * 10 + "\n@onOrder\n" + \ "orderTime: {0}; pos: {1} \n".format(order.orderTime, order.totalVolume) + \ u"status {0}; vtOrderID: {1}\n".format(order.status, order.vtOrderID)+ \ u"direction:{0}\n".format(order.direction) self.writeCtaLog(log) print log # 对于开仓,记录相关价格 if order.direction == DIRECTION_LONG and order.offset == OFFSET_OPEN: if order.totalVolume == order.tradedVolume: # 更新入场价列表,更新平均入场价 self.entryPriceList.append(order.price) self.avgEntryPrice = sum(self.entryPriceList) / len( self.entryPriceList) self.stopExit = round(self.avgEntryPrice * (100 - self.exitOnLossStop) / 100) # 固定止损价 elif order.direction == DIRECTION_SHORT and order.offset == OFFSET_OPEN: # 更新入场价列表,更新平均入场价 if order.totalVolume == order.tradedVolume: # 更新入场价列表,更新平均入场价 self.entryPriceList.append(order.price) self.avgEntryPrice = sum(self.entryPriceList) / len( self.entryPriceList) self.stopExit = round(self.avgEntryPrice * (1 + self.exitOnLossStop) / 100) # 固定止损价 self.putEvent() #---------------------------------------------------------------------- def onTrade(self, trade): # 发出状态更新事件 self.putEvent() #---------------------------------------------------------------------- def onStopOrder(self, so): """停止单推送""" pass
class BollingerBotStrategy01(CtaTemplate): """基于布林通道的交易策略""" className = 'BollingerBotStrategy01' author = 'Y.Raul' # 策略参数 bollWindow = 28 # 通道窗口数 entryDevUp = 4 # 开仓偏差 entryDevDown = 3.2 # exitDev = 1.2 # 平仓偏差 # trailingPrcnt = 0.4 # 移动止损百分比 maWindow = 10 # 过滤用均线窗口 initDays = 10 # 初始化数据所用的天数 fixedSize = 1 # 每次交易的数量 # 策略变量 bollMid = 0 # 布林带中轨 bollStd = 0 # 布林带宽度 entryUp = 0 # 开仓上轨 # exitUp = 0 # 平仓上轨 entryDown = 0 #开仓下轨 # exitDown = 0 #平仓下轨 dispacedLen = 0 #均线平移长度 maFilter = 0 # 均线过滤 maFilter1 = 0 # 上一期均线 # 分级出场设置 trailingStart1 = 20 trailingStart2 = 30 exitOnTrailingStop1 = 5 # Trailing Stop 距离 exitOnTrailingStop2 = 10 # Trailing Stop 距离 exitOnLossStop = 20 # Loss Stop 距离 # 价格相关变量 intraTradeHigh = 0 # 持仓期内的最高点 intraTradeLow = 0 # 持仓期内的最低点 avgEntryPrice = 0 minDiff = 1 trailingExit = 0 # stopExit = 0 # 空头止损 # longEntry = 0 # 多头开仓 # shortEntry = 0 # 信号相关变量 buySig = False shortSig = False sellSig = False coverSig = False # entrusted = False #是否已有委托 orderList = [] # 保存委托代码的列表 # 参数列表,保存了参数的名称 paramList = [ 'name', 'className', 'author', 'vtSymbol', 'bollWindow', 'entryDevUp', 'entryDevDown', 'trailingStart1', 'trailingStart2', 'exitOnTrailingStop1', 'exitOnTrailingStop2', 'maWindow', 'initDays', 'fixedSize' ] # 变量列表,保存了变量的名称 varList = [ 'inited', 'trading', 'pos', 'buySig', 'shortSig', 'sellSig', 'coverSig', 'entryUp', 'entryDown', 'trailingExit', 'stopExit', 'intraTradeHigh', 'intraTradeLow', 'avgEntryPrice' ] # 同步列表 syncList = ['pos', 'intraTradeHigh'] #---------------------------------------------------------------------- def __init__(self, ctaEngine, setting): """Constructor""" super(BollingerBotStrategy01, self).__init__(ctaEngine, setting) self.bm = BarGenerator(self.onBar, 5, self.onFiveBar) self.am = ArrayManager(30) self.orderList = [] self.entryPriceList = [] #---------------------------------------------------------------------- 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推送(必须由用户继承实现)""" # 观察周期1 Min,根据信号进行交易 # 回测数据传送的bar.datetime,为bar的开始时间 self.bm.updateBar(bar) if not self.trading: return self.date = bar.date self.time = bar.time # 检查交易信号 if self.buySig: res = self.buy(bar.close, self.fixedSize, True) self.orderList.extend([x.split('.')[1] for x in res]) # self.orderList.extend(res.split('.')[1]) # 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 = "-----" * 10 + "\n@onBar\n" + \ "bar.datetime: {0}; pos: {1} \n".format(bar.datetime, self.pos) + \ "buySig: {0}; shortSig: {1}\n".format(self.buySig, self.shortSig) + \ "sellSig: {0}; coverSig: {1}\n".format(self.sellSig, self.coverSig) + \ "intraTradeHigh: {0}\n".format(self.intraTradeHigh) + \ "intraTradeLow: {0}\n".format(self.intraTradeLow) self.writeCtaLog(log) # 记录log log = "\n Trading: {0}\n".format(self.trading) + \ "{0} Buy : longEntry: {1};\n".format(bar.datetime, bar.close) + \ " entryUp:{0}; maFilter:{1}; maFilter1:{2}; \n".format(self.entryUp, self.maFilter, self.maFilter1) self.writeCtaLog(log) self.buySig = False # return if self.shortSig: self.res = self.short(bar.close, self.fixedSize, True) self.orderList.extend([x.split('.')[1] for x in self.res]) # self.orderList.extend(res.split('.')[1]) # 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 = "-----" * 10 + "\n@onBar\n" + \ "bar.datetime: {0}; pos: {1} \n".format(bar.datetime, self.pos) + \ "buySig: {0}; shortSig: {1}\n".format(self.buySig, self.shortSig) + \ "sellSig: {0}; coverSig: {1}\n".format(self.sellSig, self.coverSig) + \ "intraTradeHigh: {0}\n".format(self.intraTradeHigh) + \ "intraTradeLow: {0}\n".format(self.intraTradeLow) self.writeCtaLog(log) # 记录log log = "\n Trading: {0}\n".format(self.trading) + \ "{0} Short : shortEntry: {1};\n".format(bar.datetime, bar.close) + \ " entryDown:{0}; maFilter:{1}; maFilter1:{2}; \n".format(self.entryDown, self.maFilter, self.maFilter1) self.writeCtaLog(log) self.shortSig = False # return if self.sellSig: if bar.close > self.stopExit: price = self.trailingExit else: price = bar.close res = self.sell(price, abs(self.pos), True) # self.orderList.extend(res) log = "-----" * 10 + "\n@onBar\n" + \ "bar.datetime: {0}; pos: {1} \n".format(bar.datetime, self.pos) + \ "buySig: {0}; shortSig: {1}\n".format(self.buySig, self.shortSig) + \ "sellSig: {0}; coverSig: {1}\n".format(self.sellSig, self.coverSig) + \ "intraTradeHigh: {0}\n".format(self.intraTradeHigh) + \ "intraTradeLow: {0}\n".format(self.intraTradeLow) self.writeCtaLog(log) # 记录log log = "\n Trading: {0}\n".format(self.trading) + \ "{0} Sell : {1};\n".format(bar.datetime, bar.close) + \ " price:{0}; stopExit: {1}\n".format(price,self.stopExit) self.writeCtaLog(log) # self.entryPriceList = [] # self.avgEntryPrice = 0 # self.stopExit = 0 self.sellSig = False # return if self.coverSig: if bar.close < self.stopExit: price = self.trailingExit else: price = bar.close res = self.cover(price, abs(self.pos), True) # self.orderList.extend(res) log = "-----" * 10 + "\n@onBar\n" + \ "bar.datetime: {0}; pos: {1} \n".format(bar.datetime, self.pos) + \ "buySig: {0}; shortSig: {1}\n".format(self.buySig, self.shortSig) + \ "sellSig: {0}; coverSig: {1}\n".format(self.sellSig, self.coverSig) + \ "intraTradeHigh: {0}\n".format(self.intraTradeHigh) + \ "intraTradeLow: {0}\n".format(self.intraTradeLow) self.writeCtaLog(log) # 记录log log = "\n Trading: {0}\n".format(self.trading) + \ "{0} Cover : {1};\n".format(bar.datetime, bar.close) + \ " price:{0}; stopExit: {1}\n".format(price,self.stopExit) self.writeCtaLog(log) # self.entryPriceList = [] # self.avgEntryPrice = 0 # self.stopExit = 0 self.coverSig = False # return self.putEvent() #---------------------------------------------------------------------- def onFiveBar(self, bar): """收到5分钟K线""" # 策略周期5Min,生成交易信号 # 保存K线数据 if not self.trading: return self.am.updateBar(bar) if not self.am.inited: return # 撤销之前发出的尚未成交的委托(包括限价单和停止单) self.cancelAll() # 计算指标数值 self.bollMid = self.am.sma(self.bollWindow, True)[-1 * (self.dispacedLen + 1)] self.bollStd = self.am.std(self.bollWindow) self.entryUp = round(self.bollMid + self.bollStd * self.entryDevUp) self.entryDown = round(self.bollMid - self.bollStd * self.entryDevDown) maArray = self.am.sma(self.maWindow, True) self.maFilter = round(maArray[-1]) self.maFilter1 = round(maArray[-2]) # 判断是否要进行交易 # 当前无仓位 if self.pos == 0: self.intraTradeHigh = bar.high self.intraTradeLow = bar.low self.entryPriceList = [] self.orderList = [] self.avgEntryPrice = 0 if bar.close > self.maFilter and self.maFilter > self.maFilter1: # 均线多头过滤 if bar.close >= self.entryUp: # 上轨突破 self.buySig = True if bar.close < self.maFilter and self.maFilter < self.maFilter1: # 均线空头过滤 if bar.close <= self.entryDown: # 下轨突破 self.shortSig = True log = "-----" * 10 + "\n@onFiveBar\n" + \ "bar.datetime: {0}; pos: {1} ; close: {2}\n".format(bar.datetime, self.pos,bar.close) + \ "buySig: {0}; shortSig: {1}\n".format(self.buySig, self.shortSig) + \ "intraTradeHigh: {0}\n".format(self.intraTradeHigh) + \ "intraTradeLow: {0}\n".format(self.intraTradeLow) self.writeCtaLog(log) # 当前有仓位 else: self.intraTradeHigh = max(self.intraTradeHigh, bar.high) self.intraTradeLow = min(self.intraTradeLow, bar.low) if self.pos > 0: # self.stopExit = self.avgEntryPrice - self.exitOnLossStop * self.minDiff #固定止损价位 if self.intraTradeHigh >= self.avgEntryPrice + self.trailingStart2 * self.minDiff: # 二级止赢判断 盈利80跳 if (bar.close <= self.intraTradeHigh - self.exitOnTrailingStop2 * self.minDiff): # 回撤20跳 self.trailingExit = 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) elif self.intraTradeHigh >= self.avgEntryPrice + self.trailingStart1 * self.minDiff: # 一级止赢判断,盈利50跳 if (bar.close <= self.intraTradeHigh - self.exitOnTrailingStop1 * self.minDiff): # 回撤20跳 self.trailingExit = 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) elif self.stopExit != 0: if (bar.close <= self.stopExit): # 固定止损,回撤20跳 self.sellSig = True log = "-----" * 10 + "\n@onFiveBar\n" + \ "bar.datetime: {0}; pos: {1} ; close:{2}\n".format(bar.datetime, self.pos, bar.close) + \ "sellSig: {0}; coverSig: {1}\n".format(self.sellSig, self.coverSig) + \ "intraTradeHigh: {0}\n".format(self.intraTradeHigh) + \ "intraTradeLow: {0}\n".format(self.intraTradeLow) + \ "trailingStart1: {0}\n".format(self.avgEntryPrice + self.trailingStart1 * self.minDiff) + \ "trailingStart2: {0}\n".format(self.avgEntryPrice + self.trailingStart2 * self.minDiff) + \ "avgEntryPrice: {0}\n".format(self.avgEntryPrice) + \ "trailingStop: {0}\n".format(self.trailingExit) + \ "stopExit: {0}\n".format(self.stopExit) self.writeCtaLog(log) # 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: # self.stopExit = self.avgEntryPrice + self.exitOnLossStop * self.minDiff #固定止损价 if self.intraTradeLow <= self.avgEntryPrice - self.trailingStart2 * self.minDiff: # 二级止赢判断 盈利80跳 if (bar.close >= self.intraTradeLow + self.exitOnTrailingStop2 * self.minDiff): # 回撤20跳 self.trailingExit = 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) elif self.intraTradeLow <= self.avgEntryPrice - self.trailingStart1 * self.minDiff: # 一级止赢判断,盈利50跳 if (bar.close >= self.intraTradeLow + self.exitOnTrailingStop1 * self.minDiff): # 回撤20跳 self.trailingExit = 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) elif self.stopExit != 0: if (bar.close >= self.stopExit): # 固定止损,回撤20跳 # 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) log = "-----" * 10 + "\n@onFiveBar\n" + \ "bar.datetime: {0}; pos: {1} ; close:{2}\n".format(bar.datetime, self.pos, bar.close) + \ "sellSig: {0}; coverSig: {1}\n".format(self.sellSig, self.coverSig) + \ "intraTradeHigh: {0}\n".format(self.intraTradeHigh) + \ "intraTradeLow: {0}\n".format(self.intraTradeLow) + \ "trailingStart1: {0}\n".format(self.avgEntryPrice - self.trailingStart1 * self.minDiff)+\ "trailingStart2: {0}\n".format(self.avgEntryPrice - self.trailingStart2 * self.minDiff)+\ "avgEntryPrice: {0}\n".format(self.avgEntryPrice)+\ "trailingStop: {0}\n".format(self.trailingExit)+\ "stopExit: {0}\n".format(self.stopExit) self.writeCtaLog(log) # 发出状态更新事件 self.putEvent() #---------------------------------------------------------------------- def onOrder(self, order): """收到委托变化推送(必须由用户继承实现)""" # CTA引擎中涉及到的交易方向类型 # CTAORDER_BUY = u'买开' # CTAORDER_SELL = u'卖平' # CTAORDER_SHORT = u'卖开' # CTAORDER_COVER = u'买平' log = "-----" * 10 + "\n@onOrder\n" + \ "orderTime: {0}; pos: {1} \n".format(order.orderTime, order.totalVolume) + \ u"status {0}; vtOrderID: {1}\n".format(order.status, order.vtOrderID) self.writeCtaLog(log) # 对于开仓,记录相关价格 # if order.vtOrderID in self.orderList: if order.direction == DIRECTION_LONG and order.offset == OFFSET_OPEN: if order.totalVolume == order.tradedVolume: # 更新入场价列表,更新平均入场价 self.entryPriceList.append(order.price) self.avgEntryPrice = sum(self.entryPriceList) / len( self.entryPriceList) self.stopExit = self.avgEntryPrice - self.exitOnLossStop * self.minDiff # 固定止损价 # self.orderList.remove(order.vtOrderID) elif order.direction == DIRECTION_SHORT and order.offset == OFFSET_OPEN: # 更新入场价列表,更新平均入场价 if order.totalVolume == order.tradedVolume: # 更新入场价列表,更新平均入场价 self.entryPriceList.append(order.price) self.avgEntryPrice = sum(self.entryPriceList) / len( self.entryPriceList) self.stopExit = self.avgEntryPrice + self.exitOnLossStop * self.minDiff # 固定止损价 # self.orderList.remove(order.vtOrderID) self.putEvent() #---------------------------------------------------------------------- def onTrade(self, trade): # 发出状态更新事件 data = trade.__dict__ self.putEvent() #---------------------------------------------------------------------- def onStopOrder(self, so): """停止单推送""" data = so.__dict__ self.putEvent()
class BreakThroughAndCallbackStrategy(CtaTemplate): className = 'BreakThroughAndCallbackStrategy' author = u'用Python的交易员' # 策略参数 fastWindow = 5 # 快速均线参数 first_N = 0 pre_N = 0 current_N = 0 length_of_N = 20 PDC = 0 slowWindow = 20 # 慢速均线参数 long_window = 144 # 长趋势 initDays = 20 # 初始化数据所用的天数 unit_size_of_position = 0 # 头寸规模,根据海龟交易法则进行制定,每天变化,无持仓变化 future_name = "RB" new_day = True total_capital = 100000 donchian_up = 0 donchian_down = 0 stop_donchian_up = 0 stop_donchian_down = 0 buy_open_price = 0 buy_append_price = 0 sell_open_price = 0 sell_append_price = 0 # 策略参数 long_window = 144 # 长趋势 longMA0 = EMPTY_FLOAT longMA1 = EMPTY_FLOAT # 参数列表,保存了参数的名称 paramList = ['name', 'className', 'author', 'vtSymbol', 'fastWindow', 'slowWindow'] # 变量列表,保存了变量的名称 varList = ['inited', 'trading', 'pos', 'fastMa0', 'fastMa1', 'slowMa0', 'slowMa1'] # 同步列表,保存了需要保存到数据库的变量名称 syncList = ['pos'] # ---------------------------------------------------------------------- def __init__(self, ctaEngine, setting): """Constructor""" super(DoubleMaStrategy, self).__init__(ctaEngine, setting) self.bg = BarGenerator(self.onBar) self.am = ArrayManager() self.bg15 = BarGenerator(self.onBar, 15, self.on15MinBar) self.am15 = ArrayManager() # rq.init() # 注意策略类中的可变对象属性(通常是list和dict等),在策略初始化时需要重新创建, # 否则会出现多个策略实例之间数据共享的情况,有可能导致潜在的策略逻辑错误风险, # 策略类中的这些可变对象属性可以选择不写,全都放在__init__下面,写主要是为了阅读 # 策略时方便(更多是个编程习惯的选择) # ---------------------------------------------------------------------- def onInit(self): """初始化策略(必须由用户继承实现)""" self.writeCtaLog(u'双EMA演示策略初始化') initData = self.loadBar(self.initDays) for bar in initData: self.onBar(bar) self.putEvent() # ---------------------------------------------------------------------- def onStart(self): """启动策略(必须由用户继承实现)""" self.writeCtaLog(u'双EMA演示策略启动') self.putEvent() # ---------------------------------------------------------------------- def onStop(self): """停止策略(必须由用户继承实现)""" self.writeCtaLog(u'双EMA演示策略停止') self.putEvent() # ---------------------------------------------------------------------- def onTick(self, tick): """收到行情TICK推送(必须由用户继承实现)""" self.bg.updateTick(tick) # ---------------------------------------------------------------------- def onBar(self, bar): """收到Bar推送(必须由用户继承实现)""" self.bg15.updateBar(bar) am = self.am am.updateBar(bar) if not am.inited: return if not self.am15.inited: return if not self.unit_size_of_position: self.get_unit_size_of_position() if not self.donchian_up: (self.donchian_up, self.donchian_down) = am.donchian(300) # 15 *20 (self.stop_donchian_up, self.stop_donchian_down) = am.donchian(150) # 15 * 10 return # 没有计算过通道之前不进行交易判断 # TODO 先使用最简单的入市策略,后续再根据海龟策略的系统1中的讲解,添加入市的策略 long_ma = am.sma(self.long_window, array=True) self.longMA0 = long_ma[-1] self.longMA1 = long_ma[-2] rising_trend = self.longMA0 > self.longMA1 down_trend = self.longMA0 < self.longMA1 if am.high[-1] > self.donchian_up and self.pos == 0 and rising_trend: # 超出通道上线买入 self.buy(am.close[-1], 1) self.buy_open_price = am.close[-1] self.buy_append_price = self.buy_open_price if am.low[-1] < self.donchian_down and self.pos == 0 and down_trend: # 跌穿通道下沿卖出 self.short(am.close[-1], 1) self.sell_open_price = am.close[-1] self.sell_append_price = self.sell_open_price # 加仓逻辑 if self.pos: # 多头加仓 if 4 > self.pos > 0: if am.high[-1] >= (self.buy_append_price + self.current_N / 2): self.buy(am.close[-1], 1) self.buy_append_price = am.close[-1] if -4 < self.pos < 0: if am.low[-1] <= (self.sell_append_price - self.current_N / 2): self.short(am.close[-1], 1) self.sell_append_price = am.close[-1] # 退出逻辑 if self.pos: if self.pos > 0: if am.close[-1] < self.stop_donchian_down: self.sell(am.close[-1], abs(self.pos)) if self.pos < 0: if am.close[-1] > self.stop_donchian_up: self.cover(am.close[-1], abs(self.pos)) # 止损逻辑 if self.pos: if self.pos > 0: if am.close[-1] < self.buy_append_price - 2 * self.current_N: self.sell(am.close[-1], abs(self.pos)) if self.pos < 0: if am.close[-1] > self.sell_append_price + 2 * self.current_N: self.cover(am.close[-1], abs(self.pos)) (self.donchian_up, self.donchian_down) = am.donchian(300) # 15 *20 (self.stop_donchian_up, self.stop_donchian_down) = am.donchian(150) # 15 * 10 # 发出状态更新事件 self.putEvent() def on15MinBar(self, bar): """15分钟K线推送""" self.am15.updateBar(bar) if not self.am15.inited: return # if self.am15.close[-20] > 1: # 一共有了20根bar再进行指标的计算 # print bar.datetime.strftime("%Y-%m-%d,%H:%M") # print self.am15.close[-20:] if not self.first_N: self.calculate_first_N() if not self.pre_N: self.pre_N = self.first_N self.current_N = self.pre_N # 期初使用preN 作为 currentN else: self.current_N = (self.pre_N * 19 + self.calculate_TR()) / self.length_of_N self.pre_N = self.current_N def calculate_TR(self): current_day_H_minus_L = self.am15.high[-1] - self.am15.low[-1] current_day_H_minus_yesterday_C = abs(self.am15.high[-1] - self.am15.close[-2]) yesterday_C_minus_current_day_L = abs(self.am15.close[-2] - self.am15.low[-1]) TR = max(current_day_H_minus_L, current_day_H_minus_yesterday_C, yesterday_C_minus_current_day_L) return TR # 用前二十一天的数据计算第一个N值 def calculate_first_N(self): TR_list = [] for i in range(-20, 0): current_day_H_minus_L = self.am15.high[i] - self.am15.low[i] current_day_H_minus_yesterday_C = abs(self.am15.high[i] - self.am15.close[i - 1]) yesterday_C_minus_current_day_L = abs(self.am15.close[i - 1] - self.am15.low[i]) TR = max(current_day_H_minus_L, current_day_H_minus_yesterday_C, yesterday_C_minus_current_day_L) TR_list.append(TR) self.first_N = np.array(TR_list).mean() # ---------------------------------------------------------------------- def onOrder(self, order): """收到委托变化推送(必须由用户继承实现)""" # 对于无需做细粒度委托控制的策略,可以忽略onOrder pass # ---------------------------------------------------------------------- def onTrade(self, trade): """收到成交推送(必须由用户继承实现)""" # 对于无需做细粒度委托控制的策略,可以忽略onOrder pass # ---------------------------------------------------------------------- def onStopOrder(self, so): """停止单推送""" pass def get_unit_size_of_position(self): if self.new_day and not self.pos: time_of_today = datetime.now().strftime("%Y%m%d") # 验证策略暂时先不管这一块儿,后续耗时的操作都在开盘前一个小时做好,然后策略从本地文件中读取数据 # 在数据库中记录策略上一次运行的时间,用来判断是否需要重新计算unit_size_of_position # temp_future_name = rq.get_dominant_future(self.future_name, time_of_today)[-1] # temp_info = rq.instruments(temp_future_name) # margin_rate = temp_info.margin_rate # contract_multiplier = temp_info.contract_multiplier # 合约乘数 contract_multiplier = 10 margin_rate = 0.09 self.unit_size_of_position = (0.01 * self.total_capital) / (self.current_N * contract_multiplier) self.unit_size_of_position = self.unit_size_of_position * margin_rate * 2 # 期货公司收取的保证金率为最低的三倍左右 self.unit_size_of_position = int(self.unit_size_of_position)
class TripleMAStrategy02(CtaTemplate): """基于三均线的交易策略""" className = 'TripleMAStrategy' author = 'Y.Raul' # 策略参数 # 三均线长度设置 maWindow1 = 10 maWindow2 = 20 maWindow3 = 120 maWindow4 = 5 initDays = 10 # 初始化数据所用的天数 fixedSize = 1 # 每次交易的数量 # 策略变量 # 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(TripleMAStrategy02, 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] # 判断是否要进行交易 # 当前无仓位,发送OCO开仓委托 if self.pos == 0: # 开多, 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.longExit = bar.close # 平多,MA10下穿MA20 if self.ma10 > self.ma20 and self.ma11 < self.ma21: self.sell(self.longExit, abs(self.pos), True) # 持有空头仓位 if self.pos < 0: self.shortExit = bar.close # 平空, MA10上穿MA20 if self.ma10 < self.ma20 and self.ma11 > self.ma21: self.cover(self.shortExit, abs(self.pos), True) # 发出状态更新事件 self.putEvent() # ---------------------------------------------------------------------- def onOrder(self, order): """收到委托变化推送(必须由用户继承实现)""" pass # ---------------------------------------------------------------------- def onTrade(self, trade): # 发出状态更新事件 self.putEvent() # ---------------------------------------------------------------------- def onStopOrder(self, so): """停止单推送""" pass
class SARKELStrategy(CtaTemplate): """基于sar and Keltner 交易策略""" className = 'SARKELStrategy' author = u'张老师' # 策略参数 sarAcceleration = 0.02 #加速线 sarMaximum = 0.2 # cciWindow = 20 # CCI窗口数 keltnerWindow = 25 # keltner窗口数 keltnerlMultiplier = 6.0 # 乘数 initDays = 10 # 初始化数据所用的天数 fixedSize = 1 # 每次交易的数量 barMins = 15 barMinsClose = 10 # 策略变量 sarValue = 0 # sar指标数值 cciValue = 0 # CCI指标数值 keltnerup = 0 keltnerdown = 0 longStop = 0 # 多头止损 shortStop = 0 # 空头止损 # 参数列表,保存了参数的名称 paramList = [ 'name', 'className', 'author', 'vtSymbol', 'sarAcceleration', 'sarMaximum', 'cciWindow', 'keltnerWindow', 'keltnerlMultiplier', 'initDays', 'fixedSize', 'barMinsClose', 'barMins' ] # 变量列表,保存了变量的名称 varList = ['inited', 'trading', 'pos', 'sarValue', 'cciValue', 'atrValue'] # 同步列表,保存了需要保存到数据库的变量名称 syncList = ['pos'] # ---------------------------------------------------------------------- def __init__(self, ctaEngine, setting): """Constructor""" super(SARKELStrategy, self).__init__(ctaEngine, setting) self.bg = BarGenerator(self.onBar, self.barMins, self.onXminBar) # 创建K线合成器对象 self.am = ArrayManager() self.bgclose = BarGenerator(self.onBar, self.barMinsClose, self.onminBarClose) self.amClose = ArrayManager() # ---------------------------------------------------------------------- def onminBarClose(self, bar): """分钟作为清仓周期""" # 如果没有仓位,那么不用care,直接skip # 保存K线数据 amClose = self.amClose amClose.updateBar(bar) if not amClose.inited: return # 计算指标数值 self.sarValue = amClose.sar(self.sarAcceleration, self.sarMaximum) # 判断是否要进行交易 if self.pos == 0: return # 当前无仓位,发送开仓委托 # 持有多头仓位 elif self.pos > 0: self.cancelAll() self.sell(self.sarValue, abs(self.pos), True) # 持有空头仓位 elif self.pos < 0: self.cancelAll() self.cover(self.sarValue, abs(self.pos), True) # 同步数据到数据库 self.saveSyncData() # 发出状态更新事件 self.putEvent() # ---------------------------------------------------------------------- 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.bg.updateBar(bar) self.bgclose.updateBar(bar) # ---------------------------------------------------------------------- def onXminBar(self, bar): """收到X分钟K线""" # 全撤之前发出的委托 self.cancelAll() # 保存K线数据 am = self.am am.updateBar(bar) if not am.inited: return # 计算指标数值 self.cciValue = am.cci(self.cciWindow) self.keltnerup, self.keltnerdown = am.keltner(self.keltnerWindow, self.keltnerlMultiplier) # 判断是否要进行交易 # 当前无仓位,发送开仓委托 if self.pos == 0: if self.cciValue > 0: # ru self.buy(self.keltnerup, self.fixedSize, True) elif self.cciValue < 0: self.short(self.keltnerdown, self.fixedSize, True) # 同步数据到数据库 self.saveSyncData() # 发出状态更新事件 self.putEvent() # ---------------------------------------------------------------------- def onOrder(self, order): """收到委托变化推送(必须由用户继承实现)""" pass # ---------------------------------------------------------------------- def onTrade(self, trade): # 发出状态更新事件 self.putEvent() # ---------------------------------------------------------------------- def onStopOrder(self, so): """停止单推送""" pass
class BollChannelStrategy(CtaTemplate): """基于布林通道的交易策略""" className = 'BollChannelStrategy' author = u'用Python的交易员' # 策略参数 bollWindow = 18 # 布林通道窗口数 bollDev = 3.4 # 布林通道的偏差 cciWindow = 10 # CCI窗口数 atrWindow = 30 # ATR窗口数 slMultiplier = 5.2 # 计算止损距离的乘数 initDays = 10 # 初始化数据所用的天数 fixedSize = 1 # 每次交易的数量 # 策略变量 bollUp = 0 # 布林通道上轨 bollDown = 0 # 布林通道下轨 cciValue = 0 # CCI指标数值 atrValue = 0 # ATR指标数值 intraTradeHigh = 0 # 持仓期内的最高点 intraTradeLow = 0 # 持仓期内的最低点 longStop = 0 # 多头止损 shortStop = 0 # 空头止损 # 参数列表,保存了参数的名称 paramList = [ 'name', 'className', 'author', 'vtSymbol', 'bollWindow', 'bollDev', 'cciWindow', 'atrWindow', 'slMultiplier', 'initDays', 'fixedSize' ] # 变量列表,保存了变量的名称 varList = [ 'inited', 'trading', 'pos', 'bollUp', 'bollDown', 'cciValue', 'atrValue', 'intraTradeHigh', 'intraTradeLow', 'longStop', 'shortStop' ] # 同步列表,保存了需要保存到数据库的变量名称 syncList = ['pos', 'intraTradeHigh', 'intraTradeLow'] #---------------------------------------------------------------------- def __init__(self, ctaEngine, setting): """Constructor""" super(BollChannelStrategy, self).__init__(ctaEngine, setting) self.bg = BarGenerator(self.onBar, 15, self.onXminBar) # 创建K线合成器对象 self.bg30 = BarGenerator(self.onBar, 30, self.on30minBar) self.am = ArrayManager() #---------------------------------------------------------------------- def on30minBar(self, bar): """""" #---------------------------------------------------------------------- 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.bg.updateBar(bar) #---------------------------------------------------------------------- def onXminBar(self, bar): """收到X分钟K线""" # 全撤之前发出的委托 self.cancelAll() # 保存K线数据 am = self.am am.updateBar(bar) if not am.inited: return # 计算指标数值 self.bollUp, self.bollDown = am.boll(self.bollWindow, self.bollDev) self.cciValue = am.cci(self.cciWindow) self.atrValue = am.atr(self.atrWindow) # 判断是否要进行交易 # 当前无仓位,发送开仓委托 if self.pos == 0: self.intraTradeHigh = bar.high self.intraTradeLow = bar.low if self.cciValue > 0: self.buy(self.bollUp, self.fixedSize, True) elif self.cciValue < 0: self.short(self.bollDown, self.fixedSize, True) # 持有多头仓位 elif self.pos > 0: self.intraTradeHigh = max(self.intraTradeHigh, bar.high) self.intraTradeLow = bar.low self.longStop = self.intraTradeHigh - self.atrValue * self.slMultiplier self.sell(self.longStop, abs(self.pos), True) # 持有空头仓位 elif self.pos < 0: self.intraTradeHigh = bar.high self.intraTradeLow = min(self.intraTradeLow, bar.low) self.shortStop = self.intraTradeLow + self.atrValue * self.slMultiplier self.cover(self.shortStop, abs(self.pos), True) # 同步数据到数据库 self.saveSyncData() # 发出状态更新事件 self.putEvent() #---------------------------------------------------------------------- def onOrder(self, order): """收到委托变化推送(必须由用户继承实现)""" pass #---------------------------------------------------------------------- def onTrade(self, trade): # 发出状态更新事件 self.putEvent() #---------------------------------------------------------------------- def onStopOrder(self, so): """停止单推送""" pass
class strategyHedge(CtaTemplate): """European""" className = 'strategyDemo_syt' author = u'syt' # 策略参数 takeProfitPoint = 2 #止赢点数 stopLossPoint = 1 #止损点数 # 策略变量 longPosition = 0 shortPosition = 0 # 参数列表,保存了参数的名称 paramList = ['name', 'className', 'author', 'vtSymbol'] # 变量列表,保存了变量的名称 varList = ['inited', 'trading', 'pos', 'hedgeratio'] posAdd = 0 paramFilename = 'strategyHedge_Param_Setting.json' path = os.path.abspath(os.path.dirname(__file__)) path1 = os.path.join(path, paramFilename) #---------------------------------------------------------------------- def __init__(self, ctaEngine, setting): """Constructor""" self.paramSetting() super(strategyHedge, self).__init__(ctaEngine, setting) # self.contractMode='Double' #主力合约与次主力合约模式 self.contractMode = 'Dominant' #主力合约模式 self.num = pd.Series([]) self.num_unit = pd.Series([]) self.lastOrder = None self.lastTrade = None self.orderType = '' self.contractname = '' self.contractnameAdd = '' self.initialFlag = True self.PositionCodeList = [] self.signalGroupList = [] self.rollingDays = 0 #开单条件:远-近 价差大于10,开仓 self.isBuySign = False self.isShortSign = False self.isBuyCoverSign = False self.isShortCoverSign = False #记录价格序列 self.tickListPrice = [] self.tickListAddPrice = [] self.dayList = [] self.dayListPrice = [] self.dayDates = [] self.spot = pd.Series([]) self.bg = BarGenerator(self.onDay, 1, self.onXminDay) self.am = ArrayManager() self.future = pd.Series([]) self.period = 10 #套保周期 self.Qs = 10000 #现货数量 self.direction = "LongHedging" #self.datastartdate="2015-01-01" #self.startdate="2015-03-01" #self.enddate="2015-05-01" self.contractList = defaultdict(dict) self.LongHedging_openSignal = defaultdict(dict) self.LongHedging_CloseSignal = defaultdict(dict) self.ShortHedging_openSignal = defaultdict(dict) self.ShortHedging_CloseSignal = defaultdict(dict) self.adjdates = [] self.hedgeDF = pd.Series([]) self.numDF = pd.Series([]) self.hedgeratio = 0 #---------------------------------------------------------------------- def paramSetting(self): #策略参数写入名为“strategyHedge_Param_Setting”的文件中 with open(self.path1) as f: param = json.load(f) d = self.__dict__ for key in param: d[key] = param[key] #---------------------------------------------------------------------- def onInit(self): """初始化策略(必须由用户继承实现)""" if self.initialFlag: # 初始化仓位 self.initialStrategyPos(self.className) self.initialFlag = False # plt.ion() # fig1 = plt.figure() self.writeCtaLog(u'商品套期保值策略初始化') #---------------------------------------------------------------------- def onStart(self): """启动策略(必须由用户继承实现)""" self.isNewDay = True self.isRollingClose = True #每天检查持仓 self.PositionCodeList = self.ctaEngine.dbClient[POSITION_DB_NAME][ self.className].distinct("vtSymbol") self.signalGroupList = self.ctaEngine.dbClient[POSITION_DB_NAME][ self.className].distinct("signalGroup") if self.adjdates == []: self.dateList(self.period, startdate=self.startdate) if len(self.spot) == 0: self.spot = self.spotDF(self.spotID) self.writeCtaLog(u'商品套期保值策略启动') self.check_loggerPosition() self.putEvent() #---------------------------------------------------------------------- def onStop(self): """停止策略(必须由用户继承实现)""" self.writeCtaLog(u'追涨Tick演示策略停止') self.putEvent() #---------------------------------------------------------------------- def confirmDominant(self, tick): # 确定主力合约与次主力合约 for key, details in self.ctaEngine.contract_dict[tick.date].items(): if details['property'] == 'DOMINANT': self.contractList[details['symbolInit']].update( {'DOMINANT': key}) if details['property'] == 'SUBDOMINANT': self.contractList[details['symbolInit']].update( {'SUBDOMINANT': key}) def onTick(self, tick): #检查是否还有挂单,并处理,回测与模拟盘检查挂单方式有差异,待解决 if self.ctaEngine.workingLimitOrderDict.keys(): self.killOrder(tick, 60, 3) '''推送行情前,先检查最新主力合约,否则isNewDay会错误关闭''' if self.isNewDay: # 每天判断一次 symbolInit = self.ctaEngine.contract_dict[tick.date][ tick.vtSymbol]['symbolInit'] if self.contractList == {} or self.rollingDays != 0 or self.ctaEngine.rollingFlag: self.confirmDominant(tick) self.isNewDay = False """收到行情TICK推送(必须由用户继承实现)""" self.bg.updateTick(tick) def beta(self, x, y): if len(x) == len(y): rho = np.corrcoef(x, y)[0][1] hedgeratio = np.std(y) * rho / np.std(x) else: print(u"自变量与因变量序列长度不匹配") exit() return hedgeratio def linear(self, x, y): model = LinearRegression() try: model.fit(x, y) except: # model.fit(x.values.reshape(-1,1),y) X = [] Y = [] for xx, yy in zip(x.values, y.values): X.append([float(xx)]) Y.append([float(yy)]) model.fit(X, Y) hedgeratio = model.coef_[0] return hedgeratio def spotDF(self, ID): #ID是现货指标的wind代码,字符串格式 commodity = self.symbol spotcollection = self.ctaEngine.dbClient["SPOT_DB"][commodity] ID = str(ID) datelist = sorted(self.ctaEngine.contract_dict.keys()) datadf = pd.DataFrame( list(spotcollection.find({ "ID": ID, "Date": { "$gte": datelist[0] } }))) spotdf = datadf["Price"] #spotdf.index=datadf["Date"] #spotdf=spotdf.sort_index() spotdf = spotdf[0:len(datelist)] spotdf.index = datelist return spotdf def dateList(self, period, initdelta=None, startdate=None, fixdate=10): if period % 20 != 0: fixdate = None datelist = sorted(self.ctaEngine.contract_dict.keys()) if startdate in datelist: dateList = datelist[datelist.index(startdate):] else: while startdate not in datelist: startdate = ( datetime.datetime.strptime(startdate, "%Y-%m-%d") + datetime.timedelta(days=1)).strftime("%Y-%m-%d") dateList = datelist[datelist.index(startdate):] if (datetime.datetime.strptime(startdate, "%Y-%m-%d") - datetime.datetime.strptime(datelist[0], "%Y-%m-%d")).days > period + 10: adj = [] testDf = pd.DataFrame([]) if fixdate: #dateDF = pd.DataFrame([], index=dateList, columns=[["Year", "Month", "Day"]]) for date in dateList: #dateDF.loc[date, "Year"] = date.split("-")[0] testDf.ix[date, 'Year'] = date.split("-")[0] testDf.ix[date, 'Month'] = date.split("-")[1] testDf.ix[date, 'Day'] = date.split("-")[2] for name, group in testDf.groupby(["Year", "Month"]): if fixdate > len(group): fixdate = len(group) adj.append(group.index[fixdate - 1]) adjdates = adj[0::int(period / 20)] self.adjdates = adjdates[1:] else: self.adjdates = dateList[0::period] else: print(u"回测时间太短,请重新设置startdate") exit() #---------------------------------------------------------------------- def onBar(self, bar): """收到Bar推送(必须由用户继承实现)""" self.bg.updateBar(bar) #---------------------------------------------------------------------- def onDay(self, bar): """收到Bar推送(必须由用户继承实现)""" self.bg.updateDay(bar) #---------------------------------------------------------------------- def onXminDay(self, bar): """收到Tick推送(必须由用户继承实现)""" symbolInit = re.findall("[A-Za-z]+", bar.vtSymbol)[0].upper() if self.isRollingClose: # 检查是否有换月,编写处理逻辑,全平或设定相关条件或移仓 for code in self.ctaEngine.rollingContract: # 找出涉及换月的合约 dbCusor = self.ctaEngine.dbClient[POSITION_DB_NAME][ self.className].find({'vtSymbol': code}) for contract in dbCusor: # 找出涉及换月的singalGroup所有合约 dbCusor2 = self.ctaEngine.dbClient[POSITION_DB_NAME][ self.className].find( {'signalGroup': contract['signalGroup']}) if contract['signalGroup'].split('_')[0] == 'LongHedging': self.LongHedging_openSignal.update({symbolInit: True}) elif contract['signalGroup'].split( '_')[0] == 'ShortHedging': self.ShortHedging_openSignal.update({symbolInit: True}) for d in dbCusor2: self.oneKeyClean_Bar(bar, d, contract['signalGroup']) # 关闭信号 if contract['signalGroup'].split('_')[0] == 'LongHedging': self.LongHedging_openSignal.update( {contract['signalGroup'].split('_')[1]: True}) if contract['signalGroup'].split('_')[0] == 'ShortHedging': self.ShortHedging_openSignal.update( {contract['signalGroup'].split('_')[1]: True}) self.isRollingClose = False DominantCode = self.contractList[symbolInit]['DOMINANT'] self.future[bar.date] = bar.close # self.adjdates=self.dateList(self.period,startdate=self.startdate) if bar.date in self.adjdates[0:]: self.rF = (np.log(self.future).diff(self.period)).dropna() self.rS = (np.log(self.spot[self.future.index]).diff( self.period)).dropna() hedgeratio = self.beta(self.rF, self.rS) self.hedgeDF[bar.date] = hedgeratio mult = self.ctaEngine.contract_dict[bar.date][bar.vtSymbol]["size"] self.n = int((hedgeratio * self.Qs * self.spot[bar.date]) / (mult * self.future[bar.date])) self.n_unit = int((self.Qs * self.spot[bar.date]) / (mult * self.future[bar.date])) self.num[bar.date] = self.n self.num_unit[bar.date] = self.n_unit if self.direction == "LongHedging": self.LongHedging_openSignal.update({symbolInit: True}) #如果旧持仓信号则平仓开启 if self.direction + '_' + symbolInit in self.signalGroupList: self.LongHedging_CloseSignal.update({symbolInit: True}) if self.direction == "ShortHedging": self.ShortHedging_openSignal.update({symbolInit: True}) # 如果旧持仓信号则平仓开启 if self.direction + '_' + symbolInit in self.signalGroupList: self.ShortHedging_CloseSignal.update({symbolInit: True}) # self.hedgeDF = (self.hedgeDF.reindex(self.rF.index).fillna(method="ffill")).dropna() # if bar.date in adjdates[0:]: # if self.direction=="LongHedging" not in self.signalGroupList: # self.LongHedging_openSignal.update({symbolInit: True}) # if self.direction=="ShortHedging" not in self.signalGroupList: # self.ShortHedging_openSignal.update({symbolInit: True}) MinimumPriceChange = self.ctaEngine.Param_setting_dict[symbolInit][ 'MinimumPriceChange'] #最小跳价 if self.LongHedging_CloseSignal[symbolInit] == True: # 买入套保平仓 # 净头寸调仓待优化 self.signalName = 'LongHedging_%s' % symbolInit dbCusorOrder = self.ctaEngine.dbClient[POSITION_DB_NAME][ self.className].find({'signalGroup': self.signalName}) for d in dbCusorOrder: self.oneKeyClean_Bar(bar, d, self.signalName, info='套期保值到期平仓') self.LongHedging_CloseSignal.update({symbolInit: False}) if self.ShortHedging_CloseSignal[symbolInit] == True: # 卖出套保平仓 # 待优化同步平仓 self.signalName = 'ShortHedging_%s' % symbolInit dbCusorOrder = self.ctaEngine.dbClient[POSITION_DB_NAME][ self.className].find({'signalGroup': self.signalName}) for d in dbCusorOrder: self.oneKeyClean_Bar(bar, d, self.signalName, info='套期保值到期平仓') self.ShortHedging_CloseSignal.update({symbolInit: False}) #买入套保开仓 if self.LongHedging_openSignal[symbolInit] == True: self.signalName = 'LongHedging_%s' % symbolInit self.ctaEngine.sendOrder(DominantCode, CTAORDER_BUY, bar.close + MinimumPriceChange, self.n, self) self.writeCtaLog(u'信号' + self.signalName + ': ' + u'下单时间:' + str(self.ctaEngine.dt) + u' 合约 ' + DominantCode + u' 买入套保开仓 ' + str(self.n) + u' 手 ' + ' u价格: ' + str(bar.close + MinimumPriceChange)) self.LongHedging_openSignal.update({symbolInit: False}) # 卖出套保开仓 if self.ShortHedging_openSignal[symbolInit] == True: self.signalName = 'ShortHedging_%s' % symbolInit self.ctaEngine.sendOrder(DominantCode, CTAORDER_SHORT, bar.close - MinimumPriceChange, self.n, self) self.writeCtaLog('信号' + self.signalName + ': ' + '下单时间:' + str(self.ctaEngine.dt) + ' 合约 ' + DominantCode + ' 卖出套保开仓 ' + str(self.n) + ' 手 ' + ' 价格: ' + str(bar.close + MinimumPriceChange)) self.ShortHedging_openSignal.update({symbolInit: False}) #---------------------------------------------------------------------- def onOrder(self, order): """收到委托变化推送(必须由用户继承实现)""" # 对于无需做细粒度委托控制的策略,可以忽略onOrder self.lastOrder = order self.writeCtaLog( self.ctaEngine.orderStrategyDict[order.orderID][self.className] + '委托订单成交' + ': ' + '下单时间:' + str(order.orderTime) + ' 合约 ' + order.vtSymbol + ' ' + order.direction + ' ' + order.offset + str(order.totalVolume) + ' 手 ' + ' 委托订单状态: ' + order.status) # print self.ctaEngine.orderStrategyDict[order.orderID][self.className],order.vtSymbol, order.direction, order.offset, order.totalVolume, '手', order.status #更新持仓列表,没有考虑是否成交 self.PositionCodeList = self.ctaEngine.dbClient[POSITION_DB_NAME][ self.className].distinct("vtSymbol") self.signalGroupList = self.ctaEngine.dbClient[POSITION_DB_NAME][ self.className].distinct("signalGroup") self.check_loggerPosition() def check_loggerPosition(self): dbCursor = self.ctaEngine.dbClient[POSITION_DB_NAME][ self.className].aggregate([{ '$group': { '_id': '$vtSymbol', 'shortTd': { '$sum': '$shortTd' }, 'longTd': { '$sum': '$longTd' }, 'longPosTarget': { '$sum': '$longPosTarget' }, 'shortPosTarget': { '$sum': '$shortPosTarget' } } }]) for d in dbCursor: self.writeCtaLog('持仓:' + ' 合约 ' + d['_id'] + ' 多头目标持仓 ' + str(d['longPosTarget']) + ' 多头可用持仓: ' + str(d['longTd']) + ' 空头目标持仓 ' + str(d['shortPosTarget']) + ' 空头可用持仓: ' + str(d['shortTd'])) self.writeCtaLog('持有信号:' + str(self.signalGroupList)[1:-1]) #---------------------------------------------------------------------- def onTrade(self, trade): """收到成交推送(必须由用户继承实现)""" self.lastTrade = trade order = self.ctaEngine.workingLimitOrderDict[str(trade.orderID)] # 对于无需做细粒度委托控制的策略,可以忽略onTrade self.writeCtaLog( self.ctaEngine.orderStrategyDict[trade.orderID][self.className] + '成交' + ': ' + '成交时间:' + str(trade.tradeTime) + ' 合约 ' + trade.vtSymbol + ' ' + order.direction + ' ' + order.offset + str(trade.volume) + ' 手 ' + ' 价格: ' + str(trade.price)) signalName = self.ctaEngine.orderStrategyDict[order.orderID][ self.className] self.updateTradedPos(trade, signalName, order.status) #清空0持仓数据库文档 self.cleanDataBasePos(order, signalName) def oneKeyClean(self, tick, d, signalName): if tick.vtSymbol == d['vtSymbol']: shortPos = d['shortTd'] + d['shortYd'] longPos = d['longTd'] + d['longYd'] signalList = [] self.signalName = d['signalGroup'] if longPos < shortPos: signalList.extend( self.ctaEngine.sendOrder(d['vtSymbol'], CTAORDER_COVER, tick.askPrice1 + 10, shortPos, self)) # 下单并返回orderID # self.updatePosTarget(signalList, d['signalGroup']) # self.sType = 0 print u'换月强平', tick.datetime, signalName, u'买入平仓', d[ 'vtSymbol'], shortPos, u'手' else: signalList.extend( self.ctaEngine.sendOrder(d['vtSymbol'], CTAORDER_SELL, tick.bidPrice1 - 10, longPos, self)) # self.updatePosTarget(signalList, d['signalGroup']) # self.bType = 0 print u'换月强平', tick.datetime, signalName, u'卖出平仓', d[ 'vtSymbol'], longPos, u'手' def oneKeyClean_Bar(self, bar, d, signalName, info='换月强平 '): shortPos = d['shortTd'] + d['shortYd'] longPos = d['longTd'] + d['longYd'] self.signalName = d['signalGroup'] if longPos < shortPos: self.ctaEngine.sendOrder(d['vtSymbol'], CTAORDER_COVER, self.bg.lastTick[d['vtSymbol']].askPrice1, shortPos, self) # 下单并返回orderID self.writeCtaLog('信号' + signalName + ':' + str(bar.datetime) + info + ' 合约 ' + d['vtSymbol'] + ' 买入平仓 ' + str(shortPos) + ' 手') # print u'换月强平',bar.datetime, signalName, u'买入平仓', d['vtSymbol'], shortPos, u'手' else: try: self.ctaEngine.sendOrder( d['vtSymbol'], CTAORDER_SELL, self.bg.lastTick[d['vtSymbol']].bidPrice1, longPos, self) self.writeCtaLog('信号' + signalName + ':' + str(bar.datetime) + info + ' 合约 ' + d['vtSymbol'] + ' 卖出平仓 ' + str(longPos) + ' 手') except: self.writeCtaLog(u'error: ') #---------------------------------------------------------------------- def fixedTakeProfit(self): """固定止赢处理,以股指示例,2个点止赢""" if self.bType == 1 and self.pos > 0: if self.tickList[ 3].askPrice1 - self.lastOrder.price > self.takeProfitPoint: #如果多单赢利大于2个点 self.sell(self.tickList[3].bidPrice1, 1) self.bType = 0 self.writeCtaLog(u'多单固定止盈,--平仓价:' + str(self.tickList[3].bidPrice1) + u'--赢利点数:' + str(self.tickList[3].bidPrice1 - self.lastOrder.price)) elif self.sType == 1 and self.pos < 0: if self.lastOrder.price - self.tickList[ 3].askPrice1 > self.takeProfitPoint: #如果空单赢利大于2个点 self.cover(self.tickList[3].askPrice1, 1) self.sType = 0 self.writeCtaLog(u'空单固定止盈,--平仓价:' + str(self.tickList[3].askPrice1) + u'--赢利点数:' + str(self.lastOrder.price - self.tickList[3].askPrice1)) #---------------------------------------------------------------------- def fixedStopLoss(self): """固定止损处理,以股指示例,1个点止损""" if self.bType == 1 and self.pos > 0: if self.lastTrade.price - self.tickList[ 3].lastPrice > self.stopLossPoint: #如果多单亏损大于1个点 self.sell(self.tickList[3].bidPrice1, 1) self.bType = 0 self.writeCtaLog(u'多单固定止损,--平仓价:' + str(self.tickList[3].bidPrice1) + u'--亏损点数:' + str(self.tickList[3].bidPrice1 - self.lastTrade.price)) elif self.sType == 1 and self.pos < 0: if self.tickList[ 3].lastPrice - self.lastTrade.price > self.stopLossPoint: #如果空单亏损大于1个点 self.cover(self.tickList[3].askPrice1, 1) self.sType = 0 self.writeCtaLog(u'空单固定止损,--平仓价:' + str(self.tickList[3].askPrice1) + u'--亏损点数:' + str(self.lastTrade.price - self.tickList[3].askPrice1)) #---------------------------------------------------------------------- def calculateDailyResult(self): self.hedgeDF = (self.hedgeDF.reindex( self.rF.index).fillna(method="ffill")).dropna() if self.direction == '买入套保': rHedged = (-self.rS + self.hedgeDF * self.rF).dropna() rUnit = (-self.rS[self.hedgeDF.index] + self.rF[self.hedgeDF.index]).dropna() rUnhedged = -self.rS[self.hedgeDF.index] elif self.direction == '卖出套保': rHedged = (self.rS - self.hedgeDF * self.rF).dropna() rUnit = (self.rS[self.hedgeDF.index] - self.rF[self.hedgeDF.index]).dropna() rUnhedged = self.rS[self.hedgeDF] nvHedged = rHedged.shift(1).fillna(0).cumsum() + 1 nvUnit = rUnit.shift(1).fillna(0).cumsum() + 1 nvUnhedged = rUnhedged.shift(1).fillna(0).cumsum() + 1 line = Line("套保方案净值图") line.add("最优套保", nvHedged.index, nvHedged.values) line.add("传统套保", nvUnit.index, nvUnit.values) line.add("未套保", nvUnhedged.index, nvUnhedged.values) # line.show_config() line.render()
class DualThrust_IntraDayStrategy(CtaTemplate): """DualThrust交易策略""" className = 'DualThrust_IntraDayStrategy' author = u'Leon Zhao' # 策略参数 fixedSize = 2 k1 = 0.4 k2 = 0.4 initDays = 10 # original value is 10 rangeDays = 4 # 策略变量 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' ] # 同步列表,保存了需要保存到数据库的变量名称 syncList = ['pos'] #---------------------------------------------------------------------- def __init__(self, ctaEngine, setting): """Constructor""" super(DualThrust_IntraDayStrategy, self).__init__(ctaEngine, setting) self.bg = BarGenerator(self.onBar, onDayBar=self.ondayBar) self.am = ArrayManager() 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) self.putEvent() #---------------------------------------------------------------------- def onStop(self): """停止策略(必须由用户继承实现)""" self.writeCtaLog(u'%s策略停止' % self.name) self.putEvent() #---------------------------------------------------------------------- def onTick(self, tick): """收到行情TICK推送(必须由用户继承实现)""" self.bg.updateTick(tick) #---------calcuate range for the last several days def calcRange(self): if self.am.count >= self.rangeDays: 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 if (self.range1 > self.range2): calcRange = self.range1 else: calcRange = self.range2 else: calcRange = 0 return calcRange #---------------------------------------------------------------------- def onBar(self, bar): """收到Bar推送(必须由用户继承实现)""" # 撤销之前发出的尚未成交的委托(包括限价单和停止单) self.cancelAll() self.bg.updateBar(bar) if self.am.count < self.rangeDays: 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 lastBar.datetime.minute == 59)) 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() self.dayOpen = bar.open 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 True: # Trade Time, no matter when, just send signal if self.pos == 0: self.longEntered = False self.shortEntered = False if bar.close > self.longEntry: #if not self.longEntered: #self.buy(self.longEntry + 2, self.fixedSize) self.buy(bar.close + 2, self.fixedSize) elif bar.close < self.shortEntry: #if not self.shortEntered: #self.short(self.shortEntry - 2, self.fixedSize) self.short(bar.close - 2, 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 - 2, self.fixedSize) # 空头开仓单 if not self.shortEntered: #self.short(self.shortEntry -2 , self.fixedSize) self.short(bar.close - 2, self.fixedSize) # 持有空头仓位 elif self.pos < 0: self.shortEntered = True self.longEntered = False # 空头止损单 if bar.close > self.longEntry: #self.cover(self.longEntry + 2, self.fixedSize) self.cover(bar.close + 2, self.fixedSize) # 多头开仓单 if not self.longEntered: #self.buy(self.longEntry + 2, self.fixedSize) self.buy(bar.close + 2, 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.putEvent() #---------------------------------------------------------------------- def onOrder(self, order): """收到委托变化推送(必须由用户继承实现)""" pass #---------------------------------------------------------------------- def onTrade(self, trade): # 发出状态更新事件 self.putEvent() #---------------------------------------------------------------------- def onStopOrder(self, so): """停止单推送""" pass
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 ZeroStrategy(CtaTemplate): """基于布林通道的交易策略""" className = 'ZeroStrategy' author = u'用Python的交易员' initDays = 4 # 初始化数据所用的天数 fixedSize = 1 # 每次交易的数量 # 参数列表,保存了参数的名称 paramList = [ 'name', 'className', 'author', 'vtSymbol', 'initDays', 'fixedSize' ] # 变量列表,保存了变量的名称 varList = ['inited', 'trading', 'pos'] # 同步列表,保存了需要保存到数据库的变量名称 syncList = ['pos'] #---------------------------------------------------------------------- def __init__(self, ctaEngine, setting): """Constructor""" super(ZeroStrategy, self).__init__(ctaEngine, setting) self.bg = BarGenerator(self.onBar, 30, self.on30minBar) # 创建K线合成器对象 self.am = ArrayManager() #最小价差变动 self.tickadd = 1 #有无夜盘和 相关的时间 self.yepan = True self.yepanhour = 23 self.yepanminute = 00 self.stopcount = 0 #断网变量相关 self.lastbardatetime = None self.didinited = False #仓位相关 self.posdetail = None #订单相关 self.shortOrder = None self.buyOrder = None self.sellOrder = None self.coverOrder = None #撤单后跟单相关 self.genOrder = None #tick策略相关全局函数 #交易那一分钟 self.tradeMinute = False #order那一分钟 self.orderMinute = False #---------------------------------------------------------------------- def on30minBar(self, bar): """""" #---------------------------------------------------------------------- def onInit(self): """初始化策略(必须由用户继承实现)""" self.writeCtaLog(u'%s策略初始化' % self.name) self.getPosDetail() # 载入历史数据,并采用回放计算的方式初始化策略数值 initData = self.loadBar(self.initDays) for bar in initData: self.onBar(bar) self.didinited = True 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 cancelVtOrder(self, order, yuanyin, fangxiang, price=None): self.writeCtaLog(yuanyin + 'cancle') self.cancelOrder(order) if fangxiang == 'buy': self.buyOrder = None elif fangxiang == 'short': self.shortOrder = None elif fangxiang == 'sell': self.sellOrder == None elif fangxiang == 'cover': self.coverOrder = None else: self.writeCtaLog('ohmyghoa') if price is not None: self.genOrder = price #---------------------------------------------------------------------- def buyTickCelve(self, tick): am = self.am fangxiang = None price = None if tick.datetime.second > 51: if am.diff > 0 and am.macd < 0 and am.lastmacd > 0: fangxiang = duo price = tick.bidPrice1 elif am.macd > 0 and am.lastmacd < 0: fangxiang = duoping price = tick.askPrice1 self.chulikaipingcang(fangxiang, price) # 处理错误开仓(但市场方向正确的仓),在收阳时平掉 if self.posdetail.longPos > 0 and am.macd > 0 and am.lastmacd > 0 and self.bg.bar.open > tick.lastPrice and not self.tradeMinute: fangxiang = duoping price = tick.askPrice1 self.chulikaipingcang(fangxiang, price) #---------------------------------------------------------------------- def shortTickCelve(self, tick): am = self.am fangxiang = None price = None if tick.datetime.second > 51: if am.diff < 0 and am.macd > 0 and am.lastmacd < 0: fangxiang = kong price = tick.askPrice1 elif am.macd < 0 and am.lastmacd > 0: fangxiang = kongping price = tick.bidPrice1 self.chulikaipingcang(fangxiang, price) #处理错误开仓(但市场方向正确的仓),在收阳时平掉 if self.posdetail.shortPos > 0 and am.macd < 0 and am.lastmacd < 0 and self.bg.bar.open < tick.lastPrice and not self.tradeMinute: fangxiang = kongping price = tick.bidPrice1 self.chulikaipingcang(fangxiang, price) #---------------------------------------------------------------------- def onTick(self, tick): """收到行情TICK推送(必须由用户继承实现)""" self.bg.updateTick(tick) self.am.updateTick(tick) if not self.stopcount: self.buyTickCelve(tick) self.shortTickCelve(tick) if tick.datetime.second == 59: self.checkFalseSignal(tick) def checkPingcang(self, bar): if self.didinited: am = self.am fangxiang = None price = None if self.posdetail.longPos > 0 and self.sellOrder is not None and am.lastmacd > 0 and am.mj < 0: price = am.tick.bidPrice1 self.cancelVtOrder(self.sellOrder, u'必须平多', 'sell', price) elif self.posdetail.shortPos > 0 and self.coverOrder is not None and am.lastmacd < 0 and am.mj > 0: price = am.tick.askPrice1 self.cancelVtOrder(self.coverOrder, u'必须平空', 'cover', price) #---------------------------------------------------------------------- def getPosDetail(self): self.posdetail = self.ctaEngine.mainEngine.getPositionDetail( self.vtSymbol) def onBar(self, bar): """收到Bar推送(必须由用户继承实现)""" #检查是否有效的交易时间 if self.notintradingTime(bar): print('not') runDataCleaning() return detail = self.posdetail # if not self.didinited: # posdetial = self.posdetail #print( detail.longPos,'long and short ',detail.shortPos ) if not self.lastbardatetime == bar.datetime: self.bg.updateBar(bar) self.am.updateBar(bar) am = self.am self.barCelve(bar) #tick策略数据还原 self.tradeMinute = False self.orderMinute = False print('zhibiao', 'macd', am.macd, 'diff', am.diff, 'mj', am.mj, 'time', bar.datetime, 'end') #检查是否断网 self.lastbardatetime = bar.datetime self.am.endBar() #---------------------------------------------------------------------- def barCelve(self, bar): self.checkCancelOrder() self.checkIfConnecting(bar) self.checkPingcang(bar) #---------------------------------------------------------------------- def onOrder(self, order): """收到委托变化推送(必须由用户继承实现)""" #orderminute self.orderMinute = True #对相应订单编号进行管理 if order.status == STATUS_NOTTRADED: self.reactOrder(order, order.vtOrderID) elif order.status == STATUS_ALLTRADED or order.status == STATUS_CANCELLED: self.reactOrder(order, None) elif order.status == STATUS_REJECTED: self.writeCtaLog(STATUS_REJECTED + order.offset + order.direction) print('time', self.am.tick.datetime, order.orderTime, order.price, self.genOrder) #检查需不需要跟单 if order.status == STATUS_CANCELLED and self.genOrder is not None: self.writeCtaLog('ongendan' + order.offset + order.direction) print(self.genOrder) if order.offset == OFFSET_OPEN and order.direction == DIRECTION_LONG: self.orderBuy(self.genOrder) self.genOrder = None elif order.offset == OFFSET_OPEN and order.direction == DIRECTION_SHORT: self.orderShort(self.genOrder) self.genOrder = None elif (order.offset == OFFSET_CLOSE or order.offset == OFFSET_CLOSETODAY or order.offset == OFFSET_CLOSEYESTERDAY ) and order.direction == DIRECTION_LONG: self.orderCover(self.genOrder) self.genOrder = None elif (order.offset == OFFSET_CLOSE or order.offset == OFFSET_CLOSETODAY or order.offset == OFFSET_CLOSEYESTERDAY ) and order.direction == DIRECTION_SHORT: self.orderSell(self.genOrder) self.genOrder = None print 'order', order.price, order.direction, order.offset, order.status, order.vtOrderID, order.orderTime, self.pos, self.am.tick.datetime #---------------------------------------------------------------------- def reactOrder(self, order, vtOrderID): print 'react', order.direction, order.offset if (order.direction == DIRECTION_LONG and order.offset == OFFSET_OPEN): print 'enterlong' self.buyOrder = vtOrderID elif ( order.direction == DIRECTION_SHORT and (order.offset == OFFSET_CLOSE or order.offset == OFFSET_CLOSETODAY or order.offset == OFFSET_CLOSEYESTERDAY)): print('entersell') self.sellOrder = vtOrderID elif (order.direction == DIRECTION_SHORT and order.offset == OFFSET_OPEN): print 'entershort' self.shortOrder = vtOrderID elif ( order.direction == DIRECTION_LONG and (order.offset == OFFSET_CLOSE or order.offset == OFFSET_CLOSETODAY or order.offset == OFFSET_CLOSEYESTERDAY)): print('enter coveer') self.coverOrder = vtOrderID else: print('enteranother') def onTrade(self, trade): # 发出状态更新事件 self.reactOrder(trade, None) #处理tick策略 self.tradeMinute = True print 'trade', trade.price, trade.direction, trade.offset, trade.tradeTime self.putEvent() #---------------------------------------------------------------------- def onStopOrder(self, so): """停止单推送""" pass #---------------------------------------------------------------------- '''仓位函数''' def chulikaipingcang(self, fangxiang, price): if self.didinited: if fangxiang == duo: if self.posdetail.longPos == 0: print 'buybuy' self.orderBuy(price, 1) elif fangxiang == kong: if self.posdetail.shortPos == 0: self.orderShort(price, 1) elif fangxiang == duoping: if self.posdetail.longPos > 0: self.orderSell(price, 1) if self.buyOrder is not None: self.cancelVtOrder(self.buyOrder, u'平多时候', 'buy') elif fangxiang == kongping: if self.posdetail.shortPos > 0: self.orderCover(price, 1) if self.shortOrder is not None: self.cancelVtOrder(self.shortOrder, u'平空时候', 'short') #------------------------------------------------------- '''订单管理类''' def orderBuy(self, price, volume=1, stop=False): if self.buyOrder is None: print 'buyorder' self.buyOrder = 0 self.buy(price, volume, stop) def orderSell(self, price, volume=1, stop=False): if self.sellOrder is None: self.sellOrder = 0 self.sell(price, volume, stop) def orderShort(self, price, volume=1, stop=False): if self.shortOrder is None: self.shortOrder = 0 self.short(price, volume, stop) def orderCover(self, price, volume=1, stop=False): if self.coverOrder is None: self.coverOrder = 0 self.cover(price, volume, stop) # ---------------------------------------------------------------------- '''断网判断及处理函数''' '''目前的逻辑是根据两个bar的时间间隔来判断是否断网,一个可能的风险当市场上两笔交易的间隔长于两分钟时,会错认为也是断网了,这个在不活跃品种也较容易出现。不过一般出现这种情况较少。''' def closeAllPosistion(self, price): '''出意外如断网时平仓''' self.cancelAll() print('--closeallpos--') if self.pos > 0: self.short(price - self.tickadd, abs(self.pos)) elif self.pos < 0: self.cover(price + self.tickadd, abs(self.pos)) # ---------------------------------------------------------------------- # ---------------------------------------------------------------------- def iscontinueTime(self, firstdatetime, seconddatetime): '''判断是否为连续的时间''' if (firstdatetime.hour == seconddatetime.hour and firstdatetime.minute + 1 == seconddatetime.minute) \ or ( firstdatetime.hour == seconddatetime.hour - 1 and firstdatetime.minute == 59 and seconddatetime.minute == 0): return True # ---------------------------------------------------------------------- def isTradeContinueTime(self, firstdatetime, seconddatetime): '''判断是否为连续的交易时间''' if self.iscontinueTime(firstdatetime, seconddatetime): return True elif firstdatetime.hour == 10 and ( firstdatetime.minute == 15 or firstdatetime.minute == 14 ) and seconddatetime.hour == 10 and (seconddatetime.minute == 30 or seconddatetime.minute == 31): return True elif firstdatetime.hour == 11 and ( firstdatetime.minute == 29 or firstdatetime.minute == 30 ) and seconddatetime.hour == 13 and (seconddatetime.minute == 30 or seconddatetime.minute == 31 or seconddatetime.minute == 29): return True elif self.yepan and (seconddatetime.hour == 9 or (seconddatetime.hour == 8 and seconddatetime.minute == 59)) and ( firstdatetime.hour == self.yepanhour or (firstdatetime.hour == self.yepanhour - 1 and firstdatetime.minute == 59)): return True elif (firstdatetime.hour == 15 or (firstdatetime.hour == 14 and firstdatetime.minute == 59)) and ( (seconddatetime.hour == 9 and seconddatetime.minute == 0) or (seconddatetime.hour == 8 and seconddatetime.minute == 59)): return True elif ((firstdatetime.hour == 14 and firstdatetime.minute == 59) or firstdatetime.hour == 15) and ( seconddatetime.hour == 21 or (seconddatetime.hour == 20 and seconddatetime.minute == 59)): return True elif ((firstdatetime.hour == 23 and firstdatetime.minute == 59) and (seconddatetime.hour == 0 and seconddatetime.minute == 0)) or ( (firstdatetime.hour == 0 and firstdatetime.minute == 59) and ((seconddatetime.hour == 9 and seconddatetime.minute == 0))): return True else: print('dus conne', firstdatetime, seconddatetime) return False # ---------------------------------------------------------------------- def handleDisConnected(self, price): print('DISCONNECTED', self.lastbardatetime, self.am.datetime) self.reSetOrder() self.stopcount = 15 def notintradingTime(self, bar): dt = bar.datetime.time() if ((MORNING_START <= dt < MORNING_REST) or (MORNING_RESTART <= dt < MORNING_END) or (AFTERNOON_START <= dt < AFTERNOON_END) or (dt >= NIGHT_START) or (dt < NIGHT_END)): return False else: return True def checkIfConnecting(self, bar): if self.lastbardatetime is None: self.lastbardatetime = bar.datetime elif self.lastbardatetime == bar.datetime: pass else: if not self.isTradeContinueTime(self.lastbardatetime, bar.datetime): # 断网了,需要处理断网状态 self.handleDisConnected(bar.close) # 没有断网 else: if self.stopcount > 0: self.stopcount -= 1 def checkCancelOrder(self): if self.orderMinute: print('orderminute', self.buyOrder, self.shortOrder, self.sellOrder, self.coverOrder) if self.buyOrder: if self.am.macd > 0: self.cancelVtOrder(self.buyOrder, u"没进入绿浪", 'buy') if self.shortOrder: if self.am.macd < 0: self.cancelVtOrder(self.shortOrder, u"没进入红狼", 'short') if self.sellOrder: if self.am.macd < 0: self.cancelVtOrder(self.sellOrder, u'没进入红浪', 'sell') if self.coverOrder: if self.am.macd > 0: self.cancelVtOrder(self.coverOrder, u'没进入绿琅', 'cover') def checkFalseSignal(self, tick): if self.tradeMinute: if self.posdetail.longPos > 0: if self.am.macd > 0: self.sellFok(tick.askPrice1, 1) elif self.posdetail.shortPos > 0: if self.am.macd < 0: self.coverFok(tick.bidPrice1, 1) #回测用 def barkaicang(self, bar): am = self.am if not self.stopcount: fangxiang = None price = bar.close if am.diff > 0 and am.macd < 0 and am.lastmacd > 0: fangxiang = duo elif am.macd > 0 and am.lastmacd < 0: fangxiang = duoping elif am.diff < 0 and am.macd > 0 and am.lastmacd < 0: fangxiang = kong elif am.macd < 0 and am.lastmacd > 0: fangxiang = kongping self.chulikaipingcang(fangxiang, price) def reSetOrder(self): print '---------------------reset--------------' self.shortOrder = None self.coverOrder = None self.buyOrder = None self.sellOrder = None
class TurtleTradingStrategy(CtaTemplate): """海龟交易策略""" className = 'TurtleTradingStrategy' author = u'用Python的交易员' # 策略参数 entryWindow = 20 # 入场通道窗口 exitWindow = 7 # 出场通道窗口 atrWindow = 20 # 计算ATR波动率的窗口 initDays = 1 # 初始化数据所用的天数 fixedSize = 100 # 每次交易的数量 # 策略变量 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, 60, self.onSixtyBar) 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.bg.updateBar(bar) def onSixtyBar(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 TrailingStopSignal(CtaSignal): """跟随出场信号""" # ---------------------------------------------------------------------- def __init__(self): """Constructor""" super(TrailingStopSignal, self).__init__() self.slMultiplier = 5 self.bg = BarGenerator(self.onBar, 15, self.on15Bar) self.signalPos = 100 # 当前持仓 self.holdPos = 0 # 当前atr值 self.atrValue = 0.0 self.intraTradeHigh = 0.0 self.intraTradeLow = 0.0 self.longStop = 0.0 self.shortStop = 0.0 self.stopExit = 0.0 # ------------------ ---------------------------------------------------- def onTick(self, tick): """Tick更新""" self.bg.updateTick(tick) # ---------------------------------------------------------------------- def onBar(self, bar): """K线更新""" self.bg.updateBar(bar) # print "@onBar" # ---------------------------------------------------------------------- def on15Bar(self, bar): # print "@on15Bar" if self.holdPos > 0: self.intraTradeHigh = max(self.intraTradeHigh, bar.high) self.longStop = self.intraTradeHigh - self.atrValue * self.slMultiplier # print bar.datetime # print "atrValue: ", self.atrValue # print "holdPos: ", self.holdPos # print "longStop: ",self.longStop # print "close: ", bar.close if self.longStop > bar.close: self.setSignalPos(0) # print "tailingPos: ", self.getSignalPos() elif self.holdPos < 0: self.intraTradeLow = min(self.intraTradeLow, bar.low) self.shortStop = self.intraTradeLow + self.atrValue * self.slMultiplier # print bar.datetime # print "atrValue: ", self.atrValue # print "holdPos: ", self.holdPos # print "shortStop: ",self.shortStop # print "close: ", bar.close if self.shortStop < bar.close: self.setSignalPos(0) elif self.holdPos == 0: # 空仓时返回100 self.setSignalPos(100)
class KkStrategy(CtaTemplate): """基于King Keltner通道的交易策略""" className = 'KkStrategy' author = u'张老师' # 策略参数 kkLength = 11 # 计算通道中值的窗口数 kkDev = 1.6 # 计算通道宽度的偏差 trailingPrcnt = 0.8 # 移动止损 initDays = 10 # 初始化数据所用的天数 fixedSize = 1 # 每次交易的数量 # 策略变量 kkUp = 0 # KK通道上轨 kkDown = 0 # KK通道下轨 intraTradeHigh = 0 # 持仓期内的最高点 intraTradeLow = 0 # 持仓期内的最低点 buyOrderIDList = [] # OCO委托买入开仓的委托号 shortOrderIDList = [] # OCO委托卖出开仓的委托号 orderList = [] # 保存委托代码的列表 # 参数列表,保存了参数的名称 paramList = [ 'name', 'className', 'author', 'vtSymbol', 'kkLength', 'kkDev' ] # 变量列表,保存了变量的名称 varList = ['inited', 'trading', 'pos', 'kkUp', 'kkDown'] # 同步列表,保存了需要保存到数据库的变量名称 syncList = ['pos', 'intraTradeHigh', 'intraTradeLow'] #---------------------------------------------------------------------- def __init__(self, ctaEngine, setting): """Constructor""" super(KkStrategy, self).__init__(ctaEngine, setting) self.bg = BarGenerator(self.onBar, 5, self.onFiveBar) # 创建K线合成器对象 self.am = ArrayManager() self.buyOrderIDList = [] self.shortOrderIDList = [] self.orderList = [] #---------------------------------------------------------------------- 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.bg.updateBar(bar) #---------------------------------------------------------------------- def onFiveBar(self, bar): """收到5分钟K线""" # 撤销之前发出的尚未成交的委托(包括限价单和停止单) for orderID in self.orderList: self.cancelOrder(orderID) self.orderList = [] # 保存K线数据 am = self.am am.updateBar(bar) if not am.inited: return # 计算指标数值 self.kkUp, self.kkDown = am.keltner(self.kkLength, self.kkDev) # 判断是否要进行交易 # 当前无仓位,发送OCO开仓委托 if self.pos == 0: self.intraTradeHigh = bar.high self.intraTradeLow = bar.low self.sendOcoOrder(self.kkUp, self.kkDown, self.fixedSize) # 持有多头仓位 elif self.pos > 0: self.intraTradeHigh = max(self.intraTradeHigh, bar.high) self.intraTradeLow = bar.low l = self.sell(self.intraTradeHigh * (1 - self.trailingPrcnt / 100), abs(self.pos), True) self.orderList.extend(l) # 持有空头仓位 elif self.pos < 0: self.intraTradeHigh = bar.high self.intraTradeLow = min(self.intraTradeLow, bar.low) l = self.cover(self.intraTradeLow * (1 + self.trailingPrcnt / 100), abs(self.pos), True) self.orderList.extend(l) # 同步数据到数据库 self.saveSyncData() # 发出状态更新事件 self.putEvent() #---------------------------------------------------------------------- def onOrder(self, order): """收到委托变化推送(必须由用户继承实现)""" pass #---------------------------------------------------------------------- def onTrade(self, trade): if self.pos != 0: # 多头开仓成交后,撤消空头委托 if self.pos > 0: for shortOrderID in self.shortOrderIDList: self.cancelOrder(shortOrderID) # 反之同样 elif self.pos < 0: for buyOrderID in self.buyOrderIDList: self.cancelOrder(buyOrderID) # 移除委托号 for orderID in (self.buyOrderIDList + self.shortOrderIDList): if orderID in self.orderList: self.orderList.remove(orderID) # 发出状态更新事件 self.putEvent() #---------------------------------------------------------------------- def sendOcoOrder(self, buyPrice, shortPrice, volume): """ 发送OCO委托 OCO(One Cancel Other)委托: 1. 主要用于实现区间突破入场 2. 包含两个方向相反的停止单 3. 一个方向的停止单成交后会立即撤消另一个方向的 """ # 发送双边的停止单委托,并记录委托号 self.buyOrderIDList = self.buy(buyPrice, volume, True) self.shortOrderIDList = self.short(shortPrice, volume, True) # 将委托号记录到列表中 self.orderList.extend(self.buyOrderIDList) self.orderList.extend(self.shortOrderIDList) #---------------------------------------------------------------------- def onStopOrder(self, so): """停止单推送""" pass
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 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 tempStrategy(CtaTemplate): """双指数均线策略Demo""" className = 'DoubleMaStrategy' author = u'用Python的交易员' # 策略参数 fastWindow = 12 # 快速均线参数 slowWindow = 26 # 慢速均线参数 initDays = 0 # 初始化数据所用的天数 # 策略变量 fastMa0 = EMPTY_FLOAT # 当前最新的快速EMA fastMa1 = EMPTY_FLOAT # 上一根的快速EMA slowMa0 = EMPTY_FLOAT slowMa1 = EMPTY_FLOAT huice = False # 参数列表,保存了参数的名称 paramList = [ 'name', 'className', 'author', 'vtSymbol', 'fastWindow', 'slowWindow' ] # 变量列表,保存了变量的名称 varList = [ 'inited', 'trading', 'pos', 'fastMa0', 'fastMa1', 'slowMa0', 'slowMa1' ] # 同步列表,保存了需要保存到数据库的变量名称 syncList = ['pos'] #---------------------------------------------------------------------- def __init__(self, ctaEngine, setting): """Constructor""" super(tempStrategy, self).__init__(ctaEngine, setting) self.bg = BarGenerator(self.onBar) self.am = ArrayManager() self.lastzhibiao = zhibiao(0, 0, 0) self.celve0 = zerocelve() # self.celve1 = ceshi() self.tickCelvezu = [self.celve0] self.barCelvezu = [self.celve0] self.tickadd = 1 #断网恢复变量 self.stopcount = None #交易时间和监控联网状态变量 self.yepan = False self.yepanhour = None self.yepanminute = None self.lastbardatetime = None self.tradetime = None #控制开仓和平仓稳定变量 self.tradecount = 0 self.tradingcelve = [self.celve0] # 注意策略类中的可变对象属性(通常是list和dict等),在策略初始化时需要重新创建, # 否则会出现多个策略实例之间数据共享的情况,有可能导致潜在的策略逻辑错误风险, # 策略类中的这些可变对象属性可以选择不写,全都放在__init__下面,写主要是为了阅读 # 策略时方便(更多是个编程习惯的选择) #---------------------------------------------------------------------- def onInit(self): """初始化策略(必须由用户继承实现)""" self.writeCtaLog(u'双EMA演示策略初始化') initData = self.loadBar(self.initDays) for bar in initData: self.onBar(bar) self.am.inited = True self.putEvent() #---------------------------------------------------------------------- def onStart(self): """启动策略(必须由用户继承实现)""" self.writeCtaLog(u'双EMA演示策略启动') self.putEvent() #---------------------------------------------------------------------- def closeAllPosistion(self, price): print('--closeallpos--') if self.pos > 0: self.short(price - self.tickadd, abs(self.pos)) elif self.pos < 0: self.cover(price + self.tickadd, abs(self.pos)) # ---------------------------------------------------------------------- def datetimePlusMinute(self, datelatime, minute): newdatetime = datetime.now() if datelatime.minute + minute < 60: newdatetime.minute = datelatime.minute + minute else: newdatetime.hour = datelatime.hour + 1 newdatetime.minute = datelatime.minute + minute - 60 return newdatetime # ---------------------------------------------------------------------- def iscontinueTime(self, firstdatetime, seconddatetime): if (firstdatetime.hour == seconddatetime.hour and firstdatetime.minute + 1 == seconddatetime.minute) \ or ( firstdatetime.hour == seconddatetime.hour - 1 and firstdatetime.minute == 59 and seconddatetime.minute == 0): return True # ---------------------------------------------------------------------- def isTradeContinueTime(self, firstdatetime, seconddatetime): if self.iscontinueTime(firstdatetime, seconddatetime): return True elif firstdatetime.hour == 10 and ( firstdatetime.minute == 15 or firstdatetime.minute == 14 ) and seconddatetime.hour == 10 and seconddatetime.minute == 30: return True elif firstdatetime.hour == 11 and ( firstdatetime.minute == 29 or firstdatetime.minute == 30 ) and seconddatetime.hour == 13 and seconddatetime.minute == 30: return True elif self.yepan: if firstdatetime.hour == self.yepanhour and ( firstdatetime.minute == self.yepanminute or firstdatetime.minute == self.yepanminute - 1 ) and seconddatetime.hour == 9 and seconddatetime.miute == 0: return True else: return False # ---------------------------------------------------------------------- def tickcelve(self, zhibiao, price, tick): for celve in self.tickCelvezu: xinhao = celve.celveOntick(zhibiao, self.lastzhibiao) if xinhao == 100: print(price, 'kaicangduo', tick.datetime) if xinhao == 50: print(price, 'pingcang', tick.datetime) if xinhao == 200: print(price, 'kongcang', tick.datetime) if xinhao == 250: print(price, 'pingkongcang', tick.datetime) self.chulikaipingcang(xinhao, price) # ---------------------------------------------------------------------- def barcelve(self, zhibiao, price): for celve in self.barCelvezu: xinhao = celve.celveOnbar(zhibiao, self.lastzhibiao) if xinhao == 100: print('kaicangduo,bar') if xinhao == 50: print('pingcang,bar') self.chulikaipingcang(xinhao, price) # ---------------------------------------------------------------------- def chulikaipingcang(self, celve, price): # if celve == 100: # self.buy(price,1) selfpos = 0 if celve != 0 and celve is not None: print('nowposis', self.pos, 'celveis', celve, 'andpriceis', price) if self.pos == 0 and celve == 100: if self.pos == 0: # self.weituopos = 1 self.buy(price + 100, 1) # 如果有空头持仓,则先平空,再做多 elif self.pos < selfpos: # self.weituopos = 1 self.cover(price, 1) self.buy(price, 1) elif self.pos == 1 and celve == 50: if self.pos > selfpos: # self.weituopos = 0 self.sell(price - 1, 1) elif self.pos == 0 and celve == 200: if self.pos == selfpos: # self.weituopos = -1 print('iamkonging') self.short(price - 1, 1) elif self.pos > selfpos: self.sell(price, 1) self.short(price, 1) # self.weituopos = -1 elif self.pos == -1 and celve == 250: # self.weituopos += 1 self.cover(price + 100, 1) # ---------------------------------------------------------------------- def onStop(self): """停止策略(必须由用户继承实现)""" self.writeCtaLog(u'双EMA演示策略停止') self.putEvent() #---------------------------------------------------------------------- def onTick(self, tick): """收到行情TICK推送(必须由用户继承实现)""" self.bg.updateTick(tick) zhibiao = self.am.updateTick(tick) if not self.tradecount: self.tickcelve(zhibiao, tick.lastPrice, tick) elif tick.datetime.second > 55: print('in tradecount', tick.datetime) self.tickcelve(zhibiao, tick.lastPrice, tick) #---------------------------------------------------------------------- def onBar(self, bar): """收到Bar推送(必须由用户继承实现)""" if bar.datetime.hour == 14 and bar.datetime.minute == 59: self.closeAllPosistion(bar.close) bartime = bar.datetime # 处理bar上的刚开仓 self.handleTradeCount() if self.lastbardatetime is None: self.lastbardatetime = bar.datetime else: if not self.isTradeContinueTime(self.lastbardatetime, bar.datetime): #断网了,需要处理断网状态 self.handleDisConnected(bar.close) #没有断网 else: if self.stopcount > 0: self.stopcount -= 1 am = self.am am.updateBar(bar) self.bg.updateBar(bar) # if not am.inited: # print('retr') # return # 计算快慢均线 self.celve0.nowtime = bar.datetime diff, dea, macd = am.diff, am.dea, am.macd jisuan = zhibiao(diff, dea, macd) self.barcelve(jisuan, bar.close) # 金叉和死叉的条件是互斥 # 所有的委托均以K线收盘价委托(这里有一个实盘中无法成交的风险,考虑添加对模拟市价单类型的支持) self.lastzhibiao = am.endBar() # print self.lastzhibiao.diff,self.lastzhibiao.dea # 发出状态更新事件 self.putEvent() #---------------------------------------------------------------------- def handleTradeCount(self): if self.tradecount > 0: self.tradecount -= 1 def onOrder(self, order): """收到委托变化推送(必须由用户继承实现)""" # 对于无需做细粒度委托控制的策略,可以忽略onOrder print 'order', order.price, order.direction, order.offset, order.orderTime pass #---------------------------------------------------------------------- def onTrade(self, trade): """收到成交推送(必须由用户继承实现)""" # 对于无需做细粒度委托控制的策略,可以忽略onOrder\ if trade.direction == u'多' and trade.offset == u'开仓': #self.sell(trade.price - 4, 1, stop=True) self.tradecount = 5 print('kaiduo') if trade.direction == u'空' and trade.offset == u'开仓': #self.cover(trade.price + 4, 1, stop=True) self.tradecount = 5 print('kaikong') if trade.direction == u'多' and trade.offset != u'开仓': self.cancelAll() self.celve0.cangwei += trade.volume print('pingkong') if trade.direction == u'空' and trade.offset != u'开仓': self.cancelAll() self.celve0.cangwei -= trade.volume print('pingduo') print 'trade', trade.price, trade.direction, trade.offset, trade.tradeTime pass #---------------------------------------------------------------------- def onStopOrder(self, so): """停止单推送""" pass def handleDisConnected(self, price): print('DISCONNECTED') self.closeAllPosistion(price) self.stopcount = 15
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 MultiTimeframeStrategy(CtaTemplate): """跨时间周期交易策略""" className = 'MultiTimeframeStrategy' author = u'用Python的交易员' # 策略参数 rsiSignal = 20 # RSI信号阈值 rsiWindow = 14 # RSI窗口 fastWindow = 5 # 快速均线窗口 slowWindow = 20 # 慢速均线窗口 initDays = 10 # 初始化数据所用的天数 fixedSize = 1 # 每次交易的数量 # 策略变量 rsiValue = 0 # RSI指标的数值 rsiLong = 0 # RSI买开阈值 rsiShort = 0 # RSI卖开阈值 fastMa = 0 # 5分钟快速均线 slowMa = 0 # 5分钟慢速均线 maTrend = 0 # 均线趋势,多头1,空头-1 # 参数列表,保存了参数的名称 paramList = [ 'name', 'className', 'author', 'vtSymbol', 'rsiSignal', 'rsiWindow', 'fastWindow', 'slowWindow' ] # 变量列表,保存了变量的名称 varList = [ 'inited', 'trading', 'pos', 'rsiValue', 'rsiLong', 'rsiShort', 'fastMa', 'slowMa', 'maTrend' ] # 同步列表,保存了需要保存到数据库的变量名称 syncList = ['pos'] #---------------------------------------------------------------------- def __init__(self, ctaEngine, setting): """Constructor""" super(MultiTimeframeStrategy, self).__init__(ctaEngine, setting) self.rsiLong = 50 + self.rsiSignal self.rsiShort = 50 - self.rsiSignal # 创建K线合成器对象 self.bg5 = BarGenerator(self.onBar, 5, self.on5MinBar) self.am5 = ArrayManager() self.bg15 = BarGenerator(self.onBar, 15, self.on15MinBar) self.am15 = 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推送(必须由用户继承实现)""" # 只需要要在一个BM中合成1分钟K线 self.bg5.updateTick(tick) #---------------------------------------------------------------------- def onBar(self, bar): """收到Bar推送(必须由用户继承实现)""" # 基于15分钟判断趋势过滤,因此先更新 self.bg15.updateBar(bar) # 基于5分钟判断 self.bg5.updateBar(bar) #---------------------------------------------------------------------- def on5MinBar(self, bar): """5分钟K线""" self.cancelAll() # 保存K线数据 self.am5.updateBar(bar) if not self.am5.inited: return # 如果15分钟数据尚未初始化完毕,则直接返回 if not self.maTrend: return # 计算指标数值 self.rsiValue = self.am5.rsi(self.rsiWindow) # 判断是否要进行交易 # 当前无仓位 if self.pos == 0: if self.maTrend > 0 and self.rsiValue >= self.rsiLong: self.buy(bar.close + 5, self.fixedSize) elif self.maTrend < 0 and self.rsiValue <= self.rsiShort: self.short(bar.close - 5, self.fixedSize) # 持有多头仓位 elif self.pos > 0: if self.maTrend < 0 or self.rsiValue < 50: self.sell(bar.close - 5, abs(self.pos)) # 持有空头仓位 elif self.pos < 0: if self.maTrend > 0 or self.rsiValue > 50: self.cover(bar.close + 5, abs(self.pos)) # 发出状态更新事件 self.putEvent() #---------------------------------------------------------------------- def on15MinBar(self, bar): """15分钟K线推送""" self.am15.updateBar(bar) if not self.am15.inited: return # 计算均线并判断趋势 self.fastMa = self.am15.sma(self.fastWindow) self.slowMa = self.am15.sma(self.slowWindow) if self.fastMa > self.slowMa: self.maTrend = 1 else: self.maTrend = -1 #---------------------------------------------------------------------- def onOrder(self, order): """收到委托变化推送(必须由用户继承实现)""" pass #---------------------------------------------------------------------- def onTrade(self, trade): # 发出状态更新事件 self.putEvent() #---------------------------------------------------------------------- def onStopOrder(self, so): """停止单推送""" pass
class MaPosStrategy(TargetPosTemplate): """双指数均线策略Demo""" className = 'MaPosStrategy' author = u'用Python的交易员' # 策略参数 fastWindow = 10 # 快速均线参数 slowWindow = 60 # 慢速均线参数 initDays = 10 # 初始化数据所用的天数 # 策略变量 fastMa0 = EMPTY_FLOAT # 当前最新的快速EMA fastMa1 = EMPTY_FLOAT # 上一根的快速EMA slowMa0 = EMPTY_FLOAT slowMa1 = EMPTY_FLOAT curCapital = 0 #当前可用现金 capital = 0 #初始资金 # 参数列表,保存了参数的名称 paramList = ['name', 'className', 'author', 'vtSymbol', 'fastWindow', 'slowWindow'] # 变量列表,保存了变量的名称 varList = ['inited', 'trading', 'pos', 'fastMa0', 'fastMa1', 'slowMa0', 'slowMa1'] # 同步列表,保存了需要保存到数据库的变量名称 syncList = ['pos'] #---------------------------------------------------------------------- def __init__(self, ctaEngine, setting): """Constructor""" super(MaPosStrategy, self).__init__(ctaEngine, setting) self.bg = BarGenerator(self.onBar, 5, self.onFiveBar) # 创建K线合成器对象 self.am = ArrayManager() # 注意策略类中的可变对象属性(通常是list和dict等),在策略初始化时需要重新创建, # 否则会出现多个策略实例之间数据共享的情况,有可能导致潜在的策略逻辑错误风险, # 策略类中的这些可变对象属性可以选择不写,全都放在__init__下面,写主要是为了阅读 # 策略时方便(更多是个编程习惯的选择) #---------------------------------------------------------------------- def onInit(self): """初始化策略(必须由用户继承实现)""" self.writeCtaLog(u'双EMA演示策略初始化') # initData = self.loadBar(self.initDays) # for bar in initData: # self.onBar(bar) self.putEvent() #---------------------------------------------------------------------- def onStart(self): """启动策略(必须由用户继承实现)""" self.writeCtaLog(u'双EMA演示策略启动') self.putEvent() #---------------------------------------------------------------------- def onStop(self): """停止策略(必须由用户继承实现)""" self.writeCtaLog(u'双EMA演示策略停止') self.putEvent() #---------------------------------------------------------------------- def onTick(self, tick): """收到行情TICK推送(必须由用户继承实现)""" super(MaPosStrategy, self).onTick(tick) self.bg.updateTick(tick) #---------------------------------------------------------------------- def onBar(self, bar): """收到Bar推送(必须由用户继承实现)""" super(MaPosStrategy, self).onBar(bar) self.bg.updateBar(bar) pass def onFiveBar(self, bar): am = self.am am.updateBar(bar) if not am.inited: return # 计算快慢均线 fastMa = am.sma(self.fastWindow, array=True) self.fastMa0 = fastMa[-1] self.fastMa1 = fastMa[-2] slowMa = am.sma(self.slowWindow, array=True) self.slowMa0 = slowMa[-1] self.slowMa1 = slowMa[-2] # 判断买卖 # crossOver = self.fastMa0>self.slowMa0 and self.fastMa1<self.slowMa1 # 金叉上穿 # crossBelow = self.fastMa0<self.slowMa0 and self.fastMa1>self.slowMa1 # 死叉下穿 # 判断买卖 crossOver = self.fastMa0>self.slowMa0 and self.pos <= 0 # 金叉上穿 crossBelow = self.fastMa0<self.slowMa0 and self.pos >= 0 # 死叉下穿 # 金叉和死叉的条件是互斥 # 所有的委托均以K线收盘价委托(这里有一个实盘中无法成交的风险,考虑添加对模拟市价单类型的支持) maxPos = max(int(self.curCapital / self.marginRatio /1.1 / bar.close),0) if maxPos < 20 : maxPos = 0 if self.pos == 0: if crossOver: self.setTargetPos(maxPos) elif crossBelow: self.setTargetPos(-maxPos) # 持有多头仓位 elif self.pos > 0: if crossOver: self.buy(bar.close,maxPos) elif crossBelow: self.setTargetPos(0) # 持有空头仓位 elif self.pos < 0: if crossOver: self.setTargetPos(0) elif crossBelow: self.short(bar.close,maxPos) # 发出状态更新事件 self.putEvent() #---------------------------------------------------------------------- def onOrder(self, order): """收到委托变化推送(必须由用户继承实现)""" super(MaPosStrategy, self).onOrder(order) from vnpy.trader.vtConstant import DIRECTION_LONG, DIRECTION_SHORT, OFFSET_OPEN, OFFSET_CLOSE, PRICETYPE_LIMITPRICE # 对于无需做细粒度委托控制的策略,可以忽略onOrder pass #---------------------------------------------------------------------- def onTrade(self, trade): """收到成交推送(必须由用户继承实现)""" super(MaPosStrategy, self).onTrade(trade) pass #---------------------------------------------------------------------- def onStopOrder(self, so): """停止单推送""" pass