Administrator
4 天以前 48fb7a00951f91bdc707e5dd2d196e5bccb752c3
l2/cancel_buy_strategy.py
@@ -10,37 +10,38 @@
import time
import constant
from cancel_strategy import s_l_h_cancel_strategy
from code_attribute import big_money_num_manager, gpcode_manager, code_volumn_manager
import l2_data_util
from db import redis_manager_delegate as redis_manager
from db.redis_manager_delegate import RedisUtils
from l2.code_price_manager import Buy1PriceManager
from l2.huaxin import l2_huaxin_util
from l2.l2_data_manager import OrderBeginPosInfo
from l2.l2_transaction_data_manager import HuaXinBuyOrderManager, HuaXinSellOrderStatisticManager, BigOrderDealManager
from log_module import async_log_util
from l2.l2_transaction_data_manager import HuaXinBuyOrderManager, HuaXinSellOrderStatisticManager
from trade.sell.sell_rule_manager import TradeRuleManager
from utils import tool
from l2.transaction_progress import TradeBuyQueue
from trade import trade_queue_manager, l2_trade_factor, trade_record_log_util, trade_manager
from l2 import l2_log, l2_data_source_util
from trade import trade_queue_manager, l2_trade_factor, trade_manager, trade_data_manager
from l2 import l2_log, l2_data_source_util, code_price_manager
from l2.l2_data_util import L2DataUtil, local_today_num_operate_map, local_today_datas, local_today_buyno_map, \
    local_today_canceled_buyno_map
from log_module.log import logger_buy_1_volumn, logger_l2_l_cancel, logger_l2_h_cancel, logger_l2_s_cancel
from log_module.log import logger_buy_1_volumn, logger_l2_error
from utils.tool import CodeDataCacheUtil
from settings import trade_setting
def set_real_place_position(code, index, buy_single_index=None, is_default=True):
    # DCancelBigNumComputer().set_real_order_index(code, index)
    SCancelBigNumComputer().set_real_place_order_index(code, index, is_default)
    LCancelBigNumComputer().set_real_place_order_index(code, index, buy_single_index=buy_single_index,
                                                       is_default=is_default)
    HourCancelBigNumComputer().set_real_place_order_index(code, index, buy_single_index)
    s_l_h_cancel_strategy.SCancelBigNumComputer().set_real_place_order_index(code, index, is_default)
    s_l_h_cancel_strategy.LCancelBigNumComputer().set_real_place_order_index(code, index,
                                                                             buy_single_index=buy_single_index,
                                                                             is_default=is_default)
    s_l_h_cancel_strategy.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)
    NBCancelBigNumComputer().set_real_place_order_index(code, index, buy_single_index, is_default)
    RDCancelBigNumComputer().set_real_place_order_index(code, index, buy_single_index, is_default)
class BaseCancel:
@@ -93,798 +94,87 @@
                total_num += val["num"]
        return total_count, total_num
