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