Administrator
2024-05-09 77fea830c2e11f94c364a37b8bda792a51d11eec
l2/cancel_buy_strategy.py
@@ -39,6 +39,58 @@
    HourCancelBigNumComputer().set_real_place_order_index(code, index, buy_single_index)
    NewGCancelBigNumComputer().set_real_place_order_index(code, index, buy_single_index, is_default)
    FCancelBigNumComputer().set_real_order_index(code, index, is_default)
    JCancelBigNumComputer().set_real_place_order_index(code, index, buy_single_index, is_default)
class BaseCancel:
    def set_real_place_order_index(self, code, index, buy_single_index, is_default):
        pass
        # 撤单成功
    def cancel_success(self, code):
        pass
        # 下单成功
    def place_order_success(self, code):
        pass
class L2DataComputeUtil:
    """
    L2数据计算帮助类
    """
    @classmethod
    def compute_left_buy_order(cls, code, start_index, end_index, limit_up_price, min_money=500000):
        """
        计算剩下的委托买单
        @param code: 代码
        @param start_index:起始索引(包含)
        @param end_index: 结束索引(包含)
        @param limit_up_price: 涨停价
        @param min_money: 最小的资金
        @return:笔数,手数
        """
        min_volume = min_money // int(limit_up_price * 100)
        total_datas = local_today_datas.get(code)
        canceled_buyno_map = local_today_canceled_buyno_map.get(code)
        total_count = 0
        total_num = 0
        for i in range(start_index, end_index + 1):
            data = total_datas[i]
            val = data['val']
            if not L2DataUtil.is_limit_up_price_buy(val):
                continue
            if val['num'] < min_volume:
                continue
            left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(code, i,
                                                                                                     total_datas,
                                                                                                     canceled_buyno_map)
            if left_count > 0:
                total_count += 1
                total_num += val["num"]
        return total_count, total_num
class SCancelBigNumComputer:
@@ -843,6 +895,9 @@
    __instance = None
    # 下单位之后的封单中的大单
    __follow_big_order_cache = {}
    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            cls.__instance = super(DCancelBigNumComputer, cls).__new__(cls, *args, **kwargs)
@@ -881,6 +936,18 @@
                    # 量低于
                    return True, r.id_
        return False, None
    def compute_d_cancel_watch_index(self, code):
        # 计算D撤囊括范围
        # real_place_order_index = self.__SCancelBigNumComputer.get_real_place_order_index_cache(code)
        pass
    def __clear_data(self, code):
        if code in self.__follow_big_order_cache:
            self.__follow_big_order_cache.pop(code)
    def place_order_success(self, code, buy_single_index, buy_exec_index):
        self.__clear_data(code)
# ---------------------------------L撤-------------------------------
@@ -1252,7 +1319,8 @@
    def set_real_place_order_index(self, code, index, buy_single_index=None, is_default=False):
        l2_log.l_cancel_debug(code, f"设置真实下单位-{index},buy_single_index-{buy_single_index}")
        self.__real_place_order_index_dict[code] = (index, is_default)
        RedisUtils.setex_async(self.__db, f"l_cancel_real_place_order_index-{code}", tool.get_expire(), json.dumps((index, is_default)))
        RedisUtils.setex_async(self.__db, f"l_cancel_real_place_order_index-{code}", tool.get_expire(),
                               json.dumps((index, is_default)))
        if buy_single_index is not None:
            if code in self.__last_trade_progress_dict:
                # L撤从成交进度位开始计算
@@ -1409,7 +1477,7 @@
        if real_place_order_info and real_place_order_info[0] > index:
            total_datas = local_today_datas.get(code)
            min_num = int(5000 / (float(gpcode_manager.get_limit_up_price(code))))
            for j in range(real_place_order_info[0]-1, index , -1):
            for j in range(real_place_order_info[0] - 1, index, -1):
                data = total_datas[j]
                val = data['val']
                if data["index"] in watch_indexes:
@@ -1773,7 +1841,9 @@
        if tool.trade_time_sub(tool.get_now_time_str(), total_datas[real_order_index]['val']['time']) > 1 * 60:
            return False, "下单超过60s"
        THRESHOLD_MONEY_W, THRESHOLD_COUNT = self.__get_fast_deal_threshold_value(code, total_datas[real_order_index]['val']['time'])
        THRESHOLD_MONEY_W, THRESHOLD_COUNT = self.__get_fast_deal_threshold_value(code,
                                                                                  total_datas[real_order_index]['val'][
                                                                                      'time'])
        total_left_count = 0
        total_left_num = 0
@@ -3058,5 +3128,156 @@
    pass
