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
Ejemplo n.º 3
0
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} 手'
                )
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
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)
Ejemplo n.º 6
0
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))