Administrator
65 分钟以前 5648819608a812a34a6ec757a2cbed5e5141777d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
"""
华鑫委托实际位置管理
"""
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