| | |
| | | import copy |
| | | import json |
| | | import time |
| | | |
| | |
| | | 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 third_data.code_plate_key_manager import CodePlateKeyBuyManager |
| | | |
| | | from third_data import code_plate_key_manager |
| | | from trade.buy_radical import radical_buy_data_manager |
| | | from utils import tool |
| | | from l2.transaction_progress import TradeBuyQueue |
| | | from trade import l2_trade_factor, trade_record_log_util |
| | | from trade import l2_trade_factor, trade_record_log_util, trade_constant |
| | | from l2 import l2_log, l2_data_source_util |
| | | from l2.l2_data_util import L2DataUtil, local_today_datas, local_today_canceled_buyno_map, local_today_buyno_map |
| | | from log_module.log import logger_l2_s_cancel, logger_debug, logger_l2_l_cancel, logger_l2_h_cancel |
| | |
| | | half_must_buy = False |
| | | if not must_buy: |
| | | try: |
| | | can_buy_result = CodePlateKeyBuyManager.can_buy(code) |
| | | can_buy_result = code_plate_key_manager.CodePlateKeyBuyManager.can_buy(code) |
| | | if can_buy_result and can_buy_result[0]: |
| | | if can_buy_result[0][1] <= 1 and can_buy_result[0][2] >= 3: |
| | | # 炸板率>60%以上就不加半红 |
| | |
| | | else: |
| | | return constant.S_FAST_RATE, "常规" |
| | | elif cancel_type == cls.TYPE_S_FAST_BIG: |
| | | # 重砸 |
| | | if must_buy: |
| | | return constant.S_FAST_BIG_RATE_WITH_MUST_BUY, "加红" |
| | | elif half_must_buy: |
| | |
| | | @param code: |
| | | @return:(大单阈值, 800w内大单阈值) |
| | | """ |
| | | 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 = min(round((num * float(limit_up_price)) / 1e8, 1), 7.9) |
| | | money_y = min(round((num * float(limit_up_price)) / 1e8, 1), 50) |
| | | money = int(round(15 * money_y + 276.5)) |
| | | money_800 = int(round(money * 0.668)) |
| | | return money, money_800 |
| | |
| | | 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: |
| | | # 卖金额>=均大单才触发重新囊括 |
| | | THRESHOLD_MONEY, is_temp_threshold_money = radical_buy_data_manager.BeforeSubDealBigOrderManager().get_big_order_threshold_info( |
| | | code) |
| | | if total_deal_money >= THRESHOLD_MONEY: |
| | | 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) |
| | |
| | | |
| | | # 是否有大卖单需要撤 |
| | | def __need_cancel_for_big_sell_order(self, code, big_sell_order_info, order_begin_pos: OrderBeginPosInfo): |
| | | """ |
| | | @param code: |
| | | @param big_sell_order_info:(总卖金额,[(卖单号,总手数,价格,('开始时间',买单号),('结束时间',买单号)),...]) |
| | | @param order_begin_pos: |
| | | @return: |
| | | """ |
| | | # 需要排除成交时间在下单时间之前的 |
| | | total_deal_money = 0 |
| | | max_single_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 |
| | | real_order_index = 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( |
| | |
| | | if real_order_time_ms: |
| | | if tool.trade_time_sub_with_ms(deal_time, real_order_time_ms) >= 0: |
| | | m = x[1] * x[2] |
| | | max_single_deal_money = max(max_single_deal_money, m) |
| | | total_deal_money += m |
| | | else: |
| | | m = x[1] * x[2] |
| | | max_single_deal_money = max(max_single_deal_money, m) |
| | | total_deal_money += m |
| | | |
| | | zyltgb = l2_trade_factor.L2TradeFactorUtil.get_zyltgb(code) |
| | |
| | | if total_deal_money >= threshold_big_deal: |
| | | # S重砸必撤的金额满足以后,以前是无脑撤。现在优化一下,看成交进度位---真实下单位的区间额≤重砸金额*3.3倍,就撤。 |
| | | try: |
| | | need_compute = False |
| | | 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]: |
| | | need_compute = True |
| | | # 单笔重砸 |
| | | if max_single_deal_money >= threshold_big_deal: |
| | | # 上证,下单3分钟内 |
| | | try: |
| | | if tool.is_sh_code(code) and real_order_index is not None and tool.trade_time_sub( |
| | | total_datas[-1]["val"]["time"], total_datas[real_order_index]["val"]["time"]) < 3 * 60: |
| | | # 上证如果重砸额大于阈值的1.5倍直接撤单 |
| | | if not gpcode_manager.MustBuyCodesManager().is_in_cache(code): |
| | | if total_deal_money >= threshold_big_deal * 1.5: |
| | | return True, f"1s内成交({total_deal_money}) 大于大卖单({threshold_big_deal})的1.5倍" |
| | | # 如果没有大单成交也直接撤单 |
| | | deal_big_order_count = BigOrderDealManager().get_total_buy_count(code) |
| | | if deal_big_order_count < 1: |
| | | return True, f"1s内成交({total_deal_money}) 大于大卖单({threshold_big_deal})且无大单成交" |
| | | except Exception as e: |
| | | l2_log.s_cancel_debug(code, "S重砸出错了:{}", str(e)) |
| | | |
| | | if need_compute: |
| | | total_count, total_num = L2DataComputeUtil.compute_left_buy_order(code, trade_index, |
| | | real_order_index_info[0], |
| | | limit_up_price) |
| | | if total_num == 0: |
| | | total_num = 1 |
| | | need_compute = False |
| | | 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]: |
| | | need_compute = True |
| | | |
| | | threshold_rate, threshold_rate_msg = SCancelRateManager.get_threshhold_rate(code, |
| | | SCancelRateManager.TYPE_S_FAST_BIG) |
| | | if need_compute: |
| | | total_count, total_num = L2DataComputeUtil.compute_left_buy_order(code, trade_index, |
| | | real_order_index_info[0], |
| | | limit_up_price) |
| | | if total_num == 0: |
| | | total_num = 1 |
| | | |
| | | if total_deal_money / (total_num * limit_up_price * 100) >= threshold_rate: |
| | | # 大单成交额占总剩余总囊括的30% |
| | | return True, f"1s内大于{threshold_big_deal}({threshold_rate_msg})大卖单({total_deal_money})" |
| | | else: |
| | | return True, f"1s内大于{threshold_big_deal}大卖单({total_deal_money})" |
| | | threshold_rate, threshold_rate_msg = SCancelRateManager.get_threshhold_rate(code, |
| | | SCancelRateManager.TYPE_S_FAST_BIG) |
| | | |
| | | if total_deal_money / (total_num * limit_up_price * 100) >= threshold_rate: |
| | | # 大单成交额占总剩余总囊括的30% |
| | | return True, f"1s内大于{threshold_big_deal}({threshold_rate_msg})大卖单({total_deal_money})" |
| | | else: |
| | | return True, f"1s内大于{threshold_big_deal}大卖单({total_deal_money})" |
| | | except Exception as e: |
| | | l2_log.info(code, logger_debug, f"S快计算出错:{str(e)}") |
| | | |
| | |
| | | # 15分钟内真实成交位距离真实下单位,金额≤800万 ,大单阈值变为200w |
| | | threash_money_w = threash_money_danger_w |
| | | except Exception as e: |
| | | l2_log.s_cancel_debug(code, f"S撤激进下单计算大单卖阈值出错:{str(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 |
| | |
| | | # ---------------------------------L撤------------------------------- |
| | | class LCancelRateManager: |
| | | __block_limit_up_count_dict = {} |
| | | __block_limit_up_count_for_l_up_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): |
| | | def get_cancel_rate(cls, code, is_up=False, is_l_down_recomputed=False, buy_mode=None): |
| | | try: |
| | | must_buy = cls.__MustBuyCodesManager.is_in_cache(code) |
| | | if buy_mode == OrderBeginPosInfo.MODE_RADICAL: |
| | | if must_buy: |
| | | # 扫入加红 |
| | | return constant.L_CANCEL_RATE_WITH_MUST_BUY_FOR_REDICAL_BUY, True |
| | | else: |
| | | # 根据成交额的大单成交占比来计算撤单比例 |
| | | big_money_rate = radical_buy_data_manager.TotalDealBigOrderInfoManager.get_big_order_rate(code) |
| | | if big_money_rate is not None: |
| | | threshold_rate = min(big_money_rate * 3, 0.95) |
| | | return threshold_rate, False |
| | | else: |
| | | deal_big_order_info = radical_buy_data_manager.get_total_deal_big_order_info(code, |
| | | gpcode_manager.get_limit_up_price_as_num( |
| | | code)) |
| | | deal_rate = round(deal_big_order_info[1] / deal_big_order_info[2], 2) |
| | | threshold_rate = 0.5 * deal_rate + 0.35 |
| | | threshold_rate = max(threshold_rate, 0.375) |
| | | threshold_rate = min(threshold_rate, 0.95) |
| | | return threshold_rate, False |
| | | if must_buy: |
| | | if is_up: |
| | | return constant.L_CANCEL_RATE_UP_WITH_MUST_BUY, True |
| | |
| | | base_rate = constant.L_CANCEL_RATE |
| | | if is_up: |
| | | base_rate = constant.L_CANCEL_RATE_UP |
| | | if tool.is_sh_code(code): |
| | | base_rate = constant.L_CANCEL_RATE_UP_SH |
| | | try: |
| | | block_rate = 0 |
| | | if code in cls.__block_limit_up_count_dict: |
| | | count = cls.__block_limit_up_count_dict[code] |
| | | count_dict = cls.__block_limit_up_count_dict |
| | | if is_up: |
| | | count_dict = cls.__block_limit_up_count_for_l_up_dict |
| | | if code in count_dict: |
| | | count = count_dict[code] |
| | | rates = [0, 0.03, 0.06, 0.08, 0.12] |
| | | if count >= len(rates): |
| | | block_rate = rates[-1] |
| | |
| | | |
| | | # 设置板块涨停数量(除开自己) |
| | | @classmethod |
| | | def set_block_limit_up_count(cls, reason_codes_dict): |
| | | def set_block_limit_up_count(cls, reason_codes_dict, limit_up_time_dict: dict): |
| | | for reason in reason_codes_dict: |
| | | codes = reason_codes_dict[reason] |
| | | for c in codes: |
| | | codes = list(codes) |
| | | # 目标票在确认的涨停原因中,在总的身位的≤50%以外,则L前的涨停影响比例因素不生效。 |
| | | codes.sort(key=lambda x: ( |
| | | int(limit_up_time_dict.get(x).replace(":", "")) if x in limit_up_time_dict else int("150000"))) |
| | | for i in range(len(codes)): |
| | | c = codes[i] |
| | | cls.__block_limit_up_count_dict[c] = len(codes) - 1 |
| | | if i < len(codes) / 2: |
| | | cls.__block_limit_up_count_for_l_up_dict[c] = len(codes) - 1 |
| | | else: |
| | | cls.__block_limit_up_count_for_l_up_dict[c] = 0 |
| | | |
| | | @classmethod |
| | | def set_big_num_deal_info(cls, code, buy_money, sell_money): |
| | |
| | | |
| | | __last_l_up_compute_info = {} |
| | | |
| | | __l_down_after_by_big_order_dict = {} |
| | | |
| | | # 权重 |
| | | __l_down_after_by_big_order_weight_dict = {} |
| | | __instance = None |
| | | |
| | | def __new__(cls, *args, **kwargs): |
| | |
| | | CodeDataCacheUtil.set_cache(cls.__cancel_l_down_after_place_order_index_cache, code, val) |
| | | except: |
| | | pass |
| | | |
| | | |
| | | finally: |
| | | RedisUtils.realse(__redis) |
| | | |
| | |
| | | if cache_result[0]: |
| | | return cache_result[1] |
| | | return None |
| | | |
| | | def get_l_down_watch_indexes_cache(self, code): |
| | | """ |
| | | 获取L后的监听范围 |
| | | @param code: |
| | | @return:(buy_single_index, re_compute, indexes) |
| | | """ |
| | | return self.__get_watch_indexes_cache(code) |
| | | |
| | | def get_real_place_order_index_info(self, code): |
| | | """ |
| | | 获取真实下单索引信息 |
| | | @param code: |
| | | @return:(index, is_default) |
| | | """ |
| | | return self.__real_place_order_index_dict.get(code) |
| | | |
| | | def set_l_down_watch_index_info(self, code, buy_single_index, re_compute: int, indexes): |
| | | """ |
| | | 设置l后索引 |
| | | @param code: |
| | | @param buy_single_index: |
| | | @param re_compute: |
| | | @param indexes: |
| | | @return: |
| | | """ |
| | | self.__set_watch_indexes(code, buy_single_index, re_compute, indexes) |
| | | |
| | | def __set_near_by_trade_progress_indexes(self, code, buy_single_index, indexes): |
| | | if indexes: |
| | |
| | | def clear(self, code=None): |
| | | if code: |
| | | self.del_watch_index(code) |
| | | if code in self.__l_down_after_by_big_order_dict: |
| | | self.__l_down_after_by_big_order_dict.pop(code) |
| | | if code in self.__l_down_after_by_big_order_weight_dict: |
| | | self.__l_down_after_by_big_order_weight_dict.pop(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}") |
| | |
| | | keys = RedisUtils.keys(self.__get_redis(), f"l_cancel_down_after_place_order_index-*") |
| | | for k in keys: |
| | | RedisUtils.delete(self.__get_redis(), k) |
| | | self.__l_down_after_by_big_order_dict.clear() |
| | | self.__l_down_after_by_big_order_weight_dict.clear() |
| | | |
| | | # 重新计算L上 |
| | | |
| | |
| | | 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) |
| | | last_trade_progress_index, is_default = TradeBuyQueue().get_traded_index(code) |
| | | if big_sell_info: |
| | | last_trade_progress_index = big_sell_info[0] |
| | | |
| | |
| | | # 计算的上截至位距离下截至位纯买额要小于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)))) |
| | | min_num = int(5000 / gpcode_manager.get_limit_up_price_as_num(code)) |
| | | # 统计净涨停买的数量 |
| | | not_cancel_indexes_with_num = [] |
| | | re_start_index = start_index |
| | |
| | | # 取后1/5的数据 |
| | | if temp_count >= 30: |
| | | temp_index = int(temp_count * 4 / 5) |
| | | if tool.is_sh_code(code): # 上证取后3/10 |
| | | temp_index = int(temp_count * 7 / 10) |
| | | re_start_index = not_cancel_indexes_with_num[temp_index][0] |
| | | MAX_COUNT = len(not_cancel_indexes_with_num[temp_index:]) |
| | | else: |
| | |
| | | if temp_count >= 10: |
| | | # 获取中位数 |
| | | min_num = not_cancel_indexes_with_num[temp_count // 2][1] |
| | | MIN_MONEYS = [300, 200, 100, 50] |
| | | MIN_MONEYS = [300, 200, 100] |
| | | watch_indexes = set() |
| | | for min_money in MIN_MONEYS: |
| | | for i in range(end_index, re_start_index - 1, -1): |
| | |
| | | break |
| | | if watch_indexes: |
| | | ##判断监听的数据中是否有大单## |
| | | # 之前的大单为100w,现在改为正常大单 |
| | | has_big_num = False |
| | | BIG_ORDER_NUM_THRESHOLD = l2_data_util.get_big_money_val( |
| | | gpcode_manager.get_limit_up_price_as_num(code), tool.is_ge_code(code)) |
| | | BIG_ORDER_NUM_THRESHOLD = int( |
| | | round(BIG_ORDER_NUM_THRESHOLD / (gpcode_manager.get_limit_up_price_as_num(code) * 100))) |
| | | for i in watch_indexes: |
| | | # 是否有大单 |
| | | data = total_datas[i] |
| | | val = data['val'] |
| | | if float(val['price']) * val['num'] > 100 * 100: |
| | | if val['num'] > BIG_ORDER_NUM_THRESHOLD: |
| | | has_big_num = True |
| | | break |
| | | if not has_big_num: |
| | |
| | | if not L2DataUtil.is_limit_up_price_buy(val): |
| | | continue |
| | | # 小金额过滤 |
| | | if float(val['price']) * val['num'] < 100 * 100: |
| | | if val['num'] < BIG_ORDER_NUM_THRESHOLD: |
| | | continue |
| | | cancel_data = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_canceled_data_v2(code, |
| | | i, |
| | |
| | | cancel_data['val'])) <= 0: |
| | | watch_indexes.add(i) |
| | | break |
| | | |
| | | # 计算真实下单位置到成交进度位的最大未撤大单前2 |
| | | big_order_list = [] |
| | | 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'] < BIG_ORDER_NUM_THRESHOLD: |
| | | 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: |
| | | big_order_list.append((i, val['num'])) |
| | | if big_order_list: |
| | | big_order_list.sort(key=lambda x: x[1], reverse=True) |
| | | watch_indexes |= set([x[0] for x in big_order_list[:2]]) |
| | | # 获取真实下单位后面10笔大单 |
| | | try: |
| | | # 真实下单位置后面的数据就只看大单 |
| | | MIN_NUM = int(5000 / (float(gpcode_manager.get_limit_up_price(code)))) |
| | | real_place_order_info = self.__real_place_order_index_dict.get(code) |
| | | if real_place_order_info and not real_place_order_info[1]: |
| | | # 从真实下单位往后找 |
| | | after_count = 0 |
| | | for i in range(real_place_order_info[0] + 1, total_datas[-1]['index'] + 1): |
| | | if after_count > 10: |
| | | break |
| | | data = total_datas[i] |
| | | 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, i, |
| | | total_datas, |
| | | local_today_canceled_buyno_map.get( |
| | | code)) |
| | | if left_count > 0: |
| | | after_count += 1 |
| | | if l2_data_util.is_big_money(val): |
| | | watch_indexes.add(i) |
| | | self.__set_cancel_l_down_after_place_order_index(code, i, after_count - 1) |
| | | except Exception as e: |
| | | pass |
| | | watch_indexes_after = self.__compute_l_down_watch_index_after_real_place_order_index(code) |
| | | if watch_indexes_after: |
| | | watch_indexes |= watch_indexes_after |
| | | 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}") |
| | |
| | | l2_log.l_cancel_debug(code, f"计算L后囊括范围出错:{str(e)}") |
| | | async_log_util.exception(logger_l2_l_cancel, e) |
| | | |
| | | def __need_update_l_down_after(self, code, start_index, end_index): |
| | | """ |
| | | 是否需要更新l后真实下单位置之后的囊括范围 |
| | | @param code: |
| | | @return: |
| | | """ |
| | | real_place_order_info = self.__real_place_order_index_dict.get(code) |
| | | if not real_place_order_info or real_place_order_info[1]: |
| | | return False |
| | | # 判断L后是否包含后面的数据 |
| | | watch_indexes_info = self.__get_watch_indexes_cache(code) |
| | | if not watch_indexes_info: |
| | | # 没有囊括 |
| | | return False |
| | | watch_indexes = set([int(i) for i in watch_indexes_info[2]]) |
| | | real_place_order_index = real_place_order_info[0] |
| | | for index in watch_indexes: |
| | | if index > real_place_order_index: |
| | | # L后后段已经囊括 |
| | | return False |
| | | # 下单位置之后数10笔买单 |
| | | watch_indexes = set() |
| | | total_datas = local_today_datas.get(code) |
| | | MIN_NUM = int(5000 / gpcode_manager.get_limit_up_price_as_num(code)) |
| | | MAX_COUNT = 10 |
| | | for i in range(real_place_order_index + 1, end_index): |
| | | data = total_datas[i] |
| | | val = data['val'] |
| | | if not L2DataUtil.is_limit_up_price_buy(val): |
| | | continue |
| | | if val['num'] < MIN_NUM: |
| | | continue |
| | | watch_indexes.add(i) |
| | | if len(watch_indexes) >= MAX_COUNT: |
| | | break |
| | | # 看里面的撤单率是否 |
| | | if len(watch_indexes) < MAX_COUNT: |
| | | # 数量不够 |
| | | return False |
| | | # 计算撤单比例是否足够 |
| | | canceled_buyno_map = local_today_canceled_buyno_map.get(code) |
| | | cancel_count = 0 |
| | | for index in watch_indexes: |
| | | # 是否撤单 |
| | | left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2( |
| | | code, index, |
| | | total_datas, |
| | | canceled_buyno_map) |
| | | if left_count <= 0: |
| | | cancel_count += 1 |
| | | if cancel_count > len(watch_indexes) * 0.5: |
| | | return True |
| | | return False |
| | | |
| | | def __compute_l_down_watch_index_after_real_place_order_index(self, code): |
| | | """ |
| | | 计算L后真实下单位置之后的监控索引 |
| | | @return: |
| | | """ |
| | | watch_indexes = set() |
| | | total_datas = local_today_datas.get(code) |
| | | is_ge_code = tool.is_ge_code(code) |
| | | try: |
| | | # 真实下单位置后面的数据就只看大单 |
| | | MIN_NUM = int(5000 / gpcode_manager.get_limit_up_price_as_num(code)) |
| | | real_place_order_info = self.__real_place_order_index_dict.get(code) |
| | | if real_place_order_info and not real_place_order_info[1]: |
| | | # 从真实下单位往后找 |
| | | after_count = 0 |
| | | for i in range(real_place_order_info[0] + 1, total_datas[-1]['index'] + 1): |
| | | if after_count > 10: |
| | | break |
| | | data = total_datas[i] |
| | | 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, i, |
| | | total_datas, |
| | | local_today_canceled_buyno_map.get( |
| | | code)) |
| | | if left_count > 0: |
| | | after_count += 1 |
| | | if l2_data_util.is_big_money(val, is_ge_code): |
| | | watch_indexes.add(i) |
| | | # 记录索引的位置 |
| | | self.__set_cancel_l_down_after_place_order_index(code, i, after_count - 1) |
| | | except Exception as e: |
| | | pass |
| | | return watch_indexes |
| | | |
| | | def __compute_l_down_watch_index_after_by(self, code): |
| | | """ |
| | | 计算L后后半段大单备用 |
| | | @param code: |
| | | @return: |
| | | """ |
| | | if self.__l_down_after_by_big_order_dict.get(code): |
| | | # 不用重复计算 |
| | | return |
| | | real_place_order_info = self.__real_place_order_index_dict.get(code) |
| | | if not real_place_order_info or real_place_order_info[1]: |
| | | return |
| | | real_place_order_index = real_place_order_info[0] |
| | | total_datas = local_today_datas.get(code) |
| | | limit_up_price = gpcode_manager.get_limit_up_price(code) |
| | | limit_up_price = round(float(limit_up_price), 2) |
| | | bigger_num = l2_data_util.get_big_money_val(limit_up_price, tool.is_ge_code(code)) // (limit_up_price * 100) |
| | | min_num = 500000 // (limit_up_price * 100) |
| | | index = -1 |
| | | watch_indexes = set() |
| | | if code not in self.__l_down_after_by_big_order_weight_dict: |
| | | self.__l_down_after_by_big_order_weight_dict[code] = {} |
| | | 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 val["num"] < min_num: |
| | | 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 val["num"] >= bigger_num: |
| | | if index < 0: |
| | | # 开始计数 |
| | | index = 0 |
| | | # 加入大单监听 |
| | | self.__l_down_after_by_big_order_weight_dict[code][str(i)] = int(str(index)) |
| | | watch_indexes.add(i) |
| | | if index >= 0: |
| | | index += 1 |
| | | if index > 10: |
| | | break |
| | | self.__l_down_after_by_big_order_dict[code] = watch_indexes |
| | | l2_log.l_cancel_debug(code, f"L后后半段大单备用囊括范围:{watch_indexes}") |
| | | |
| | | try: |
| | | watch_indexes_info = self.get_l_down_watch_indexes_cache(code) |
| | | if watch_indexes_info: |
| | | total_watch_indexes = set(watch_indexes) | set(watch_indexes_info[2]) |
| | | trade_record_log_util.add_cancel_watch_indexes_log(code, |
| | | trade_record_log_util.CancelWatchIndexesInfo( |
| | | trade_record_log_util.CancelWatchIndexesInfo.CANCEL_TYPE_L_DOWN, |
| | | watch_indexes_info[0], |
| | | list(total_watch_indexes))) |
| | | except: |
| | | pass |
| | | |
| | | # 设置真实下单位置 |
| | | 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))) |
| | | |
| | | trade_index, is_default = TradeBuyQueue().get_traded_index(code) |
| | | if trade_index is None: |
| | | trade_index = 0 |
| | | |
| | | 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) |
| | | # L后撤从成交进度位开始计算 |
| | | self.compute_watch_index(code, buy_single_index, trade_index, index) |
| | | self.__compute_trade_progress_near_by_indexes(code, buy_single_index, trade_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上 |
| | | # 重新计算L前 |
| | | def re_compute_l_up_watch_indexes(self, code, buy_single_index): |
| | | if code not in self.__last_trade_progress_dict: |
| | | trade_index, is_default = TradeBuyQueue().get_traded_index(code) |
| | | if trade_index is None: |
| | | 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, |
| | | trade_index + 1, |
| | | self.__real_place_order_index_dict.get(code)[0]) |
| | | |
| | | # 计算范围内的成交位临近未撤大单 |
| | |
| | | break |
| | | |
| | | changed = True |
| | | if code in self.__last_l_up_compute_info: |
| | | if self.__last_l_up_compute_info[code] == watch_indexes: |
| | | l_up_compute_info = self.__last_l_up_compute_info.get(code) |
| | | if l_up_compute_info: |
| | | if l_up_compute_info[1] == watch_indexes: |
| | | changed = False |
| | | # 保存数据 |
| | | if changed: |
| | | threshold_time = 1 if tool.is_sz_code(code) else 2 |
| | | if l_up_compute_info and l_up_compute_info[1]: |
| | | if time.time() - l_up_compute_info[0] < threshold_time: |
| | | l2_log.l_cancel_debug(code, f"L前监控更新太频繁:{threshold_time}") |
| | | return |
| | | 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) |
| | |
| | | if not l_down_cancel_info: |
| | | return |
| | | |
| | | trade_progress = self.__last_trade_progress_dict.get(code) |
| | | trade_progress, is_default = TradeBuyQueue().get_traded_index(code) |
| | | if trade_progress is None: |
| | | trade_progress = 0 |
| | | |
| | | if trade_progress 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) |
| | | try: |
| | | # 计算L后后半段大单监控范围 |
| | | self.__compute_l_down_watch_index_after_by(code) |
| | | except Exception as e: |
| | | l2_log.l_cancel_debug(code, "__compute_l_down_watch_index_after_by出错") |
| | | |
| | | real_place_order_index_info = self.__real_place_order_index_dict.get(code) |
| | | real_place_order_index = None |
| | |
| | | return |
| | | # 重新囊括1笔 |
| | | real_place_order_info = self.__real_place_order_index_dict.get(code) |
| | | is_ge_code = tool.is_ge_code(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)))) |
| | | min_num = int(5000 / gpcode_manager.get_limit_up_price_as_num(code)) |
| | | for j in range(real_place_order_info[0] - 1, index, -1): |
| | | data = total_datas[j] |
| | | val = data['val'] |
| | |
| | | code)) |
| | | if left_count > 0: |
| | | left_count_after += 1 |
| | | if l2_data_util.is_big_money(val) and j not in watch_indexes: |
| | | if l2_data_util.is_big_money(val, is_ge_code) and j not in watch_indexes: |
| | | watch_indexes.add(j) |
| | | l2_log.l_cancel_debug(code, f"L后有成交后半段增加囊括:{j}") |
| | | self.__set_cancel_l_down_after_place_order_index(code, j, left_count_after - 1) |
| | | break |
| | | except: |
| | |
| | | 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 |
| | | |
| | | # 这是下单位置之后的索引: key为字符串 |
| | | after_place_order_index_dict = self.__get_cancel_l_down_after_place_order_index_dict(code) |
| | | if after_place_order_index_dict is None: |
| | | after_place_order_index_dict = {} |
| | | after_place_order_index_by_dict = self.__l_down_after_by_big_order_weight_dict.get(code) |
| | | if after_place_order_index_by_dict is None: |
| | | after_place_order_index_by_dict = {} |
| | | |
| | | watch_indexes = set([int(i) for i in watch_indexes_info[2]]) |
| | | try: |
| | | # 将备用订单加进去 |
| | | watch_indexes_by = self.__l_down_after_by_big_order_dict.get(code) |
| | | if watch_indexes_by: |
| | | # 是否是下单30分钟内 |
| | | real_place_order_info = self.__real_place_order_index_dict.get(code) |
| | | if real_place_order_info and tool.trade_time_sub(total_data[-1]["val"]["time"], |
| | | total_data[real_place_order_info[0]]["val"][ |
| | | "time"]) < 30 * 60: |
| | | # 下单30分钟内有效 |
| | | watch_indexes |= watch_indexes_by |
| | | else: |
| | | # 清除备用大单 |
| | | watch_indexes_by.clear() |
| | | except Exception as e: |
| | | l2_log.l_cancel_debug(code, "将L2后后半段备用大单加入计算出错:{}", str(e)) |
| | | # 计算监听的总条数 |
| | | total_num = 0 |
| | | max_num = 0 |
| | | thresh_hold_rate, must_buy = LCancelRateManager.get_cancel_rate(code) |
| | | |
| | | for wi in watch_indexes: |
| | | if str(wi) in after_place_order_index_dict: |
| | | # 如果加红就需要计算分母 |
| | | if must_buy: |
| | | total_num += total_data[wi]["val"]["num"] * ( |
| | | 10 - after_place_order_index_dict[str(wi)]) // 10 |
| | | continue |
| | | elif str(wi) in after_place_order_index_by_dict: |
| | | if must_buy: |
| | | total_num += total_data[wi]["val"]["num"] * ( |
| | | 10 - after_place_order_index_by_dict[str(wi)]) // 10 |
| | | continue |
| | | |
| | | 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"] |
| | |
| | | canceled_num = 0 |
| | | # 记录撤单索引 |
| | | canceled_indexes = [] |
| | | |
| | | 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 |
| | | canceled_buyno_map = local_today_canceled_buyno_map.get(code) |
| | | 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)) |
| | | cancel_data = L2DataComputeUtil.is_canceled(code, wi, total_data, canceled_buyno_map, trade_index, deal_order_nos) |
| | | if cancel_data: |
| | | if str(wi) in after_place_order_index_dict: |
| | | # 真实下单位置之后的按照权重比例来计算 |
| | | canceled_num += total_data[wi]["val"]["num"] * ( |
| | | 10 - after_place_order_index_dict[str(wi)]) // 10 |
| | | 10 - after_place_order_index_dict[str(wi)]) // 10 |
| | | elif str(wi) in after_place_order_index_by_dict: |
| | | canceled_num += total_data[wi]["val"]["num"] * ( |
| | | 10 - after_place_order_index_by_dict[str(wi)]) // 10 |
| | | else: |
| | | canceled_num += total_data[wi]["val"]["num"] |
| | | |
| | | canceled_indexes.append(cancel_data["index"]) |
| | | # if wi == watch_indexes_list[-1] and left_count == 0: |
| | | # # 离下单位置最近的一个撤单,必须触发撤单 |
| | |
| | | # 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) |
| | | if thresh_hold_rate > temp_thresh_hold_rate: |
| | | # 目标撤单比例大于大单撤单比例就取比例均值 |
| | | thresh_hold_rate = round((thresh_hold_rate + temp_thresh_hold_rate) / 2, 2) |
| | | |
| | | l2_log.l_cancel_debug(code, |
| | | f"L后计算范围:{start_index}-{end_index},已撤单比例:{rate}/{thresh_hold_rate}, 下单位之后的索引:{after_place_order_index_dict}") |
| | | if rate >= thresh_hold_rate: |
| | |
| | | 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}") |
| | | thresh_cancel_rate, must_buy = LCancelRateManager.get_cancel_rate(code, is_up=True) |
| | | l2_log.l_cancel_debug(code, f"计算范围:{start_index}-{end_index},L前已撤单比例:{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) |
| | | trade_progress_index, is_default = TradeBuyQueue().get_traded_index(code) |
| | | if trade_progress_index is None: |
| | | trade_progress_index = 0 |
| | | 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) |
| | | thresh_hold_num = thresh_hold_money // (gpcode_manager.get_limit_up_price_as_num(code) * 100) |
| | | for i in range(trade_progress_index + 1, real_place_order_index_info[0]): |
| | | data = total_data[i] |
| | | val = data['val'] |
| | |
| | | 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}") |
| | | f"L前撤阻断: 成交位-{trade_progress_index} 真实下单位-{real_place_order_index_info[0]} 阈值-{thresh_hold_money}") |
| | | return False, None |
| | | |
| | | canceled_indexes.sort() |
| | | l2_log.l_cancel_debug(code, f"L上撤单,撤单位置:{canceled_indexes[-1]}") |
| | | l2_log.l_cancel_debug(code, f"L前撤单,撤单位置:{canceled_indexes[-1]}") |
| | | return True, total_data[canceled_indexes[-1]] |
| | | |
| | | return False, None |
| | | |
| | | # L后重新囊括的时间 |
| | | __recompute_l_down_time_dict = {} |
| | | |
| | | def set_big_sell_order_info(self, code, big_sell_order_info): |
| | | """ |
| | | 设置大卖单信息 |
| | | @param code: |
| | | @param big_sell_order_info: |
| | | @return: |
| | | """ |
| | | if not big_sell_order_info or not big_sell_order_info[0] or not big_sell_order_info[1]: |
| | | return False, "" |
| | | |
| | | total_datas = local_today_datas.get(code) |
| | | # 查询是否是真的真实下单位置 |
| | | 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(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): |
| | | 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 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"] |
| | | |
| | | # 卖金额>=均大单才触发重新囊括 |
| | | THRESHOLD_MONEY = radical_buy_data_manager.BeforeSubDealBigOrderManager().get_big_sell_order_threshold(code) |
| | | if total_deal_money >= THRESHOLD_MONEY: |
| | | l2_log.l_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.l_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后 |
| | | # 撤单时间比早成交时间大就需要计算在里面 |
| | | self.re_compute_l_down_watch_indexes(code, big_sell_info=( |
| | | real_trade_index, latest_deal_time_ms)) |
| | | l2_log.l_cancel_debug(code, f"更新L后囊括完成(大卖单):{(real_trade_index, latest_deal_time_ms)}") |
| | | else: |
| | | l2_log.l_cancel_debug(code, f"大卖单金额不足({THRESHOLD_MONEY})") |
| | | return False, "" |
| | | |
| | | # 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) |
| | | trade_index, is_default = TradeBuyQueue().get_traded_index(code) |
| | | if trade_index is None: |
| | | trade_index = 0 |
| | | if trade_index is None: |
| | | return True |
| | | real_place_order_index_info = self.__real_place_order_index_dict.get(code) |
| | | if not real_place_order_index_info: |
| | | return True |
| | | |
| | | # 计算已经成交的比例 |
| | | total_datas = local_today_datas.get(code) |
| | | total_deal_nums = 0 |
| | | total_nums = 1 |
| | | for index in watch_indexes_info[2]: |
| | | # 不能计算L后后半段 |
| | | if index > real_place_order_index_info[0]: |
| | | continue |
| | | data = total_datas[index] |
| | | val = data["val"] |
| | | left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(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"]) |
| | | thresh_hold_rate, must_buy = LCancelRateManager.get_cancel_rate(code) |
| | | 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, "尚未找到下单位置" |
| | | return False, None, "尚未找到下单位置", None |
| | | # 守护S撤以外的数据 |
| | | if int(tool.get_now_time_str().replace(":", "")) > int("145700") and not constant.TEST: |
| | | return False, None, "" |
| | | return False, None, "", None |
| | | try: |
| | | LCancelOutOfDateWatchIndexesManager().process(code, start_index, end_index) |
| | | except Exception as e: |
| | | l2_log.l_cancel_debug("L后稳定更新出错:{}", str(e)) |
| | | # 下单位临近撤 |
| | | can_cancel, cancel_data = False, None |
| | | can_cancel, cancel_data, cancel_type = False, None, None |
| | | try: |
| | | can_cancel, cancel_data = self.__compute_need_cancel(code, buy_exec_index, start_index, end_index, |
| | | total_data, |
| | | is_first_code) |
| | | if can_cancel: |
| | | cancel_type = trade_constant.CANCEL_TYPE_L_DOWN |
| | | except Exception as e: |
| | | logger_l2_l_cancel.exception(e) |
| | | raise e |
| | |
| | | start_index, end_index, |
| | | total_data, |
| | | is_first_code) |
| | | if can_cancel: |
| | | cancel_type = trade_constant.CANCEL_TYPE_L_UP |
| | | extra_msg = "L前" |
| | | except Exception as e: |
| | | logger_l2_l_cancel.exception(e) |
| | | raise e |
| | | return can_cancel, cancel_data, extra_msg |
| | | |
| | | try: |
| | | if self.__need_update_l_down_after(code, start_index, end_index): |
| | | # 更新后半段 |
| | | watch_indexes = self.__compute_l_down_watch_index_after_real_place_order_index(code) |
| | | if watch_indexes: |
| | | l2_log.l_cancel_debug(code, "L后后半段囊括:{}", watch_indexes) |
| | | watch_indexes_info = self.__get_watch_indexes_cache(code) |
| | | if watch_indexes_info and watch_indexes_info[2]: |
| | | # 没有囊括 |
| | | watch_indexes |= set(watch_indexes_info[2]) |
| | | self.__set_watch_indexes(code, watch_indexes_info[0], watch_indexes_info[1], watch_indexes) |
| | | except Exception as e: |
| | | l2_log.l_cancel_debug(code, "L后后半段计算出错:{}", str(e)) |
| | | |
| | | return can_cancel, cancel_data, extra_msg, cancel_type |
| | | |
| | | def place_order_success(self, code): |
| | | self.clear(code) |
| | |
| | | self.__set_cancel_l_down_after_place_order_index(code, 122, 1) |
| | | |
| | | print(self.__get_cancel_l_down_after_place_order_index_dict(code)) |
| | | |
| | | |
| | | class LCancelOutOfDateWatchIndexesManager: |
| | | """ |
| | | L撤过期数据更新 |
| | | """ |
| | | __latest_cancel_time_dict = {} |
| | | |
| | | __instance = None |
| | | |
| | | def __new__(cls, *args, **kwargs): |
| | | if not cls.__instance: |
| | | cls.__instance = super(LCancelOutOfDateWatchIndexesManager, cls).__new__(cls, *args, **kwargs) |
| | | return cls.__instance |
| | | |
| | | @classmethod |
| | | def compute_latest_canceled_watch_index_time(cls, code, watch_indexes): |
| | | """ |
| | | 获取 最近撤单的索引 |
| | | @param code: |
| | | @param watch_indexes: |
| | | @return: |
| | | """ |
| | | total_datas = local_today_datas.get(code) |
| | | canceled_buyno_map = local_today_canceled_buyno_map.get(code) |
| | | max_cancel_index = -1 |
| | | for index in watch_indexes: |
| | | orderNo = total_datas[index]["val"]["orderNo"] |
| | | canceled_data = canceled_buyno_map.get(str(orderNo)) |
| | | if canceled_data: |
| | | max_cancel_index = max(max_cancel_index, canceled_data["index"]) |
| | | if max_cancel_index < 0: |
| | | # 没有撤单的 |
| | | return None |
| | | return total_datas[max_cancel_index]["val"]["time"] |
| | | |
| | | @classmethod |
| | | def compute_l_down_common_watch_indexes(cls, code, start_index, end_index, max_count, min_num, |
| | | exclude_watch_indexes): |
| | | """ |
| | | 获取L后监听数据 |
| | | @param code: |
| | | @param re_start_index: |
| | | @param end_index: |
| | | @param max_count: |
| | | @return: |
| | | """ |
| | | total_datas = local_today_datas.get(code) |
| | | MIN_MONEYS = [300, 200, 100] |
| | | watch_indexes = set() |
| | | for min_money in MIN_MONEYS: |
| | | for i in range(end_index, 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 and i not in exclude_watch_indexes: |
| | | 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 |
| | | return watch_indexes |
| | | |
| | | def process(self, code, start_index, end_index): |
| | | # 获取监听数据 |
| | | watch_indexes_info = LCancelBigNumComputer().get_l_down_watch_indexes_cache(code) |
| | | if not watch_indexes_info: |
| | | return |
| | | if watch_indexes_info[1]: |
| | | # 已经重新计算过就不再执行 |
| | | return |
| | | watch_indexes = set(watch_indexes_info[2]) |
| | | # 获取真实下单位数据 |
| | | real_place_order_index_info = LCancelBigNumComputer().get_real_place_order_index_info(code) |
| | | if not real_place_order_index_info or real_place_order_index_info[1]: |
| | | return |
| | | real_place_order_index = real_place_order_index_info[0] |
| | | total_datas = local_today_datas.get(code) |
| | | # 计算最近的撤单时间 |
| | | buyno_map = local_today_buyno_map.get(code) |
| | | # 已经改过了就不需要修改 |
| | | for i in range(start_index, end_index + 1): |
| | | # 判断里面是否有撤单 |
| | | data = total_datas[i] |
| | | val = data["val"] |
| | | if not L2DataUtil.is_limit_up_price_buy_cancel(val): |
| | | continue |
| | | if str(val["orderNo"]) not in buyno_map: |
| | | continue |
| | | |
| | | buy_index = buyno_map[str(val["orderNo"])]["index"] |
| | | if buy_index in watch_indexes and real_place_order_index > buy_index: |
| | | # 下单位之前的L后囊括范围的订单被撤单 |
| | | self.__latest_cancel_time_dict[code] = val["time"] |
| | | |
| | | if tool.trade_time_sub(total_datas[-1]["val"]["time"], |
| | | total_datas[real_place_order_index]["val"]["time"]) < 10 * 60: |
| | | # 下单10分钟后才能生效 |
| | | return |
| | | if code not in self.__latest_cancel_time_dict: |
| | | return |
| | | if tool.trade_time_sub(total_datas[-1]["val"]["time"], self.__latest_cancel_time_dict.get(code)) < 10 * 60: |
| | | # 最近的撤单时间小于10分钟 |
| | | return |
| | | l2_log.l_cancel_debug(code, f"L后稳定") |
| | | canceled_buyno_map = local_today_canceled_buyno_map.get(code) |
| | | cancel_time_dict = {} |
| | | for index in watch_indexes: |
| | | if index > real_place_order_index: |
| | | continue |
| | | orderNo = str(total_datas[index]["val"]["orderNo"]) |
| | | if orderNo not in canceled_buyno_map: |
| | | continue |
| | | if tool.trade_time_sub(total_datas[-1]["val"]["time"], |
| | | canceled_buyno_map[orderNo]["val"]["time"]) < 10 * 60: |
| | | continue |
| | | # 统计撤单时间 |
| | | cancel_val = canceled_buyno_map[orderNo]["val"] |
| | | time_str = L2DataUtil.get_time_with_ms(cancel_val) |
| | | if time_str not in cancel_time_dict: |
| | | cancel_time_dict[time_str] = set() |
| | | cancel_time_dict[time_str].add(index) |
| | | remove_indexes = set() |
| | | for t in cancel_time_dict: |
| | | if len(cancel_time_dict[t]) > 1: |
| | | remove_indexes |= cancel_time_dict[t] |
| | | if remove_indexes: |
| | | # 移除索引 |
| | | add_count = len(remove_indexes) |
| | | limit_up_price = gpcode_manager.get_limit_up_price_as_num(code) |
| | | min_num = int(5000 / limit_up_price) |
| | | trade_index, is_default = TradeBuyQueue().get_traded_index(code) |
| | | if trade_index is None: |
| | | trade_index = 0 |
| | | add_watch_index = self.compute_l_down_common_watch_indexes(code, trade_index, real_place_order_index, |
| | | add_count, min_num, watch_indexes) |
| | | watch_indexes -= remove_indexes |
| | | watch_indexes |= add_watch_index |
| | | # 保存数据 / |
| | | LCancelBigNumComputer().set_l_down_watch_index_info(code, watch_indexes_info[0], True, watch_indexes) |
| | | l2_log.l_cancel_debug(code, f"L后稳定后更新监控范围:删除-{remove_indexes} 增加-{add_watch_index}") |
| | | |
| | | |
| | | # --------------------------------H撤------------------------------- |
| | |
| | | |
| | | # L撤触发的代码 |
| | | __l_cancel_triggered_codes = set() |
| | | __h_cancel_update_time_cache = {} |
| | | |
| | | __instance = None |
| | | |
| | |
| | | trade_record_log_util.CancelWatchIndexesInfo.CANCEL_TYPE_H, |
| | | buy_single_index, |
| | | list(indexes))) |
| | | self.__set_watch_indexes(code, indexes) |
| | | |
| | | def __set_watch_indexes(self, code, 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(), |
| | |
| | | if watch_indexes or watch_indexes_up: |
| | | watch_indexes |= watch_indexes_up |
| | | self.__save_watch_index_set(code, buy_single_index, watch_indexes) |
| | | self.__h_cancel_update_time_cache[code] = total_datas[-1]["val"]["time"] |
| | | l2_log.h_cancel_debug(code, f"设置监听范围, 数据范围:{real_place_order_index}-{end_index} 监听范围-{watch_indexes}") |
| | | # 设置真实下单位置 |
| | | |
| | | def __remove_cancel_long_time(self, code, buy_single_index): |
| | | """ |
| | | 删除已经撤单很久的数据 |
| | | @param code: |
| | | @return: |
| | | """ |
| | | watch_indexes = self.__get_watch_index_set_cache(code) |
| | | if not watch_indexes: |
| | | return |
| | | if code not in self.__h_cancel_update_time_cache or tool.trade_time_sub(tool.get_now_time_str(), |
| | | self.__h_cancel_update_time_cache[ |
| | | code]) < 30 * 60: |
| | | # 没有更新过或者更新时间小于30分钟就不更新 |
| | | return |
| | | watch_indexes = copy.deepcopy(watch_indexes) |
| | | # 删除撤单半小时之前的数据 |
| | | total_datas = local_today_datas.get(code) |
| | | remove_indexes = set() |
| | | for i in watch_indexes: |
| | | 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 cancel_data and tool.trade_time_sub(total_datas[-1]["val"]["time"], |
| | | cancel_data["val"]["time"]) > 60 * 30: |
| | | # 删除撤单时间30分钟以上的数据 |
| | | remove_indexes.add(i) |
| | | if not remove_indexes: |
| | | return |
| | | real_place_order_index = self.__SCancelBigNumComputer.get_real_place_order_index_cache(code) |
| | | if not real_place_order_index: |
| | | return |
| | | transaction_index = self.__transaction_progress_index_dict.get(code) |
| | | if transaction_index is None: |
| | | return |
| | | |
| | | # 起点为真实下单位置往上数3笔 |
| | | start_index = real_place_order_index |
| | | count = 0 |
| | | for i in range(real_place_order_index - 1, transaction_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'] < 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: |
| | | count += 1 |
| | | start_index = i |
| | | if count >= 3: |
| | | break |
| | | watch_indexes = watch_indexes - remove_indexes |
| | | # 新增加囊括 |
| | | add_indexes = set() |
| | | for i in range(start_index, total_datas[-1]["index"]): |
| | | data = total_datas[i] |
| | | val = data['val'] |
| | | if not L2DataUtil.is_limit_up_price_buy(val): |
| | | continue |
| | | if i in watch_indexes: |
| | | 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: |
| | | add_indexes.add(i) |
| | | if len(add_indexes) >= len(remove_indexes): |
| | | break |
| | | watch_indexes |= add_indexes |
| | | l2_log.h_cancel_debug(code, f"H撤更新:新增索引-{add_indexes} 删除索引-{remove_indexes}") |
| | | self.__save_watch_index_set(code, buy_single_index, watch_indexes) |
| | | # 设置更新时间, |
| | | self.__h_cancel_update_time_cache[code] = total_datas[-1]["val"]["time"] |
| | | |
| | | def __need_compute_watch_indexes(self, code, transaction_index): |
| | | """ |
| | |
| | | return |
| | | try: |
| | | # 计算触发位置 |
| | | min_num = int(5000 / (float(gpcode_manager.get_limit_up_price(code)))) |
| | | min_num = int(5000 / (gpcode_manager.get_limit_up_price_as_num(code))) |
| | | # 统计净涨停买的数量 |
| | | not_cancel_indexes = [] |
| | | total_datas = local_today_datas.get(code) |
| | |
| | | if rate >= threshold_rate: |
| | | l2_log.h_cancel_debug(code, f"撤单比例:{rate}") |
| | | return True, total_data[-1] |
| | | try: |
| | | self.__remove_cancel_long_time(code, buy_single_index) |
| | | except Exception as e: |
| | | l2_log.h_cancel_debug(code, f"更新H撤囊括范围出错:{str(e)}") |
| | | return False, None |
| | | |
| | | # 下单成功 |