class DynamicResidualModelStrategy(StrategyTemplate): """""" author = "yiran" x_symbol: str = None y_symbol: str = None # 策略内动态调整周期 # 天数 renew_interval: int = 7 # 分钟线数据,90天约等于90*240 hedge_ratio_window: int = 240 #轨道宽度 entry_multiplier: float = 3 price_add = 5 # 预期价差盈利 difference_filter_num: float = 50 difference_exit_rato: float = 1 # 预期止盈为预期价差盈利的1/2 # 指标计算参数 std_window = 240 std_mean_window_ratio = 0.5 #固定下单单位 fixed_size = 1 #价差值 spread_value: float = 0 # 用来过滤交易量低的情况 spread_volume_threshold = 0 spread_volume: float = 0 spread_volume_filter: bool = False # 进场,出场点位的缓存 spread_long_entry: float = 0 spread_long_exit: float = 0 spread_long_loss_exit: float = 0 spread_short_entry: float = 0 spread_short_exit: float = 0 spread_short_loss_exit: float = 0 #用来判断操作价差的方向 open_direction_dict: Dict[str, float] = {} close_direction_dict: Dict[str, float] = {} # 用来进行参数更新判断 last_renew_date: datetime = None renew_date: datetime = None renew_status: bool = False # 盈利记录 last_long_trade_profit: bool = False last_short_trade_profit: bool = False parameters = [ "renew_interval", "hedge_ratio_window", 'entry_multiplier', "fixed_size", "std_window", "difference_filter_num" ] variables = [ "x_multiplier", "y_multiplier", "spread_value", "spread_long_entry", "spread_long_exit", "spread_long_loss_exit" "spread_short_entry", "spread_short_exit", "spread_short_loss_exit", "spread_volume_filter" ] def __init__(self, strategy_engine: StrategyEngine, strategy_name: str, vt_symbols: List[str], setting: dict): """""" super().__init__(strategy_engine, strategy_name, vt_symbols, setting) self.last_tick_time: datetime = None self.ams: Dict[str, ArrayManager] = {} self.bgs: Dict[str, BarGenerator] = {} # 策略内动态调整OLS相关参数,小数参数会被int() self.x_multiplier = 0 self.y_multiplier = 1 # 不考虑intercept self.intercept = 0 self.short_entry_multiplier = abs(self.entry_multiplier) self.short_exit_multiplier = 0 self.long_entry_multiplier = -abs(self.entry_multiplier) self.long_exit_multiplier = 0 self.mean_window = int(self.std_window * self.std_mean_window_ratio) self.difference_exit_num = self.difference_filter_num * self.difference_exit_rato self.y_symbol = self.vt_symbols[0] self.x_symbol = self.vt_symbols[1] self.x_fixed_size = np.abs(self.fixed_size * self.x_multiplier) self.y_fixed_size = np.abs(self.fixed_size * self.y_multiplier) self.x_pos_target = 0 self.y_pos_target = 0 # 实例化缓存期货品种价格序列容器 for vt_symbol in self.vt_symbols: self.ams[vt_symbol] = ArrayManager(size=self.hedge_ratio_window + 50) #实例化bg容器 for vt_symbol in self.vt_symbols: def on_bar(bar: BarData): """""" pass self.bgs[vt_symbol] = BarGenerator(on_bar) # 实例化缓存价差价格序列容器 self.sam = SpreadArrayManager( size=max(self.std_window, self.mean_window) + 50) def on_init(self): """ Callback when strategy is inited. """ self.write_log("策略初始化") self.load_bars(10) 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. """ if (self.last_tick_time and self.last_tick_time.minute != tick.datetime.minute): bars = {} for vt_symbol, bg in self.bgs.items(): bars[vt_symbol] = bg.generate() self.on_bars(bars) bg: BarGenerator = self.bgs[tick.vt_symbol] bg.update_tick(tick) self.last_tick_time = tick.datetime def on_bars(self, bars: Dict[str, BarData]): """""" self.cancel_all() # OLS动态线性回归,需要缓存close_array self.ams[self.y_symbol].update_bar(bars[self.y_symbol]) self.ams[self.x_symbol].update_bar(bars[self.x_symbol]) # 动态线性回归函数 self.renew_hedge_ratio(bars[self.y_symbol]) # 计算价差 # 将价差放入sam 进行后续技术指标计算 self.spread_value = self.y_multiplier * ( bars[self.y_symbol].close_price) - self.x_multiplier * ( bars[self.x_symbol].close_price) - self.intercept self.spread_volume = min(bars[self.y_symbol].volume, bars[self.x_symbol].volume) self.sam.update_spread(self.spread_value, self.spread_volume) # 成交量过滤,成交量低于指定阈值将不会进行操作 if self.spread_volume < self.spread_volume_threshold: self.spread_volume_filter = False else: self.spread_volume_filter = True if not self.sam.inited: return # 计算技术指标 sam = self.sam std = sam.std(self.std_window) mean = sam.sma(self.mean_window) # 计算是否满足做多价差要求 spread_long_entry = mean + self.long_entry_multiplier * std spred_long_exit = mean + self.long_exit_multiplier * std # 计算是否满足作空价差要求 spread_short_entry = mean + self.short_entry_multiplier * std spread_short_exit = mean + self.short_exit_multiplier * std # 预期收益筛选 if np.abs(spread_long_entry - spred_long_exit) >= self.difference_filter_num: self.spread_long_entry = spread_long_entry # self.spread_long_exit = spred_long_exit else: self.spread_long_entry = None # 预期收益筛选 if np.abs(spread_short_entry - spread_short_exit) >= self.difference_filter_num: self.spread_short_enrtry = spread_short_entry # self.spread_short_exit = spred_short_exit else: self.spread_short_enrtry = None # 上笔亏损过滤 if not self.last_long_trade_profit: self.spread_long_entry = None self.last_long_trade_profit = True elif not self.last_short_trade_profit: self.spread_short_enrtry = None self.last_short_trade_profit = True # 获取每个品种持仓 self.x_pos = self.get_pos(self.x_symbol) self.y_pos = self.get_pos(self.y_symbol) if self.x_pos == 0 and self.y_pos == 0: # 多开 if self.spread_long_entry and self.spread_value <= self.spread_long_entry and self.spread_volume_filter: self.y_pos_target = self.y_fixed_size self.x_pos_target = -self.x_fixed_size self.spread_long_profit_exit = mean + self.long_exit_multiplier * std self.spread_long_loss_exit = self.spread_value - self.difference_exit_num print(self.spread_long_loss_exit, self.spread_long_profit_exit) print( f'时间{bars[self.y_symbol].datetime}', '多开 ', f'多{self.y_symbol} {self.y_fixed_size} 手 空{self.x_symbol} {self.x_fixed_size} 手', f'价差{self.spread_value}') # 空开 elif self.spread_short_entry and self.spread_value >= self.spread_short_entry and self.spread_volume_filter: self.y_pos_target = -self.y_fixed_size self.x_pos_target = self.x_fixed_size self.spread_short_profit_exit = mean + self.short_exit_multiplier * std self.spread_short_loss_exit = self.spread_value + self.difference_exit_num print(self.spread_short_loss_exit, self.spread_short_profit_exit) print( f'时间{bars[self.y_symbol].datetime}', '空开 ', f'空{self.y_symbol} {self.y_fixed_size} 手 多{self.x_symbol} {self.x_fixed_size} 手', f'价差{self.spread_value}') elif self.y_pos > 0 and self.x_pos < 0: # 多平止盈 if self.spread_value >= self.spread_long_profit_exit: self.spread_long_profit_exit = self.spread_long_profit_exit + 10 self.spread_long_loss_exit = self.spread_value - 5 self.last_long_trade_profit = True print( f'时间{bars[self.y_symbol].datetime}', '进入盈利状态', f'待多{self.y_symbol} {self.y_fixed_size} 手 待空{self.x_symbol} {self.x_fixed_size} 手', f'价差{self.spread_value}') # 多平止损 elif self.spread_value <= self.spread_long_loss_exit: self.y_pos_target = 0 self.x_pos_target = 0 self.last_long_trade_profit = False print( f'时间{bars[self.y_symbol].datetime}', '多平 止损', f'平多{self.y_symbol} {self.y_fixed_size} 手 平空{self.x_symbol} {self.x_fixed_size} 手', f'价差{self.spread_value}') # 多平调参 # elif self.renew_status: # self.y_pos_target = 0 # self.x_pos_target = 0 # print(f'时间{bars[self.y_symbol].datetime}','多平 调参',f'平多{self.y_symbol} {self.y_fixed_size} 手 平空{self.x_symbol} {self.x_fixed_size} 手',f'价差{self.spread_value}') elif self.y_pos < 0 and self.x_pos > 0: if self.spread_value <= self.spread_short_profit_exit: self.spread_short_profit_exit = self.spread_short_profit_exit + 10 self.spread_short_loss_exit = self.spread_value + 5 self.last_short_trade_profit = True print( f'时间{bars[self.y_symbol].datetime}', '进入盈利状态', f'待空{self.y_symbol} {self.y_fixed_size} 手 待多{self.x_symbol} {self.x_fixed_size} 手', f'价差{self.spread_value}') elif self.spread_value >= self.spread_short_loss_exit: self.y_pos_target = 0 self.x_pos_target = 0 self.last_short_trade_profit = False print( f'时间{bars[self.y_symbol].datetime}', '空平 止损', f'平空{self.y_symbol} {self.y_fixed_size} 手 平多{self.x_symbol} {self.x_fixed_size} 手', f'价差{self.spread_value}') # elif self.renew_status: # self.y_pos_target = 0 # self.x_pos_target = 0 # print(f'时间{bars[self.y_symbol].datetime}','空平 调参',f'平空{self.y_symbol} {self.y_fixed_size} 手 平多{self.x_symbol} {self.x_fixed_size} 手',f'价差{self.spread_value}') target = { self.x_symbol: self.x_pos_target, self.y_symbol: self.y_pos_target } for vt_symbol in self.vt_symbols: target_pos = target[vt_symbol] current_pos = self.get_pos(vt_symbol) pos_diff = target_pos - current_pos volume = abs(pos_diff) bar = bars[vt_symbol] if pos_diff > 0: price = bar.close_price + self.price_add if current_pos < 0: self.cover(vt_symbol, price, volume) else: self.buy(vt_symbol, price, volume) elif pos_diff < 0: price = bar.close_price - self.price_add if current_pos > 0: self.sell(vt_symbol, price, volume) else: self.short(vt_symbol, price, volume) def renew_hedge_ratio(self, bar: BarData): """ renew the hedge ratio based on the passed days including the not trading days. """ # 计算动态调参时间 if not self.last_renew_date: self.last_renew_date = bar.datetime self.renew_date = self.last_renew_date + timedelta( days=self.renew_interval) return # 动态调参时间 if bar.datetime >= self.renew_date: self.last_renew_date = bar.datetime self.renew_date = self.last_renew_date + timedelta( days=self.renew_interval) X = self.ams[self.x_symbol].close[-self.hedge_ratio_window:] y = self.ams[self.y_symbol].close[-self.hedge_ratio_window:] result = sm.OLS(y, X).fit() hedge_ratio = result.params intercept = 0 for n in range(10): mulitiplier_x = n * hedge_ratio mulitipler_intercept = n * intercept mulitipler = n if int(mulitiplier_x) >= 1: if hedge_ratio * self.x_multiplier < 0: print( 'warning ! the hedge ratio direction is changed ') self.x_multiplier = mulitiplier_x self.y_multiplier = mulitipler self.intercept = mulitipler_intercept self.x_fixed_size = int( np.abs(self.fixed_size * self.x_multiplier)) self.y_fixed_size = int( np.abs(self.fixed_size * self.y_multiplier)) # 可以加入历史协整测试 break self.renew_status = True else: self.renew_status = False
class DynamicResidualModelStrategy(StrategyTemplate): """""" author = "yiran" x_symbol: str = None y_symbol: str = None # 策略内动态调整 x_multiplier: float = 0 y_multiplier: float = 1 intercept: float = 0 # 策略内动态调整周期 renew_interval: int = 3 hedge_ratio_window: int = 30 #轨道宽度 entry_multiplier: float = 2.5 # 预期价差盈利 difference_filter_num: float = 20 # 预期止盈为预期价差盈利的1/2 # 指标计算参数 std_window = 40 #固定下单单位 fixed_size = 1 x_fixed_size: float = None y_fixed_size: float = None #价差值 spread_value: float = 0 # 用来过滤交易量低的情况 spread_volume_threshold = 0 spread_volume: float = 0 spread_volume_filter: bool = False # 进场,出场点位的缓存 spread_long_entry: float = 0 spread_long_exit: float = 0 spread_long_loss_exit: float = 0 spread_short_entry: float = 0 spread_short_exit: float = 0 spread_short_loss_exit: float = 0 #用来判断操作价差的方向 open_direction_dict: Dict[str, float] = {} close_direction_dict: Dict[str, float] = {} # 用来进行参数更新判断 last_renew_date: datetime = None renew_date: datetime = None renew_status: bool = False # 用来进行预期价差收益过滤 price_diff: bool = False parameters = [ "renew_interval", "hedge_ratio_window", 'entry_multiplier', "fixed_size", "std_window", "difference_filter_num" ] variables = [ "x_multiplier", "y_multiplier", "spread_value", "spread_long_entry", "spread_long_exit", "spread_long_loss_exit" "spread_short_entry", "spread_short_exit", "spread_short_loss_exit", "spread_volume_filter" ] def __init__(self, strategy_engine: StrategyEngine, strategy_name: str, vt_symbols: List[str], setting: dict): """""" super().__init__(strategy_engine, strategy_name, vt_symbols, setting) self.short_entry_multiplier = abs(self.entry_multiplier) self.short_exit_multiplier = 0 self.long_entry_multiplier = -abs(self.entry_multiplier) self.long_exit_multiplier = 0 self.mean_window = int(self.std_window / 2) self.difference_exit_num = self.difference_filter_num / 2 self.y_symbol = self.vt_symbols[0] self.x_symbol = self.vt_symbols[1] self.x_fixed_size = int(np.abs(self.fixed_size * self.x_multiplier)) self.y_fixed_size = int(np.abs(self.fixed_size * self.y_multiplier)) self.open_direction_dict['x_symbol'] = 0 self.open_direction_dict['y_symbol'] = 0 self.close_direction_dict['x_symbol'] = 0 self.close_direction_dict['y_symbol'] = 0 # 实例化缓存期货品种价格序列容器 self.ams: Dict = {} self.ams[self.y_symbol] = ArrayManager(size=self.hedge_ratio_window + 50) self.ams[self.x_symbol] = ArrayManager(size=self.hedge_ratio_window + 50) # 实例化缓存价差价格序列容器 self.sam = SpreadArrayManager( size=max(self.std_window, self.mean_window) + 50) def on_init(self): """ Callback when strategy is inited. """ self.write_log("策略初始化") self.load_bars(10) 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_bars(self, bars: Dict[str, BarData]): """""" self.cancel_all() # OLS动态线性回归,需要缓存close_array self.ams[self.y_symbol].update_bar(bars[self.y_symbol]) self.ams[self.x_symbol].update_bar(bars[self.x_symbol]) # 动态线性回归函数 self.renew_hedge_ratio(bars[self.y_symbol]) # 计算价差 # 将价差放入sam 进行后续技术指标计算 self.spread_value = self.y_multiplier * ( bars[self.y_symbol].close_price) - self.x_multiplier * ( bars[self.x_symbol].close_price) - self.intercept self.spread_volume = min(bars[self.y_symbol].volume, bars[self.x_symbol].volume) self.price_diff = self.y_multiplier * ( bars[self.y_symbol].close_price) - self.x_multiplier * ( bars[self.x_symbol].close_price) self.sam.update_spread(self.spread_value, self.spread_volume) # 成交量过滤,成交量低于指定阈值将不会进行操作 if self.spread_volume < self.spread_volume_threshold: self.spread_volume_filter = False else: self.spread_volume_filter = True if not self.sam.inited: return # 计算技术指标 sam = self.sam std = sam.std(self.std_window) mean = sam.sma(self.mean_window) # 计算是否满足做多价差要求 spread_long_entry = mean + self.long_entry_multiplier * std spred_long_exit = mean + self.long_exit_multiplier * std # 预期收益筛选 if np.abs(spread_long_entry - spred_long_exit) >= self.difference_filter_num: self.spread_long_entry = spread_long_entry # self.spread_long_exit = spred_long_exit else: self.spread_long_entry = None # 计算是否满足作空价差要求 spread_short_entry = mean + self.short_entry_multiplier * std spread_short_exit = mean + self.short_exit_multiplier * std # 预期收益筛选 if np.abs(spread_short_entry - spread_short_exit) >= self.difference_filter_num: self.spread_short_enrtry = spread_short_entry # self.spread_short_exit = spred_short_exit else: self.spread_short_enrtry = None # 获取每个品种持仓 x_pos = self.get_pos(self.x_symbol) y_pos = self.get_pos(self.y_symbol) # 平仓逻辑判断(平仓之后,在当前bar中不会再开仓),通过判断open_direction的方向来判断是做多还是做空价差 if self.open_direction_dict[ 'y_symbol'] == 1 and self.open_direction_dict['x_symbol'] == -1: # 更新hedge ratio之后对当前仓位进行清空,当前bar不进行操作 if self.renew_status: self.sell(self.y_symbol, bars[self.y_symbol].close_price * 0.95, np.abs(y_pos)) self.cover(self.x_symbol, bars[self.x_symbol].close_price * 1.05, np.abs(x_pos)) self.open_direction_dict['y_symbol'] = 0 self.open_direction_dict['x_symbol'] = 0 self.close_direction_dict['y_symbol'] = -1 self.close_direction_dict['x_symbol'] = 1 print( f'时间{bars[self.y_symbol].datetime}', '多平 rebalance', f'平多{self.y_symbol} {self.y_fixed_size} 手 平空{self.x_symbol} {self.x_fixed_size} 手', f'价差{self.price_diff}') return # 做多价差止盈平仓,不进行成交量过滤; if self.spread_value >= self.spread_long_exit: self.sell(self.y_symbol, bars[self.y_symbol].close_price * 0.95, np.abs(y_pos)) self.cover(self.x_symbol, bars[self.x_symbol].close_price * 1.05, np.abs(x_pos)) self.open_direction_dict['y_symbol'] = 0 self.open_direction_dict['x_symbol'] = 0 self.close_direction_dict['y_symbol'] = -1 self.close_direction_dict['x_symbol'] = 1 print( f'时间{bars[self.y_symbol].datetime}', '多平 调参', f'平多{self.y_symbol} {self.y_fixed_size} 手 平空{self.x_symbol} {self.x_fixed_size} 手', f'价差{self.price_diff}') return # 做多价差止损平仓,不进行成交量过滤; if self.spread_value <= self.spread_long_loss_exit: self.sell(self.y_symbol, bars[self.y_symbol].close_price * 0.95, np.abs(y_pos)) self.cover(self.x_symbol, bars[self.x_symbol].close_price * 1.05, np.abs(x_pos)) self.open_direction_dict['y_symbol'] = 0 self.open_direction_dict['x_symbol'] = 0 self.close_direction_dict['y_symbol'] = -1 self.close_direction_dict['x_symbol'] = 1 print( f'时间{bars[self.y_symbol].datetime}', '多平 止损', f'平多{self.y_symbol} {self.y_fixed_size} 手 平空{self.x_symbol} {self.x_fixed_size} 手', f'价差{self.price_diff}') return elif self.open_direction_dict[ 'y_symbol'] == -1 and self.open_direction_dict['x_symbol'] == 1: # 更新hedge ratio之后对当前仓位进行清空,当前bar不进行操作 if self.renew_status: self.cover(self.y_symbol, bars[self.y_symbol].close_price * 1.05, np.abs(y_pos)) self.sell(self.x_symbol, bars[self.x_symbol].close_price * 0.95, np.abs(x_pos)) self.open_direction_dict['y_symbol'] = 0 self.open_direction_dict['x_symbol'] = 0 self.close_direction_dict['y_symbol'] = 1 self.close_direction_dict['x_symbol'] = -1 print( f'时间{bars[self.y_symbol].datetime}', '空平 调参', f'平空{self.y_symbol} {self.y_fixed_size} 手 平多{self.x_symbol} {self.x_fixed_size} 手', f'价差{self.price_diff}') return # 做空价差止盈平仓,不进行成交量过滤; if self.spread_value <= self.spread_short_exit: self.cover(self.y_symbol, bars[self.y_symbol].close_price * 1.05, np.abs(y_pos)) self.sell(self.x_symbol, bars[self.x_symbol].close_price * 0.95, np.abs(x_pos)) self.open_direction_dict['y_symbol'] = 0 self.open_direction_dict['x_symbol'] = 0 self.close_direction_dict['y_symbol'] = 1 self.close_direction_dict['x_symbol'] = -1 print( f'时间{bars[self.y_symbol].datetime}', '空平 止盈', f'平空{self.y_symbol} {self.y_fixed_size} 手 平多{self.x_symbol} {self.x_fixed_size} 手', f'价差{self.price_diff}') return # 做空价差止损平仓,不进行成交量过滤; if self.spread_value >= self.spread_short_loss_exit: self.cover(self.y_symbol, bars[self.y_symbol].close_price * 1.05, np.abs(y_pos)) self.sell(self.x_symbol, bars[self.x_symbol].close_price * 0.95, np.abs(x_pos)) self.open_direction_dict['y_symbol'] = 0 self.open_direction_dict['x_symbol'] = 0 self.close_direction_dict['y_symbol'] = 1 self.close_direction_dict['x_symbol'] = -1 print( f'时间{bars[self.y_symbol].datetime}', '空平 止损', f'平空{self.y_symbol} {self.y_fixed_size} 手 平多{self.x_symbol} {self.x_fixed_size} 手', f'价差{self.price_diff}') return # 开仓逻辑判断,只有两个品种都是空仓的时候才会进行开仓 if x_pos == 0 and y_pos == 0: # 做多价差开仓,加入对成交量过滤; if self.spread_long_entry and self.spread_value <= self.spread_long_entry and self.spread_volume_filter: self.buy(self.y_symbol, bars[self.y_symbol].close_price * 1.01, self.y_fixed_size) self.short(self.x_symbol, bars[self.x_symbol].close_price * 0.99, self.x_fixed_size) self.open_direction_dict['y_symbol'] = 1 self.open_direction_dict['x_symbol'] = -1 self.close_direction_dict['y_symbol'] = 0 self.close_direction_dict['x_symbol'] = 0 # 退出点位 self.spread_long_exit = mean + self.long_exit_multiplier * std self.spread_long_loss_exit = self.spread_value - self.difference_exit_num print( f'时间{bars[self.y_symbol].datetime}', '多开', f'多{self.y_symbol} {self.y_fixed_size} 手 空{self.x_symbol} {self.x_fixed_size} 手', f'价差{self.price_diff}') # 做空价差开仓,加入对成交量过滤; elif self.spread_short_entry and self.spread_value >= self.spread_short_entry and self.spread_volume_filter: self.short(self.y_symbol, bars[self.y_symbol].close_price * 0.99, self.y_fixed_size) self.buy(self.x_symbol, bars[self.x_symbol].close_price * 1.01, self.x_fixed_size) self.open_direction_dict['y_symbol'] = -1 self.open_direction_dict['x_symbol'] = 1 self.close_direction_dict['y_symbol'] = 0 self.close_direction_dict['x_symbol'] = 0 #退出点位 self.spread_short_exit = mean + self.short_exit_multiplier * std self.spread_short_loss_exit = self.spread_value + self.difference_exit_num print( f'时间{bars[self.y_symbol].datetime}', '空开', f'空{self.y_symbol} {self.y_fixed_size} 手 多{self.x_symbol} {self.x_fixed_size} 手', f'价差{self.price_diff}') def renew_hedge_ratio(self, bar: BarData): """ renew the hedge ratio based on the passed days including the not trading days. """ # 计算动态调参时间 if not self.last_renew_date: self.last_renew_date = bar.datetime self.renew_date = self.last_renew_date + timedelta( days=self.renew_interval) return # 动态调参时间 if bar.datetime >= self.renew_date: self.last_renew_date = bar.datetime self.renew_date = self.last_renew_date + timedelta( days=self.renew_interval) X = self.ams[self.x_symbol].close[-self.hedge_ratio_window:] X = sm.add_constant(X) y = self.ams[self.y_symbol].close[-self.hedge_ratio_window:] result = sm.OLS(y, X).fit() hedge_ratio = result.params[1] intercept = result.params[0] for n in range(10): mulitiplier_x = n * hedge_ratio mulitipler_intercept = n * intercept mulitipler = n if int(mulitiplier_x) >= 1: if hedge_ratio * self.x_multiplier < 0: print( 'warning ! the hedge ratio direction is changed ') self.x_multiplier = mulitiplier_x self.y_multiplier = mulitipler self.intercept = mulitipler_intercept self.x_fixed_size = int( np.abs(self.fixed_size * self.x_multiplier)) self.y_fixed_size = int( np.abs(self.fixed_size * self.y_multiplier)) # print(f'the hedge ratio is updated at {bar.datetime}') # print(f'hedge ratio is {self.x_multiplier}, intercept is {self.intercept}, multiplier is {self.y_multiplier}') break self.renew_status = True else: self.renew_status = False
class ResidualModelStrategy(StrategyTemplate): """""" author = "yiran" x_symbol: str = None y_symbol: str = None x_multiplier: float = 0 y_multiplier: float = 1 intercept: float = 0 short_entry_multiplier: float = 3 short_exit_multiplier: float = 0 long_entry_multiplier: float = -3 long_exit_multiplier: float = 0 std_window = 60 mean_window = 30 fixed_size = 1 spread_volume_threshold = 50 x_fixed_size: float = None y_fixed_size: float = None spread_value: float = 0 # 用来过滤交易量低的情况 spread_volume: float = 0 spread_long_entry: float = 0 spread_long_exit: float = 0 spread_short_entry: float = 0 spread_volume_filter: bool = False open_direction_dict: Dict[str, float] = {} close_direction_dict: Dict[str, float] = {} intra_trade_low = 0 parameters = [ "x_multiplier", "y_multiplier", "intercept", 'short_entry_multiplier', "short_exit_multiplier", "long_entry_multiplier", "long_exit_multiplier", "fixed_size", "std_window", "mean_window" ] variables = [ "spread_value", "spread_long_entry", "spread_short_entry", "spread_volume_filter" ] def __init__(self, strategy_engine: StrategyEngine, strategy_name: str, vt_symbols: List[str], setting: dict): """""" super().__init__(strategy_engine, strategy_name, vt_symbols, setting) # self.y_symbol = (self.vt_symbols[0].split('.'))[0] # self.x_symbol = (self.vt_symbols[1].split('.'))[1] self.y_symbol = self.vt_symbols[0] self.x_symbol = self.vt_symbols[1] self.x_fixed_size = int(np.abs(self.fixed_size * self.x_multiplier)) self.y_fixed_size = int(np.abs(self.fixed_size * self.y_multiplier)) self.open_direction_dict['x_symbol'] = 0 self.open_direction_dict['y_symbol'] = 0 self.close_direction_dict['x_symbol'] = 0 self.close_direction_dict['y_symbol'] = 0 self.sam = SpreadArrayManager() def on_init(self): """ Callback when strategy is inited. """ self.write_log("策略初始化") self.load_bars(10) 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_bars(self, bars: Dict[str, BarData]): """""" self.cancel_all() # 计算价差 # 将价差放入sam 进行后续技术指标计算 self.spread_value = self.y_multiplier * ( bars[self.y_symbol].close_price) - self.x_multiplier * ( bars[self.x_symbol].close_price) - self.intercept self.spread_volume = min(bars[self.y_symbol].volume, bars[self.x_symbol].volume) self.sam.update_spread(self.spread_value, self.spread_volume) # 成交量过滤,成交量低于指定阈值将不会进行操作 if self.spread_volume < self.spread_volume_threshold: self.spread_volume_filter = False else: self.spread_volume_filter = True if not self.sam.inited: return # 计算技术指标 sam = self.sam std = sam.std(self.std_window) mean = sam.sma(self.mean_window) # 计算入场和出场价位 self.spread_long_entry = mean + self.long_entry_multiplier * std self.spread_long_exit = mean + self.long_exit_multiplier * std self.spread_short_entry = mean + self.short_entry_multiplier * std self.spread_short_exit = mean + self.short_exit_multiplier * std # 获取每个品种持仓 x_pos = self.get_pos(self.x_symbol) y_pos = self.get_pos(self.y_symbol) # 平仓逻辑判断(平仓之后,在当前bar中不会再开仓),通过判断open_direction的方向来判断是做多还是做空价差 if self.open_direction_dict[ 'y_symbol'] == 1 and self.open_direction_dict['x_symbol'] == -1: # 做多价差平仓,不进行成交量过滤; if self.spread_value >= self.spread_long_exit: self.sell(self.y_symbol, bars[self.y_symbol].close_price * 0.95, np.abs(y_pos)) self.cover(self.x_symbol, bars[self.x_symbol].close_price * 1.05, np.abs(x_pos)) self.open_direction_dict['y_symbol'] = 0 self.open_direction_dict['x_symbol'] = 0 self.close_direction_dict['y_symbol'] = -1 self.close_direction_dict['x_symbol'] = 1 print( f'时间{bars[self.y_symbol].datetime}', '做多价差平仓', f'平多{self.y_symbol} {self.y_fixed_size} 手 平空{self.x_symbol} {self.x_fixed_size} 手' ) return elif self.open_direction_dict[ 'y_symbol'] == -1 and self.open_direction_dict['x_symbol'] == 1: # 做空价差平仓,不进行成交量过滤; if self.spread_value <= self.spread_short_exit: self.cover(self.y_symbol, bars[self.y_symbol].close_price * 1.05, np.abs(y_pos)) self.sell(self.x_symbol, bars[self.x_symbol].close_price * 0.95, np.abs(x_pos)) self.open_direction_dict['y_symbol'] = 0 self.open_direction_dict['x_symbol'] = 0 self.close_direction_dict['y_symbol'] = 1 self.close_direction_dict['x_symbol'] = -1 print( f'时间{bars[self.y_symbol].datetime}', '做空价差平仓', f'平空{self.y_symbol} {self.y_fixed_size} 手 平多{self.x_symbol} {self.x_fixed_size} 手' ) return # 针对两种情况1.可能出现某一个leg没有买满的情况 2. 可能出现某一个leg仓位没有平掉的情况 # 没有考虑: 多买的情况;似乎不可能出现 # 这种情况下,如果有其它买入信号,不会去执行相应买入动作 if (x_pos != 0 or y_pos != 0): symbols = ['y_symbol', 'x_symbol'] vt_symbol_dict = { 'y_symbol': self.y_symbol, 'x_symbol': self.x_symbol } pos_dict = {'y_symbol': y_pos, 'x_symbol': x_pos} fixed_size_dict = { 'y_symbol': self.y_fixed_size, 'x_symbol': self.x_fixed_size } bar_dict = { 'y_symbol': bars[self.y_symbol].close_price, 'x_symbol': bars[self.x_symbol].close_price } for symbol in symbols: # direction = self.open_direction_dict[symbol] diff = int(np.abs(pos_dict[symbol] - fixed_size_dict[symbol])) # 如果处于平仓状态:对没有平掉的仓位,进行平仓 if self.open_direction_dict[symbol] == 0: if self.close_direction_dict[symbol] == -1: self.sell(vt_symbol_dict[symbol], bar_dict[symbol] * 0.95, np.abs(pos_dict[symbol])) elif self.close_direction_dict[symbol] == -1: self.cover(vt_symbol_dict[symbol], bar_dict[symbol] * 1.05, np.abs(pos_dict[symbol])) # 如果是处于持仓状态,对没有买到指定数量的品种,进行补仓 if self.open_direction_dict[symbol] != 0 and diff >= 1: if self.open_direction_dict[symbol] == 1: self.buy(vt_symbol_dict[symbol], bar_dict[symbol] * 1.01, diff) elif self.open_direction_dict[symbol] == -1: self.short(vt_symbol_dict[symbol], bar_dict[symbol] * 0.99, diff) # 开仓逻辑判断,只有两个品种都是空仓的时候才会进行开仓 if x_pos == 0 and y_pos == 0: # 做多价差开仓,加入对成交量过滤; if self.spread_value <= self.spread_long_entry and self.spread_volume_filter: self.buy(self.y_symbol, bars[self.y_symbol].close_price * 1.01, self.y_fixed_size) self.short(self.x_symbol, bars[self.x_symbol].close_price * 0.99, self.x_fixed_size) self.open_direction_dict['y_symbol'] = 1 self.open_direction_dict['x_symbol'] = -1 self.close_direction_dict['y_symbol'] = 0 self.close_direction_dict['x_symbol'] = 0 print( f'时间{bars[self.y_symbol].datetime}', '做多价差开仓', f'做多{self.y_symbol} {self.y_fixed_size} 手 做空{self.x_symbol} {self.x_fixed_size} 手' ) # 做空价差开仓,加入对成交量过滤; elif self.spread_value >= self.spread_short_entry and self.spread_volume_filter: self.short(self.y_symbol, bars[self.y_symbol].close_price * 0.99, self.y_fixed_size) self.buy(self.x_symbol, bars[self.x_symbol].close_price * 1.01, self.x_fixed_size) self.open_direction_dict['y_symbol'] = -1 self.open_direction_dict['x_symbol'] = 1 self.close_direction_dict['y_symbol'] = 0 self.close_direction_dict['x_symbol'] = 0 print( f'时间{bars[self.y_symbol].datetime}', '做空价差开仓', f'做空{self.y_symbol} {self.y_fixed_size} 手 做多{self.x_symbol} {self.x_fixed_size} 手' )
class DynamicResidualModelStrategy(StrategyTemplate): """""" author = "yiran" x_symbol: str = None y_symbol: str = None x_multiplier: float = 0 y_multiplier: float = 1 intercept: float = 0 renew_interval: int = 30 hedge_ratio_window: int = 90 short_entry_multiplier: float = 3 short_exit_multiplier: float = 0 long_entry_multiplier: float = -3 long_exit_multiplier: float = -0 # 预期价差盈利 difference_filter_num: float = 60 # 止损价差 difference_exit_num: float = 30 std_window = 120 mean_window = 60 fixed_size = 1 spread_volume_threshold = 30 x_fixed_size: float = None y_fixed_size: float = None spread_value: float = 0 # 用来过滤交易量低的情况 spread_volume: float = 0 spread_volume_filter: bool = False spread_long_entry: float = 0 spread_long_exit: float = 0 spread_long_loss_exit: float = 0 spread_short_entry: float = 0 spread_short_exit: float = 0 spread_short_loss_exit: float = 0 #用来判断操作价差的方向 open_direction_dict: Dict[str, float] = {} close_direction_dict: Dict[str, float] = {} intra_trade_low = 0 # 用来进行参数更新判断 last_renew_date: datetime = None renew_date: datetime = None renew_status: bool = False # 用来进行预期价差收益过滤 price_diff: bool = False parameters = [ "x_multiplier", "y_multiplier", "intercept", "renew_interval", 'short_entry_multiplier', "short_exit_multiplier", "long_entry_multiplier", "long_exit_multiplier", "hedge_ratio_window", "fixed_size", "std_window", "mean_window" ] variables = [ "spread_value", "spread_long_entry", "spread_short_entry", "spread_volume_filter" ] def __init__(self, strategy_engine: StrategyEngine, strategy_name: str, vt_symbols: List[str], setting: dict): """""" super().__init__(strategy_engine, strategy_name, vt_symbols, setting) # self.y_symbol = (self.vt_symbols[0].split('.'))[0] # self.x_symbol = (self.vt_symbols[1].split('.'))[1] self.y_symbol = self.vt_symbols[0] self.x_symbol = self.vt_symbols[1] self.x_fixed_size = int(np.abs(self.fixed_size * self.x_multiplier)) self.y_fixed_size = int(np.abs(self.fixed_size * self.y_multiplier)) self.open_direction_dict['x_symbol'] = 0 self.open_direction_dict['y_symbol'] = 0 self.close_direction_dict['x_symbol'] = 0 self.close_direction_dict['y_symbol'] = 0 self.ams: Dict = {} self.ams[self.y_symbol] = ArrayManager(size=self.hedge_ratio_window + 50) self.ams[self.x_symbol] = ArrayManager(size=self.hedge_ratio_window + 50) self.sam = SpreadArrayManager( size=max(self.std_window, self.mean_window) + 50) def on_init(self): """ Callback when strategy is inited. """ self.write_log("策略初始化") self.load_bars(10) 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_bars(self, bars: Dict[str, BarData]): """""" self.cancel_all() self.ams[self.y_symbol].update_bar(bars[self.y_symbol]) self.ams[self.x_symbol].update_bar(bars[self.x_symbol]) self.renew_hedge_ratio(bars[self.y_symbol]) # 计算价差 # 将价差放入sam 进行后续技术指标计算 self.spread_value = self.y_multiplier * ( bars[self.y_symbol].close_price) - self.x_multiplier * ( bars[self.x_symbol].close_price) - self.intercept self.spread_volume = min(bars[self.y_symbol].volume, bars[self.x_symbol].volume) self.price_diff = self.y_multiplier * ( bars[self.y_symbol].close_price) - self.x_multiplier * ( bars[self.x_symbol].close_price) self.sam.update_spread(self.spread_value, self.spread_volume) # 成交量过滤,成交量低于指定阈值将不会进行操作 if self.spread_volume < self.spread_volume_threshold: self.spread_volume_filter = False else: self.spread_volume_filter = True if not self.sam.inited: return # 计算技术指标 sam = self.sam std = sam.std(self.std_window) mean = sam.sma(self.mean_window) spread_long_entry = mean + self.long_entry_multiplier * std spred_long_exit = mean + self.long_exit_multiplier * std # 预期收益筛选 if np.abs(spread_long_entry - spred_long_exit) >= self.difference_filter_num: self.spread_long_entry = spread_long_entry # self.spread_long_exit = spred_long_exit else: self.spread_long_entry = None spread_short_entry = mean + self.short_entry_multiplier * std spread_short_exit = mean + self.short_exit_multiplier * std if np.abs(spread_short_entry - spread_short_exit) >= self.difference_filter_num: self.spread_short_enrtry = spread_short_entry # self.spread_short_exit = spred_short_exit else: self.spread_short_enrtry = None # 获取每个品种持仓 x_pos = self.get_pos(self.x_symbol) y_pos = self.get_pos(self.y_symbol) # 平仓逻辑判断(平仓之后,在当前bar中不会再开仓),通过判断open_direction的方向来判断是做多还是做空价差 if self.open_direction_dict[ 'y_symbol'] == 1 and self.open_direction_dict['x_symbol'] == -1: # 更新hedge ratio之后对当前仓位进行清空,当前bar不进行操作 if self.renew_status: self.sell(self.y_symbol, bars[self.y_symbol].close_price * 0.95, np.abs(y_pos)) self.cover(self.x_symbol, bars[self.x_symbol].close_price * 1.05, np.abs(x_pos)) self.open_direction_dict['y_symbol'] = 0 self.open_direction_dict['x_symbol'] = 0 self.close_direction_dict['y_symbol'] = -1 self.close_direction_dict['x_symbol'] = 1 print( f'时间{bars[self.y_symbol].datetime}', '多平 rebalance', f'平多{self.y_symbol} {self.y_fixed_size} 手 平空{self.x_symbol} {self.x_fixed_size} 手', f'价差{self.price_diff}') return # 做多价差止盈平仓,不进行成交量过滤; if self.spread_value >= self.spread_long_exit: self.sell(self.y_symbol, bars[self.y_symbol].close_price * 0.95, np.abs(y_pos)) self.cover(self.x_symbol, bars[self.x_symbol].close_price * 1.05, np.abs(x_pos)) self.open_direction_dict['y_symbol'] = 0 self.open_direction_dict['x_symbol'] = 0 self.close_direction_dict['y_symbol'] = -1 self.close_direction_dict['x_symbol'] = 1 print( f'时间{bars[self.y_symbol].datetime}', '多平 调参', f'平多{self.y_symbol} {self.y_fixed_size} 手 平空{self.x_symbol} {self.x_fixed_size} 手', f'价差{self.price_diff}') return # 做多价差止损平仓,不进行成交量过滤; if self.spread_value <= self.spread_long_loss_exit: self.sell(self.y_symbol, bars[self.y_symbol].close_price * 0.95, np.abs(y_pos)) self.cover(self.x_symbol, bars[self.x_symbol].close_price * 1.05, np.abs(x_pos)) self.open_direction_dict['y_symbol'] = 0 self.open_direction_dict['x_symbol'] = 0 self.close_direction_dict['y_symbol'] = -1 self.close_direction_dict['x_symbol'] = 1 print( f'时间{bars[self.y_symbol].datetime}', '多平 止损', f'平多{self.y_symbol} {self.y_fixed_size} 手 平空{self.x_symbol} {self.x_fixed_size} 手', f'价差{self.price_diff}') return elif self.open_direction_dict[ 'y_symbol'] == -1 and self.open_direction_dict['x_symbol'] == 1: # 更新hedge ratio之后对当前仓位进行清空,当前bar不进行操作 if self.renew_status: self.cover(self.y_symbol, bars[self.y_symbol].close_price * 1.05, np.abs(y_pos)) self.sell(self.x_symbol, bars[self.x_symbol].close_price * 0.95, np.abs(x_pos)) self.open_direction_dict['y_symbol'] = 0 self.open_direction_dict['x_symbol'] = 0 self.close_direction_dict['y_symbol'] = 1 self.close_direction_dict['x_symbol'] = -1 print( f'时间{bars[self.y_symbol].datetime}', '空平 调参', f'平空{self.y_symbol} {self.y_fixed_size} 手 平多{self.x_symbol} {self.x_fixed_size} 手', f'价差{self.price_diff}') return # 做空价差止盈平仓,不进行成交量过滤; if self.spread_value <= self.spread_short_exit: self.cover(self.y_symbol, bars[self.y_symbol].close_price * 1.05, np.abs(y_pos)) self.sell(self.x_symbol, bars[self.x_symbol].close_price * 0.95, np.abs(x_pos)) self.open_direction_dict['y_symbol'] = 0 self.open_direction_dict['x_symbol'] = 0 self.close_direction_dict['y_symbol'] = 1 self.close_direction_dict['x_symbol'] = -1 print( f'时间{bars[self.y_symbol].datetime}', '空平 止盈', f'平空{self.y_symbol} {self.y_fixed_size} 手 平多{self.x_symbol} {self.x_fixed_size} 手', f'价差{self.price_diff}') return # 做空价差止损平仓,不进行成交量过滤; if self.spread_value >= self.spread_short_loss_exit: self.cover(self.y_symbol, bars[self.y_symbol].close_price * 1.05, np.abs(y_pos)) self.sell(self.x_symbol, bars[self.x_symbol].close_price * 0.95, np.abs(x_pos)) self.open_direction_dict['y_symbol'] = 0 self.open_direction_dict['x_symbol'] = 0 self.close_direction_dict['y_symbol'] = 1 self.close_direction_dict['x_symbol'] = -1 print( f'时间{bars[self.y_symbol].datetime}', '空平 止损', f'平空{self.y_symbol} {self.y_fixed_size} 手 平多{self.x_symbol} {self.x_fixed_size} 手', f'价差{self.price_diff}') return # 针对两种情况1.可能出现某一个leg没有买满的情况 2. 可能出现某一个leg仓位没有平掉的情况 # 没有考虑: 多买的情况;似乎不可能出现 # 这种情况下,如果有其它买入信号,不会去执行相应买入动作 if (x_pos != 0 or y_pos != 0): symbols = ['y_symbol', 'x_symbol'] vt_symbol_dict = { 'y_symbol': self.y_symbol, 'x_symbol': self.x_symbol } pos_dict = {'y_symbol': y_pos, 'x_symbol': x_pos} fixed_size_dict = { 'y_symbol': self.y_fixed_size, 'x_symbol': self.x_fixed_size } bar_dict = { 'y_symbol': bars[self.y_symbol].close_price, 'x_symbol': bars[self.x_symbol].close_price } for symbol in symbols: # direction = self.open_direction_dict[symbol] diff = int(np.abs(pos_dict[symbol] - fixed_size_dict[symbol])) # 如果处于平仓状态:对没有平掉的仓位,进行平仓 if self.open_direction_dict[symbol] == 0: if self.close_direction_dict[symbol] == -1: self.sell(vt_symbol_dict[symbol], bar_dict[symbol] * 0.95, np.abs(pos_dict[symbol])) elif self.close_direction_dict[symbol] == -1: self.cover(vt_symbol_dict[symbol], bar_dict[symbol] * 1.05, np.abs(pos_dict[symbol])) # 如果是处于持仓状态,对没有买到指定数量的品种,进行补仓 if self.open_direction_dict[symbol] != 0 and diff >= 1: if self.open_direction_dict[symbol] == 1: self.buy(vt_symbol_dict[symbol], bar_dict[symbol] * 1.01, diff) elif self.open_direction_dict[symbol] == -1: self.short(vt_symbol_dict[symbol], bar_dict[symbol] * 0.99, diff) # 开仓逻辑判断,只有两个品种都是空仓的时候才会进行开仓 if x_pos == 0 and y_pos == 0: # 做多价差开仓,加入对成交量过滤; if self.spread_long_entry and self.spread_value <= self.spread_long_entry and self.spread_volume_filter: self.buy(self.y_symbol, bars[self.y_symbol].close_price * 1.01, self.y_fixed_size) self.short(self.x_symbol, bars[self.x_symbol].close_price * 0.99, self.x_fixed_size) self.open_direction_dict['y_symbol'] = 1 self.open_direction_dict['x_symbol'] = -1 self.close_direction_dict['y_symbol'] = 0 self.close_direction_dict['x_symbol'] = 0 # 退出点位 self.spread_long_exit = mean + self.long_exit_multiplier * std self.spread_long_loss_exit = self.spread_value - self.difference_exit_num print( f'时间{bars[self.y_symbol].datetime}', '多开', f'多{self.y_symbol} {self.y_fixed_size} 手 空{self.x_symbol} {self.x_fixed_size} 手', f'价差{self.price_diff}') # 做空价差开仓,加入对成交量过滤; elif self.spread_short_entry and self.spread_value >= self.spread_short_entry and self.spread_volume_filter: self.short(self.y_symbol, bars[self.y_symbol].close_price * 0.99, self.y_fixed_size) self.buy(self.x_symbol, bars[self.x_symbol].close_price * 1.01, self.x_fixed_size) self.open_direction_dict['y_symbol'] = -1 self.open_direction_dict['x_symbol'] = 1 self.close_direction_dict['y_symbol'] = 0 self.close_direction_dict['x_symbol'] = 0 #退出点位 self.spread_short_exit = mean + self.short_exit_multiplier * std self.spread_short_loss_exit = self.spread_value + self.difference_exit_num print( f'时间{bars[self.y_symbol].datetime}', '空开', f'空{self.y_symbol} {self.y_fixed_size} 手 多{self.x_symbol} {self.x_fixed_size} 手', f'价差{self.price_diff}') def renew_hedge_ratio(self, bar: BarData): """ renew the hedge ratio based on the passed days including the not trading days. """ # the last renew date if not self.last_renew_date: self.last_renew_date = bar.datetime self.renew_date = self.last_renew_date + timedelta( days=self.renew_interval) return if bar.datetime >= self.renew_date: self.last_renew_date = bar.datetime self.renew_date = self.last_renew_date + timedelta( days=self.renew_interval) X = self.ams[self.x_symbol].close[-self.hedge_ratio_window:] X = sm.add_constant(X) y = self.ams[self.y_symbol].close[-self.hedge_ratio_window:] result = sm.OLS(y, X).fit() hedge_ratio = result.params[1] intercept = result.params[0] for n in range(10): mulitiplier_x = n * hedge_ratio mulitipler_intercept = n * intercept mulitipler = n if int(mulitiplier_x) >= 1: if hedge_ratio * self.x_multiplier < 0: print( 'warning ! the hedge ratio direction is changed ') self.x_multiplier = mulitiplier_x self.y_multiplier = mulitipler self.intercept = mulitipler_intercept self.x_fixed_size = int( np.abs(self.fixed_size * self.x_multiplier)) self.y_fixed_size = int( np.abs(self.fixed_size * self.y_multiplier)) # print(f'the hedge ratio is updated at {bar.datetime}') # print(f'hedge ratio is {self.x_multiplier}, intercept is {self.intercept}, multiplier is {self.y_multiplier}') break self.renew_status = True else: self.renew_status = False
class SimpleBollupSell(StrategyTemplate): """""" author = "yiran" x_symbol: str = None y_symbol: str = None # 退出条件 hold_window: int = 100 profit_point: float = 20 exit_point: float = -5 # profit_pct: float = 0.2 # exit_pct: float = -0.06 #轨道宽度 entry_multiplier: float = 3 price_add = 2 # 可交易的价差范围 spread_low_range = 200 spread_high_range = 400 # 指标计算参数 std_window = 240*30 std_mean_window_ratio = 1 boll_up_cum_threshold = 10 #固定下单单位 fixed_size = 1 #价差值 spread_value: float = 0 # 用来过滤交易量低的情况 spread_volume_threshold = 20 spread_volume: float = 0 spread_volume_filter: bool = False # 进场,出场点位的缓存 spread_long_entry: float = 0 spread_long_exit: float = 0 spread_short_loss_exit: float = 0 boll_up_cum = 0 day_cum = 0 day_cum_threshold = 5 # 盈利记录 last_short_trade_profit:bool = False trade_date:datetime = None stop_trade_date = 5 parameters = [ 'entry_multiplier', "fixed_size", "std_window", 'std_mean_window_ratio', 'profit_point', 'exit_point', 'hold_window', 'boll_up_cum_threshold', 'day_cum_threshold', 'spread_low_range', 'spread_high_range' ] variables = ["x_multiplier","y_multiplier",'x_pos_target','y_pos_target',"spread_volume_filter"] def __init__( self, strategy_engine: StrategyEngine, strategy_name: str, vt_symbols: List[str], setting: dict ): """""" super().__init__(strategy_engine, strategy_name, vt_symbols, setting) self.last_tick_time: datetime = None self.ams:Dict[str, ArrayManager] = {} self.bgs: Dict[str, BarGenerator] = {} # 策略内动态调整OLS相关参数,小数参数会被int() self.short_entry_multiplier = abs(self.entry_multiplier) self.short_exit_multiplier = 0 self.long_entry_multiplier = -abs(self.entry_multiplier) self.long_exit_multiplier = 0 self.mean_window = int(self.std_window * self.std_mean_window_ratio) self.y_symbol = self.vt_symbols[0] self.x_symbol = self.vt_symbols[1] self.x_fixed_size = np.abs(self.fixed_size * 1) self.y_fixed_size = np.abs(self.fixed_size * 1) self.x_pos_target = 0 self.y_pos_target = 0 # 实例化缓存期货品种价格序列容器 for vt_symbol in self.vt_symbols: self.ams[vt_symbol] = ArrayManager(size=max(self.std_window,self.mean_window)+50) #实例化bg容器 for vt_symbol in self.vt_symbols: def on_bar(bar: BarData): """""" pass self.bgs[vt_symbol] = BarGenerator(on_bar) # 实例化缓存价差价格序列容器 self.sam = SpreadArrayManager(size=max(self.std_window,self.mean_window)+50) def on_init(self): """ Callback when strategy is inited. """ self.write_log("策略初始化") self.load_bars(max(int(self.std_window/240),10)) 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. """ if ( self.last_tick_time and self.last_tick_time.minute != tick.datetime.minute ): bars = {} for vt_symbol, bg in self.bgs.items(): bars[vt_symbol] = bg.generate() self.on_bars(bars) bg: BarGenerator = self.bgs[tick.vt_symbol] bg.update_tick(tick) self.last_tick_time = tick.datetime def on_bars(self, bars: Dict[str, BarData]): """""" self.cancel_all() #初始化 if not self.trade_date: self.trade_date = (bars[self.y_symbol].datetime) self.last_short_trade_profit = True # 一笔交易亏损停止交易天数 if not self.last_short_trade_profit: if self.trade_date.date() != bars[self.y_symbol].datetime.date(): self.trade_date = bars[self.y_symbol].datetime self.day_cum +=1 print(self.trade_date.date(), bars[self.y_symbol].datetime.date(),self.day_cum) if self.day_cum > self.day_cum_threshold: self.day_cum = 0 self.last_short_trade_profit = True # 计算价差 # 将价差放入sam 进行后续技术指标计算 self.spread_value = self.y_fixed_size*(bars[self.y_symbol].close_price) - self.x_fixed_size*(bars[self.x_symbol].close_price) self.spread_volume = min(bars[self.y_symbol].volume, bars[self.x_symbol].volume) self.sam.update_spread(self.spread_value, self.spread_volume) # 成交量过滤,成交量低于指定阈值将不会进行操作 if self.spread_volume < self.spread_volume_threshold: self.spread_volume_filter = False else: self.spread_volume_filter = True if not self.sam.inited: return # 计算技术指标 sam = self.sam std = sam.std(self.std_window) mean = sam.sma(self.mean_window) # 计算是否满足做多价差要求 spread_long_entry = mean + self.long_entry_multiplier * std # 计算连续突破的个数 if self.spread_value > spread_long_entry: self.boll_up_cum += 1 else: self.boll_up_cum = 0 # 获取每个品种持仓 self.x_pos = self.get_pos(self.x_symbol) self.y_pos = self.get_pos(self.y_symbol) if self.x_pos == 0 and self.y_pos == 0: # 多开 if self.boll_up_cum>self.boll_up_cum_threshold and self.spread_value >= self.spread_long_entry and self.spread_volume_filter: #if self.last_short_trade_profit: if self.last_short_trade_profit and self.spread_low_range<self.spread_value<self.spread_high_range: self.y_pos_target = -self.y_fixed_size self.x_pos_target = self.x_fixed_size self.hold_time = 0 self.start_order = True self.boll_up_cum = 0 self.trade_date = (bars[self.y_symbol].datetime) # print(self.spread_short_loss_exit,self.spread_short_profit_exit,self.difference_exit_num,self.spread_value - self.difference_exit_num) print(f'时间{bars[self.y_symbol].datetime}','空开 ',f'空{self.y_symbol} {self.y_fixed_size} 手 多{self.x_symbol} {self.x_fixed_size} 手',f'价差{self.spread_value}') else: self.start_order = False return elif self.y_pos < 0 and self.x_pos > 0 : self.boll_up_cum = 0 if self.start_order: # 近似实际开仓价格 # self.spread_short_profit_exit = self.spread_value*(1+self.profit_pct) self.spread_short_profit_exit = self.spread_value - self.profit_point # self.spread_short_loss_exit = self.spread_value*(1+self.exit_pct) self.spread_short_loss_exit = self.spread_value - self.exit_point self.trade_date = bars[self.y_symbol].datetime self.start_order = False print(f'时间{bars[self.y_symbol].datetime},{self.spread_short_profit_exit},{self.spread_short_loss_exit}') # 持仓周期计算 if self.hold_time < self.hold_window: self.hold_time += 1 elif self.hold_time >= self.hold_window: self.y_pos_target = 0 self.x_pos_target = 0 self.trade_date =(bars[self.y_symbol].datetime) self.last_short_trade_profit = False print(f'时间{bars[self.y_symbol].datetime}','空平 超出持仓时间',f'平空{self.y_symbol} {self.y_fixed_size} 手 平多{self.x_symbol} {self.x_fixed_size} 手',f'价差{self.spread_value} {self.hold_time} {self.profit_point}{self.trade_date}{self.last_short_trade_profit}') self.hold_time = 0 # 多平止盈 if self.spread_value <= self.spread_short_profit_exit: self.y_pos_target = 0 self.x_pos_target = 0 self.last_short_trade_profit = False self.hold_time = 0 self.trade_date =(bars[self.y_symbol].datetime) print(f'时间{bars[self.y_symbol].datetime}','空平 止盈',f'平空{self.y_symbol} {self.y_fixed_size} 手 平多{self.x_symbol} {self.x_fixed_size} 手',f'价差{self.spread_value}') # 多平止损 elif self.spread_value >= self.spread_short_loss_exit: self.y_pos_target = 0 self.x_pos_target = 0 self.last_short_trade_profit = False self.hold_time = 0 self.trade_date = (bars[self.y_symbol].datetime) print(f'时间{bars[self.y_symbol].datetime}','空平 止损',f'平空{self.y_symbol} {self.y_fixed_size} 手 平多{self.x_symbol} {self.x_fixed_size} 手',f'价差{self.spread_value}') target = {self.x_symbol:self.x_pos_target, self.y_symbol:self.y_pos_target} for vt_symbol in self.vt_symbols: target_pos = target[vt_symbol] current_pos = self.get_pos(vt_symbol) pos_diff = target_pos - current_pos volume = abs(pos_diff) bar = bars[vt_symbol] if pos_diff > 0: price = bar.close_price + self.price_add if current_pos < 0: self.cover(vt_symbol, price, volume) else: self.buy(vt_symbol, price, volume) elif pos_diff < 0: price = bar.close_price - self.price_add if current_pos > 0: self.sell(vt_symbol, price, volume) else: self.short(vt_symbol, price, volume)
class DynamicResidualModelStrategy(StrategyTemplate): """""" author = "yiran" x_symbol: str = None y_symbol: str = None # 策略内动态调整周期 # 天数 renew_interval: int = 7 # 分钟线数据,90天约等于90*240 hedge_ratio_window: int = 240 #轨道宽度 entry_multiplier: float = 3 price_add = 5 # 预期价差盈利 difference_filter_num: float = 50 difference_exit_rato: float = 1 # 预期止盈为预期价差盈利的1/2 # 指标计算参数 std_window = 240 std_mean_window_ratio = 0.5 #固定下单单位 fixed_size = 1 #价差值 spread_value: float = 0 # 用来过滤交易量低的情况 spread_volume_threshold = 0 spread_volume: float = 0 spread_volume_filter: bool = False # 进场,出场点位的缓存 spread_long_entry: float = 0 spread_long_exit: float = 0 spread_long_loss_exit: float = 0 spread_short_entry: float = 0 spread_short_exit: float = 0 spread_short_loss_exit: float = 0 #用来判断操作价差的方向 open_direction_dict: Dict[str, float] = {} close_direction_dict: Dict[str, float] = {} # 用来进行参数更新判断 last_renew_date: datetime = None renew_date: datetime = None renew_status: bool = False # 盈利记录 last_long_trade_profit: bool = False last_short_trade_profit: bool = False parameters = [ "renew_interval", "hedge_ratio_window", 'entry_multiplier', "fixed_size", "std_window", "difference_filter_num" ] variables = [ "x_multiplier", "y_multiplier", 'x_pos_target', 'y_pos_target', "spread_volume_filter" ] spread_record = [] datetime_record = [] volume_record = [] def __init__(self, strategy_engine: StrategyEngine, strategy_name: str, vt_symbols: List[str], setting: dict): """""" super().__init__(strategy_engine, strategy_name, vt_symbols, setting) self.last_tick_time: datetime = None self.ams: Dict[str, ArrayManager] = {} self.bgs: Dict[str, BarGenerator] = {} # 策略内动态调整OLS相关参数,小数参数会被int() self.x_multiplier = 0 self.y_multiplier = 1 # 不考虑intercept self.intercept = 0 self.short_entry_multiplier = abs(self.entry_multiplier) self.short_exit_multiplier = 0 self.long_entry_multiplier = -abs(self.entry_multiplier) self.long_exit_multiplier = 0 self.mean_window = int(self.std_window * self.std_mean_window_ratio) self.difference_exit_num = self.difference_filter_num * self.difference_exit_rato self.y_symbol = self.vt_symbols[0] self.x_symbol = self.vt_symbols[1] self.x_fixed_size = np.abs(self.fixed_size * self.x_multiplier) self.y_fixed_size = np.abs(self.fixed_size * self.y_multiplier) self.x_pos_target = 0 self.y_pos_target = 0 self.spread_record = [] self.datetime_record = [] self.volume_record = [] # 实例化缓存期货品种价格序列容器 for vt_symbol in self.vt_symbols: self.ams[vt_symbol] = ArrayManager(size=self.hedge_ratio_window + 50) #实例化bg容器 for vt_symbol in self.vt_symbols: def on_bar(bar: BarData): """""" pass self.bgs[vt_symbol] = BarGenerator(on_bar) # 实例化缓存价差价格序列容器 self.sam = SpreadArrayManager( size=max(self.std_window, self.mean_window) + 50) def on_init(self): """ Callback when strategy is inited. """ self.write_log("策略初始化") self.load_bars(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. """ if (self.last_tick_time and self.last_tick_time.minute != tick.datetime.minute): bars = {} for vt_symbol, bg in self.bgs.items(): bars[vt_symbol] = bg.generate() self.on_bars(bars) bg: BarGenerator = self.bgs[tick.vt_symbol] bg.update_tick(tick) self.last_tick_time = tick.datetime def on_bars(self, bars: Dict[str, BarData]): """""" self.cancel_all() # OLS动态线性回归,需要缓存close_array self.ams[self.y_symbol].update_bar(bars[self.y_symbol]) self.ams[self.x_symbol].update_bar(bars[self.x_symbol]) # 动态线性回归函数 # 计算价差 # 将价差放入sam 进行后续技术指标计算 self.spread_value = (bars[self.y_symbol].close_price) - ( bars[self.x_symbol].close_price) self.spread_volume = min(bars[self.y_symbol].volume, bars[self.x_symbol].volume) self.sam.update_spread(self.spread_value, self.spread_volume) self.spread_record.append(self.spread_value) self.datetime_record.append(bars[self.y_symbol].datetime) self.volume_record.append( min(bars[self.y_symbol].volume, bars[self.x_symbol].volume))