| | |
| | | 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: |
| | |
| | | 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撤------------------------------- |
| | |
| | | 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撤,根据成交数据来撤 |
| | |
| | | __db = 0 |
| | | __redis_manager = redis_manager.RedisManager(0) |
| | | __real_order_index_cache = {} |
| | | __max_buy_order_num_cache = {} |
| | | # 下单距离太远计算 |
| | | __far_away_computed_cache = {} |
| | | |
| | | __instance = None |
| | | |
| | |
| | | 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: |
| | |
| | | # 设置真实的下单位置 |
| | | 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): |
| | | """ |
| | |
| | | @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 |
| | | |
| | |
| | | # 是否是下单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'][ |
| | |
| | | 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: |
| | |
| | | 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 = {} |
| | |
| | | |
| | | 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"] |
| | |
| | | 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() |
| | |
| | | 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) |
| | |
| | | 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) |
| | |
| | | # 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]: |
| | | # 没有真实下单位置 |
| | |
| | | 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, |
| | |
| | | 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: |
| | |
| | | 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 |
| | |
| | | __db = 0 |
| | | __redis_manager = redis_manager.RedisManager(0) |
| | | __cancel_real_order_index_cache = {} |
| | | __SCancelBigNumComputer = SCancelBigNumComputer() |
| | | |
| | | __instance = None |
| | | |
| | |
| | | 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 |
| | |
| | | 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) |
| | |
| | | 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 |
| | |
| | | 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: |
| | | # 更新需要计算信号 |
| | |
| | | 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) |
| | | # [时间, 真实下单位置, 信号总手数, 目前手数, 最新计算的索引] |
| | |
| | | |
| | | # 计算剩余数量 |
| | | 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): |
| | |
| | | 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块" |
| | | |
| | |
| | | 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}' |
| | |
| | | |
| | | def place_order_success(self, code): |
| | | self.__clear_data(code) |
| | | |
| | | |
| | | |
| | | |
| | | if __name__ == "__main__": |