"""
|
华鑫委托实际位置管理
|
"""
|
import time
|
|
import constant
|
from huaxin_client import constant as huaxin_client_constant
|
from l2 import l2_data_util, l2_data_source_util
|
from l2.huaxin import l2_huaxin_util
|
from l2.l2_data_util import L2DataUtil
|
from l2.transaction_progress import TradeBuyQueue
|
from log_module import async_log_util
|
from log_module.log import hx_logger_trade_debug, logger_real_place_order_position, logger_debug
|
from trade.huaxin import huaxin_trade_record_manager
|
from utils import tool
|
import concurrent.futures
|
|
_place_order_info_dict = {}
|
_place_order_info_dict_new = {}
|
|
__place_order_position = {}
|
|
|
# 下单
|
def place_order(code, price, volume, exec_index, latest_data, order_ref, shadow_price,
|
shadow_volume=huaxin_client_constant.SHADOW_ORDER_VOLUME):
|
async_log_util.info(logger_real_place_order_position,
|
f"下单:code-{code} price-{price} shadow_price-{shadow_price} volume-{volume} exec-index-{exec_index} order_ref-{order_ref} shadow_volume-{shadow_volume}")
|
_place_order_info_dict[code] = (
|
price, volume, latest_data, time.time(), order_ref, shadow_price, exec_index, shadow_volume)
|
|
|
# 获取下单信息
|
def get_order_info(code):
|
info = _place_order_info_dict.get(code)
|
TIME_SPACE_THRESHHOD = 3 if tool.is_sz_code(code) else 20
|
if info and time.time() - info[3] > TIME_SPACE_THRESHHOD:
|
async_log_util.info(logger_real_place_order_position, "get_order_info 间隔{}s以上:code-{}", TIME_SPACE_THRESHHOD,
|
code)
|
# 间隔3s以上就无效了
|
info = None
|
_place_order_info_dict.pop(code)
|
return info
|
|
|
RELIABILITY_TYPE_REAL = 1 # 真实下单位
|
RELIABILITY_TYPE_VIRTUAL = 2 # 影子单下单位
|
RELIABILITY_TYPE_ESTIMATE = 3 # 估算下单位
|
|
|
class RealDelegateOrderPositionManager:
|
"""
|
真实下单位管理
|
"""
|
__place_order_info_dict = {}
|
# 真实下单位置
|
__place_order_position = {}
|
|
# 获取下单信息
|
@classmethod
|
def get_order_info(cls, code):
|
"""
|
|
@param code:
|
@return: ([(量,价格, order_ref)], 下单时最新的L2数据,时间戳, 执行位 )
|
"""
|
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, strict_match):
|
THRESHOLD_MS = 20 if tool.is_sz_code(code) else 100
|
exec_time_with_ms = L2DataUtil.get_time_with_ms(exec_data["val"])
|
# 获取下单的量
|
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以上
|
# volumes_info_list:目标量列表
|
temp_volumes_info_list = volumes_info_list[i:i + len(target_volumes)]
|
sub_time_list = []
|
sub_index_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_index = temp_volumes_info_list[j + 1][2]["index"] - temp_volumes_info_list[j][2]["index"]
|
sub_time_list.append(abs(sub_ms))
|
sub_index_list.append(sub_index)
|
max_sub_time = max(sub_time_list)
|
max_sub_index = max(sub_index_list)
|
if max_sub_time > THRESHOLD_MS:
|
continue
|
|
if strict_match:
|
# 严格匹配,序号相差1,时间一致
|
if max_sub_index > 1 or max_sub_time > 0:
|
continue
|
|
# 最大的时间差,最大的索引差, 数据
|
# 深证与执行位置相差时间>=10ms以上,上证与执行位置相差时间>=100ms以上
|
data_ = temp_volumes_info_list[0][2]
|
match_list.append((max_sub_time, max_sub_index, data_))
|
if not match_list:
|
# 没有找到真实下单位
|
return None
|
THRESHOLD_MIN_MS = 0 if tool.is_sz_code(code) else 70
|
if strict_match and tool.is_sz_code(code) and len(match_list) == 1:
|
# 防止下单下在本10ms
|
# 严格匹配,且只匹配到1个数据
|
pass
|
else:
|
MIN_SPCE_MS = 30 if tool.is_sh_code(code) else 10
|
new_match_list = []
|
for m in match_list:
|
if tool.trade_time_sub_with_ms(L2DataUtil.get_time_with_ms(m[2]["val"]),
|
exec_time_with_ms) < MIN_SPCE_MS:
|
# 与执行时间相差指定时间
|
continue
|
new_match_list.append(m)
|
match_list = new_match_list
|
if not match_list:
|
# 没有找到真实下单位
|
return None
|
# 最合适的是时间相差为0,索引相差为1
|
for m in match_list:
|
if m[0] == 0 and m[1] == 1:
|
# 与执行位L2的数据必须相差指定秒数
|
if tool.trade_time_sub_with_ms(L2DataUtil.get_time_with_ms(m[2]["val"]),
|
exec_time_with_ms) < THRESHOLD_MIN_MS:
|
continue
|
return m[2]
|
|
# 获取时间差最小的数据
|
real_place_order_info = match_list[0]
|
for x in match_list:
|
# 与执行位L2的数据必须相差指定秒数
|
if tool.trade_time_sub_with_ms(L2DataUtil.get_time_with_ms(x[2]["val"]),
|
exec_time_with_ms) < THRESHOLD_MIN_MS:
|
continue
|
if x[0] < real_place_order_info[0]:
|
real_place_order_info = x
|
if tool.trade_time_sub_with_ms(L2DataUtil.get_time_with_ms(real_place_order_info[2]["val"]),
|
exec_time_with_ms) < THRESHOLD_MIN_MS:
|
return None
|
return real_place_order_info[2]
|
|
@classmethod
|
def compute_l2_place_order_position(cls, code, add_datas, strict_match=False):
|
"""
|
计算真实下单位置
|
@param code: 代码
|
@param add_datas: 本批次数据
|
@param strict_match: 严格匹配
|
@return:
|
"""
|
order_info = cls.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, exec_data, order_info_list, add_datas,
|
strict_match)
|
real_place_index_info = None
|
if place_order_data:
|
real_place_index_info = place_order_data["index"], 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:
|
real_place_index_info = estimate_index, RELIABILITY_TYPE_ESTIMATE
|
|
if real_place_index_info:
|
# 获取到了下单位置
|
async_log_util.info(hx_logger_trade_debug, f"真实下单位置:{code}-{real_place_index_info}")
|
async_log_util.info(logger_real_place_order_position,
|
f"真实下单位置:{code}-{real_place_index_info}-{0}")
|
if code in cls.__place_order_info_dict:
|
cls.__place_order_info_dict.pop(code)
|
cls.__place_order_position[code] = real_place_index_info[0]
|
return real_place_index_info[0], order_info, real_place_index_info[1]
|
else:
|
return None, order_info, None
|
|
@classmethod
|
def recompute_l2_place_order_position(cls, code, order_info, real_place_index, compute_type):
|
"""
|
重新计算真实下单位
|
@param code:
|
@param order_info:
|
@param real_place_index:
|
@param compute_type:
|
@return:
|
"""
|
# 重新计算, 根据实际订单来计算
|
# 如果真实下单位在实际成交位置之后就需要矫正
|
trade_index, is_default = TradeBuyQueue().get_traded_index(code)
|
if is_default:
|
return None
|
# if real_place_index >= trade_index:
|
# return None
|
current_delegates = huaxin_trade_record_manager.DelegateRecordManager().list_current_delegates(code)
|
if not current_delegates:
|
return None
|
accept_time = current_delegates[0]["acceptTime"]
|
accept_time = int(accept_time.replace(":", ""))
|
# 从真实下单位置开始,到委托时间截止
|
total_datas = l2_data_util.local_today_datas.get(code)
|
end_index = None
|
for i in range(trade_index + 1, total_datas[-1]["index"]):
|
data = total_datas[i]
|
val = data["val"]
|
if int(val["time"].replace(":", "")) > accept_time:
|
end_index = data["index"]
|
break
|
if end_index is None:
|
return None
|
|
order_info_list = order_info[0] # [(量,价格, order_ref)]
|
exec_data = order_info[1]
|
place_order_data = cls.__compute_real_place_order_position(code, exec_data, order_info_list,
|
total_datas[trade_index + 1:end_index],
|
strict_match=True)
|
if not place_order_data:
|
return None
|
real_place_index_info = place_order_data["index"], RELIABILITY_TYPE_REAL
|
return real_place_index_info[0]
|
|
@classmethod
|
def get_place_order_position(cls, code):
|
return cls.__place_order_position.get(code)
|
|
@classmethod
|
def set_place_order_position(cls, code, index):
|
cls.__place_order_position[code] = index
|
|
|
def __compute_estimate_order_position(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
|
|
|
# L2数据列表
|
# 范围(位置信息,可信的类型)
|
def get_l2_place_order_position(code, limit_up_price, datas):
|
"""
|
计算真实下单位置
|
@param code:
|
@param limit_up_price:
|
@param datas:
|
@return:(真实下单位置,下单数据,真实下单计算类型)
|
"""
|
order_info = get_order_info(code)
|
if not order_info:
|
# 暂无下单信息
|
return None, order_info, None
|
price = order_info[0]
|
volume = order_info[1]
|
exec_data = order_info[2]
|
order_time = order_info[3] # 下单时间
|
order_ref = order_info[4]
|
shadow_price = order_info[5]
|
shadow_volume = order_info[7]
|
shadow_place_order_index = None
|
# 查找影子挂单
|
for d in datas:
|
val = d['val']
|
# 判断影子订单位置
|
if val["num"] != shadow_volume // 100:
|
continue
|
if abs(shadow_price - float(val["price"])) >= 0.001:
|
continue
|
if d["index"] <= exec_data["index"]:
|
continue
|
if L2DataUtil.time_sub_as_ms(val, exec_data['val']) >= 2000:
|
# 不能排除与下单时间一致的订单
|
current_delegates = huaxin_trade_record_manager.DelegateRecordManager().list_current_delegates(code)
|
if not current_delegates or current_delegates[0]["acceptTime"] != val["time"]:
|
continue
|
if not L2DataUtil.is_buy(val):
|
continue
|
shadow_place_order_index = d["index"]
|
break
|
real_place_index_info = None
|
estimate_time_space = 1 if tool.is_sz_code(code) else 2.5
|
if shadow_place_order_index:
|
total_datas = l2_data_util.local_today_datas.get(code)
|
# 找到不是同一ms的结束
|
temp_start_index = shadow_place_order_index
|
for i in range(shadow_place_order_index - 1, -1, -1):
|
val = total_datas[i]["val"]
|
if val["tms"] != total_datas[shadow_place_order_index]["val"]["tms"]:
|
break
|
temp_start_index = i
|
start_index = max(min(temp_start_index, min(datas[0]["index"], shadow_place_order_index - 15)), 0)
|
end_index = min(shadow_place_order_index + 10, datas[-1]["index"])
|
real_place_index = None
|
# 从中间向两头遍历
|
# 从虚拟单位置向上找
|
try:
|
exec_time_with_ms = L2DataUtil.get_time_with_ms(exec_data["val"])
|
except:
|
pass
|
for i in range(shadow_place_order_index - 1, start_index - 1, -1):
|
d = total_datas[i]
|
if d["val"]["num"] != volume // 100:
|
continue
|
if abs(float(price) - float(d["val"]["price"])) >= 0.001:
|
continue
|
# 必须是买入
|
if not L2DataUtil.is_limit_up_price_buy(d["val"]):
|
continue
|
try:
|
if tool.is_sh_code(code) and tool.trade_time_sub_with_ms(L2DataUtil.get_time_with_ms(d["val"]),
|
exec_time_with_ms) <= 30:
|
# 上证真实下单位置与执行位置要相差30ms以上才行
|
continue
|
except:
|
pass
|
|
real_place_index = i
|
break
|
if not real_place_index:
|
# 从虚拟单位置向下找
|
for i in range(shadow_place_order_index + 1, end_index):
|
d = total_datas[i]
|
if d["val"]["num"] != volume // 100:
|
continue
|
if abs(float(price) - float(d["val"]["price"])) >= 0.001:
|
continue
|
# 必须是买入
|
if not L2DataUtil.is_limit_up_price_buy(d["val"]):
|
continue
|
real_place_index = i
|
break
|
if not real_place_index:
|
real_place_index_info = shadow_place_order_index, RELIABILITY_TYPE_VIRTUAL
|
else:
|
real_place_index_info = real_place_index, RELIABILITY_TYPE_REAL
|
elif tool.trade_time_sub(datas[-1]['val']['time'],
|
exec_data['val']['time']) >= estimate_time_space and time.time() - order_time > 5:
|
# 下单超过2s且绝对时间超过5S以上才会估算真实下单位置
|
estimate_index = __compute_estimate_order_position(code, exec_data["index"], shadow_price, shadow_volume)
|
if estimate_index:
|
real_place_index_info = estimate_index, RELIABILITY_TYPE_ESTIMATE
|
if real_place_index_info:
|
# 获取到了下单位置
|
async_log_util.info(hx_logger_trade_debug, f"真实下单位置:{code}-{real_place_index_info}")
|
async_log_util.info(logger_real_place_order_position,
|
f"真实下单位置:{code}-{real_place_index_info}-{shadow_place_order_index}")
|
if code in _place_order_info_dict:
|
_place_order_info_dict.pop(code)
|
__place_order_position[code] = real_place_index_info[0]
|
return real_place_index_info[0], order_info, real_place_index_info[1]
|
else:
|
return None, order_info, None
|
|
|
# 因为L2数据慢的问题而重新计算
|
def recompute_for_slow_time(code, order_info, real_place_index, compute_type):
|
try:
|
# 计算当前时间是否满足时间条件
|
now_time_str = tool.get_now_time_str()
|
if tool.trade_time_sub(now_time_str, "09:30:00") < 60 or 0 <= tool.trade_time_sub(now_time_str,
|
"13:00:00") <= 60 or compute_type == RELIABILITY_TYPE_VIRTUAL:
|
price = order_info[0]
|
volume = order_info[1]
|
exec_data = order_info[2]
|
order_time = order_info[3] # 下单时间
|
order_ref = order_info[4]
|
shadow_price = order_info[5]
|
shadow_volume = order_info[7]
|
# 在重新计算真实下单位置,在2s后计算
|
for t in range(0, 10):
|
time.sleep(1)
|
# 判断是否需要重新计算(手数不一致/已撤单)
|
total_datas = l2_data_util.local_today_datas.get(code)
|
real_place_index_data = total_datas[real_place_index]
|
try:
|
current_delegates = huaxin_trade_record_manager.DelegateRecordManager().list_current_delegates(code)
|
if not current_delegates:
|
continue
|
delegate_info = current_delegates[0]
|
|
if str(delegate_info["orderRef"]) != str(order_ref):
|
continue
|
# 看是否需要矫正
|
need_update = False
|
if not need_update and delegate_info["acceptTime"] != real_place_index_data["val"]["time"]:
|
# 下单时间不一致
|
need_update = True
|
|
if not need_update and real_place_index_data["val"]["num"] != volume // 100:
|
# 下单量不一致
|
need_update = True
|
|
if not need_update and L2DataUtil.is_limit_up_price_buy(real_place_index_data["val"]):
|
# 不是涨停买
|
need_update = True
|
|
if not need_update:
|
left_count = l2_data_source_util.L2DataSourceUtils.get_limit_up_buy_no_canceled_count_v2(code,
|
real_place_index,
|
total_datas,
|
l2_data_util.local_today_canceled_buyno_map.get(
|
code))
|
if left_count < 1:
|
# 已经撤单
|
need_update = True
|
if need_update:
|
async_log_util.info(logger_debug,
|
f"下单位矫正# {code}需要更新真实下单位置: 当前真实下单位-{real_place_index} order_info-{order_info}")
|
# 需要更新真实下单位置
|
# 在时间内找引子单
|
shadow_order_indexes = []
|
for i in range(exec_data["index"] + 1, total_datas[-1]["index"]):
|
data = total_datas[i]
|
val = data['val']
|
# 默认引子单与真实单下单时间一致
|
if val["time"] != delegate_info["acceptTime"]:
|
continue
|
if not L2DataUtil.is_buy(val):
|
continue
|
if val["num"] != shadow_volume // 100:
|
continue
|
if abs(shadow_price - float(val["price"])) >= 0.001:
|
continue
|
shadow_order_indexes.append(i)
|
async_log_util.info(logger_debug, f"下单位矫正# {code}获取到影子单索引:{shadow_order_indexes}")
|
|
# 获取引子单前最近且符合真实下单位置特征的数据
|
findexes_info = []
|
for index in shadow_order_indexes:
|
|
for i in range(index - 1, exec_data["index"], -1):
|
data = total_datas[i]
|
val = data['val']
|
if val["time"] != total_datas[index]["val"]["time"]:
|
continue
|
if not L2DataUtil.is_limit_up_price_buy(val):
|
continue
|
if volume // 100 != val["num"]:
|
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
|
findexes_info.append((index, i))
|
break
|
async_log_util.info(logger_debug, f"下单位矫正# {code}真实下单位计算结果:{findexes_info}")
|
if findexes_info:
|
findexes_info.sort(key=lambda x: x[0] - x[1])
|
# 获取成功
|
real_place_index = findexes_info[0][1]
|
|
return real_place_index
|
finally:
|
pass
|
else:
|
return None
|
pass
|
except Exception as e:
|
logger_real_place_order_position.exception(e)
|
return None
|
|
|
# 获取真实下单位置
|
def get_place_order_position(code):
|
if constant.IS_NEW_VERSION_PLACE_ORDER:
|
return RealDelegateOrderPositionManager.get_place_order_position(code)
|
return __place_order_position.get(code)
|
|
|
def set_place_order_position(code, index):
|
if constant.IS_NEW_VERSION_PLACE_ORDER:
|
RealDelegateOrderPositionManager.set_place_order_position(code, index)
|
else:
|
__place_order_position[code] = index
|