class SCancelBigNumComputer:
    __db = 0
    __redis_manager = redis_manager.RedisManager(0)
    __sCancelParamsManager = l2_trade_factor.SCancelParamsManager
    __s_cancel_real_place_order_index_cache = {}
    __s_down_watch_indexes_dict = {}
    __recompute_l_down_time_dict = {}
    # 最近处理的卖单号
    __latest_process_sell_order_no = {}
    # S慢砸包含的范围
    __s_slow_sell_watch_indexes_dict = {}
    # 成交进度位置
    __trade_progress_index_dict = {}
    __instance = None
    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            cls.__instance = super(SCancelBigNumComputer, cls).__new__(cls, *args, **kwargs)
            cls.__load_datas()
        return cls.__instance
    @classmethod
    def __get_redis(cls):
        return cls.__redis_manager.getRedis()
    @classmethod
    def __load_datas(cls):
        __redis = cls.__get_redis()
        try:
            keys = RedisUtils.keys(__redis, "s_cancel_real_place_order_index-*")
            for k in keys:
                code = k.split("-")[-1]
                val = RedisUtils.get(__redis, k)
                val = json.loads(val)
                tool.CodeDataCacheUtil.set_cache(cls.__s_cancel_real_place_order_index_cache, code, val)
        finally:
            RedisUtils.realse(__redis)
    # 设置真实下单位置
    def __save_real_place_order_index(self, code, index, is_default):
        CodeDataCacheUtil.set_cache(self.__s_cancel_real_place_order_index_cache, code, (index, is_default))
        key = "s_cancel_real_place_order_index-{}".format(code)
        RedisUtils.setex_async(self.__db, key, tool.get_expire(), json.dumps((index, is_default)))
    def __get_real_place_order_index_info_cache(self, code):
        cache_result = CodeDataCacheUtil.get_cache(self.__s_cancel_real_place_order_index_cache, code)
        if cache_result[0]:
            return cache_result[1]
        return None
    def get_real_place_order_index_cache(self, code):
        cache_result = self.__get_real_place_order_index_info_cache(code)
        if cache_result:
            return cache_result[0]
        return None
    def __clear_data(self, code):
        CodeDataCacheUtil.clear_cache(self.__s_cancel_real_place_order_index_cache, code)
        CodeDataCacheUtil.clear_cache(self.__s_down_watch_indexes_dict, code)
        CodeDataCacheUtil.clear_cache(self.__trade_progress_index_dict, code)
        CodeDataCacheUtil.clear_cache(self.__s_slow_sell_watch_indexes_dict, code)
        ks = ["s_big_num_cancel_compute_data-{}".format(code), "s_cancel_real_place_order_index-{}".format(code)]
        for key in ks:
            RedisUtils.delete_async(self.__db, key)
    # 设置真实下单位置
    def set_real_place_order_index(self, code, index, is_default):
        self.__save_real_place_order_index(code, index, is_default)
        if not is_default:
            self.__compute_s_slow_sell(code)
    def set_transaction_index(self, code, buy_single_index, index):
        self.__trade_progress_index_dict[code] = index
        self.__compute_s_slow_sell(code)
    def clear_data(self):
        ks = ["s_big_num_cancel_compute_data-*", "s_cancel_real_place_order_index-*"]
        for key in ks:
            keys = RedisUtils.keys(self.__get_redis(), key)
            for k in keys:
                code = k.split("-")[1]
                self.__clear_data(code)
    # 撤单成功
    def cancel_success(self, code):
        self.__clear_data(code)
    # 下单成功
    def place_order_success(self, code):
        self.__clear_data(code)
    def __get_fast_threshold_value(self, code):
    def compute_end_index(cls, code, start_index, end_index, limit_up_price, max_count, min_money=500000):
        """
        获取S快炸的阈值
        @param code:
        @return:(大单阈值, 800w内大单阈值)
        计算结束索引
        @param code: 代码
        @param start_index:起始索引(包含)
        @param end_index: 结束索引(包含)
        @param limit_up_price: 涨停价
        @param max_count: 最大数量
        @param min_money: 最小的资金
        @return:结束索引
        """
        max60, yesterday = code_volumn_manager.get_histry_volumn(code)
        if max60:
            num = max60[0]
            limit_up_price = gpcode_manager.get_limit_up_price(code)
            if limit_up_price:
                money_y = min(round((num * float(limit_up_price)) / 1e8, 1), 7.9)
                money = int(round(15 * money_y + 276.5))
                money_800 = int(round(money * 0.668))
                return money, money_800
        # 默认值
        return 299, 200
    # S前撤实现
    def __need_cancel_for_up(self, code, big_sell_order_info, total_datas):
        # 查询是否是真的真实下单位置
        trade_index, is_default = TradeBuyQueue().get_traded_index(code)
        if trade_index is None:
            trade_index = 0
        real_order_index_info = self.__get_real_place_order_index_info_cache(code)
        if real_order_index_info is None or real_order_index_info[1]:
            return False, "没找到真实下单位"
        real_order_index = real_order_index_info[0]
        total_deal_money = sum([x[1] * x[2] for x in big_sell_order_info[1]])
        start_order_no = big_sell_order_info[1][0][3][1]
        # 防止分母位0
        total_num = 1
        # 获取正在成交的数据
        dealing_info = HuaXinBuyOrderManager.get_dealing_order_info(code)
        for i in range(trade_index, real_order_index):
        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
        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 int(val['orderNo']) < start_order_no:
            if val['num'] < min_volume:
                continue
            if i == trade_index and dealing_info and str(total_datas[trade_index]["val"]["orderNo"]) == str(
                    dealing_info[0]):
                # 减去当前正在成交的数据中已经成交了的数据
                total_num -= dealing_info[1] // 100
            left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(code, i,
                                                                                                     total_datas,
                                                                                                     local_today_canceled_buyno_map.get(
                                                                                                         code))
                                                                                                     canceled_buyno_map)
            if left_count > 0:
                total_num += val["num"]
                total_count += 1
                if total_count >= max_count:
                    return i
        return end_index
        # 大于10w
        if total_deal_money >= 10 * 10000:
            cancel_result = self.__need_cancel_for_slow_sell(code, total_datas)
            if cancel_result[0]:
                return True, f"S慢砸:{cancel_result[1]}"
        if total_deal_money >= 100 * 10000:
            l2_log.s_cancel_debug(code, "准备更新L后囊括")
            start_order_no = big_sell_order_info[1][-1][4][1]
            latest_deal_time_ms = l2_huaxin_util.convert_time(big_sell_order_info[1][-1][4][0], with_ms=True)
            real_trade_index = None
            for i in range(trade_index, real_order_index):
                data = total_datas[i]
                val = data['val']
                if not L2DataUtil.is_limit_up_price_buy(val):
                    continue
                if int(val['orderNo']) < start_order_no:
                    continue
                real_trade_index = i
                break
            if real_trade_index is None:
                l2_log.s_cancel_debug(code, f"没找到真实的成交进度:start_order_no-{start_order_no} 卖单-{big_sell_order_info}")
                return False, ""
            # 间隔1S以上才能重新囊括
            if code in self.__recompute_l_down_time_dict and tool.trade_time_sub_with_ms(latest_deal_time_ms,
                                                                                         self.__recompute_l_down_time_dict[
                                                                                             code]) < 1000:
                l2_log.s_cancel_debug(code,
                                      f"更新L后囊括:更新间隔在1s内,{latest_deal_time_ms}-{self.__recompute_l_down_time_dict[code]}")
                return False, ""
            self.__recompute_l_down_time_dict[code] = latest_deal_time_ms
            # 重新囊括L后
            # 撤单时间比早成交时间大就需要计算在里面
            LCancelBigNumComputer().re_compute_l_down_watch_indexes(code, big_sell_info=(
                real_trade_index, latest_deal_time_ms))
            l2_log.s_cancel_debug(code, f"更新L后囊括完成:{(real_trade_index, latest_deal_time_ms)}")
        return False, ""
    def __need_cancel_for_slow_sell(self, code, total_datas):
    @classmethod
    def compute_max_buy_order_info(cls, code, start_index, end_index):
        """
        S慢撤的目的是判断较大金额卖占整个卖的比例不能太大
        计算区间最大买单
        @param code:
        @param start_index:
        @param end_index:
        @return:
        """
        total_datas = local_today_datas.get(code)
        canceled_buyno_map = local_today_canceled_buyno_map.get(code)
        max_info = (0, 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
            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:
                if val["num"] > max_info[1]:
                    max_info = (i, val["num"])
        return total_datas[max_info[0]]
    @classmethod
    def is_canceled(cls, code, index, total_datas, canceled_buyno_map, trade_index, deal_order_nos):
        """
        是否已经撤单
        @param deal_order_nos: 成交大单集合
        @param trade_index: 成交进度位
        @param index: 索引
        @param code: 代码
        @param total_datas:
        @param canceled_buyno_map:撤单的订单号
        @return:
        """
        if code not in self.__s_slow_sell_watch_indexes_dict:
            return False, "S慢砸没有囊括"
        # 下单3分钟内有效
        real_place_order_info = self.__get_real_place_order_index_info_cache(code)
        if not real_place_order_info:
            return False, "没有获取到真实下单位"
        if tool.trade_time_sub(total_datas[-1]['val']['time'],
                               total_datas[real_place_order_info[0]]['val']['time']) > 180:
            return False, "超过守护时间"
        # 格式:[{监听index},当时正在成交的订单号,已经成交的手数]
        watch_info = self.__s_slow_sell_watch_indexes_dict.get(code)
        # 计算总买额
        total_num = 0
        for i in watch_info[0]:
            data = total_datas[i]
            val = data['val']
            left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(code, i,
                                                                                                     total_datas,
                                                                                                     local_today_canceled_buyno_map.get(
                                                                                                         code))
            if left_count > 0:
                total_num += val['num']
                if val['orderNo'] == watch_info[1]:
                    total_num -= watch_info[2]
        # 过滤最小金额
        zyltgb = l2_trade_factor.L2TradeFactorUtil.get_zyltgb(code)
        zyltgb_unit_y = round(zyltgb / 100000000, 1)
        min_sell_money = int((zyltgb_unit_y / 2 + 5) * 10000)
        sell_orders = HuaXinSellOrderStatisticManager.get_latest_transaction_datas(code, min_sell_order_no=
        total_datas[real_place_order_info[0]]['val']['orderNo'], min_sell_money=min_sell_money)
        sell_order_num = sum([x[1] for x in sell_orders]) // 100
        rate = round(sell_order_num / total_num, 2)
        if rate > 0.49:
            return True, f"慢砸成交比例:{rate}/0.49  成交:{sell_order_num}/{total_num}"
        cancel_data = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_canceled_data_v2(code, index,
                                                                                              total_datas,
                                                                                              canceled_buyno_map)
        if cancel_data:
            # 已经撤单
            return True
        else:
            return False, f"尚未触发撤单比例:{rate}"
    # 计算S后撤监听的数据范围
    def __compute_down_cancel_watch_index(self, code, big_sell_order_info, total_datas):
        trade_index, is_default = TradeBuyQueue().get_traded_index(code)
        real_order_index_info = self.__get_real_place_order_index_info_cache(code)
        if real_order_index_info is None or real_order_index_info[1]:
            return
        real_order_index = real_order_index_info[0]
        start_order_no = big_sell_order_info[1][-1][4][1]
        latest_deal_time = l2_huaxin_util.convert_time(big_sell_order_info[1][-1][4][0], with_ms=True)
        if code in self.__s_down_watch_indexes_dict:
            # 更新囊括范围后1s内不能再次更新
            if tool.trade_time_sub_with_ms(latest_deal_time, self.__s_down_watch_indexes_dict[code][0]) <= 1000:
                return
        watch_index_info = []
        for i in range(trade_index, real_order_index):
            data = total_datas[i]
            val = data['val']
            if not L2DataUtil.is_limit_up_price_buy(val):
                continue
            if int(val['orderNo']) < start_order_no:
                continue
            if val['num'] * float(val['price']) < 5000:
                continue
            left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(code, i,
                                                                                                     total_datas,
                                                                                                     local_today_canceled_buyno_map.get(
                                                                                                         code))
            if left_count > 0:
                watch_index_info.append((i, val['num']))
        watch_index_info.sort(key=lambda x: x[1], reverse=True)
        watch_indexes = set([x[0] for x in watch_index_info[:5]])
        l2_log.s_cancel_debug(code, f"S后计算那概括范围:{watch_indexes}  最近成交的卖单信息:{big_sell_order_info[1][-1]}")
        if watch_indexes:
            # 保存卖单信息
            self.__s_down_watch_indexes_dict[code] = (latest_deal_time, watch_indexes)
    # 是否有大卖单需要撤
    def __need_cancel_for_big_sell_order(self, code, big_sell_order_info, order_begin_pos: OrderBeginPosInfo):
        # 需要排除成交时间在下单时间之前的
        total_deal_money = 0
        total_datas = local_today_datas.get(code)
        real_order_index_info = self.__get_real_place_order_index_info_cache(code)
        real_order_time_ms = None
        if real_order_index_info and not real_order_index_info[1]:
            real_order_index = real_order_index_info[0]
            real_order_time_ms = total_datas[real_order_index]["val"]["time"] + ".{0:0>3}".format(
                total_datas[real_order_index]["val"]["tms"])
        for x in big_sell_order_info[1]:
            deal_time = l2_huaxin_util.convert_time(x[4][0], with_ms=True)
            if real_order_time_ms:
                if tool.trade_time_sub_with_ms(deal_time, real_order_time_ms) >= 0:
                    m = x[1] * x[2]
                    total_deal_money += m
            else:
                m = x[1] * x[2]
                total_deal_money += m
        zyltgb = l2_trade_factor.L2TradeFactorUtil.get_zyltgb(code)
        zyltgb_unit_y = round(zyltgb / 100000000, 1)
        threshold_big_deal = (2 * zyltgb_unit_y + 339) * 10000
        if total_deal_money >= threshold_big_deal:
            return True, f"1s内大于{threshold_big_deal}大卖单({total_deal_money})"
        limit_up_price = gpcode_manager.get_limit_up_price(code)
        # 判断是否为激进下单
        threash_money_w, threash_money_danger_w = self.__get_fast_threshold_value(code)
        total_fast_num = 0
        try:
            # 15分钟内真实成交位距离真实下单位,金额≤800万 ,大单阈值变为200w
            trade_index, is_default = TradeBuyQueue().get_traded_index(code)
            if trade_index is None:
                trade_index = 0
            if real_order_index_info and not real_order_index_info[1]:
                real_order_index = real_order_index_info[0]
                # 开始第一笔成交数据
                fdeal = big_sell_order_info[1][0][3]
                start_order_no = fdeal[1]
                sell_time_str = l2_huaxin_util.convert_time(fdeal[0], with_ms=False)
                if tool.trade_time_sub(sell_time_str, total_datas[real_order_index]['val']['time']) < 15 * 60:
                    total_num = 0
                    # 获取正在成交的数据
                    dealing_info = HuaXinBuyOrderManager.get_dealing_order_info(code)
                    for i in range(trade_index, real_order_index):
                        data = total_datas[i]
                        val = data['val']
                        if int(val['orderNo']) < start_order_no:
                            continue
                        if i == trade_index and dealing_info and str(
                                total_datas[trade_index]["val"]["orderNo"]) == str(
                            dealing_info[0]):
                            # 减去当前正在成交的数据中已经成交了的数据
                            total_num -= dealing_info[1] // 100
                        left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(
                            code,
                            i,
                            total_datas,
                            local_today_canceled_buyno_map.get(
                                code))
                        if left_count > 0:
                            total_num += val["num"]
                            total_fast_num += val['num']
                    if total_num * float(limit_up_price) < 800 * 100:
                        threash_money_w = threash_money_danger_w
        except Exception as e:
            l2_log.s_cancel_debug(code, f"S撤激进下单计算大单卖阈值出错:{str(e)}")
        total_fast_money = int(total_fast_num * 100 * float(limit_up_price))
        if total_fast_money == 0:
            # 防止分母为0
            total_fast_money = 1
        rate = round(total_deal_money / total_fast_money, 2)
        threshold_rate = constant.F_FAST_RATE
        if gpcode_manager.MustBuyCodesManager().is_in_cache(code):
            threshold_rate = constant.F_FAST_RATE_WITH_MUST_BUY
        if total_deal_money >= threash_money_w * 10000 and rate >= threshold_rate:
            return True, f"近1s有大卖单({round(total_deal_money / 10000, 1)}万/{threash_money_w}万,成交占比:{total_deal_money}/{total_fast_money})"
        else:
            l2_log.s_cancel_debug(code,
                                  f"S快撤没达到撤单比例:成交金额({total_deal_money})/囊括金额({total_fast_money}),比例:{rate} 大单阈值:{threash_money_w}w")
        return False, f"无{threash_money_w}大单"
    #  big_sell_order_info格式:[总共的卖单金额, 大卖单详情列表]
    def set_big_sell_order_info_for_cancel(self, code, big_sell_order_info, order_begin_pos: OrderBeginPosInfo):
        if order_begin_pos is None or order_begin_pos.buy_exec_index is None or order_begin_pos.buy_exec_index < 0:
            return False, "还未下单"
        if big_sell_order_info[0] < 500000 or not big_sell_order_info[1]:
            return False, "无大单卖"
        l2_log.s_cancel_debug(code, f"主动卖:{big_sell_order_info}")
        try:
            need_cancel, cancel_msg = self.__need_cancel_for_big_sell_order(code, big_sell_order_info, order_begin_pos)
            if need_cancel:
                return need_cancel, cancel_msg
            total_datas = local_today_datas.get(code)
            need_cancel, cancel_msg = self.__need_cancel_for_up(code, big_sell_order_info, total_datas)
            if need_cancel:
                return need_cancel, cancel_msg
            # S后撤取消
            # if self.__latest_process_sell_order_no.get(code) != big_sell_order_info[1][-1][0]:
            #     # 不处理重复的卖单
            #     # 获取最新的成交时间
            #     latest_trade_time = l2_huaxin_util.convert_time(big_sell_order_info[1][-1][4][0])
            #     if tool.trade_time_sub(latest_trade_time,
            #                            total_datas[order_begin_pos.buy_single_index]['val']['time']) >= 180:
            #         self.__compute_down_cancel_watch_index(code, big_sell_order_info, total_datas)
            return False, "不满足撤单条件"
        finally:
            # self.__latest_process_sell_order_no[code] = big_sell_order_info[1][-1][0]
            pass
    # ----------------------------S慢砸范围计算------------------------------------
    def __compute_s_slow_sell(self, code):
        try:
            if code in self.__s_slow_sell_watch_indexes_dict:
                return
            real_place_order_info = self.__get_real_place_order_index_info_cache(code)
            if not real_place_order_info or real_place_order_info[1]:
                return
            trade_index = self.__trade_progress_index_dict.get(code)
            if trade_index is None:
                return
            start_index = trade_index
            end_index = real_place_order_info[0]
            total_datas = local_today_datas.get(code)
            watch_indexes = set()
            for i in range(start_index, end_index):
                data = total_datas[i]
                val = data['val']
                if not L2DataUtil.is_limit_up_price_buy(val):
                    continue
                if val['num'] * float(val['price']) < 5000:
                    continue
                left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(code, i,
                                                                                                         total_datas,
                                                                                                         local_today_canceled_buyno_map.get(
                                                                                                             code))
                if left_count > 0:
                    watch_indexes.add(i)
            dealing_order_no = None
            dealed_num = 0
            # 格式:[监听的索引,正在成交索引,已成交的数量]
            dealing_info = HuaXinBuyOrderManager.get_dealing_order_info(code)
            if dealing_info:
                dealing_order_no = dealing_info[0]
                dealed_num = dealing_info[1] // 100
            watch_info = [watch_indexes, dealing_order_no, dealed_num]
            self.__s_slow_sell_watch_indexes_dict[code] = watch_info
            l2_log.s_cancel_debug(code, f"S慢砸监听范围:{watch_info}")
        except Exception as e:
            logger_l2_s_cancel.exception(e)
# --------------------------------H撤-------------------------------
class HourCancelBigNumComputer:
    __db = 0
    __redis_manager = redis_manager.RedisManager(0)
    __tradeBuyQueue = TradeBuyQueue()
    __SCancelBigNumComputer = SCancelBigNumComputer()
    # 计算触发位置
    __start_compute_index_dict = {}
    # 成交位置
    __transaction_progress_index_dict = {}
    # 成交位置更新时间
    __transaction_progress_index_updatetime_dict = {}
    # 缓存
    __cancel_watch_indexs_cache = {}
    # L撤触发的代码
    __l_cancel_triggered_codes = set()
    __instance = None
    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            cls.__instance = super(HourCancelBigNumComputer, cls).__new__(cls, *args, **kwargs)
            cls.__load_datas()
        return cls.__instance
    @classmethod
    def __get_redis(cls):
        return cls.__redis_manager.getRedis()
    @classmethod
    def __load_datas(cls):
        __redis = cls.__get_redis()
        try:
            keys = RedisUtils.keys(__redis, "h_cancel_watch_indexs-*")
            for k in keys:
                code = k.split("-")[-1]
                val = RedisUtils.get(__redis, k)
                val = json.loads(val)
                if val:
                    val = set(val)
                else:
                    val = set()
                CodeDataCacheUtil.set_cache(cls.__cancel_watch_indexs_cache, code, val)
            keys = RedisUtils.keys(__redis, "h_cancel_transaction_index-*")
            for k in keys:
                code = k.split("-")[-1]
                val = RedisUtils.get(__redis, k)
                val = int(val)
                CodeDataCacheUtil.set_cache(cls.__transaction_progress_index_dict, code, val)
        finally:
            RedisUtils.realse(__redis)
    # 保存成交位置到执行位置的揽括范围数据
    def __save_watch_index_set(self, code, buy_single_index, indexes):
        trade_record_log_util.add_cancel_watch_indexes_log(code,
                                                           trade_record_log_util.CancelWatchIndexesInfo(
                                                               trade_record_log_util.CancelWatchIndexesInfo.CANCEL_TYPE_H,
                                                               buy_single_index,
                                                               list(indexes)))
        CodeDataCacheUtil.set_cache(self.__cancel_watch_indexs_cache, code, indexes)
        key = f"h_cancel_watch_indexs-{code}"
        RedisUtils.setex_async(self.__db, key, tool.get_expire(),
                               json.dumps(list(indexes)))
    # 保存成交进度
    def __get_watch_index_set(self, code):
        key = f"h_cancel_watch_indexs-{code}"
        val = RedisUtils.get(self.__get_redis(), key)
        if val is None:
            return None
        val = json.loads(val)
        return val
    def __get_watch_index_set_cache(self, code):
        cache_result = CodeDataCacheUtil.get_cache(self.__cancel_watch_indexs_cache, code)
        if cache_result[0]:
            return cache_result[1]
        return set()
    def __save_transaction_index(self, code, index):
        CodeDataCacheUtil.set_cache(self.__transaction_progress_index_dict, code, index)
        key = f"h_cancel_transaction_index-{code}"
        RedisUtils.setex_async(self.__db, key, tool.get_expire(), index)
    def __clear_data(self, code):
        CodeDataCacheUtil.clear_cache(self.__cancel_watch_indexs_cache, code)
        CodeDataCacheUtil.clear_cache(self.__transaction_progress_index_dict, code)
        CodeDataCacheUtil.clear_cache(self.__start_compute_index_dict, code)
        self.__l_cancel_triggered_codes.discard(code)
        ks = [f"h_cancel_watch_indexs-{code}", f"h_cancel_transaction_index-{code}"]
        for key in ks:
            RedisUtils.delete_async(self.__db, key)
        # 计算观察索引,倒序计算
    def __compute_watch_index(self, code, buy_single_index):
        """
        @param code:
        @param buy_single_index:
        @return:
        """
        if buy_single_index is None:
            return
        if self.__cancel_watch_indexs_cache.get(code):
            return
        real_place_order_index = self.__SCancelBigNumComputer.get_real_place_order_index_cache(code)
        if not real_place_order_index:
            l2_log.h_cancel_debug(code, "尚未找到真实下单位置")
            return
        start_compute_index = self.__start_compute_index_dict.get(code)
        if not start_compute_index:
            l2_log.h_cancel_debug(code, "尚未找到开始计算位置")
            return
        transaction_index = self.__transaction_progress_index_dict.get(code)
        if transaction_index:
            # 不能计算成交进度以前的数据
            start_compute_index = transaction_index + 1  # max(transaction_index + 1, start_compute_index)
        total_datas = local_today_datas.get(code)
        # h撤计算必须超过3分钟
        if tool.trade_time_sub(total_datas[-1]["val"]["time"],
                               total_datas[real_place_order_index]["val"]["time"]) < 180:
            l2_log.h_cancel_debug(code, "180s内囊括计算H撤")
            return
        # -----------------计算H上-------------------
        watch_indexes_up = set()
        for i in range(real_place_order_index - 1, start_compute_index + 1, -1):
            data = total_datas[i]
            val = data['val']
            if not L2DataUtil.is_limit_up_price_buy(val):
                continue
                # 小金额过滤
            if float(val['price']) * val['num'] < 50 * 100:
                continue
            left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(code, i,
                                                                                                     total_datas,
                                                                                                     local_today_canceled_buyno_map.get(
                                                                                                         code))
            if left_count > 0:
                watch_indexes_up.add(i)
                if len(watch_indexes_up) >= 3:
                    # 最多取3笔
                    break
        # ------------------计算H下-----------------------
        limit_up_price = gpcode_manager.get_limit_up_price(code)
        if not limit_up_price:
            return
        # 计算结束位置
        total_num = 0
        # 获取m值数据
        thresh_hold_money = Buy1PriceManager().get_latest_buy1_money(code)
        # 封单额的1/10
        thresh_hold_money = thresh_hold_money / 10
        thresh_hold_num = thresh_hold_money // (float(limit_up_price) * 100)
        end_index = real_place_order_index + 1
        watch_indexes = set()
        for i in range(real_place_order_index + 1, total_datas[-1]["index"]):
            # 看是否撤单
            data = total_datas[i]
            val = data['val']
            if not L2DataUtil.is_limit_up_price_buy(val):
                continue
            if float(val['price']) * val['num'] < 50 * 100:
                continue
            left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(code, i,
                                                                                                     total_datas,
                                                                                                     local_today_canceled_buyno_map.get(
                                                                                                         code))
            if left_count > 0:
                watch_indexes.add(i)
                total_num += left_count * val["num"]
                count = len(watch_indexes)
                # 最小5笔,最大10笔
                if (total_num > thresh_hold_num and count >= 5) or count >= 10:
                    break
        if watch_indexes or watch_indexes_up:
            watch_indexes |= watch_indexes_up
            self.__save_watch_index_set(code, buy_single_index, watch_indexes)
            l2_log.h_cancel_debug(code, f"设置监听范围, 数据范围:{real_place_order_index}-{end_index} 监听范围-{watch_indexes}")
        # 设置真实下单位置
    def __need_compute_watch_indexes(self, code, transaction_index):
        """
        1.成交进度位距离真实下单位置<=5笔触发囊括
        2.成交到(下单那一刻的成交进度位)到(真实下单位置)的后1/10处也要触发囊括
        3.L撤无法生效触发囊括
        @param code:
        @param transaction_index:
        @return:
        """
        start_compute_index = self.__start_compute_index_dict.get(code)
        if start_compute_index is None:
            if trade_index and trade_index > index:
                # 成交进度大于索引位置,且还没成交
                if total_datas[index]["val"]["orderNo"] not in deal_order_nos:
                    return True
            return False
        real_place_order_index = self.__SCancelBigNumComputer.get_real_place_order_index_cache(code)
        total_datas = local_today_datas.get(code)
        if real_place_order_index and real_place_order_index > transaction_index:
            # 成交位置离我们真实下单的位置只有5笔没撤的大单就需要计算H撤的囊括范围了
            total_left_count = 0
            for index in range(transaction_index + 1, real_place_order_index):
                data = total_datas[index]
                val = data['val']
                if not L2DataUtil.is_limit_up_price_buy(val):
                    continue
                if float(val['price']) * val['num'] < 5000:
                    continue
                left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(code, index,
                                                                                                         total_datas,
                                                                                                         local_today_canceled_buyno_map.get(
                                                                                                             code))
                if left_count > 0:
                    total_left_count += 1
                    if total_left_count > 5:
                        break
            if total_left_count <= 5:
                return True
        # 成交位到开始计算位置没有买单之后
        for index in range(transaction_index + 1, start_compute_index):
            data = total_datas[index]
            val = data['val']
            if not L2DataUtil.is_limit_up_price_buy(val):
                continue
            if float(val['price']) * val['num'] < 5000:
                continue
            left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(code, index,
                                                                                                     total_datas,
                                                                                                     local_today_canceled_buyno_map.get(
                                                                                                         code))
            if left_count > 0:
                # 中间还有未撤的买单
                return False
        return True
    # 设置成交进度
    def set_transaction_index(self, code, buy_single_index, index):
        try:
            # 每3s或者成交进度变化就囊括一次
            if index == self.__transaction_progress_index_dict.get(code):
                if code in self.__transaction_progress_index_updatetime_dict and time.time() - self.__transaction_progress_index_updatetime_dict.get(
                        code) < 3:
                    return
            self.__transaction_progress_index_dict[code] = index
            self.__transaction_progress_index_updatetime_dict[code] = time.time()
            if self.__need_compute_watch_indexes(code, index):
                self.__compute_watch_index(code, buy_single_index)
        except Exception as e:
            l2_log.h_cancel_debug(code, "设置成交进度位置出错:{}", str(e))
            async_log_util.exception(logger_l2_h_cancel, e)
    # 设置真实下单位置
    def set_real_place_order_index(self, code, index, buy_single_index):
        if buy_single_index is None:
            return
        try:
            # 计算触发位置
            min_num = int(5000 / (float(gpcode_manager.get_limit_up_price(code))))
            # 统计净涨停买的数量
            not_cancel_indexes = []
            total_datas = local_today_datas.get(code)
            for j in range(buy_single_index, index):
                data = total_datas[j]
                val = data['val']
                if not L2DataUtil.is_limit_up_price_buy(val):
                    continue
                if val["num"] < min_num:
                    continue
                left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(code,
                                                                                                         j,
                                                                                                         total_datas,
                                                                                                         local_today_canceled_buyno_map.get(
                                                                                                             code))
                if left_count > 0:
                    not_cancel_indexes.append(j)
            if not_cancel_indexes:
                temp_count = len(not_cancel_indexes)
                temp_index = int(temp_count * 9 / 10)
                if temp_index + 1 >= temp_count:
                    temp_index = temp_count - 1
                self.__start_compute_index_dict[code] = not_cancel_indexes[temp_index]
        except Exception as e:
            async_log_util.exception(logger_l2_h_cancel, e)
            l2_log.h_cancel_debug(code, "设置真实下单位置出错:{}", str(e))
    def need_cancel(self, code, buy_single_index, buy_exec_index, start_index, end_index, total_data,
                    buy_volume_index, volume_index,
                    is_first_code):
        if buy_single_index is None or buy_exec_index is None:
            return False, "尚未找到下单位置"
        if int(tool.get_now_time_str().replace(":", "")) > int("145000"):
            return False, None
        # 设置成交进度
        if code not in self.__transaction_progress_index_dict:
            return False, "没找到成交进度"
        watch_index_set = self.__get_watch_index_set_cache(code)
        if not watch_index_set:
            # 是否有涨停买撤
            need_compute = False
            # 有涨停买撤才会计算位置
            for i in range(start_index, end_index + 1):
                data = total_data[i]
                val = data['val']
                if L2DataUtil.is_limit_up_price_buy_cancel(val):
                    need_compute = True
                    break
            if need_compute:
                if self.__need_compute_watch_indexes(code, self.__transaction_progress_index_dict.get(code)):
                    self.__compute_watch_index(code, buy_single_index)
                    watch_index_set = self.__get_watch_index_set_cache(code)
        if not watch_index_set:
            return False, "没有监听索引"
        l2_log.cancel_debug(code, "H级是否需要撤单,数据范围:{}-{} ", start_index, end_index)
        if watch_index_set:
            cancel_num = 0
            total_num = 0
            for i in watch_index_set:
                if i is None:
                    l2_log.h_cancel_debug(code, f"空值:{watch_index_set}")
                    continue
                data = total_data[i]
                val = data['val']
                total_num += val['num'] * data['re']
                # 判断是否撤单
                left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(code, i,
                                                                                                         total_data,
                                                                                                         local_today_canceled_buyno_map.get(
                                                                                                             code))
                cancel_num += val['num'] * (data['re'] - left_count)
            rate = round(cancel_num / total_num, 4)
            threshold_rate = constant.H_CANCEL_RATE_WITH_LDOWN_CANT_INVALID if code in self.__l_cancel_triggered_codes else constant.H_CANCEL_RATE
            try:
                must_buy = gpcode_manager.MustBuyCodesManager().is_in_cache(code)
                if must_buy:
                    threshold_rate = constant.H_CANCEL_RATE_WITH_MUST_BUY
            except Exception as e:
                async_log_util.error(logger_l2_h_cancel, str(e))
            if rate >= threshold_rate:
                l2_log.h_cancel_debug(code, f"撤单比例:{rate}")
                return True, total_data[-1]
        return False, None
    # 下单成功
    def place_order_success(self, code, buy_single_index, buy_exec_index, total_data):
        self.__clear_data(code)
    # 获取H撤监听的数据索引范围
    # 返回监听范围与已撤单索引
    def get_watch_index_dict(self, code):
        return {}, set()
    def start_compute_watch_indexes(self, code, buy_single_index):
        """
        L后撤无法生效触发的囊括
        @param code:
        @param buy_single_index:
        @return:
        """
        self.__l_cancel_triggered_codes.add(code)
        self.__compute_watch_index(code, buy_single_index)
# ---------------------------------D撤-------------------------------
@@ -951,775 +241,129 @@
        self.__clear_data(code)
# ---------------------------------L撤-------------------------------
class LCancelRateManager:
    __block_limit_up_count_dict = {}
    __big_num_deal_rate_dict = {}
    __MustBuyCodesManager = gpcode_manager.MustBuyCodesManager()
    # 获取撤单比例,返回(撤单比例,是否必买)
    @classmethod
    def get_cancel_rate(cls, code, buy_exec_time, is_up=False, is_l_down_recomputed=False):
        try:
            must_buy = cls.__MustBuyCodesManager.is_in_cache(code)
            if must_buy:
                if is_up:
                    return constant.L_CANCEL_RATE_UP_WITH_MUST_BUY, True
                else:
                    return constant.L_CANCEL_RATE_WITH_MUST_BUY, True
        except Exception as e:
            async_log_util.error(logger_l2_l_cancel, str(e))
        base_rate = constant.L_CANCEL_RATE
        if is_up:
            base_rate = constant.L_CANCEL_RATE_UP
        try:
            block_rate = 0
            if code in cls.__block_limit_up_count_dict:
                count = cls.__block_limit_up_count_dict[code]
                rates = [0, 0.03, 0.06, 0.08, 0.12]
                if count >= len(rates):
                    block_rate = rates[-1]
                else:
                    block_rate = rates[count]
            deal_rate = 0
            if code in cls.__big_num_deal_rate_dict:
                deal_rate = round(cls.__big_num_deal_rate_dict[code] / 100)
            base_rate += block_rate
            base_rate += deal_rate
        except Exception as e:
            l2_log.l_cancel_debug(code, f"计算撤单比例出错:{e}")
        return round(base_rate, 2), False
    # 获取L后成交太快的撤单比例
    @classmethod
    def get_fast_deal_cancel_rate(cls, code):
        must_buy_cancel_rate = cls.__MustBuyCodesManager.get_cancel_rate_cache(code)
        if must_buy_cancel_rate is not None:
            return must_buy_cancel_rate
        return constant.L_CANCEL_FAST_DEAL_RATE
    # 设置板块涨停数量(除开自己)
    @classmethod
    def set_block_limit_up_count(cls, reason_codes_dict):
        for reason in reason_codes_dict:
            codes = reason_codes_dict[reason]
            for c in codes:
                cls.__block_limit_up_count_dict[c] = len(codes) - 1
    @classmethod
    def set_big_num_deal_info(cls, code, buy_money, sell_money):
        left_money_w = (buy_money - sell_money) // 10000
        if left_money_w > 0:
            rate = ((left_money_w + 300) // 900) * 2
        else:
            rate = ((left_money_w + 599) // 900) * 2
        if rate < -10:
            rate = -10
        if rate > 10:
            rate = 10
        cls.__big_num_deal_rate_dict[code] = rate
    @classmethod
    def compute_big_num_deal_info(cls, code):
        total_buy_money = BigOrderDealManager().get_total_buy_money(code)
        total_sell_money = BigOrderDealManager().get_total_sell_money(code)
        cls.set_big_num_deal_info(code, total_buy_money, total_sell_money)
# 计算成交位置之后的大单(特定笔数)的撤单比例
class LCancelBigNumComputer:
# ---------------------------------RD撤-------------------------------
# 扫入下单大单撤
class RDCancelBigNumComputer:
    __db = 0
    __redis_manager = redis_manager.RedisManager(0)
    __last_trade_progress_dict = {}
    __real_place_order_index_dict = {}
    __cancel_watch_index_info_cache = {}
    # 成交位附近临近大单索引
    __near_by_trade_progress_index_cache = {}
    __SCancelBigNumComputer = SCancelBigNumComputer()
    # L后囊括范围未撤单/未成交的总手数
    __total_l_down_not_deal_num_dict = {}
    # L后最近的成交数信息
    __l_down_latest_deal_info_dict = {}
    __last_l_up_compute_info = {}
    __instance = None
    # 下单位之后的封单中的大单
    __watch_indexes_cache = {}
    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            cls.__instance = super(LCancelBigNumComputer, cls).__new__(cls, *args, **kwargs)
            cls.__instance = super(RDCancelBigNumComputer, cls).__new__(cls, *args, **kwargs)
            cls.__load_datas()
        return cls.__instance
    @classmethod
    def __load_datas(cls):
        __redis = cls.__get_redis()
        try:
            keys = RedisUtils.keys(__redis, "l_cancel_watch_index_info-*")
            for k in keys:
                code = k.split("-")[-1]
                val = RedisUtils.get(__redis, k)
                if val:
                    val = json.loads(val)
                    val[2] = set(val[2])
                CodeDataCacheUtil.set_cache(cls.__cancel_watch_index_info_cache, code, val)
            keys = RedisUtils.keys(__redis, "l_cancel_real_place_order_index-*")
            for k in keys:
                code = k.split("-")[-1]
                val = RedisUtils.get(__redis, k)
                val = json.loads(val)
                CodeDataCacheUtil.set_cache(cls.__real_place_order_index_dict, code, val)
            keys = RedisUtils.keys(__redis, "l_cancel_near_by_index-*")
            for k in keys:
                code = k.split("-")[-1]
                val = RedisUtils.get(__redis, k)
                try:
                    val = set(json.loads(val))
                    CodeDataCacheUtil.set_cache(cls.__near_by_trade_progress_index_cache, code, val)
                except:
                    pass
        finally:
            RedisUtils.realse(__redis)
    @classmethod
    def __get_redis(cls):
        return cls.__redis_manager.getRedis()
    def __set_watch_indexes(self, code, buy_single_index, re_compute: int, indexes):
        self.__cancel_watch_index_info_cache[code] = (buy_single_index, re_compute, indexes)
        RedisUtils.delete_async(self.__db, f"l_cancel_watch_index_info-{code}")
        RedisUtils.setex_async(self.__db, f"l_cancel_watch_index_info-{code}", tool.get_expire(),
                               json.dumps((buy_single_index, re_compute, list(indexes))))
        if indexes:
            trade_record_log_util.add_cancel_watch_indexes_log(code,
                                                               trade_record_log_util.CancelWatchIndexesInfo(
                                                                   trade_record_log_util.CancelWatchIndexesInfo.CANCEL_TYPE_L_DOWN,
                                                                   buy_single_index,
                                                                   list(indexes)))
    @classmethod
    def __load_datas(cls):
        keys = RedisUtils.keys(cls.__get_redis(), "radical_big_order_watch-*")
        for k in keys:
            code = k.split("-")[1]
            val = RedisUtils.get(cls.__get_redis(), k)
            val = json.loads(val)
            cls.__watch_indexes_cache[code] = set(val)
    def __get_watch_indexes_cache(self, code):
        cache_result = CodeDataCacheUtil.get_cache(self.__cancel_watch_index_info_cache, code)
        if cache_result[0]:
            return cache_result[1]
        return None
    def __set_near_by_trade_progress_indexes(self, code, buy_single_index, indexes):
        if indexes:
            trade_record_log_util.add_cancel_watch_indexes_log(code,
                                                               trade_record_log_util.CancelWatchIndexesInfo(
                                                                   trade_record_log_util.CancelWatchIndexesInfo.CANCEL_TYPE_L_UP,
                                                                   buy_single_index,
                                                                   list(indexes)))
        self.__near_by_trade_progress_index_cache[code] = indexes
        RedisUtils.setex_async(self.__db, f"l_cancel_near_by_index-{code}", tool.get_expire(),
    def set_watch_indexes(self, code, indexes):
        l2_log.d_cancel_debug(code, f"扫入大单监听:{indexes}")
        self.__watch_indexes_cache[code] = set(indexes)
        RedisUtils.setex_async(self.__db, f"radical_big_order_watch-{code}", tool.get_expire(),
                               json.dumps(list(indexes)))
    def __get_near_by_trade_progress_indexes(self, code):
        val = RedisUtils.get(self.__get_redis(), f"l_cancel_near_by_index-{code}")
        if val is None:
            return None
        return set(json.loads(val))
    def __get_near_by_trade_progress_indexes_cache(self, code):
        cache_result = CodeDataCacheUtil.get_cache(self.__near_by_trade_progress_index_cache, code)
        if cache_result[0]:
            return cache_result[1]
        return None
    def del_watch_index(self, code):
        CodeDataCacheUtil.clear_cache(self.__cancel_watch_index_info_cache, code)
        RedisUtils.delete_async(self.__db, f"l_cancel_watch_index_info-{code}")
    def clear(self, code=None):
        if code:
            self.del_watch_index(code)
            if code in self.__real_place_order_index_dict:
                self.__real_place_order_index_dict.pop(code)
                RedisUtils.delete_async(self.__db, f"l_cancel_real_place_order_index-{code}")
        else:
            keys = RedisUtils.keys(self.__get_redis(), f"l_cancel_watch_index_info-*")
            for k in keys:
                code = k.replace("l_cancel_watch_index_info-", "")
                if code in self.__last_trade_progress_dict:
                    self.__last_trade_progress_dict.pop(code)
                if code in self.__real_place_order_index_dict:
                    self.__real_place_order_index_dict.pop(code)
                self.del_watch_index(code)
            keys = RedisUtils.keys(self.__get_redis(), f"l_cancel_real_place_order_index-*")
            for k in keys:
                RedisUtils.delete(self.__get_redis(), k)
        # 重新计算L上
    # big_sell_info卖单信息,格式:[最近成交索引,最近成交时间(带毫秒)]
    def re_compute_l_down_watch_indexes(self, code, big_sell_info=None):
        watch_index_info = self.__cancel_watch_index_info_cache.get(code)
        if not watch_index_info or watch_index_info[1] > 0:
    def set_real_place_order_index(self, code, index, buy_single_index, is_default):
        if is_default:
            return
        # 获取成交进度位与真实下单位置
        real_place_order_index_info = self.__real_place_order_index_dict.get(code)
        last_trade_progress_index = self.__last_trade_progress_dict.get(code)
        if big_sell_info:
            last_trade_progress_index = big_sell_info[0]
        if not real_place_order_index_info or last_trade_progress_index is None:
        if code in self.__watch_indexes_cache and len(self.__watch_indexes_cache[code]) > 1:
            # 囊括的单大于1个
            return
        min_cancel_time_with_ms = None
        if big_sell_info:
            min_cancel_time_with_ms = big_sell_info[1]
        self.compute_watch_index(code, watch_index_info[0], last_trade_progress_index + 1,
                                 real_place_order_index_info[0],
                                 re_compute=1, min_cancel_time_with_ms=min_cancel_time_with_ms,
                                 msg=f"大单卖: 成交进度-{big_sell_info}" if big_sell_info is not None else '')
    # 计算观察索引,倒序计算
    # re_compute:是否是重新计算的
    # min_cancel_time_with_ms:最小撤单时间,大于等于此时间的撤单需要囊括进去
    def compute_watch_index(self, code, buy_single_index, start_index, end_index, re_compute=0,
                            min_cancel_time_with_ms=None, msg=""):
        try:
            l2_log.l_cancel_debug(code, f"计算L后囊括范围:{start_index}-{end_index}")
            total_datas = local_today_datas.get(code)
            if re_compute > 0 and tool.trade_time_sub(total_datas[-1]["val"]["time"],
                                                      total_datas[buy_single_index]["val"][
                                                          "time"]) < 2 * 60 and min_cancel_time_with_ms is None:
                # 封单额稳了以后,间隔超过2分钟才能重新计算
                l2_log.l_cancel_debug(code, f"要间隔2分钟过后才能重新计算")
                return
            if total_datas:
                # 计算的上截至位距离下截至位纯买额要小于2.5倍m值
                # thresh_hold_money = l2_trade_factor.L2PlaceOrderParamsManager.get_base_m_val(code)
                # thresh_hold_money = int(thresh_hold_money * 2.5)
                min_num = int(5000 / (float(gpcode_manager.get_limit_up_price(code))))
                # 统计净涨停买的数量
                not_cancel_indexes_with_num = []
                re_start_index = start_index
                MAX_COUNT = 5
                for j in range(start_index, end_index):
                    data = total_datas[j]
                    val = data['val']
                    if not L2DataUtil.is_limit_up_price_buy(val):
                        continue
                    if val["num"] < min_num:
                        continue
                    cancel_data = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_canceled_data_v2(code,
                                                                                                          j,
                                                                                                          total_datas,
                                                                                                          local_today_canceled_buyno_map.get(
                                                                                                              code))
                    if not cancel_data:
                        not_cancel_indexes_with_num.append((j, val["num"]))
                    elif min_cancel_time_with_ms and tool.compare_time_with_ms(min_cancel_time_with_ms,
                                                                               L2DataUtil.get_time_with_ms(
                                                                                   cancel_data['val'])) <= 0:
                        not_cancel_indexes_with_num.append((j, val["num"]))
                min_num = 0
                if not_cancel_indexes_with_num:
                    temp_count = len(not_cancel_indexes_with_num)
                    # 取后1/5的数据
                    if temp_count >= 30:
                        temp_index = int(temp_count * 4 / 5)
                        re_start_index = not_cancel_indexes_with_num[temp_index][0]
                        MAX_COUNT = len(not_cancel_indexes_with_num[temp_index:])
                    else:
                        not_cancel_indexes_with_num.sort(key=lambda x: x[1])
                        if temp_count >= 10:
                            # 获取中位数
                            min_num = not_cancel_indexes_with_num[temp_count // 2][1]
                MIN_MONEYS = [300, 200, 100, 50]
                watch_indexes = set()
                for min_money in MIN_MONEYS:
                    for i in range(end_index, re_start_index - 1, -1):
                        try:
                            data = total_datas[i]
                            val = data['val']
                            if not L2DataUtil.is_limit_up_price_buy(val):
                                continue
                            # 小金额过滤
                            if float(val['price']) * val['num'] < min_money * 100:
                                continue
                            if val['num'] < min_num:
                                continue
                            cancel_data = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_canceled_data_v2(code,
                                                                                                                  i,
                                                                                                                  total_datas,
                                                                                                                  local_today_canceled_buyno_map.get(
                                                                                                                      code))
                            if not cancel_data:
                                watch_indexes.add(i)
                                if len(watch_indexes) >= MAX_COUNT:
                                    break
                            elif min_cancel_time_with_ms and tool.compare_time_with_ms(min_cancel_time_with_ms,
                                                                                       L2DataUtil.get_time_with_ms(
                                                                                           cancel_data['val'])) <= 0:
                                watch_indexes.add(i)
                                if len(watch_indexes) >= MAX_COUNT:
                                    break
                        except Exception as e:
                            logger_l2_l_cancel.error(f"{code}: 范围: {start_index}-{end_index}  位置:{i}")
                            logger_l2_l_cancel.exception(e)
                    if len(watch_indexes) >= MAX_COUNT:
                        break
                if watch_indexes:
                    ##判断监听的数据中是否有大单##
                    has_big_num = False
                    for i in watch_indexes:
                        # 是否有大单
                        data = total_datas[i]
                        val = data['val']
                        if float(val['price']) * val['num'] > 100 * 100:
                            has_big_num = True
                            break
                    if not has_big_num:
                        # 无大单,需要找大单
                        for i in range(re_start_index, start_index, -1):
                            data = total_datas[i]
                            val = data['val']
                            # 涨停买,且未撤单
                            if not L2DataUtil.is_limit_up_price_buy(val):
                                continue
                            # 小金额过滤
                            if float(val['price']) * val['num'] < 100 * 100:
                                continue
                            cancel_data = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_canceled_data_v2(code,
                                                                                                                  i,
                                                                                                                  total_datas,
                                                                                                                  local_today_canceled_buyno_map.get(
                                                                                                                      code))
                            if not cancel_data:
                                watch_indexes.add(i)
                                break
                            elif min_cancel_time_with_ms and tool.compare_time_with_ms(min_cancel_time_with_ms,
                                                                                       L2DataUtil.get_time_with_ms(
                                                                                           cancel_data['val'])) <= 0:
                                watch_indexes.add(i)
                                break
                    self.__set_watch_indexes(code, buy_single_index, re_compute, watch_indexes)
                    l2_log.l_cancel_debug(code,
                                          f"设置监听范围({msg}){'(重新计算)' if re_compute else ''}, 数据范围:{re_start_index}-{end_index} 监听范围-{watch_indexes}")
        except Exception as e:
            l2_log.l_cancel_debug(code, f"计算L后囊括范围出错:{str(e)}")
            async_log_util.exception(logger_l2_l_cancel, e)
    # 设置真实下单位置
    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)))
        if buy_single_index is not None:
            if code in self.__last_trade_progress_dict:
                # L撤从成交进度位开始计算
                self.compute_watch_index(code, buy_single_index,
                                         self.__last_trade_progress_dict[code], index)
            else:
                self.compute_watch_index(code, buy_single_index, buy_single_index, index)
        if self.__last_trade_progress_dict.get(code):
            self.__compute_trade_progress_near_by_indexes(code, buy_single_index,
                                                          self.__last_trade_progress_dict.get(code) + 1, index)
        else:
            self.__compute_trade_progress_near_by_indexes(code, buy_single_index, buy_single_index, index)
    # 重新计算L上
    def re_compute_l_up_watch_indexes(self, code, buy_single_index):
        if code not in self.__last_trade_progress_dict:
            return
        if code not in self.__real_place_order_index_dict:
            return
        if code not in self.__last_l_up_compute_info or time.time() - self.__last_l_up_compute_info[code][0] >= 3:
            self.__compute_trade_progress_near_by_indexes(code, buy_single_index,
                                                          self.__last_trade_progress_dict.get(code) + 1,
                                                          self.__real_place_order_index_dict.get(code)[0])
    # 计算范围内的成交位临近未撤大单
    def __compute_trade_progress_near_by_indexes(self, code, buy_single_index, start_index, end_index):
        if start_index is None or end_index is None:
            return
        # 从买入信号位开始囊括
        limit_up_price = gpcode_manager.get_limit_up_price_as_num(code)
        min_money = l2_data_util.get_big_money_val(limit_up_price, tool.is_ge_code(code))
        min_num = int(round(min_money / limit_up_price / 100))
        total_datas = local_today_datas.get(code)
        MIN_MONEY = 99 * 100
        MAX_COUNT = 15
        watch_indexes = set()
        total_num = 0
        # thresh_hold_money = l2_trade_factor.L2PlaceOrderParamsManager.get_base_m_val(code)
        # threshold_num = thresh_hold_money // (float(gpcode_manager.get_limit_up_price(code)) * 100)
        for i in range(start_index, end_index):
        for i in range(buy_single_index, index):
            data = total_datas[i]
            val = data['val']
            val = data["val"]
            if not L2DataUtil.is_limit_up_price_buy(val):
                continue
            # 小金额过滤
            if float(val['price']) * val['num'] < MIN_MONEY:
            if val["num"] < min_num:
                continue
            left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(code, i,
            left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(code,
                                                                                                     i,
                                                                                                     total_datas,
                                                                                                     local_today_canceled_buyno_map.get(
                                                                                                         code))
            if left_count > 0:
                total_num += val['num'] * left_count
                watch_indexes.add(i)
                if len(watch_indexes) >= MAX_COUNT:
                    break
        if watch_indexes:
            l2_log.d_cancel_debug(code, f"更新扫入大单监听:{watch_indexes}")
            self.__watch_indexes_cache[code] = watch_indexes
        changed = True
        if code in self.__last_l_up_compute_info:
            if self.__last_l_up_compute_info[code] == watch_indexes:
                changed = False
        # 保存数据
        if changed:
            l2_log.l_cancel_debug(code, f"L前监控范围:{watch_indexes} 计算范围:{start_index}-{end_index}")
            self.__set_near_by_trade_progress_indexes(code, buy_single_index, watch_indexes)
        self.__last_l_up_compute_info[code] = (time.time(), watch_indexes)
    # 计算L后还没成交的手数
    def __compute_total_l_down_not_deal_num(self, code):
        # 只有真实获取到下单位置后才开始计算
        try:
            if code in self.__total_l_down_not_deal_num_dict and time.time() - \
                    self.__total_l_down_not_deal_num_dict[code][
                        1] < 1:
                # 需要间隔1s更新一次
                return
            l_down_cancel_info = self.__cancel_watch_index_info_cache.get(code)
            if not l_down_cancel_info:
                return
            trade_progress = self.__last_trade_progress_dict.get(code)
            if trade_progress is None:
                return
            l_down_indexes = l_down_cancel_info[2]
            # 统计还未成交的数据
            total_left_num = 0
            total_datas = local_today_datas.get(code)
            for i in l_down_indexes:
                # 已经成交了的不计算
                if i < trade_progress:
                    continue
                data = total_datas[i]
                val = data['val']
                left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(code,
                                                                                                         i,
                                                                                                         total_datas,
                                                                                                         local_today_canceled_buyno_map.get(
                                                                                                             code))
                if left_count > 0:
                    fnum = val["num"]
                    if i == trade_progress:
                        # 需要减去已经成交的数据
                        dealing_info = HuaXinBuyOrderManager.get_dealing_order_info(code)
                        if dealing_info:
                            if str(val["orderNo"]) == str(dealing_info[0]):
                                fnum -= dealing_info[1] // 100
                    total_left_num += fnum
            self.__total_l_down_not_deal_num_dict[code] = (total_left_num, time.time())
        except Exception as e:
            async_log_util.exception(logger_l2_l_cancel, e)
    # 设置成交位置,成交位置变化之后相应的监听数据也会发生变化
    def set_trade_progress(self, code, buy_single_index, index, total_datas):
        if self.__last_trade_progress_dict.get(code) == index:
            self.__compute_total_l_down_not_deal_num(code)
            return
        self.__last_trade_progress_dict[code] = index
        self.__compute_total_l_down_not_deal_num(code)
        if total_datas is None:
            return
        if not self.__is_l_down_can_cancel(code, buy_single_index):
            # L后已经不能守护
            l2_log.l_cancel_debug(code, f"L后已经无法生效:buy_single_index-{buy_single_index}")
            HourCancelBigNumComputer().start_compute_watch_indexes(code, buy_single_index)
        real_place_order_index_info = self.__real_place_order_index_dict.get(code)
        real_place_order_index = None
        if real_place_order_index_info:
            real_place_order_index = real_place_order_index_info[0]
        # 重新计算成交位置临近大单撤单
        self.__compute_trade_progress_near_by_indexes(code, buy_single_index, index + 1, real_place_order_index)
    # 已经成交的索引
    def add_deal_index(self, code, index, buy_single_index):
    def need_cancel(self, code, start_index, end_index):
        """
        L后囊括范围成交一笔重新囊括1笔
        是否需要撤单
        @param code:
        @param index:
        @param buy_single_index:
        @return:
        @param start_index:
        @param end_index:
        @return: 是否需要撤单,撤单索引, 消息
        """
        watch_indexes_info = self.__get_watch_indexes_cache(code)
        if not watch_indexes_info:
            return
        watch_indexes = watch_indexes_info[2]
        if index not in watch_indexes:
            return
        if buy_single_index is None:
            return
        # 重新囊括1笔
        real_place_order_info = self.__real_place_order_index_dict.get(code)
        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):
                data = total_datas[j]
                val = data['val']
                if data["index"] in watch_indexes:
                    continue
                if not L2DataUtil.is_limit_up_price_buy(val):
                    continue
                if val["num"] < min_num:
                    continue
                left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(code,
                                                                                                         j,
                                                                                                         total_datas,
                                                                                                         local_today_canceled_buyno_map.get(
                                                                                                             code))
                if left_count > 0:
                    watch_indexes.add(data["index"])
                    l2_log.l_cancel_debug(code, f"L后有成交重新囊括:成交索引-{index} 囊括索引-{data['index']}")
                    break
        self.__set_watch_indexes(code, watch_indexes_info[0], watch_indexes_info[1], watch_indexes)
    def __compute_need_cancel(self, code, buy_exec_index, start_index, end_index, total_data, is_first_code):
        watch_indexes_info = self.__get_watch_indexes_cache(code)
        if not watch_indexes_info:
            return False, None
        watch_indexes = set([int(i) for i in watch_indexes_info[2]])
        # 计算监听的总条数
        total_num = 0
        max_num = 0
        for wi in watch_indexes:
            total_num += total_data[wi]["val"]["num"] * total_data[wi]["re"]
            if total_data[wi]["val"]["num"] > max_num:
                max_num = total_data[wi]["val"]["num"]
        # 判断撤单中是否有监听中的索引
        need_compute = False
        for i in range(start_index, end_index + 1):
            data = total_data[i]
            val = data["val"]
            if L2DataUtil.is_limit_up_price_buy_cancel(val):
                # 查询买入位置
                buy_index = l2_data_source_util.L2DataSourceUtils.get_buy_index_with_cancel_data_v2(data,
                                                                                                    local_today_buyno_map.get(
                                                                                                        code))
                if buy_index is not None and buy_index in watch_indexes:
                    need_compute = True
                    break
        if need_compute:
            # 计算撤单比例
            watch_indexes_list = list(watch_indexes)
            watch_indexes_list.sort()
            canceled_num = 0
            # 记录撤单索引
            canceled_indexes = []
            for wi in watch_indexes:
                cancel_data = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_canceled_data_v2(code,
                                                                                                      wi,
                                                                                                      total_data,
                                                                                                      local_today_canceled_buyno_map.get(
                                                                                                          code))
                if cancel_data:
                    canceled_num += total_data[wi]["val"]["num"] * total_data[wi]["re"]
                    canceled_indexes.append(cancel_data["index"])
                # if wi == watch_indexes_list[-1] and left_count == 0:
                #     # 离下单位置最近的一个撤单,必须触发撤单
                #     l2_log.l_cancel_debug(code, f"计算范围:{start_index}-{end_index},临近撤单:{wi}")
                #     return True, total_data[-1]
            rate = round(canceled_num / total_num, 3)
            thresh_hold_rate, must_buy = LCancelRateManager.get_cancel_rate(code,
                                                                            total_data[buy_exec_index]["val"]["time"])
            # 除开最大单的影响权重
            if not must_buy:
                temp_thresh_hold_rate = round((total_num - max_num) * 0.9 / total_num, 2)
                thresh_hold_rate = min(thresh_hold_rate, temp_thresh_hold_rate)
            l2_log.l_cancel_debug(code, f"L后计算范围:{start_index}-{end_index},已撤单比例:{rate}/{thresh_hold_rate}")
            if rate >= thresh_hold_rate:
                canceled_indexes.sort()
                l2_log.l_cancel_debug(code, f"L后撤单,撤单位置:{canceled_indexes[-1]}")
                return True, total_data[canceled_indexes[-1]]
        return False, None
    def __compute_near_by_trade_progress_need_cancel(self, code, buy_exec_index, start_index, end_index, total_data,
                                                     is_first_code):
        # L前守护时间为3分钟
        if tool.trade_time_sub(total_data[-1]['val']['time'],
                               total_data[buy_exec_index]['val']['time']) > constant.L_CANCEL_UP_EXPIRE_TIME:
            return False, None
        watch_indexes = self.__get_near_by_trade_progress_indexes_cache(code)
        buyno_map = local_today_buyno_map.get(code)
        watch_indexes = self.__watch_indexes_cache.get(code)
        if not watch_indexes:
            return False, None
        # 监听范围小于5笔不生效
        if len(watch_indexes) < 5:
            return False, None
        # 计算监听的总条数
        # 权重
        WATCH_INDEX_WEIGHTS = [3, 2, 1]
        total_count_weight = 0
        for wi in range(0, len(watch_indexes)):
            if wi < len(WATCH_INDEX_WEIGHTS):
                total_count_weight += WATCH_INDEX_WEIGHTS[wi]
            else:
                total_count_weight += WATCH_INDEX_WEIGHTS[-1]
        # 判断撤单中是否有监听中的索引
            return False, None, "无大单监听"
        total_datas = local_today_datas.get(code)
        need_compute = False
        for i in range(start_index, end_index + 1):
            data = total_data[i]
            data = total_datas[i]
            val = data["val"]
            if L2DataUtil.is_limit_up_price_buy_cancel(val):
                # 查询买入位置
                buy_index = l2_data_source_util.L2DataSourceUtils.get_buy_index_with_cancel_data_v2(data,
                                                                                                    local_today_buyno_map.get(
                                                                                                        code))
                if buy_index is not None and buy_index in watch_indexes:
                    need_compute = True
                    break
        if need_compute:
            watch_indexes_list = list(watch_indexes)
            watch_indexes_list.sort()
            # 计算撤单比例
            canceled_count_weight = 0
            canceled_indexes = []
            for wi in watch_indexes:
                canceled_data = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_canceled_data_v2(code,
                                                                                                        wi,
                                                                                                        total_data,
                                                                                                        local_today_canceled_buyno_map.get(
                                                                                                            code))
                if canceled_data:
                    canceled_indexes.append(canceled_data["index"])
                    # 获取索引权重
                    pos_index = watch_indexes_list.index(wi)
                    if pos_index < len(WATCH_INDEX_WEIGHTS):
                        canceled_count_weight += WATCH_INDEX_WEIGHTS[pos_index]
                    else:
                        canceled_count_weight += WATCH_INDEX_WEIGHTS[-1]
            rate = round(canceled_count_weight / total_count_weight, 3)
            thresh_cancel_rate, must_buy = LCancelRateManager.get_cancel_rate(code,
                                                                              total_data[buy_exec_index]["val"]["time"],
                                                                              is_up=True)
            l2_log.l_cancel_debug(code, f"计算范围:{start_index}-{end_index},成交位临近已撤单比例:{rate}/{thresh_cancel_rate}")
            if rate >= thresh_cancel_rate:
                # 计算成交进度位置到当前下单位置的纯买额
                real_place_order_index_info = self.__real_place_order_index_dict.get(code)
                trade_progress_index = self.__last_trade_progress_dict.get(code)
                if real_place_order_index_info and trade_progress_index:
                    total_num = 0
                    thresh_hold_money = l2_trade_factor.L2PlaceOrderParamsManager.get_base_m_val(code)
                    thresh_hold_money = thresh_hold_money * 3
                    # 阈值为2倍m值
                    thresh_hold_num = thresh_hold_money // (float(gpcode_manager.get_limit_up_price(code)) * 100)
                    for i in range(trade_progress_index + 1, real_place_order_index_info[0]):
                        data = total_data[i]
                        val = data['val']
                        if not L2DataUtil.is_limit_up_price_buy(val):
                            continue
                        canceled_data = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_canceled_data_v2(code,
                                                                                                                i,
                                                                                                                total_data,
                                                                                                                local_today_canceled_buyno_map.get(
                                                                                                                    code))
                        if not canceled_data:
                            # 没有撤单
                            total_num += val["num"] * data["re"]
                            if total_num > thresh_hold_num:
                                # 成交位到下单位还有足够的单没撤
                                l2_log.l_cancel_debug(code,
                                                      f"L上撤阻断: 成交位-{trade_progress_index} 真实下单位-{real_place_order_index_info[0]} 阈值-{thresh_hold_money}")
                                return False, None
            if not L2DataUtil.is_limit_up_price_buy_cancel(val):
                continue
            if val["num"] * float(val["price"]) < 5000:
                continue
            buy_index = l2_data_source_util.L2DataSourceUtils.get_buy_index_with_cancel_data_v2(data, buyno_map)
            if buy_index is None:
                continue
            if buy_index in watch_indexes:
                need_compute = True
                break
        cancel_indexes = set()
        canceled_buyno_map = local_today_canceled_buyno_map.get(code)
        if need_compute or True:
            cancel_count = 0
            cancel_data = None
            # 成交买单号
            deal_order_nos = HuaXinBuyOrderManager().get_deal_buy_order_nos(code)
            if deal_order_nos is None:
                deal_order_nos = set()
            trade_index, is_default = TradeBuyQueue().get_traded_index(code)
            if is_default:
                trade_index = None
            for index in watch_indexes:
                if L2DataComputeUtil.is_canceled(code, index, total_datas, canceled_buyno_map, trade_index,
                                                 deal_order_nos):
                    # 买单已撤单
                    cancel_count += 1
                    cancel_indexes.add(index)
            rate = round(cancel_count / len(watch_indexes), 2)
            if rate >= 0.8:
                return True, cancel_data, f"撤单比例-{rate},大单撤单({cancel_indexes})"
        return False, None, "无大单撤单"
                canceled_indexes.sort()
                l2_log.l_cancel_debug(code, f"L上撤单,撤单位置:{canceled_indexes[-1]}")
                return True, total_data[canceled_indexes[-1]]
    def __clear_data(self, code):
        if code in self.__watch_indexes_cache:
            self.__watch_indexes_cache.pop(code)
            RedisUtils.delete_async(self.__db, f"radical_big_order_watch-{code}")
        return False, None
    # L后是否还有可能撤单
    def __is_l_down_can_cancel(self, code, buy_exec_index):
        watch_indexes_info = self.__get_watch_indexes_cache(code)
        if not watch_indexes_info:
            return True
        trade_index = self.__last_trade_progress_dict.get(code)
        if trade_index is None:
            return True
        # 计算已经成交的比例
        total_datas = local_today_datas.get(code)
        total_deal_nums = 0
        total_nums = 1
        for index in watch_indexes_info[2]:
            data = total_datas[index]
            val = data["val"]
            left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(code,
                                                                                                     index,
                                                                                                     total_datas,
                                                                                                     local_today_canceled_buyno_map.get(
                                                                                                         code))
            total_nums += val["num"]
            if left_count > 0 and index < trade_index:
                total_deal_nums += val["num"]
        thresh_hold_rate, must_buy = LCancelRateManager.get_cancel_rate(code,
                                                                        total_datas[buy_exec_index]["val"]["time"])
        if total_deal_nums / total_nums > 1 - thresh_hold_rate - 0.05:
            return False
        return True
    def need_cancel(self, code, buy_exec_index, start_index, end_index, total_data, is_first_code):
        if buy_exec_index is None:
            return False, None, "尚未找到下单位置"
        # 守护S撤以外的数据
        if int(tool.get_now_time_str().replace(":", "")) > int("145700") and not constant.TEST:
            return False, None, ""
        # 下单位临近撤
        can_cancel, cancel_data = False, None
        try:
            can_cancel, cancel_data = self.__compute_need_cancel(code, buy_exec_index, start_index, end_index,
                                                                 total_data,
                                                                 is_first_code)
        except Exception as e:
            logger_l2_l_cancel.exception(e)
            raise e
        extra_msg = "L后"
        if not can_cancel:
            # 成交位临近撤
            try:
                can_cancel, cancel_data = self.__compute_near_by_trade_progress_need_cancel(code, buy_exec_index,
                                                                                            start_index, end_index,
                                                                                            total_data,
                                                                                            is_first_code)
                extra_msg = "L前"
            except Exception as e:
                logger_l2_l_cancel.exception(e)
                raise e
        return can_cancel, cancel_data, extra_msg
    def place_order_success(self, code):
        self.clear(code)
    def cancel_success(self, code):
        self.clear(code)
    def clear_data(self, code):
        self.__clear_data(code)
# 新F撤,根据成交数据来撤
@@ -1727,6 +371,9 @@
    __db = 0
    __redis_manager = redis_manager.RedisManager(0)
    __real_order_index_cache = {}
    __max_buy_order_num_cache = {}
    # 下单距离太远计算
    __far_away_computed_cache = {}
    __instance = None
@@ -1778,6 +425,8 @@
    def clear(self, code=None):
        if code:
            self.__del_real_order_index(code)
            if code in self.__max_buy_order_num_cache:
                self.__max_buy_order_num_cache.pop(code)
        else:
            keys = RedisUtils.keys(self.__get_redis(), "f_cancel_real_order_index-*")
            if keys:
@@ -1788,12 +437,28 @@
    # 设置真实的下单位置
    def set_real_order_index(self, code, index, is_default):
        self.__set_real_order_index(code, index, is_default)
        self.__far_away_computed_cache[code] = False
        # if not is_default and code.find("60") == 0:
        try:
            # 统计未成交的最大单
            trade_index = 0
            trade_info = TradeBuyQueue().get_traded_index(code)
            if trade_info and not trade_info[1] and trade_info[0] is not None:
                trade_index = trade_info[0]
            data = L2DataComputeUtil.compute_max_buy_order_info(code, trade_index, index)
            # 保存最大单
            self.__max_buy_order_num_cache[code] = data["val"]["num"]
            l2_log.f_cancel_debug(code, f"最大单计算:索引-{data['index']} 范围:{trade_index}-{index}")
        except Exception as e:
            logger_l2_error.exception(e)
    def place_order_success(self, code):
        self.clear(code)
    def cancel_success(self, code):
        self.clear(code)
        # 撤单成功之后就清除当前的成交
        HuaXinSellOrderStatisticManager.clear_latest_deal_volume(code)
    def __get_fast_deal_threshold_value(self, code, place_order_time_str):
        """
@@ -1802,18 +467,15 @@
        @param place_order_time_str:下单时间
        @return:(金额(单位:W),笔数)
        """
        max60, yesterday = code_volumn_manager.get_histry_volumn(code)
        max60, yesterday = code_volumn_manager.CodeVolumeManager().get_histry_volumn(code)
        if max60:
            num = max60[0]
            limit_up_price = gpcode_manager.get_limit_up_price(code)
            if limit_up_price:
                money_y = round((num * float(limit_up_price)) / 1e8, 1)
                money = int(200 * money_y + 280)
                if tool.trade_time_sub(place_order_time_str, "10:00:00") <= 0:
                    # 10点前下单打7折
                    money = int(money * 0.7)
                count = int(money_y * 10) // 10 + 3
                return money, count
            money_y = code_volumn_manager.CodeVolumeManager().get_reference_volume_as_money_y(code)
            money = int(200 * money_y + 280)
            if tool.trade_time_sub(place_order_time_str, "10:00:00") <= 0:
                # 10点前下单打7折
                money = int(money * 0.7)
            count = int(money_y * 10) // 10 + 3
            return money, count
        # 默认值
        return 300, 3
@@ -1841,6 +503,23 @@
        # 是否是下单1分钟内
        if tool.trade_time_sub(tool.get_now_time_str(), total_datas[real_order_index]['val']['time']) > 1 * 60:
            return False, "下单超过60s"
        try:
            if code in self.__max_buy_order_num_cache:
                max_num = self.__max_buy_order_num_cache[code]
                if max_num and tool.is_ge_code(code):
                    max_num = max_num * 6.6
                details = HuaXinSellOrderStatisticManager.get_latest_3s_continue_deal_volumes(code)
                threshold_num = int(max_num * 0.5 * 100)
                count = 0
                for d in details:
                    if d[1] > threshold_num:
                        count += 1
                if count >= 2:
                    return True, f"连续3秒有2s抛压过大:{details} 最大值:{max_num}"
        except Exception as e:
            l2_log.f_cancel_debug(code, f"大抛压撤单计算出错:{str(e)}")
        # 查询最近2秒成交是否超过阈值
        THRESHOLD_MONEY_W, THRESHOLD_COUNT = self.__get_fast_deal_threshold_value(code,
                                                                                  total_datas[real_order_index]['val'][
@@ -1871,18 +550,26 @@
                if dealing_info and str(dealing_info[0]) == str(val["orderNo"]):
                    total_left_num -= dealing_info[1] // 100
        limit_up_price = gpcode_manager.get_limit_up_price(code)
        if total_left_count <= 1 or (total_left_count <= THRESHOLD_COUNT and limit_up_price and total_left_num * float(
                limit_up_price) < THRESHOLD_MONEY_W * 100):
            return True, f"剩余笔数({total_left_count})/金额({round(total_left_num * float(limit_up_price) * 100)})不足,成交进度:{trade_index},真实下单位置:{real_order_index} 阈值:({THRESHOLD_MONEY_W},{THRESHOLD_COUNT}) "
        total_left_money = total_left_num * float(limit_up_price) * 100
        if total_left_money < 1e8:
            if total_left_count <= 1 or (
                    total_left_count <= THRESHOLD_COUNT and limit_up_price and total_left_num * float(
                    limit_up_price) < THRESHOLD_MONEY_W * 100):
                return True, f"剩余笔数({total_left_count})/金额({round(total_left_num * float(limit_up_price) * 100)})不足,成交进度:{trade_index},真实下单位置:{real_order_index} 阈值:({THRESHOLD_MONEY_W},{THRESHOLD_COUNT}) "
        return False, f"不满足撤单条件: 成交进度-{trade_index} 真实下单位置-{real_order_index} total_left_count-{total_left_count} total_left_num-{total_left_num}"
    # 距离太近,封单不足,有大单50w大单砸下来就撤
    def need_cancel_for_p(self, code, big_sell_order_info, order_begin_pos):
    # 距离太近,封单不足
    def need_cancel_for_p(self, code, order_begin_pos):
        if True:
            return False, ""
        if gpcode_manager.MustBuyCodesManager().is_in_cache(code):
            return False, "已加红"
        if not order_begin_pos or not order_begin_pos.buy_exec_index or order_begin_pos.buy_exec_index < 0:
            return False, "尚未下单"
        if big_sell_order_info[0] < 50 * 10000 or not big_sell_order_info[1]:
            return False, "不为大单"
        # 判断是否具有真实的下单位置
        real_order_index_info = self.__get_real_order_index_cache(code)
        if not real_order_index_info:
@@ -1894,76 +581,100 @@
        if trade_index is None:
            trade_index = 0
        real_order_index = real_order_index_info[0]
        if real_order_index_info[0] <= trade_index:
        if real_order_index <= trade_index:
            return False, "真实下单位在成交位之前"
        start_order_no = big_sell_order_info[1][-1][4][1]
        real_trade_index = trade_index
        # 统计未撤订单的数量与金额
        total_datas = local_today_datas.get(code)
        for i in range(trade_index, real_order_index):
            data = total_datas[i]
            val = data['val']
            if not L2DataUtil.is_limit_up_price_buy(val):
                continue
            if int(val['orderNo']) < start_order_no:
                continue
            real_trade_index = i
            break
        # 是否是下单3分钟内
        if tool.trade_time_sub(total_datas[-1]['val']['time'], total_datas[real_order_index]['val']['time']) > 180:
            return False, "下单超过180s"
        sub_time = tool.trade_time_sub(total_datas[-1]['val']['time'], total_datas[real_order_index]['val']['time'])
        if sub_time > 60:
            return False, "下单超过60s"
        limit_up_price = gpcode_manager.get_limit_up_price_as_num(code)
        # 判断累计大单数量
        min_money = l2_data_util.get_big_money_val(limit_up_price, tool.is_ge_code(code))
        total_count, total_num = L2DataComputeUtil.compute_left_buy_order(code, trade_index, real_order_index,
                                                                          limit_up_price, min_money)
        total_left_count = 0
        # 下单后3秒,排撤比例≥65%则撤掉,视为P撤的一种,排得太后了。
        # if sub_time > 3 and not self.__far_away_computed_cache.get(code):
        #     self.__far_away_computed_cache[code] = True
        #     # 成交进度位到真实下单位的位置过远
        #     total_count_, total_num_ = L2DataComputeUtil.compute_left_buy_order(code, trade_index, real_order_index,
        #                                                                         limit_up_price, 500000)
        #     # 获取买1金额
        #     buy1_money = code_price_manager.Buy1PriceManager().get_latest_buy1_money(code)
        #     if buy1_money:
        #         if total_num_ * limit_up_price * 100 > buy1_money * 0.65:
        #             return True, f"P撤:成交位置距离下单位置太远 成交位-{trade_index} 下单位-{real_order_index} 买1-{buy1_money}"
        min_time_s, max_time_s = 2, 60
        # if total_num * limit_up_price >= 299 * 100:
        #     min_time_s, max_time_s = 30, 60
        if sub_time <= min_time_s:
            return False, f"下单在{min_time_s}s内"
        if sub_time > max_time_s:
            return False, f"下单超过{max_time_s}s"
        # 计算我们后面的大单与涨停纯买额
        total_left_num = 0
        # 成交位到真实下单位剩余的未成交的单
        for i in range(real_trade_index + 1, real_order_index_info[0]):
        total_big_num_count = 0
        canceled_buyno_map = local_today_canceled_buyno_map.get(code)
        for i in range(real_order_index + 1, len(total_datas)):
            data = total_datas[i]
            val = data["val"]
            if not L2DataUtil.is_limit_up_price_buy(val):
                continue
            if val["num"] * float(val["price"]) < 5000:
            money = val["num"] * float(val["price"]) * 100
            if money < 500000:
                continue
            left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(code,
                                                                                                     i,
                                                                                                     total_datas,
                                                                                                     local_today_canceled_buyno_map.get(
                                                                                                         code))
                                                                                                     canceled_buyno_map)
            if left_count > 0:
                total_left_count += left_count
                total_left_num += val["num"] * left_count
        limit_up_price = gpcode_manager.get_limit_up_price(code)
        if total_left_count < 5 or total_left_num * float(limit_up_price) < 500 * 100:
            # 距离成交进度位5笔以内或500万以内
            # 计算我们后面的大单与涨停纯买额
            total_left_num = 0
            total_big_num_count = 0
            for i in range(real_order_index + 1, len(total_datas)):
                data = total_datas[i]
                val = data["val"]
                if not L2DataUtil.is_limit_up_price_buy(val):
                    continue
                money = val["num"] * float(val["price"])
                if money < 5000:
                    continue
                left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(code,
                                                                                                         i,
                                                                                                         total_datas,
                                                                                                         local_today_canceled_buyno_map.get(
                                                                                                             code))
                if left_count > 0:
                    if money > 299 * 100:
                        total_big_num_count += 1
                    total_left_num += val["num"]
            left_money = total_left_num * float(limit_up_price)
            if total_big_num_count == 0 or left_money < 1000 * 100:
                # 实际下单位后方所有涨停纯买额≤1000万或没有任何大单(≥299万)
                return True, f"P撤:封单纯买额-{round(left_money / 100, 1)}万 大单数量-{total_big_num_count} 下单位-{real_order_index} 成交位-{real_trade_index}"
                if money >= min_money:
                    total_big_num_count += 1
                total_left_num += val["num"]
        left_money = total_left_num * float(limit_up_price)
        if left_money < 1000 * 100 or total_big_num_count < 2:
            # 实际下单位后方所有涨停纯买额≤1000万
            return True, f"P撤:封单纯买额-{round(left_money / 100, 1)}万 剩余大单数量-{total_big_num_count} 下单位-{real_order_index}"
        return False, "不满足撤单条件"
    # w撤
    def need_cancel_for_w(self, code):
        real_order_index_info = self.__get_real_order_index_cache(code)
        if not real_order_index_info:
            return False, "没找到真实下单位"
        if real_order_index_info[1]:
            return False, "真实下单位为默认"
        place_order_count = trade_data_manager.PlaceOrderCountManager().get_place_order_count(code)
        if place_order_count > 1:
            return False, "不是初次下单"
        trade_index, is_default = TradeBuyQueue().get_traded_index(code)
        if trade_index is None:
            return False, "没获取到成交进度位"
        real_order_index = real_order_index_info[0]
        total_datas = local_today_datas.get(code)
        if tool.trade_time_sub(total_datas[-1]['val']['time'], total_datas[real_order_index]['val']['time']) > 60:
            return False, "超过守护时间"
        limit_up_price = gpcode_manager.get_limit_up_price_as_num(code)
        end_index = L2DataComputeUtil.compute_end_index(code, real_order_index + 1, total_datas[-1]["index"],
                                                        limit_up_price, 10)
        # 从成交进度位到截至位置计算大单
        min_money = l2_data_util.get_big_money_val(limit_up_price, tool.is_ge_code(code))
        left_count, left_money = L2DataComputeUtil.compute_left_buy_order(code, trade_index, end_index, limit_up_price,
                                                                          min_money=min_money)
        if left_count < 1:
            return True, f"范围:{trade_index}-{end_index}  大单数量:{left_count}"
        return False, "大单数量够"
# ---------------------------------G撤-------------------------------
# 已不效
class GCancelBigNumComputer:
    __real_place_order_index_dict = {}
    __trade_progress_index_dict = {}
@@ -2086,7 +797,28 @@
        total_datas = local_today_datas.get(code)
        watch_indexes = set()
        for i in range(start_index, real_order_index):
        # G撤的囊括范围向后面延申5笔
        end_index = real_order_index
        count = 0
        for i in range(real_order_index + 1, total_datas[-1]["index"] + 1):
            data = total_datas[i]
            val = data["val"]
            if not L2DataUtil.is_limit_up_price_buy(val):
                continue
            if val["num"] * float(val["price"]) < 5000:
                continue
            left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(code, i,
                                                                                                     total_datas,
                                                                                                     local_today_canceled_buyno_map.get(
                                                                                                         code))
            if left_count <= 0:
                continue
            count += 1
            if count > 5:
                break
            end_index = i
        for i in range(start_index, end_index + 1):
            # 判断是否有未撤的大单
            data = total_datas[i]
            val = data["val"]
@@ -2101,13 +833,15 @@
                                                                                                         code))
            if left_count > 0:
                watch_indexes.add(i)
        if watch_indexes:
            # 还有300万以上的大单没有撤单
            if from_real_order_index_changed or recompute:
                # 真实下单位改变后才会更新
                final_watch_indexes = origin_watch_index | watch_indexes
                self.__watch_indexes_dict[code] = final_watch_indexes
                l2_log.g_cancel_debug(code, f"大单监听:{final_watch_indexes} 是否重新计算:{recompute}")
                l2_log.g_cancel_debug(code,
                                      f"大单监听:{final_watch_indexes} 是否重新计算:{recompute} 计算范围:{start_index}-{end_index}")
                # 有大单监听,需要移除之前的小单监听
                if code in self.__watch_indexes_by_dict:
                    self.__watch_indexes_by_dict[code].clear()
@@ -2223,8 +957,8 @@
                    canceled_indexes.add(cancel_data["index"])
            cancel_rate = round(len(canceled_indexes) / len(watch_indexes), 2)
            threshhold_rate = constant.G_CANCEL_RATE
            situation = trade_manager.MarketSituationManager().get_situation_cache()
            if situation == trade_manager.MarketSituationManager.SITUATION_GOOD:
            situation = trade_setting.MarketSituationManager().get_situation_cache()
            if situation == trade_setting.MarketSituationManager.SITUATION_GOOD:
                threshhold_rate = constant.G_CANCEL_RATE_FOR_GOOD_MARKET
            if cancel_rate > threshhold_rate:
                canceled_indexes_list = list(canceled_indexes)
@@ -2242,8 +976,8 @@
                    canceled_indexes.add(cancel_data["index"])
            cancel_rate = round(len(canceled_indexes) / len(watch_indexes_by), 2)
            threshhold_rate = constant.G_CANCEL_RATE
            situation = trade_manager.MarketSituationManager().get_situation_cache()
            if situation == trade_manager.MarketSituationManager.SITUATION_GOOD:
            situation = trade_setting.MarketSituationManager().get_situation_cache()
            if situation == trade_setting.MarketSituationManager.SITUATION_GOOD:
                threshhold_rate = constant.G_CANCEL_RATE_FOR_GOOD_MARKET
            if cancel_rate > threshhold_rate:
                canceled_indexes_list = list(canceled_indexes)
@@ -2255,6 +989,8 @@
    # B撤单
    # 剩余一个大单撤半截就撤单
    def need_cancel_for_b(self, code, start_index, end_index):
        if gpcode_manager.MustBuyCodesManager().is_in_cache(code):
            return False, None, "已加红"
        real_place_order_info = self.__real_place_order_index_dict.get(code)
        if not real_place_order_info or real_place_order_info[1]:
            # 没有真实下单位置
@@ -2401,14 +1137,18 @@
            start_index = traded_index + 1
        total_datas = local_today_datas.get(code)
        watch_indexes = set()
        for i in range(start_index, real_order_index):
        # 查找成交进度位到真实下单位置所有的索引
        watch_indexes_info = []
        limit_up_price = gpcode_manager.get_limit_up_price_as_num(code)
        big_num = int(l2_data_util.get_big_money_val(limit_up_price, tool.is_ge_code(code)) / (limit_up_price * 100))
        for i in range(start_index, real_order_index + 1):
            # 判断是否有未撤的大单
            data = total_datas[i]
            val = data["val"]
            if not L2DataUtil.is_limit_up_price_buy(val):
                continue
            if val["num"] * float(val["price"]) < 29900:
            if val["num"] < big_num:
                continue
            # 是否已撤单
            left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(code, i,
@@ -2416,14 +1156,28 @@
                                                                                                     local_today_canceled_buyno_map.get(
                                                                                                         code))
            if left_count > 0:
                watch_indexes.add(i)
                watch_indexes_info.append((i, val["num"]))
        # 当所有大单≤6笔时则G撤全部囊括
        #  大单>6笔时囊括其三分之一
        if len(watch_indexes_info) > 6:
            watch_indexes = set([x[0] for x in watch_indexes_info[:int(round(len(watch_indexes_info) / 3, 0))]])
            # 找出最大单
            max_info = watch_indexes_info[0]
            for mi in watch_indexes_info:
                if mi[1] > max_info[1]:
                    max_info = mi
            watch_indexes.add(max_info[0])
        else:
            watch_indexes = set([x[0] for x in watch_indexes_info])
        if watch_indexes:
            # 还有300万以上的大单没有撤单
            if from_real_order_index_changed or recompute:
                # 真实下单位改变后才会更新
                final_watch_indexes = origin_watch_index | watch_indexes
                self.__set_watch_index(code, final_watch_indexes)
                l2_log.g_cancel_debug(code, f"大单监听:{final_watch_indexes} 是否重新计算:{recompute}")
                l2_log.g_cancel_debug(code,
                                      f"大单监听:{final_watch_indexes} 是否重新计算:{recompute} 计算范围:{start_index}-{real_order_index}")
    def set_trade_progress(self, code, buy_single_index, index):
        # if self.__trade_progress_index_dict.get(code) != index:
@@ -2473,8 +1227,8 @@
                    canceled_indexes.add(cancel_data["index"])
            cancel_rate = round(len(canceled_indexes) / len(watch_indexes), 2)
            threshhold_rate = constant.G_CANCEL_RATE
            situation = trade_manager.MarketSituationManager().get_situation_cache()
            if situation == trade_manager.MarketSituationManager.SITUATION_GOOD:
            situation = trade_setting.MarketSituationManager().get_situation_cache()
            if situation == trade_setting.MarketSituationManager.SITUATION_GOOD:
                threshhold_rate = constant.G_CANCEL_RATE_FOR_GOOD_MARKET
            if gpcode_manager.MustBuyCodesManager().is_in_cache(code):
                threshhold_rate = constant.G_CANCEL_RATE_WITH_MUST_BUY
@@ -2552,7 +1306,6 @@
    __db = 0
    __redis_manager = redis_manager.RedisManager(0)
    __cancel_real_order_index_cache = {}
    __SCancelBigNumComputer = SCancelBigNumComputer()
    __instance = None
@@ -2591,7 +1344,8 @@
        time_sub = tool.trade_time_sub(tool.get_now_time_str(),
                                       total_datas[order_begin_pos.buy_exec_index]["val"]["time"])
        if 2 < time_sub < 30 * 60:
            real_place_order_index = self.__SCancelBigNumComputer.get_real_place_order_index_cache(code)
            real_place_order_index = s_l_h_cancel_strategy.SCancelBigNumComputer().get_real_place_order_index_cache(
                code)
            if not real_place_order_index:
                return False, "尚未找到真实下单位置"
            total_left_count = 0
@@ -2820,7 +1574,7 @@
            self.__set_l2_second_money_record(code, t_, time_dict_num[t_], time_dict_num_index[t_]["s"],
                                              time_dict_num_index[t_]["e"])
        print("保存涨停封单额时间:", round(time.time() * 1000) - start_time)
        # print("保存涨停封单额时间:", round(time.time() * 1000) - start_time)
        # 累计最新的金额
        total_num, index = self.__get_l2_latest_money_record(code)
@@ -2940,7 +1694,7 @@
        if not with_cancel:
            cancel_index = None
        print("封单额计算时间:", round(time.time() * 1000) - start_time)
        # print("封单额计算时间:", round(time.time() * 1000) - start_time)
        process_end_index = end_index
        if cancel_index:
            process_end_index = cancel_index
@@ -3181,7 +1935,7 @@
            if not outoftime:
                # 上次计算还未超时
                return
        limit_up_price = round(float(gpcode_manager.get_limit_up_price(code)), 2)
        limit_up_price = gpcode_manager.get_limit_up_price_as_num(code)
        if cancel_single_info and outoftime:
            # 更新需要计算信号
@@ -3225,6 +1979,8 @@
        l2_log.j_cancel_debug(code, f"触发囊括:{self.__cancel_single_cache[code]}")
    def need_cancel(self, code, start_index, end_index):
        if 1 > 0:
            return False, None, "J撤不生效"
        # 需要先计算
        self.__compute_cancel_single(code, start_index, end_index)
        # [时间, 真实下单位置, 信号总手数, 目前手数, 最新计算的索引]
@@ -3234,11 +1990,11 @@
        # 计算剩余数量
        total_datas = local_today_datas.get(code)
        if int(total_datas[end_index]['val']['time'].replace(":","")) > 145000:
        if int(total_datas[end_index]['val']['time'].replace(":", "")) > 145000:
            return False, None, "超过生效时间"
        buyno_map = local_today_buyno_map.get(code)
        limit_up_price = round(float(gpcode_manager.get_limit_up_price(code)), 2)
        limit_up_price = gpcode_manager.get_limit_up_price_as_num(code)
        min_volume = 50 * 10000 // int(limit_up_price * 100)
        # 计算纯买额
        for i in range(start_index, end_index + 1):
@@ -3301,13 +2057,15 @@
        self.__real_place_order_index_info_dict[code] = (index, is_default)
    def need_cancel(self, code, trade_index):
        if gpcode_manager.MustBuyCodesManager().is_in_cache(code):
            return False, "已加红"
        # [时间, 真实下单位置, 信号总手数, 目前手数, 最新计算的索引]
        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 False, "没有找到真实下单位"
        real_place_order_index = real_place_order_index_info[0]
        limit_up_price = round(float(gpcode_manager.get_limit_up_price(code)),2)
        limit_up_price = gpcode_manager.get_limit_up_price_as_num(code)
        if limit_up_price < 3:
            return False, "股价小于3块"
@@ -3320,7 +2078,9 @@
        total_datas = local_today_datas.get(code)
        if tool.trade_time_sub(tool.get_now_time_str(), total_datas[real_place_order_index]['val']['time']) > 300:
            return False, "超过生效时间"
        total_left_count, total_left_num = L2DataComputeUtil.compute_left_buy_order(code, trade_index, real_place_order_index,limit_up_price, 299*10000)
        total_left_count, total_left_num = L2DataComputeUtil.compute_left_buy_order(code, trade_index,
                                                                                    real_place_order_index,
                                                                                    limit_up_price, 299 * 10000)
        if total_left_count > 0:
            return False, '有大单'
        return True, f'无大单:{trade_index}-{real_place_order_index}'
@@ -3334,8 +2094,6 @@
    def place_order_success(self, code):
        self.__clear_data(code)
if __name__ == "__main__":