class JCancelBigNumComputer(BaseCancel):
    """
    J撤:
    1000ms内若有三笔,我们后面的涨停撤买L ,
    此时算一次信号,即开始计算我们后面的未撤的所有涨停总额,
    当此总涨停额下降至50%则撤单。
    更新屏幕,3分钟以后有新的信号,
    则重新计算总额。如果3分钟后没有新的信号,
    则沿用上一次计算的后涨停额。
    守护时间14点50分00秒,
    此撤关于我们后面的不想顶而撤,
    导致封单额陡然降低
    """
    __cancel_single_cache = {}
    __real_place_order_index_info_dict = {}
    __instance = None
    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            cls.__instance = super(JCancelBigNumComputer, cls).__new__(cls, *args, **kwargs)
        return cls.__instance
    def set_real_place_order_index(self, code, index, buy_single_index, is_default):
        self.__real_place_order_index_info_dict[code] = (index, is_default)
    def __compute_cancel_single(self, code, start_index, end_index):
        """
        计算撤单信号:1000ms内有3笔撤单
        @param code:
        @param start_index: 开始索引
        @param end_index: 结束索引
        @return:
        """
        # 获取真实下单位置
        real_place_order_index_info = self.__real_place_order_index_info_dict.get(code)
        if not real_place_order_index_info or real_place_order_index_info[1]:
            # 尚未获取到真实下单位置
            return
        real_place_order_index = real_place_order_index_info[0]
        # 获取撤单信号[时间,真实下单位置, 信号总手数,目前手数,最新计算的索引]
        cancel_single_info = self.__cancel_single_cache.get(code)
        outoftime = False
        total_datas = local_today_datas.get(code)
        if cancel_single_info:
            outoftime = tool.trade_time_sub(total_datas[-1]['val']['time'], cancel_single_info[0]) > 180
            if not outoftime:
                # 上次计算还未超时
                return
        limit_up_price = round(float(gpcode_manager.get_limit_up_price(code)), 2)
        if cancel_single_info and outoftime:
            # 更新需要计算信号
            min_volume = 50 * 10000 // int(limit_up_price * 100)
            # 计算本批数据是否有撤单
            single_info = None  # (index, 时间ms)
            for i in range(end_index, start_index - 1, -1):
                data = total_datas[i]
                val = data['val']
                if not L2DataUtil.is_limit_up_price_buy_cancel(val):
                    continue
                if val['num'] < min_volume:
                    continue
                single_info = (i, L2DataUtil.get_time_with_ms(val))
                break
            if not single_info:
                # "无涨停撤单"
                return
            indexes = [single_info[0]]  # 包含信号笔数
            for i in range(single_info[0] - 1, real_place_order_index, -1):
                data = total_datas[i]
                val = data['val']
                if not L2DataUtil.is_limit_up_price_buy_cancel(val):
                    continue
                if val['num'] < min_volume:
                    continue
                time_ms = L2DataUtil.get_time_with_ms(val)
                if tool.trade_time_sub_with_ms(single_info[1], time_ms) > 1000:
                    break
                indexes.append(i)
                if len(indexes) >= 3:
                    break
            if len(indexes) < 3:
                # 不满足更新条件
                return
        total_count, total_num = L2DataComputeUtil.compute_left_buy_order(code, real_place_order_index + 1,
                                                                          total_datas[-1]['index'], limit_up_price)
        self.__cancel_single_cache[code] = [total_datas[-1]['val']['time'], real_place_order_index, total_num,
                                            total_num,
                                            total_datas[-1]['index']]
        l2_log.j_cancel_debug(code, f"触发囊括:{self.__cancel_single_cache[code]}")
    def need_cancel(self, code, start_index, end_index):
        # 需要先计算
        self.__compute_cancel_single(code, start_index, end_index)
        # [时间, 真实下单位置, 信号总手数, 目前手数, 最新计算的索引]
        cancel_single_info = self.__cancel_single_cache.get(code)
        if not cancel_single_info:
            return False, None, "没有监听"
        # 计算剩余数量
        total_datas = local_today_datas.get(code)
        buyno_map = local_today_buyno_map.get(code)
        limit_up_price = round(float(gpcode_manager.get_limit_up_price(code)), 2)
        min_volume = 50 * 10000 // int(limit_up_price * 100)
        # 计算纯买额
        for i in range(start_index, end_index + 1):
            data = total_datas[i]
            val = data['val']
            if data['index'] <= cancel_single_info[4]:
                continue
            if val['num'] < min_volume:
                continue
            if L2DataUtil.is_limit_up_price_buy_cancel(val):
                orderNo = val['orderNo']
                buy_data = buyno_map.get(f"{orderNo}")
                if buy_data and buy_data['index'] > cancel_single_info[1]:
                    cancel_single_info[3] -= val['num']
            elif L2DataUtil.is_limit_up_price_buy(val):
                cancel_single_info[3] += val['num']
            else:
                continue
        cancel_single_info[4] = end_index
        # self.__cancel_single_cache[code] = cancel_single_info
        threshold_rate = constant.J_CANCEL_RATE
        if gpcode_manager.MustBuyCodesManager().is_in_cache(code):
            threshold_rate = constant.J_CANCEL_RATE_WITH_MUST_BUY
        cancel_num = cancel_single_info[2] - cancel_single_info[3]
        rate = round(cancel_num / cancel_single_info[2], 2)
        if rate >= threshold_rate:
            return True, total_datas[
                end_index], f"撤单比例达到:{rate}/{threshold_rate} 剩余手数:{cancel_single_info[3]}/{cancel_single_info[2]}"
        return False, None, f"尚未达到撤单比例{rate} , {cancel_num}/{cancel_single_info[2]}"
    def __clear_data(self, code):
        if code in self.__cancel_single_cache:
            self.__cancel_single_cache.pop(code)
        if code in self.__real_place_order_index_info_dict:
            self.__real_place_order_index_info_dict.pop(code)
    def cancel_success(self, code):
        self.__clear_data(code)
    def place_order_success(self, code):
        self.__clear_data(code)
if __name__ == "__main__":
    pass