class TurtleSignalStrategy(CtaTemplate): """""" author = "中科云集" entry_window = 20 exit_window = 10 atr_window = 20 fixed_size = 1 entry_up = 0 entry_down = 0 exit_up = 0 exit_down = 0 atr_value = 0 long_entry = 0 short_entry = 0 long_stop = 0 short_stop = 0 parameters = ["entry_window", "exit_window", "atr_window", "fixed_size"] variables = ["entry_up", "entry_down", "exit_up", "exit_down", "atr_value"] def __init__(self, cta_engine, strategy_name, vt_symbol, setting): """""" super(TurtleSignalStrategy, self).__init__(cta_engine, strategy_name, vt_symbol, setting) self.bg = BarGenerator(self.on_bar) self.am = ArrayManager() def on_init(self): """ Callback when strategy is inited. """ self.write_log("策略初始化") self.load_bar(20) def on_start(self): """ Callback when strategy is started. """ self.write_log("策略启动") def on_stop(self): """ Callback when strategy is stopped. """ self.write_log("策略停止") def on_tick(self, tick: TickData): """ Callback of new tick data update. """ self.bg.update_tick(tick) def on_bar(self, bar: BarData): """ Callback of new bar data update. """ self.cancel_all() self.am.update_bar(bar) if not self.am.inited: return self.entry_up, self.entry_down = self.am.donchian(self.entry_window) self.exit_up, self.exit_down = self.am.donchian(self.exit_window) if not self.pos: self.atr_value = self.am.atr(self.atr_window) self.long_entry = 0 self.short_entry = 0 self.long_stop = 0 self.short_stop = 0 self.send_buy_orders(self.entry_up) self.send_short_orders(self.entry_down) elif self.pos > 0: self.send_buy_orders(self.long_entry) sell_price = max(self.long_stop, self.exit_down) self.sell(sell_price, abs(self.pos), True) elif self.pos < 0: self.send_short_orders(self.short_entry) cover_price = min(self.short_stop, self.exit_up) self.cover(cover_price, abs(self.pos), True) self.put_event() def on_trade(self, trade: TradeData): """ Callback of new trade data update. """ if trade.direction == Direction.LONG: self.long_entry = trade.price self.long_stop = self.long_entry - 2 * self.atr_value else: self.short_entry = trade.price self.short_stop = self.short_entry + 2 * self.atr_value def on_order(self, order: OrderData): """ Callback of new order data update. """ pass def on_stop_order(self, stop_order: StopOrder): """ Callback of stop order update. """ pass def send_buy_orders(self, price): """""" t = self.pos / self.fixed_size if t < 1: self.buy(price, self.fixed_size, True) if t < 2: self.buy(price + self.atr_value * 0.5, self.fixed_size, True) if t < 3: self.buy(price + self.atr_value, self.fixed_size, True) if t < 4: self.buy(price + self.atr_value * 1.5, self.fixed_size, True) def send_short_orders(self, price): """""" t = self.pos / self.fixed_size if t > -1: self.short(price, self.fixed_size, True) if t > -2: self.short(price - self.atr_value * 0.5, self.fixed_size, True) if t > -3: self.short(price - self.atr_value, self.fixed_size, True) if t > -4: self.short(price - self.atr_value * 1.5, self.fixed_size, True)
class TurtleSignal: """ 用于产生海龟策略交易信号,包括入场,止损,止盈委托价格与目标仓位 """ def __init__(self, portfolio, vt_symbol, entry_window, exit_window, atr_window, profit_check=False): """Constructor, 初始化海龟信号的策略参数(默认不检查上一笔盈亏,默认缓存60根K线)""" self.portfolio = portfolio # 投资组合 self.vt_symbol = vt_symbol # 合约代码 self.entry_window = entry_window # 入场通道周期数 self.exit_window = exit_window # 出场通道周期数 self.atr_window = atr_window # 计算ATR周期数 self.profit_check = profit_check # 是否检查上一笔盈利 self.am = ArrayManager(60) # K线容器 self.atr_volatility = 0 # ATR波动率 self.entry_up = 0 # 入场通道 self.entry_down = 0 self.exit_up = 0 # 出场通道 self.exit_down = 0 self.long_entry1 = 0 # 多头入场位 self.long_entry2 = 0 self.long_entry3 = 0 self.long_entry4 = 0 self.long_stop = 0 # 多头止损位 self.short_entry1 = 0 # 空头入场位 self.short_entry2 = 0 self.short_entry3 = 0 self.short_entry4 = 0 self.short_stop = 0 # 空头止损位 self.unit = 0 # 信号持仓 self.result = None # 当前的交易 self.result_list = [] # 交易列表 self.bar = None # 最新K线 def on_bar(self, bar): """ 缓存足够K线后,开始计算相关技术指标,判断交易信号 """ self.bar = bar self.am.update_bar(bar) if not self.am.inited: return self.generate_signal(bar) self.calulate_indicator() def generate_signal(self, bar): """ 判断交易信号 要注意在任何一个数据点:buy/sell/short/cover只允许执行一类动作 负责交易信号的判断,平仓信号与开仓信号是分开的:优先检查平仓,没有仓位或者持有多头仓位的时候,在设置好入场位做多或加仓; 没有仓位或者持有空头仓位的时候,在设置好入场位做空或者加仓 """ # 如果指标尚未初始化,则忽略 # 在 calculate_indicator里面初始化,初始化的时候,所有long+short 1,2,3,4都计算完成且不会变了。 if not self.long_entry1: return # 优先检查平仓 if self.unit > 0: long_exit = max(self.long_stop, self.exit_down) if bar.low <= long_exit: self.sell(long_exit) return elif self.unit < 0: short_exit = min(self.short_stop, self.exit_up) if bar.high >= short_exit: self.cover(short_exit) return # 没有仓位或者持有多头仓位的时候,可以做多(加仓) if self.unit >= 0: trade = False if bar.high >= self.long_entry1 and self.unit < 1: self.buy(self.long_entry1, 1) trade = True if bar.high >= self.long_entry2 and self.unit < 2: self.buy(self.long_entry2, 1) trade = True if bar.high >= self.long_entry3 and self.unit < 3: self.buy(self.long_entry3, 1) trade = True if bar.high >= self.long_entry4 and self.unit < 4: self.buy(self.long_entry4, 1) trade = True if trade: return # 没有仓位或者持有空头仓位的时候,可以做空(加仓) if self.unit <= 0: if bar.low <= self.short_entry1 and self.unit > -1: self.short(self.short_entry1, 1) if bar.low <= self.short_entry2 and self.unit > -2: self.short(self.short_entry2, 1) if bar.low <= self.short_entry3 and self.unit > -3: self.short(self.short_entry3, 1) if bar.low <= self.short_entry4 and self.unit > -4: self.short(self.short_entry4, 1) def calculate_indicator(self): """ 计算技术指标 负责计算指标的产生,包括计算入场和止盈离场的唐奇安通道上下轨,判断到有单位持仓后, 计算ATR指标并且设定随后8个入场位置(做多4个和做空4个),同时初始化离场价格。 """ self.entry_up, self.entry_down = self.am.donchian(self.entry_window) self.exit_up, self.exit_down = self.am.donchian(self.exit_window) # 有持仓后,ATR波动率和入场位等都不再变化 if not self.unit: self.atr_volatility = self.am.atr(self.atr_window) self.long_entry1 = self.entry_up self.long_entry2 = self.entry_up + self.atr_volatility * 0.5 self.long_entry3 = self.entry_up + self.atr_volatility * 1 self.long_entry4 = self.entry_up + self.atr_volatility * 1.5 self.long_stop = 0 # 只有等第一笔买入时才能计算 self.short_entry1 = self.entry_down self.short_entry2 = self.entry_down - self.atr_volatility * 0.5 self.short_entry3 = self.entry_down - self.atr_volatility * 1 self.short_entry4 = self.entry_down - self.atr_volatility * 1.5 self.short_stop = 0 # 只有等第一笔卖出时才能计算 def new_signal(self, direction, offset, price, volume): """ 定义海龟投资组合的发单委托,分别是多空方向、开仓平仓、停止单价格、合约手数。 """ # ??? 最终的交易是在portfolio class 里完成的吗??? self.portfolio.new_signal(self, direction, offset, price, volume) def buy(self, price, volume): """ 买入开仓; 先传入计算好的停止单价格,缓存开仓委托的价格和手数,发出投资组合的多开委托,基于最后一次加仓价格计算止损离场位置。 关键字:开仓 """ price = self.calculate_trade_price(Direction.LONG, price) self.open(price, volume) self.new_signal(Direction.LONG, Offset.OPEN, price, volume) # 以最后一次加仓价格,加上两倍N计算止损 self.long_stop = price - self.atr_volatility * 2 def sell(self, price, volume): """ 卖出平仓; 先传入计算好的停止单价格,缓存平仓委托的价格,发出投资组合空平的委托 关键字:平仓 """ price = self.calculate_trade_price(Direction.SHORT, price) volume = abs(self.unit) self.close(price) self.new_signal(Direction.SHORT, Offset.CLOSE, price, volume) def short(self, price, volume): """卖出开仓 先传入计算好的停止单价格,缓存开仓委托的价格和手数,发出投资组合的空开委托,基于最后一次加仓价格计算止损离场位置。 关键字:开仓 """ price = self.calculate_trade_price(Direction.SHORT, price) self.open(price, -volume) self.new_signal(Direction.SHORT, Offset.OPEN, price, volume) # 以最后一次加仓价格,加上两倍N计算止损 self.short_stop = price + self.atr_volatility * 2 def cover(self, price, volume): """买入平仓; 先传入计算好的停止单价格,缓存平仓委托的价格,发出投资组合多平的委托。 关键字:平仓 """ price = self.calculate_trade_price(Direction.LONG, price) volume = abs(self.unit) self.close(price) self.new_signal(Direction.LONG, Offset.CLOSE, price, volume) def open(self, price, change): """ 开仓 计算累计开仓手数 / 单位头寸,调用TurtleResult类定义的open函数计算开仓平均成本。""" self.unit += change if not self.result: self.result = TurtleResult() self.result.open(price, change) def close(self, price): """平仓 调用TurtleResult类定义的close函数计算单笔开平仓交易盈亏。创建列表专门缓存开平仓交易盈亏。""" self.unit = 0 self.result.close(price) self.result_list.append(self.result) self.result = None def get_last_pnl(self): """ 获取上一笔交易的盈亏; 在开平仓交易盈亏列表中获取上一笔交易的盈亏""" if not self.result_list: return 0 result = self.result_list[-1] return result.pnl def calculate_trade_price(self, direction, price): """计算成交价格; 设置停止单价格,要求买入时,停止单成交的最优价格不能低于当前K线开盘价; 卖出时,停止单成交的最优价格不能高于当前K线开盘价""" # 买入时,停止单成交的最优价格不能低于当前K线开盘价 if direction == Direction.LONG: # ZL: 如果开盘的时候突破了通道,则只能以开盘的价格来成交 # 这里关键字是:停止单 trade_price = max(self.bar.open, price) # 卖出时,停止单成交的最优价格不能高于当前K线开盘价 else: trade_price = min(self.bar.open, price) return trade_price
class TurtleSignal(object): """海龟信号""" #---------------------------------------------------------------------- def __init__(self, portfolio, vtSymbol, entryWindow, exitWindow, atrWindow, profitCheck=False): """Constructor""" self.portfolio = portfolio # 投资组合 self.vtSymbol = vtSymbol # 合约代码 self.entryWindow = entryWindow # 入场通道周期数 self.exitWindow = exitWindow # 出场通道周期数 self.atrWindow = atrWindow # 计算ATR周期数 self.profitCheck = profitCheck # 是否检查上一笔盈利 self.am = ArrayManager(60) # K线容器 self.atrVolatility = 0 # ATR波动率 self.entryUp = 0 # 入场通道 self.entryDown = 0 self.exitUp = 0 # 出场通道 self.exitDown = 0 self.longEntry1 = 0 # 多头入场位 self.longEntry2 = 0 self.longEntry3 = 0 self.longEntry4 = 0 self.longStop = 0 # 多头止损位 self.shortEntry1 = 0 # 空头入场位 self.shortEntry2 = 0 self.shortEntry3 = 0 self.shortEntry4 = 0 self.shortStop = 0 # 空头止损位 self.unit = 0 # 信号持仓 self.result = None # 当前的交易 self.resultList = [] # 交易列表 self.bar = None # 最新K线 #---------------------------------------------------------------------- def onBar(self, bar): """""" self.bar = bar self.am.updateBar(bar) if not self.am.inited: return self.generateSignal(bar) self.calculateIndicator() #---------------------------------------------------------------------- def generateSignal(self, bar): """ 判断交易信号 要注意在任何一个数据点:buy/sell/short/cover只允许执行一类动作 """ # 如果指标尚未初始化,则忽略 if not self.longEntry1: return # 优先检查平仓 if self.unit > 0: longExit = max(self.longStop, self.exitDown) if bar.low <= longExit: self.sell(longExit) return elif self.unit < 0: shortExit = min(self.shortStop, self.exitUp) if bar.high >= shortExit: self.cover(shortExit) return # 没有仓位或者持有多头仓位的时候,可以做多(加仓) if self.unit >= 0: trade = False if bar.high >= self.longEntry1 and self.unit < 1: self.buy(self.longEntry1, 1) trade = True if bar.high >= self.longEntry2 and self.unit < 2: self.buy(self.longEntry2, 1) trade = True if bar.high >= self.longEntry3 and self.unit < 3: self.buy(self.longEntry3, 1) trade = True if bar.high >= self.longEntry4 and self.unit < 4: self.buy(self.longEntry4, 1) trade = True if trade: return # 没有仓位或者持有空头仓位的时候,可以做空(加仓) if self.unit <= 0: if bar.low <= self.shortEntry1 and self.unit > -1: self.short(self.shortEntry1, 1) if bar.low <= self.shortEntry2 and self.unit > -2: self.short(self.shortEntry2, 1) if bar.low <= self.shortEntry3 and self.unit > -3: self.short(self.shortEntry3, 1) if bar.low <= self.shortEntry4 and self.unit > -4: self.short(self.shortEntry4, 1) #---------------------------------------------------------------------- def calculateIndicator(self): """计算技术指标""" self.entryUp, self.entryDown = self.am.donchian(self.entryWindow) self.exitUp, self.exitDown = self.am.donchian(self.exitWindow) # 有持仓后,ATR波动率和入场位等都不再变化 if not self.unit: self.atrVolatility = self.am.atr(self.atrWindow) self.longEntry1 = self.entryUp self.longEntry2 = self.entryUp + self.atrVolatility * 0.5 self.longEntry3 = self.entryUp + self.atrVolatility * 1 self.longEntry4 = self.entryUp + self.atrVolatility * 1.5 self.longStop = 0 self.shortEntry1 = self.entryDown self.shortEntry2 = self.entryDown - self.atrVolatility * 0.5 self.shortEntry3 = self.entryDown - self.atrVolatility * 1 self.shortEntry4 = self.entryDown - self.atrVolatility * 1.5 self.shortStop = 0 #---------------------------------------------------------------------- def newSignal(self, direction, offset, price, volume): """""" self.portfolio.newSignal(self, direction, offset, price, volume) #---------------------------------------------------------------------- def buy(self, price, volume): """买入开仓""" price = self.calculateTradePrice(Direction.LONG, price) self.open(price, volume) self.newSignal(Direction.LONG, Offset.OPEN, price, volume) # 以最后一次加仓价格,加上两倍N计算止损 self.longStop = price - self.atrVolatility * 2 #---------------------------------------------------------------------- def sell(self, price): """卖出平仓""" price = self.calculateTradePrice(Direction.SHORT, price) volume = abs(self.unit) self.close(price) self.newSignal(Direction.SHORT, Offset.CLOSE, price, volume) #---------------------------------------------------------------------- def short(self, price, volume): """卖出开仓""" price = self.calculateTradePrice(Direction.SHORT, price) self.open(price, -volume) self.newSignal(Direction.SHORT, Offset.OPEN, price, volume) # 以最后一次加仓价格,加上两倍N计算止损 self.shortStop = price + self.atrVolatility * 2 #---------------------------------------------------------------------- def cover(self, price): """买入平仓""" price = self.calculateTradePrice(Direction.LONG, price) volume = abs(self.unit) self.close(price) self.newSignal(Direction.LONG, Offset.CLOSE, price, volume) #---------------------------------------------------------------------- def open(self, price, change): """开仓""" self.unit += change if not self.result: self.result = TurtleResult() self.result.open(price, change) #---------------------------------------------------------------------- def close(self, price): """平仓""" self.unit = 0 self.result.close(price) self.resultList.append(self.result) self.result = None #---------------------------------------------------------------------- def getLastPnl(self): """获取上一笔交易的盈亏""" if not self.resultList: return 0 result = self.resultList[-1] return result.pnl #---------------------------------------------------------------------- def calculateTradePrice(self, direction, price): """计算成交价格""" # 买入时,停止单成交的最优价格不能低于当前K线开盘价 if direction == Direction.LONG: tradePrice = max(self.bar.open, price) # 卖出时,停止单成交的最优价格不能高于当前K线开盘价 else: tradePrice = min(self.bar.open, price) return tradePrice
class SuperTurtleStrategyHNTest(CtaTemplate): """""" author = "Huang Ning" entry_window = 28 exit_window = 7 atr_window = 4 risk_level = 0.2 trading_size = 0 entry_up = 0 entry_down = 0 exit_up = 0 exit_down = 0 atr_value = 0 long_entry = 0 short_entry = 0 long_stop = 0 short_stop = 0 parameters = ["entry_window", "exit_window", "atr_window", "risk_level"] variables = [ "entry_up", "entry_down", "exit_up", "exit_down", "trading_size", "atr_value" ] def __init__(self, cta_engine, strategy_name, vt_symbol, setting): """""" super().__init__(cta_engine, strategy_name, vt_symbol, setting) self.bg = XminBarGenerator(self.on_bar, 1, self.on_hour_bar, interval=Interval.HOUR) self.am = ArrayManager() self.liq_price = 0 self.trading_size = 0 self.on_bar_time = time1(0, 0) self.clearance_time = time1(14, 57) self.liq_time = time1(14, 59) self.day_clearance = False self.buy_svt_orderids = [] self.sell_svt_orderids = [] self.short_svt_orderids = [] self.cover_svt_orderids = [] self.sell_lvt_orderids = [] self.cover_lvt_orderids = [] def on_init(self): """""" self.write_log("策略初始化") self.load_bar(20) def on_start(self): """""" self.write_log("策略启动") def on_stop(self): """""" self.write_log("策略停止") def on_tick(self, tick: TickData): """""" self.bg.update_tick(tick) def on_bar(self, bar: BarData): """""" self.liq_price = bar.close_price self.on_bar_time = bar.datetime.time() self.on_bar_date = bar.datetime.date() # 2021年5月10日有极端行情,收益异常高,不具有普遍参考价值,因此在回测中跳过这一天 extreme_date = "2021-05-10" extreme_date = time.strptime(extreme_date, "%Y-%m-%d") year, month, day = extreme_date[:3] extreme_date = datetime.date(year, month, day) self.day_clearance = (self.clearance_time <= self.on_bar_time <= self.liq_time) if self.on_bar_date != extreme_date: self.bg.update_bar(bar) if self.day_clearance: if not self.buy_svt_orderids and not self.short_svt_orderids\ and not self.sell_svt_orderids and not self.cover_svt_orderids\ and not self.sell_lvt_orderids and not self.cover_lvt_orderids: if self.pos > 0: self.sell_lvt_orderids = self.sell( self.liq_price - 5, abs(self.pos)) elif self.pos < 0: self.cover_lvt_orderids = self.cover( self.liq_price + 5, abs(self.pos)) else: for buf_orderids in [ self.buy_svt_orderids, self.sell_svt_orderids, self.short_svt_orderids, self.cover_svt_orderids, self.sell_lvt_orderids, self.cover_lvt_orderids ]: if buf_orderids: for vt_orderid in buf_orderids: self.cancel_order(vt_orderid) def on_hour_bar(self, bar: BarData): """""" am = self.am am.update_bar(bar) if not am.inited: return self.entry_up, self.entry_down = self.am.donchian(self.entry_window) self.exit_up, self.exit_down = self.am.donchian(self.exit_window) if not self.day_clearance: if not self.pos: self.atr_value = self.am.atr(self.atr_window) if self.atr_value == 0: return atr_risk = talib.ATR(1 / self.am.high, 1 / self.am.low, 1 / self.am.close, self.atr_window)[-1] self.trading_size = max(int(self.risk_level / atr_risk), 1) self.long_entry = 0 self.short_entry = 0 self.long_stop = 0 self.short_stop = 0 self.buy(self.entry_up, self.trading_size, True) self.short(self.entry_down, self.trading_size, True) elif self.pos > 0: sell_price = max(self.long_stop, self.exit_down) self.sell(sell_price, abs(self.pos), True) elif self.pos < 0: cover_price = min(self.short_stop, self.exit_up) self.cover(cover_price, abs(self.pos), True) self.put_event() def on_trade(self, trade: TradeData): """ Callback of new trade data update. """ if trade.direction == Direction.LONG: self.long_entry = trade.price self.long_stop = self.long_entry - 2 * self.atr_value else: self.short_entry = trade.price self.short_stop = self.short_entry + 2 * self.atr_value self.sync_data() def on_order(self, order: OrderData): """ Callback of new order data update. """ pass def on_stop_order(self, stop_order: StopOrder): """ Callback of stop order update. """ pass