| | |
| | | price, volume, latest_data, time.time(), order_ref, shadow_price, exec_index, shadow_volume) |
| | | |
| | | |
| | | def place_order_new(code, oredr_info_list, exec_index, latest_data): |
| | | """ |
| | | 设置的下单信息 |
| | | @param code: |
| | | @param oredr_info_list: 例如-[(量,价格, order_ref)], 先下单的在前 |
| | | @param exec_index: |
| | | @param latest_data: |
| | | @return: |
| | | """ |
| | | async_log_util.info(logger_real_place_order_position, |
| | | f"新版下单:code-{code} oredr_info_list-{oredr_info_list} exec-index-{exec_index}") |
| | | _place_order_info_dict_new[code] = (oredr_info_list, latest_data, time.time(), exec_index) |
| | | |
| | | |
| | | # 获取下单信息 |
| | | def get_order_info(code): |
| | | info = _place_order_info_dict.get(code) |
| | |
| | | RELIABILITY_TYPE_ESTIMATE = 3 # 估算下单位 |
| | | |
| | | |
| | | # 计算预估下单位 |
| | | class RealDelegateOrderPositionManager: |
| | | """ |
| | | 真实下单位管理 |
| | | """ |
| | | __place_order_info_dict = {} |
| | | |
| | | # 获取下单信息 |
| | | @classmethod |
| | | def get_order_info(cls, code): |
| | | info = cls.__place_order_info_dict.get(code) |
| | | TIME_SPACE_THRESHHOD = 3 if tool.is_sz_code(code) else 20 |
| | | if info and time.time() - info[2] > TIME_SPACE_THRESHHOD: |
| | | async_log_util.info(logger_real_place_order_position, "get_order_info 间隔{}s以上:code-{}", |
| | | TIME_SPACE_THRESHHOD, |
| | | code) |
| | | # 间隔3s以上就无效了 |
| | | info = None |
| | | cls.__place_order_info_dict.pop(code) |
| | | return info |
| | | |
| | | @classmethod |
| | | def place_order(cls, code, oredr_info_list, exec_index, latest_data): |
| | | """ |
| | | 设置的下单信息 |
| | | @param code: |
| | | @param oredr_info_list: 例如-[(量,价格, order_ref)], 先下单的在前 |
| | | @param exec_index: |
| | | @param latest_data: |
| | | @return: |
| | | """ |
| | | async_log_util.info(logger_real_place_order_position, |
| | | f"新版下单:code-{code} oredr_info_list-{oredr_info_list} exec-index-{exec_index}") |
| | | cls.__place_order_info_dict[code] = (oredr_info_list, latest_data, time.time(), exec_index) |
| | | |
| | | @classmethod |
| | | def __compute_estimate_order_position(cls, code, exec_buy_index, shadow_price, shadow_volume): |
| | | total_datas = l2_data_util.local_today_datas.get(code) |
| | | try: |
| | | if tool.is_sh_code(code): |
| | | # 通过影子单买撤数据的订单号确认大致位置 |
| | | shadow_place_order_cancel_index = None |
| | | for i in range(exec_buy_index, total_datas[-1]['index'] + 1): |
| | | d = total_datas[i] |
| | | val = d['val'] |
| | | # 判断影子订单位置 |
| | | if val["num"] != shadow_volume // 100: |
| | | continue |
| | | if abs(shadow_price - float(val["price"])) >= 0.001: |
| | | continue |
| | | if not L2DataUtil.is_buy_cancel(val): |
| | | continue |
| | | shadow_place_order_cancel_index = d["index"] |
| | | if shadow_place_order_cancel_index: |
| | | async_log_util.info(logger_debug, |
| | | f"{code} 执行位:{exec_buy_index} 获取到影子单的买撤:{shadow_place_order_cancel_index}") |
| | | order_no = int(total_datas[shadow_place_order_cancel_index]) |
| | | for i in range(shadow_place_order_cancel_index, 0, -1): |
| | | d = total_datas[i] |
| | | val = d['val'] |
| | | if not L2DataUtil.is_buy(val): |
| | | continue |
| | | if order_no > int(val['orderNo']) and abs(tool.trade_time_sub(val['time'], total_datas[ |
| | | shadow_place_order_cancel_index]['val']["time"])) <= 1: |
| | | real_place_order_index = min(i + 1, total_datas[-1]['index']) |
| | | async_log_util.info(logger_debug, |
| | | f"{code} 执行位:{exec_buy_index} 根据影子单的买撤获取真实下单位置:{real_place_order_index}") |
| | | return real_place_order_index |
| | | except Exception as e: |
| | | async_log_util.error(logger_debug, f"真实下单位置(影子单撤单)出错:{code} - {str(e)}") |
| | | |
| | | exec_data = total_datas[exec_buy_index] |
| | | THRESH_MS = 20 if tool.is_sz_code(code) else 100 |
| | | for i in range(exec_buy_index, total_datas[-1]["index"]): |
| | | if L2DataUtil.time_sub_as_ms(total_datas[i]['val'], exec_data["val"]) >= THRESH_MS: |
| | | return i |
| | | return None |
| | | |
| | | @classmethod |
| | | def __compute_real_place_order_position(cls, code, exec_data, oredr_info_list, add_datas): |
| | | THRESHOLD_MS = 20 if tool.is_sz_code(code) else 100 |
| | | # 获取下单的量 |
| | | target_volumes = [x[0] // 100 for x in oredr_info_list] |
| | | volumes_info_list = [] |
| | | |
| | | current_delegates = huaxin_trade_record_manager.DelegateRecordManager().list_current_delegates(code) |
| | | # 下单量对应的委托时间 |
| | | current_delegate_place_order_time_dict = {} |
| | | if current_delegates: |
| | | # 下单时间不能早于执行位置的时间 |
| | | if tool.trade_time_sub(current_delegates[0]["acceptTime"], exec_data["val"]["time"]) >= 0: |
| | | current_delegate_place_order_time_dict = {x["volume"] // 100: x["acceptTime"] for x in |
| | | current_delegates} |
| | | for data in add_datas: |
| | | val = data["val"] |
| | | if not L2DataUtil.is_limit_up_price_buy(val): |
| | | continue |
| | | if val["num"] not in target_volumes: |
| | | continue |
| | | if val['num'] in current_delegate_place_order_time_dict: |
| | | if current_delegate_place_order_time_dict[val['num']] != val['time']: |
| | | # 与下单时间明确不符合 |
| | | continue |
| | | volumes_info_list.append((data["index"], val["num"], data)) |
| | | if not volumes_info_list: |
| | | return None |
| | | volumes_list = [x[1] for x in volumes_info_list] |
| | | lack_volumes = set(target_volumes) - set(volumes_list) |
| | | if lack_volumes: |
| | | # 量不够, 往前找一定的量 |
| | | total_datas = l2_data_util.local_today_datas.get(code) |
| | | end_data = add_datas[0] |
| | | # 获取还差的量, 最多往前找THRESHOLD_MS 的毫秒 |
| | | for i in range(end_data["index"] - 1, -1, -1): |
| | | data = total_datas[i] |
| | | val = data["val"] |
| | | if val['num'] not in lack_volumes: |
| | | continue |
| | | if not L2DataUtil.is_limit_up_price_buy(val): |
| | | continue |
| | | if tool.trade_time_sub_with_ms(L2DataUtil.get_time_with_ms(end_data['val']), |
| | | L2DataUtil.get_time_with_ms(val)) > THRESHOLD_MS: |
| | | break |
| | | |
| | | if val['num'] in current_delegate_place_order_time_dict: |
| | | if current_delegate_place_order_time_dict[val['num']] != val['time']: |
| | | # 与下单时间明确不符合 |
| | | continue |
| | | |
| | | left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(code, |
| | | i, |
| | | total_datas, |
| | | l2_data_util.local_today_canceled_buyno_map.get( |
| | | code)) |
| | | if left_count < 1: |
| | | continue |
| | | lack_volumes.discard(val['num']) |
| | | volumes_info_list.insert(0, (data["index"], val["num"], data)) |
| | | volumes_list = [x[1] for x in volumes_info_list] |
| | | lack_volumes = set(target_volumes) - set(volumes_list) |
| | | if lack_volumes: |
| | | return None |
| | | |
| | | match_list = [] |
| | | for i in range(0, len(volumes_list) - len(target_volumes) + 1): |
| | | # 量与委托量不相等 |
| | | if set(target_volumes) != set(volumes_list[i:i + len(target_volumes)]): |
| | | continue |
| | | # 委托间隔时间不能相差100ms以上 |
| | | temp_volumes_info_list = volumes_info_list[i:i + len(target_volumes)] |
| | | sub_time_list = [] |
| | | for j in range(0, len(temp_volumes_info_list) - 1): |
| | | sub_ms = tool.trade_time_sub_with_ms( |
| | | L2DataUtil.get_time_with_ms(temp_volumes_info_list[j + 1][2]["val"]), |
| | | L2DataUtil.get_time_with_ms(temp_volumes_info_list[j][2]["val"])) |
| | | sub_time_list.append(abs(sub_ms)) |
| | | max_sub_time = max(sub_time_list) |
| | | if max_sub_time > THRESHOLD_MS: |
| | | continue |
| | | # 最大的时间差 |
| | | match_list.append((max_sub_time, temp_volumes_info_list[0][2])) |
| | | if not match_list: |
| | | # 没有找到真实下单位 |
| | | return None |
| | | # 获取时间差最小的数据 |
| | | real_place_order_info = match_list[0] |
| | | for x in match_list: |
| | | if x[0] < real_place_order_info[0]: |
| | | real_place_order_info = x |
| | | return real_place_order_info[1] |
| | | |
| | | @classmethod |
| | | def compute_l2_place_order_position(cls, code, add_datas): |
| | | """ |
| | | 计算真实下单位置 |
| | | @param code: 代码 |
| | | @param add_datas: 本批次数据 |
| | | @return: |
| | | """ |
| | | order_info = get_order_info(code) |
| | | if not order_info: |
| | | # 暂无下单信息 |
| | | return None, order_info, None |
| | | order_info_list = order_info[0] # [(量,价格, order_ref)] |
| | | exec_data = order_info[1] |
| | | order_time = order_info[2] |
| | | estimate_time_space = 1 if tool.is_sz_code(code) else 2.5 |
| | | place_order_data = cls.__compute_real_place_order_position(code, order_info_list, add_datas) |
| | | if place_order_data: |
| | | return place_order_data["index"], order_info, RELIABILITY_TYPE_REAL |
| | | elif tool.trade_time_sub(add_datas[-1]['val']['time'], |
| | | exec_data['val']['time']) >= estimate_time_space and time.time() - order_time > 5: |
| | | estimate_index = cls.__compute_estimate_order_position(code, exec_data["index"], order_info_list[-1][1], |
| | | order_info_list[-1][0]) |
| | | if estimate_index: |
| | | return estimate_index, order_info, RELIABILITY_TYPE_ESTIMATE |
| | | return None, order_info, None |
| | | |
| | | @classmethod |
| | | def recompute_for_slow_time(cls, code, order_info, real_place_index, compute_type): |
| | | """ |
| | | 因为L2数据延迟问题而重新计算真实下单位 |
| | | @param code: |
| | | @param order_info: |
| | | @param real_place_index: |
| | | @param compute_type: |
| | | @return: |
| | | """ |
| | | # TODO 重新计算, 根据实际订单来计算 |
| | | pass |
| | | |
| | | # 计算预估下单位 |
| | | |
| | | |
| | | def __compute_estimate_order_position(code, exec_buy_index, shadow_price, shadow_volume): |
| | | total_datas = l2_data_util.local_today_datas.get(code) |
| | | try: |