dataquant 1.1.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1744 @@
1
+ # -*- coding: UTF-8 -*-
2
+
3
+ import re
4
+ import os
5
+ import time
6
+ import mmap
7
+ import gevent
8
+ import platform
9
+ import warnings
10
+ import numpy as np
11
+ import pandas as pd
12
+ from functools import wraps
13
+ import traceback
14
+ import socket
15
+ from concurrent.futures import as_completed, ThreadPoolExecutor
16
+
17
+ from dataquant.apis.base import get_data, get_qdata
18
+ from dataquant.utils.convert import convert_fields, sort_merge_df, sort_merge_df_dic
19
+ from dataquant.utils.datetime_func import get_current_date
20
+ from dataquant.utils.config import read_config
21
+ from dataquant.utils.error import MaxTryExceed, RunTimeError
22
+
23
+ __all__ = [
24
+ "load_conf",
25
+ "get_kline",
26
+ "get_tick",
27
+ "get_order",
28
+ "get_deal",
29
+ "get_tree_sum",
30
+ "get_tree_detail",
31
+ "get_tick2",
32
+
33
+
34
+
35
+
36
+ # "get_stock_snapshot",
37
+ # "get_stock_entrust",
38
+ # "get_stock_tick",
39
+ # "get_future_snapshot"
40
+ ]
41
+
42
+
43
+ # 请求服务端URL
44
+ URL_GET_STK_TICK = 'get_stock_tick'
45
+ URL_GET_STK_ORDER = 'get_stock_order'
46
+ URL_GET_STK_DEAL = 'get_stock_deal'
47
+ URL_GET_STK_KLINE = 'get_stock_kline'
48
+ URL_GET_FUT_TICK = 'get_future_tick'
49
+ URL_GET_FUT_TICK2 = 'get_future_tick2'
50
+ URL_GET_FUT_KLINE = 'get_future_kline'
51
+ URL_GET_BND_TICK = 'get_bond_tick'
52
+ URL_GET_BND_ORDER = 'get_bond_order'
53
+ URL_GET_BND_DEAL = 'get_bond_deal'
54
+ URL_GET_BND_KLINE = 'get_bond_kline'
55
+ URL_GET_FND_TICK = 'get_fund_tick'
56
+ URL_GET_FND_ORDER = 'get_fund_order'
57
+ URL_GET_FND_DEAL = 'get_fund_deal'
58
+ URL_GET_FND_KLINE = 'get_fund_kline'
59
+ URL_GET_STK_TREESUM = 'get_stock_treesum'
60
+ URL_GET_STK_TREEDETAIL = 'get_stock_treedetail'
61
+
62
+
63
+ # 代码与证券类型映射
64
+ CODE_SECURITY_MAP = {}
65
+
66
+
67
+ global CONFIG_PAGE_SIZE, PARALLEL_NUM, PARALLEL_MODE, EXECUTOR, EXECUTOR_INNER, \
68
+ HF_SORT_COLS, KLINED_SORT_COLS, KLINEM_SORT_COLS, PROCESS_MMAP
69
+
70
+ if platform.system().lower() == 'windows':
71
+ PROCESS_MMAP = mmap.mmap(-1, 1, tagname='dataquant_sdk_pid')
72
+ else:
73
+ MMAP_FILE = open('/dev/shm/dataquant_sdk_pid', 'w+b')
74
+ MMAP_FILE.write(b"\x00")
75
+ MMAP_FILE.flush()
76
+ PROCESS_MMAP = mmap.mmap(MMAP_FILE.fileno(), 1, flags=mmap.MAP_SHARED, prot=mmap.PROT_READ | mmap.PROT_WRITE)
77
+
78
+ PROCESS_MMAP.seek(0)
79
+ if PROCESS_MMAP.read() != b'\x31':
80
+ PROCESS_MMAP.seek(0)
81
+ PROCESS_MMAP.write(b"\x00")
82
+ PROCESS_MMAP.flush()
83
+
84
+
85
+ def reset():
86
+ PROCESS_MMAP.seek(0)
87
+ PROCESS_MMAP.write(b"\x00")
88
+ PROCESS_MMAP.flush()
89
+
90
+
91
+ def load_basic_info():
92
+ int_param = []
93
+ float_param = []
94
+
95
+ params = {
96
+ "mkt_code_list": ['XSHG', 'XSHE'],
97
+ "cols": ['scr_num', 'scr_name', 'scr_abbr', 'scr_code', 'mkt_code', 'scr_clas', 'list_date'],
98
+ "int_param": int_param,
99
+ "float_param": float_param
100
+ }
101
+ result = get_data("get_scr_basc_info", **params)
102
+ global CODE_SECURITY_MAP
103
+ CODE_SECURITY_MAP = result.set_index('scr_num')['scr_clas'].to_dict()
104
+
105
+
106
+ reset()
107
+
108
+
109
+ def load_conf():
110
+ global CONFIG_PAGE_SIZE, PARALLEL_NUM, PARALLEL_MODE, EXECUTOR, EXECUTOR_INNER, \
111
+ HF_SORT_COLS, KLINED_SORT_COLS, KLINEM_SORT_COLS
112
+ CONFIG = read_config()['system']
113
+ CONFIG_PAGE_SIZE = CONFIG.get('page_size')
114
+ PARALLEL_NUM = CONFIG.get("quote_parallel")
115
+ PARALLEL_MODE = CONFIG.get("parallel_mode")
116
+ if PARALLEL_MODE == 'Thread':
117
+ EXECUTOR = ThreadPoolExecutor(max_workers=PARALLEL_NUM)
118
+ EXECUTOR_INNER = ThreadPoolExecutor(max_workers=PARALLEL_NUM)
119
+ elif PARALLEL_MODE == 'Coroutine':
120
+ from gevent import monkey
121
+ monkey.patch_all()
122
+ from gevent.pool import Pool as gPool
123
+ EXECUTOR = gPool(PARALLEL_NUM)
124
+ EXECUTOR_INNER = gPool(PARALLEL_NUM)
125
+ EXECUTOR.submit = EXECUTOR.spawn
126
+ EXECUTOR_INNER.submit = EXECUTOR_INNER.spawn
127
+ HF_SORT_COLS = {
128
+ 'future': list(CONFIG.get('quote_hf_future_sort').split(',')),
129
+ 'stock': list(CONFIG.get('quote_hf_stock_sort').split(',')),
130
+ 'hkstock': list(CONFIG.get('quote_hf_hkstock_sort').split(',')),
131
+ 'bond': list(CONFIG.get('quote_hf_bond_sort').split(',')),
132
+ 'fund': list(CONFIG.get('quote_hf_fund_sort').split(','))
133
+ }
134
+ KLINED_SORT_COLS = {
135
+ 'future': list(CONFIG.get('quote_kd_future_sort').split(',')),
136
+ 'stock': list(CONFIG.get('quote_kd_stock_sort').split(',')),
137
+ 'bond': list(CONFIG.get('quote_kd_bond_sort').split(',')),
138
+ 'fund': list(CONFIG.get('quote_kd_fund_sort').split(','))
139
+ }
140
+ KLINEM_SORT_COLS = {
141
+ 'future': list(CONFIG.get('quote_km_future_sort').split(',')),
142
+ 'stock': list(CONFIG.get('quote_km_stock_sort').split(',')),
143
+ 'bond': list(CONFIG.get('quote_km_bond_sort').split(',')),
144
+ 'fund': list(CONFIG.get('quote_km_fund_sort').split(','))
145
+ }
146
+
147
+
148
+ load_conf()
149
+ _config = read_config()['system']
150
+
151
+ TWO_STEP_QUERY = False # 20220812 行情请求是否使用分页模式(第一步获取总条数,第二步实际查询)
152
+ ONE_STEP_NUM = 9999999 # 20220812 一步请求的最大请求数量需要很大以覆盖所有情况
153
+
154
+ KLINE_CODE_GROUP = 10
155
+ HF_DAY_DELTA = 1
156
+ HF_CODE_GROUP = 1
157
+ KLINE_DAY_DELTA = 90
158
+
159
+ KLINE_DAY_DELTA_CANDLE_PERIOD = {
160
+ '1m', '1',
161
+ '5m', '2',
162
+ '15m', '3',
163
+ '30m', '4',
164
+ '1h', '5',
165
+ '1d', '6',
166
+ # '1w': '7',
167
+ # '1M': '8',
168
+ # '1y': '9',
169
+ '2h', '11',
170
+ '3h', '12',
171
+ '4h', '13'
172
+ }
173
+
174
+
175
+ CANDLE_PERIOD_MAP = {
176
+ '1m': '1',
177
+ '5m': '2',
178
+ '15m': '3',
179
+ '30m': '4',
180
+ '1h': '5',
181
+ '1d': '6',
182
+ '1w': '7',
183
+ '1M': '8',
184
+ '1y': '9',
185
+ '2h': '11',
186
+ '3h': '12',
187
+ '4h': '13'
188
+ }
189
+
190
+
191
+ CANDLE_MODE_MAP = {
192
+ None: '0',
193
+ 'pre': '1',
194
+ 'post': '2'
195
+ }
196
+
197
+ STOCK = ['XSHG', 'XSHE']
198
+ FUTURE = ['XZCE', 'XDCE', 'XSGE', 'CCFX', 'XINE']
199
+
200
+
201
+ def bind(_socket=None):
202
+ if _socket is None:
203
+ PROCESS_MMAP.seek(0)
204
+ # 共享区域没有数据,则写入对应数据
205
+ if PROCESS_MMAP.read() == b'\x00':
206
+ # 执行绑定操作
207
+ PROCESS_MMAP.seek(0)
208
+ PROCESS_MMAP.write(b'\x31')
209
+ PROCESS_MMAP.flush()
210
+
211
+ # 检验绑定是否成功
212
+ PROCESS_MMAP.seek(0)
213
+ if PROCESS_MMAP.read() == b'\x31':
214
+ return True
215
+ else:
216
+ return False
217
+
218
+ else:
219
+ return False
220
+ else:
221
+ try:
222
+ _socket.bind(('127.0.0.1', _config["bind_port"]))
223
+ return True
224
+ except Exception as ex:
225
+ return False
226
+
227
+
228
+ def unbind(_socket=None):
229
+ if _socket is None:
230
+ PROCESS_MMAP.seek(0)
231
+ PROCESS_MMAP.write(b'\x00')
232
+ PROCESS_MMAP.flush()
233
+ else:
234
+ _socket.close()
235
+
236
+
237
+ def wait_until_bind(count=10000, time_delta=1.0):
238
+ def decorate(func):
239
+ @wraps(func)
240
+ def wrap(*args, **kwargs):
241
+ # 尝试进行绑定
242
+ bind_method = _config.get("bind_method", "socket")
243
+ if bind_method == 'socket':
244
+ _socket = socket.socket()
245
+ else:
246
+ _socket = None
247
+ cnt = 0
248
+ while True:
249
+ if bind(_socket):
250
+ try:
251
+ return func(*args, **kwargs)
252
+ except:
253
+ print(f"进程{os.getpid()}运行函数报错")
254
+ print(traceback.format_exc())
255
+ raise RunTimeError("运行时报错")
256
+ finally:
257
+ unbind(_socket)
258
+
259
+ else:
260
+ cnt += 1
261
+ if cnt > count:
262
+ raise MaxTryExceed(f"进程{os.getpid()}等待次数超过上线")
263
+ if cnt % 60 == 0:
264
+ print(f"进程{os.getpid()}正常排队查询")
265
+ time.sleep(time_delta)
266
+ return wrap
267
+ return decorate
268
+
269
+
270
+ def quote_single_part(method, params, two_step: bool = TWO_STEP_QUERY):
271
+ init_count = 0 # 初次查询已获取的条数
272
+ init_page = 0 # 初次查询已获取的页数
273
+ total_pages = 0
274
+ df_dic = {}
275
+
276
+ # 两步模式下第一步获取总条数以取得分页
277
+ if two_step:
278
+ # 约定使用0作为条数查询
279
+ params['total_num'] = 0
280
+ init_result = get_qdata(method, **params)
281
+ if str(init_result.get('result_code', '0')) != '0':
282
+ raise Exception('异常代码[%s], 异常信息[%s]' % (init_result['result_code'], init_result['result_msg']))
283
+ if init_result['data'] is not None:
284
+ init_count = len(init_result['data'])
285
+ df_dic[0] = init_result['data']
286
+ total_pages = (init_result['total_num'] + params['page_size'] - 1 - init_count) // params['page_size']
287
+ # 只可能是1或0,表示初次查询是否返回值,否则有问题
288
+ init_page = init_count // params['page_size']
289
+
290
+ if two_step and total_pages > init_page:
291
+ _parts = []
292
+ for i in range(init_page + 1, total_pages + 1):
293
+ params['total_num'] = init_result['total_num']
294
+ params['page_num'] = i
295
+ _part = EXECUTOR_INNER.submit(get_qdata, method=method, **params)
296
+ _parts.append(_part)
297
+
298
+ for _p in as_completed(_parts):
299
+ try:
300
+ if _p is None:
301
+ continue
302
+ elif _p.result() is None:
303
+ continue
304
+
305
+ result_data = _p.result()
306
+
307
+ df_part = result_data['data']
308
+ if len(df_part) > 0:
309
+ df_part.index = df_part.index + (result_data['page_num'] - 1) * params['page_size']
310
+ df_dic[result_data['page_num']] = df_part
311
+
312
+ except Exception as ex:
313
+ raise Exception("并发处理异常,异常函数[%s]" % method)
314
+ df = sort_merge_df_dic(df_dic)
315
+ else:
316
+ # 一步模式请求所有数据
317
+ params['total_num'] = ONE_STEP_NUM
318
+ params['page_size'] = ONE_STEP_NUM
319
+ result = get_qdata(method, **params)
320
+ if str(result.get('result_code', '0')) != '0':
321
+ raise Exception('异常代码[%s], 异常信息[%s]' % (result['result_code'], result['result_msg']))
322
+ df = result['data']
323
+ ret = {'method': method, 'params': params, 'data': df, }
324
+ return ret
325
+
326
+
327
+ def get_exchange_calendar(mkt_code, strt_date='19900101', end_date=None, trdy_flag=None, cols=None, rslt_type=0):
328
+ """
329
+ 获取交易所交易日日历,包括:上海证券交易所,深圳证券交易所等。
330
+
331
+ """
332
+
333
+ int_param = []
334
+ float_param = []
335
+ if cols:
336
+ int_param = list(set([]).intersection(set(convert_fields(cols))))
337
+ float_param = list(set([]).intersection(set(convert_fields(cols))))
338
+ else:
339
+ cols = ['mkt_code', 'busi_date', 'trdy_flag']
340
+
341
+ if mkt_code:
342
+ params = {
343
+ "mkt_code": mkt_code,
344
+ "strt_date": strt_date,
345
+ "end_date": end_date,
346
+ "trdy_flag": trdy_flag,
347
+ "cols": cols,
348
+ "rslt_type": rslt_type,
349
+ "int_param": int_param,
350
+ "float_param": float_param
351
+ }
352
+ return get_data("get_exch_trd_cldr", **params)
353
+ else:
354
+ warnings.warn("函数[get_exchange_calendar]的参数(mkt_code)为必填项")
355
+ return None
356
+
357
+
358
+ def _async_get_kline_data(func, scr_num_list, start_date, end_date,
359
+ candle_period, candle_mode, columns, code_type):
360
+ """
361
+ 异步获取K线数据
362
+ """
363
+
364
+ if code_type == 'stock':
365
+ int_param = ['trd_vol', 'fp_vol']
366
+
367
+ float_param = ['open_px', 'high_px', 'low_px', 'close_px', 'trd_amt', 'fp_amt', 'preclose_px']
368
+ else:
369
+ int_param = ['trd_vol', 'position', 'preposition']
370
+ float_param = ['open_px', 'high_px', 'low_px', 'close_px', 'preclose_px',
371
+ 'up_limit', 'down_limit', 'settlement_px', 'presettlement_px',
372
+ 'avg_px', 'trd_amt', 'up_low', 'up_low_rate', 'up_low_k', 'up_low_rate_k']
373
+
374
+ if columns:
375
+ # 分钟级别K线
376
+ if int(CANDLE_PERIOD_MAP[candle_period]) < int(CANDLE_PERIOD_MAP['1d']):
377
+ if code_type == 'stock':
378
+ fix_cols = \
379
+ ['scr_num', 'date_time', 'min_time']
380
+ else:
381
+ fix_cols = \
382
+ ['scr_num', 'date_time', 'action_date', 'min_time']
383
+ else:
384
+ fix_cols = \
385
+ ['scr_num', 'date_time']
386
+
387
+ if isinstance(columns, str):
388
+ columns = columns.split(',')
389
+ tmp_cols = fix_cols + columns
390
+ columns = list(set(tmp_cols))
391
+ columns.sort(key=tmp_cols.index)
392
+
393
+ int_param = \
394
+ list(set(int_param).intersection(set(columns)))
395
+ float_param = \
396
+ list(set(float_param).intersection(set(columns)))
397
+
398
+ if isinstance(columns, list):
399
+ columns = ','.join(columns)
400
+
401
+ current_date = get_current_date()
402
+ if start_date is None and end_date is None:
403
+ df = get_exchange_calendar('XSHG', end_date=current_date, trdy_flag=1)
404
+ if df.shape[0] > 2:
405
+ start_date = end_date = df['busi_date'].tolist()[-2]
406
+ else:
407
+ start_date = end_date = current_date
408
+ elif start_date is None:
409
+ start_date = end_date
410
+ elif end_date is None:
411
+ end_date = start_date
412
+
413
+ trading_day = [start_date]
414
+ if start_date != end_date:
415
+ df = get_exchange_calendar('XSHG', start_date, end_date, trdy_flag=1)
416
+ if df is None:
417
+ return None
418
+ trading_day = df['busi_date'].tolist()
419
+
420
+ # 根据查询日期跨度判断采用的股票代码步长
421
+ scr_num_list = regroup_scr_nums(scr_num_list, 1)
422
+
423
+ else:
424
+ # 根据查询日期跨度判断采用的股票代码步长
425
+ scr_num_list = regroup_scr_nums(scr_num_list, KLINE_CODE_GROUP)
426
+
427
+ if candle_period in KLINE_DAY_DELTA_CANDLE_PERIOD:
428
+ # 仅对日K或更短周期的蜡烛图做按天切分
429
+ day_delta = KLINE_DAY_DELTA if KLINE_DAY_DELTA < len(trading_day) else len(trading_day)
430
+ _start_date_list = trading_day[::day_delta]
431
+ _end_date_list = trading_day[day_delta - 1::day_delta]
432
+ if len(_start_date_list) != len(_end_date_list):
433
+ _end_date_list.append(trading_day[-1])
434
+ else:
435
+ _start_date_list = [start_date]
436
+ _end_date_list = [end_date]
437
+
438
+ _futures = []
439
+ params_list = []
440
+ for _idx in range(len(_start_date_list)):
441
+ for _scr in scr_num_list:
442
+ params = {
443
+ "scr_num_list": _scr,
444
+ "candle_period": CANDLE_PERIOD_MAP[candle_period],
445
+ "candle_mode": CANDLE_MODE_MAP[candle_mode],
446
+ "strt_date": _start_date_list[_idx],
447
+ "end_date": _end_date_list[_idx],
448
+ "cols": columns,
449
+ "api_type": "kline",
450
+ "int_param": int_param,
451
+ "float_param": float_param
452
+ }
453
+ params_list.append(params)
454
+
455
+ for params in params_list:
456
+ _future = EXECUTOR.submit(get_qdata, method=func, **params)
457
+ _futures.append(_future)
458
+
459
+ dfs = collect_dfs_parallel(func, _futures)
460
+
461
+ # 分钟K线
462
+ if int(CANDLE_PERIOD_MAP[candle_period]) < int(CANDLE_PERIOD_MAP['1d']):
463
+ global KLINEM_SORT_COLS
464
+ result = sort_merge_df(dfs, KLINEM_SORT_COLS[code_type])
465
+ else:
466
+ global KLINED_SORT_COLS
467
+ result = sort_merge_df(dfs, KLINED_SORT_COLS[code_type])
468
+ return result
469
+
470
+
471
+ @wait_until_bind()
472
+ def get_kline(scr_num_list, candle_period='1d', candle_mode=None, strt_date=None, end_date=None,
473
+ cols=None, rslt_type=0):
474
+ """
475
+ 批量获取不同种类的K线历史数据查询
476
+
477
+ """
478
+
479
+ if scr_num_list is None:
480
+ warnings.warn("函数[get_kline]的参数(scr_num_list)为必填项")
481
+ return None
482
+
483
+ if isinstance(scr_num_list, str):
484
+ scr_num_list = scr_num_list.split(',')
485
+
486
+ stk_list = []
487
+ futu_list = []
488
+ bond_list = []
489
+ fund_list = []
490
+
491
+ global CODE_SECURITY_MAP
492
+ if len(CODE_SECURITY_MAP) == 0:
493
+ load_basic_info()
494
+
495
+ scr_code_list = \
496
+ [
497
+ _code for _code in scr_num_list if _code.split('.')[1] in ['XSHG', 'XSHE']
498
+ ]
499
+
500
+ noexist_code_list = set(scr_code_list) - set(list(CODE_SECURITY_MAP.keys()))
501
+ if len(noexist_code_list):
502
+ warnings.warn("函数[scr_num_list]中指定代码不存在[%s]" % ''.join(noexist_code_list))
503
+ return None
504
+
505
+ try:
506
+ for _scr in scr_num_list:
507
+ _market_code = _scr.split('.')[1]
508
+ if _market_code in FUTURE:
509
+ futu_list.append(_scr)
510
+ elif CODE_SECURITY_MAP[_scr] == 'STOCK':
511
+ stk_list.append(_scr)
512
+ elif CODE_SECURITY_MAP[_scr] == 'BOND':
513
+ bond_list.append(_scr)
514
+ elif CODE_SECURITY_MAP[_scr] == 'FUND':
515
+ fund_list.append(_scr)
516
+ except IndexError:
517
+ raise Exception('scr_num_list输入格式异常[%s]' % scr_num_list)
518
+
519
+ stk_result = None
520
+ bond_result = None
521
+ fund_result = None
522
+ futu_result = None
523
+ if len(stk_list) > 0:
524
+ stk_result = _async_get_kline_data(
525
+ URL_GET_STK_KLINE, stk_list, strt_date, end_date, candle_period,
526
+ candle_mode, cols, 'stock'
527
+ )
528
+
529
+ if len(bond_list) > 0:
530
+ bond_result = _async_get_kline_data(
531
+ URL_GET_BND_KLINE, bond_list, strt_date, end_date, candle_period,
532
+ candle_mode, cols, 'bond'
533
+ )
534
+
535
+ if len(fund_list) > 0:
536
+ fund_result = _async_get_kline_data(
537
+ URL_GET_FND_KLINE, fund_list, strt_date, end_date, candle_period,
538
+ candle_mode, cols, 'fund'
539
+ )
540
+
541
+ if len(futu_list) > 0:
542
+ futu_candle_mode = None
543
+ futu_result = _async_get_kline_data(
544
+ URL_GET_FUT_KLINE, futu_list, strt_date, end_date, candle_period,
545
+ futu_candle_mode, cols, 'future'
546
+ )
547
+
548
+ final_result = None
549
+ concat_list = []
550
+ if stk_result is not None:
551
+ concat_list.append(stk_result)
552
+ if bond_result is not None:
553
+ concat_list.append(bond_result)
554
+ if fund_result is not None:
555
+ concat_list.append(fund_result)
556
+ if futu_result is not None:
557
+ concat_list.append(futu_result)
558
+
559
+ if len(concat_list) > 1:
560
+ final_result = pd.concat(
561
+ concat_list,
562
+ axis=0, sort=False, ignore_index=True
563
+ )
564
+ elif len(concat_list) == 1:
565
+ final_result = concat_list[0]
566
+
567
+ final_result = convert_result_data(final_result, rslt_type)
568
+
569
+ return final_result
570
+
571
+
572
+ def _async_get_hf_data(func, scr_num_list, start_time, end_time, columns, code_type):
573
+ """
574
+ 异步获取高频数据
575
+ """
576
+ # future和hkstock默认用的单日起止时间
577
+ day_end_time = '235959'
578
+ day_start_time = '000000'
579
+
580
+ if code_type == 'stock' or code_type == 'bond' or code_type == 'fund':
581
+ # stock用的时间
582
+ day_end_time = '150159'
583
+ day_start_time = '091500'
584
+
585
+ scr_num_list = regroup_scr_nums(scr_num_list, HF_CODE_GROUP)
586
+
587
+ if HF_DAY_DELTA > 0:
588
+ # 获取交易日
589
+ if start_time[: 8] == end_time[: 8]:
590
+ trading_day = [start_time[: 8]]
591
+ else:
592
+ #20240606 新增港股交易日判斷
593
+ if code_type == "hkstock" :
594
+ df = get_exchange_calendar('XHKG', start_time[: 8], end_time[: 8], trdy_flag=1)
595
+ if df is None:
596
+ df = get_exchange_calendar('XHKG', trdy_flag=1).tail(1)
597
+ else:
598
+ df = get_exchange_calendar('XSHG', start_time[: 8], end_time[: 8], trdy_flag=1)
599
+ if df is None:
600
+ df = get_exchange_calendar('XSHG', trdy_flag=1).tail(1)
601
+ trading_day = df['busi_date'].tolist()
602
+
603
+ params_list = []
604
+ _futures = []
605
+ if HF_DAY_DELTA > 0:
606
+ day_delta = HF_DAY_DELTA if HF_DAY_DELTA < len(trading_day) else len(trading_day)
607
+ _start_date_list = trading_day[::day_delta]
608
+ _end_date_list = trading_day[day_delta - 1::day_delta]
609
+ if len(_start_date_list) != len(_end_date_list):
610
+ _end_date_list.append(trading_day[-1])
611
+
612
+ for _idx in range(len(_start_date_list)):
613
+ _start_time = _start_date_list[_idx]
614
+ _end_time = _end_date_list[_idx]
615
+ if _idx == 0 and _start_time == start_time[: 8]:
616
+ _start_time = start_time
617
+ if _idx == len(trading_day) - 1 and _end_time == end_time[: 8]:
618
+ _end_time = end_time
619
+ if len(end_time) == 8:
620
+ end_time = '%s%s' % (end_time, day_end_time)
621
+
622
+ for _s in scr_num_list:
623
+ params = {
624
+ "scr_num": _s,
625
+ "strt_time": _start_time,
626
+ "end_time": _end_time,
627
+ "cols": columns,
628
+ }
629
+ params_list.append(params)
630
+
631
+ else: # if SPILIT_DAY > 0
632
+ _start_time = start_time
633
+ _end_time = end_time
634
+ for _s in scr_num_list:
635
+ params = {
636
+ "scr_num": _s,
637
+ "strt_time": _start_time,
638
+ "end_time": _end_time,
639
+ "cols": columns,
640
+ }
641
+ params_list.append(params)
642
+
643
+ for params in params_list:
644
+ _future = EXECUTOR.submit(func, **params)
645
+ _futures.append(_future)
646
+
647
+ dfs = collect_dfs_parallel(func, _futures)
648
+
649
+ global HF_SORT_COLS
650
+ result = sort_merge_df(dfs, HF_SORT_COLS[code_type])
651
+ return result
652
+
653
+
654
+ @wait_until_bind()
655
+ def get_tick(scr_num_list, strt_time, end_time, cols=None, rslt_type=0):
656
+ """
657
+ 获取快照数据
658
+ """
659
+
660
+ if scr_num_list is None or strt_time is None or end_time is None:
661
+ warnings.warn("函数[get_tick]的参数(scr_num_list, strt_time, end_time)为必填项")
662
+ return None
663
+
664
+ if isinstance(scr_num_list, str):
665
+ scr_num_list = scr_num_list.split(',')
666
+
667
+ stk_list = []
668
+ futu_list = []
669
+ bond_list = []
670
+ fund_list = []
671
+
672
+ global CODE_SECURITY_MAP
673
+ if len(CODE_SECURITY_MAP) == 0:
674
+ load_basic_info()
675
+
676
+ scr_code_list = \
677
+ [
678
+ _code for _code in scr_num_list if _code.split('.')[1] in ['XSHG', 'XSHE']
679
+ ]
680
+
681
+ noexist_code_list = set(scr_code_list) - set(list(CODE_SECURITY_MAP.keys()))
682
+ if len(noexist_code_list):
683
+ warnings.warn("函数[scr_num_list]中指定代码不存在[%s]" % ''.join(noexist_code_list))
684
+ return None
685
+
686
+ try:
687
+ for _scr in scr_num_list:
688
+ _market_code = _scr.split('.')[1]
689
+ if _market_code in FUTURE:
690
+ futu_list.append(_scr)
691
+ elif CODE_SECURITY_MAP[_scr] == 'STOCK':
692
+ stk_list.append(_scr)
693
+ elif CODE_SECURITY_MAP[_scr] == 'BOND':
694
+ bond_list.append(_scr)
695
+ elif CODE_SECURITY_MAP[_scr] == 'FUND':
696
+ fund_list.append(_scr)
697
+ except IndexError:
698
+ raise Exception('scr_num_list输入格式异常[%s]' % scr_num_list)
699
+
700
+ stk_result = None
701
+ bond_result = None
702
+ fund_result = None
703
+ futu_result = None
704
+ if len(stk_list) > 0:
705
+ stk_result = _async_get_hf_data(
706
+ get_stock_tick, stk_list, strt_time, end_time,
707
+ cols, 'stock'
708
+ )
709
+
710
+ if len(bond_list) > 0:
711
+ bond_result = _async_get_hf_data(
712
+ get_bond_tick, bond_list, strt_time, end_time,
713
+ cols, 'bond')
714
+
715
+ if len(fund_list) > 0:
716
+ fund_result = _async_get_hf_data(
717
+ get_fund_tick, fund_list, strt_time, end_time,
718
+ cols, 'fund')
719
+
720
+ if len(futu_list) > 0:
721
+ futu_result = _async_get_hf_data(
722
+ get_future_tick, futu_list, strt_time, end_time,
723
+ cols, 'future')
724
+
725
+ final_result = None
726
+ concat_list = []
727
+ if stk_result is not None:
728
+ concat_list.append(stk_result)
729
+ if bond_result is not None:
730
+ concat_list.append(bond_result)
731
+ if fund_result is not None:
732
+ concat_list.append(fund_result)
733
+ if futu_result is not None:
734
+ concat_list.append(futu_result)
735
+
736
+ if len(concat_list) > 1:
737
+ final_result = pd.concat(
738
+ concat_list,
739
+ axis=0, sort=False, ignore_index=True
740
+ )
741
+ elif len(concat_list) == 1:
742
+ final_result = concat_list[0]
743
+
744
+ final_result = convert_result_data(final_result, rslt_type)
745
+
746
+ return final_result
747
+
748
+
749
+ @wait_until_bind()
750
+ def get_tick2(scr_num_list, strt_time, end_time, cols=None, rslt_type=0):
751
+ """
752
+ 获取期货Level2快照数据
753
+ """
754
+
755
+ if scr_num_list is None or strt_time is None or end_time is None:
756
+ warnings.warn("函数[get_tick2]的参数(scr_num_list, strt_time, end_time)为必填项")
757
+ return None
758
+
759
+ if isinstance(scr_num_list, str):
760
+ scr_num_list = scr_num_list.split(',')
761
+
762
+ futu_list = []
763
+
764
+ global CODE_SECURITY_MAP
765
+ if len(CODE_SECURITY_MAP) == 0:
766
+ load_basic_info()
767
+
768
+ try:
769
+ for _scr in scr_num_list:
770
+ _market_code = _scr.split('.')[1]
771
+ futu_list.append(_scr)
772
+ except IndexError:
773
+ raise Exception('scr_num_list输入格式异常[%s]' % scr_num_list)
774
+
775
+ futu_result = None
776
+ if len(futu_list) > 0:
777
+ futu_result = _async_get_hf_data(
778
+ get_future_tick2, futu_list, strt_time, end_time,
779
+ cols, 'future')
780
+
781
+ final_result = None
782
+ concat_list = []
783
+ if futu_result is not None:
784
+ concat_list.append(futu_result)
785
+
786
+ if len(concat_list) > 1:
787
+ final_result = pd.concat(
788
+ concat_list,
789
+ axis=0, sort=False, ignore_index=True
790
+ )
791
+ elif len(concat_list) == 1:
792
+ final_result = concat_list[0]
793
+
794
+ final_result = convert_result_data(final_result, rslt_type)
795
+
796
+ return final_result
797
+
798
+
799
+ def get_stock_tick(scr_num, strt_time=None, end_time=None, cols=None, rslt_type=0):
800
+ """
801
+ 批量获取股票代码快照数据
802
+
803
+ """
804
+
805
+ int_param = [
806
+ 'total_num', 'page_num', 'total_pages',
807
+ 'trd_count', 'withdraw_buy_count', 'withdraw_sell_count',
808
+ 'bid_size1', 'bid_size2', 'bid_size3', 'bid_size4', 'bid_size5',
809
+ 'bid_size6', 'bid_size7', 'bid_size8', 'bid_size9', 'bid_size10',
810
+ 'offer_size1', 'offer_size2', 'offer_size3', 'offer_size4', 'offer_size5',
811
+ 'offer_size6', 'offer_size7', 'offer_size8', 'offer_size9', 'offer_size10',
812
+ ]
813
+ float_param = [
814
+ 'preclose_px', 'open_px', 'high_px', 'low_px', 'last_px',
815
+ 'up_limit', 'down_limit', 'totl_trd_vol', 'totl_trd_amt',
816
+ 'withdraw_buy_px', 'withdraw_buy_amount', 'withdraw_sell_px', 'withdraw_sell_amount',
817
+ 'bid_price1', 'bid_price2', 'bid_price3', 'bid_price4', 'bid_price5',
818
+ 'bid_price6', 'bid_price7', 'bid_price8', 'bid_price9', 'bid_price10',
819
+ 'offer_price1', 'offer_price2', 'offer_price3', 'offer_price4', 'offer_price5',
820
+ 'offer_price6', 'offer_price7', 'offer_price8', 'offer_price9', 'offer_price10',
821
+ 'totl_buy_amount', 'totl_sell_amount', 'weighted_avg_buy_px', 'weighted_avg_sell_px'
822
+ ]
823
+ if cols:
824
+ fix_cols = \
825
+ ['scr_num', 'date_time']
826
+ if isinstance(cols, str):
827
+ cols = cols.split(',')
828
+ tmp_cols = fix_cols + cols
829
+ cols = list(set(tmp_cols))
830
+ cols.sort(key=tmp_cols.index)
831
+
832
+ int_param = list(set(int_param).intersection(set(cols)))
833
+ float_param = list(set(float_param).intersection(set(cols)))
834
+
835
+ if isinstance(cols, list):
836
+ cols = ','.join(cols)
837
+
838
+ if scr_num:
839
+ params = {
840
+ "scr_num": scr_num,
841
+ "strt_time": strt_time,
842
+ "end_time": end_time,
843
+ "cols": cols,
844
+ "page_num": 1,
845
+ "page_size": CONFIG_PAGE_SIZE,
846
+ "rslt_type": rslt_type,
847
+ "api_type": "quote",
848
+ "int_param": int_param,
849
+ "float_param": float_param
850
+ }
851
+
852
+ # 20220812 统一改为与期货快照一致
853
+ quote_data = quote_single_part(URL_GET_STK_TICK, params)
854
+ return quote_data
855
+
856
+ else:
857
+ warnings.warn("函数[get_stock_tick]的参数(scr_num)为必填项")
858
+ return None
859
+
860
+
861
+ def get_bond_tick(scr_num, strt_time=None, end_time=None, cols=None, rslt_type=0):
862
+ """
863
+ 批量获取债券代码快照数据
864
+
865
+ """
866
+
867
+ int_param = [
868
+ 'total_num', 'page_num', 'total_pages',
869
+ 'trd_count', 'withdraw_buy_count', 'withdraw_sell_count',
870
+ 'bid_size1', 'bid_size2', 'bid_size3', 'bid_size4', 'bid_size5',
871
+ 'bid_size6', 'bid_size7', 'bid_size8', 'bid_size9', 'bid_size10',
872
+ 'offer_size1', 'offer_size2', 'offer_size3', 'offer_size4', 'offer_size5',
873
+ 'offer_size6', 'offer_size7', 'offer_size8', 'offer_size9', 'offer_size10',
874
+ ]
875
+ float_param = [
876
+ 'preclose_px', 'open_px', 'high_px', 'low_px', 'last_px',
877
+ 'up_limit', 'down_limit', 'totl_trd_vol', 'totl_trd_amt',
878
+ 'withdraw_buy_px', 'withdraw_buy_amount', 'withdraw_sell_px', 'withdraw_sell_amount',
879
+ 'bid_price1', 'bid_price2', 'bid_price3', 'bid_price4', 'bid_price5',
880
+ 'bid_price6', 'bid_price7', 'bid_price8', 'bid_price9', 'bid_price10',
881
+ 'offer_price1', 'offer_price2', 'offer_price3', 'offer_price4', 'offer_price5',
882
+ 'offer_price6', 'offer_price7', 'offer_price8', 'offer_price9', 'offer_price10',
883
+ 'totl_buy_amount', 'totl_sell_amount', 'weighted_avg_buy_px', 'weighted_avg_sell_px'
884
+ ]
885
+ if cols:
886
+ fix_cols = \
887
+ ['scr_num', 'date_time']
888
+ if isinstance(cols, str):
889
+ cols = cols.split(',')
890
+ tmp_cols = fix_cols + cols
891
+ cols = list(set(tmp_cols))
892
+ cols.sort(key=tmp_cols.index)
893
+
894
+ int_param = list(set(int_param).intersection(set(cols)))
895
+ float_param = list(set(float_param).intersection(set(cols)))
896
+
897
+ if isinstance(cols, list):
898
+ cols = ','.join(cols)
899
+
900
+ if scr_num:
901
+ params = {
902
+ "scr_num": scr_num,
903
+ "strt_time": strt_time,
904
+ "end_time": end_time,
905
+ "cols": cols,
906
+ "page_num": 1,
907
+ "page_size": CONFIG_PAGE_SIZE,
908
+ "rslt_type": rslt_type,
909
+ "api_type": "quote",
910
+ "int_param": int_param,
911
+ "float_param": float_param
912
+ }
913
+
914
+ # 20220812 统一改为与期货快照一致
915
+ quote_data = quote_single_part(URL_GET_BND_TICK, params)
916
+ return quote_data
917
+
918
+ else:
919
+ warnings.warn("函数[get_bond_tick]的参数(scr_num)为必填项")
920
+ return None
921
+
922
+
923
+ def get_fund_tick(scr_num, strt_time=None, end_time=None, cols=None, rslt_type=0):
924
+ """
925
+ 批量获取基金代码快照数据
926
+
927
+ """
928
+
929
+ int_param = [
930
+ 'total_num', 'page_num', 'total_pages',
931
+ 'trd_count', 'withdraw_buy_count', 'withdraw_sell_count',
932
+ 'bid_size1', 'bid_size2', 'bid_size3', 'bid_size4', 'bid_size5',
933
+ 'bid_size6', 'bid_size7', 'bid_size8', 'bid_size9', 'bid_size10',
934
+ 'offer_size1', 'offer_size2', 'offer_size3', 'offer_size4', 'offer_size5',
935
+ 'offer_size6', 'offer_size7', 'offer_size8', 'offer_size9', 'offer_size10',
936
+ 'etf_bid_count', 'etf_bid_volume', 'etf_offer_count', 'etf_offer_volumne',
937
+ ]
938
+ float_param = [
939
+ 'preclose_px', 'open_px', 'high_px', 'low_px', 'last_px',
940
+ 'up_limit', 'down_limit', 'totl_trd_vol', 'totl_trd_amt',
941
+ 'withdraw_buy_px', 'withdraw_buy_amount', 'withdraw_sell_px', 'withdraw_sell_amount',
942
+ 'bid_price1', 'bid_price2', 'bid_price3', 'bid_price4', 'bid_price5',
943
+ 'bid_price6', 'bid_price7', 'bid_price8', 'bid_price9', 'bid_price10',
944
+ 'offer_price1', 'offer_price2', 'offer_price3', 'offer_price4', 'offer_price5',
945
+ 'offer_price6', 'offer_price7', 'offer_price8', 'offer_price9', 'offer_price10',
946
+ 'totl_buy_amount', 'totl_sell_amount', 'weighted_avg_buy_px', 'weighted_avg_sell_px',
947
+ 'etf_bid_amount', 'etf_offer_amount', 'pre_nav', 'real_nav', 'iopv',
948
+ ]
949
+ if cols:
950
+ fix_cols = \
951
+ ['scr_num', 'date_time']
952
+ if isinstance(cols, str):
953
+ cols = cols.split(',')
954
+ tmp_cols = fix_cols + cols
955
+ cols = list(set(tmp_cols))
956
+ cols.sort(key=tmp_cols.index)
957
+
958
+ int_param = list(set(int_param).intersection(set(cols)))
959
+ float_param = list(set(float_param).intersection(set(cols)))
960
+
961
+ if isinstance(cols, list):
962
+ cols = ','.join(cols)
963
+
964
+ if scr_num:
965
+ params = {
966
+ "scr_num": scr_num,
967
+ "strt_time": strt_time,
968
+ "end_time": end_time,
969
+ "cols": cols,
970
+ "page_num": 1,
971
+ "page_size": CONFIG_PAGE_SIZE,
972
+ "rslt_type": rslt_type,
973
+ "api_type": "quote",
974
+ "int_param": int_param,
975
+ "float_param": float_param
976
+ }
977
+
978
+ # 20220812 统一改为与期货快照一致
979
+ quote_data = quote_single_part(URL_GET_FND_TICK, params)
980
+ return quote_data
981
+
982
+ else:
983
+ warnings.warn("函数[get_fund_tick]的参数(scr_num)为必填项")
984
+ return None
985
+
986
+
987
+ @wait_until_bind()
988
+ def get_order(scr_num_list, strt_time, end_time, cols=None, rslt_type=0):
989
+ """
990
+
991
+ """
992
+
993
+ if scr_num_list is None or strt_time is None or end_time is None:
994
+ warnings.warn("函数[get_order]的参数(scr_num_list, strt_time, end_time)为必填项")
995
+ return None
996
+
997
+ if isinstance(scr_num_list, str):
998
+ scr_num_list = scr_num_list.split(',')
999
+
1000
+ stk_list = []
1001
+ bond_list = []
1002
+ fund_list = []
1003
+
1004
+ global CODE_SECURITY_MAP
1005
+ if len(CODE_SECURITY_MAP) == 0:
1006
+ load_basic_info()
1007
+
1008
+ scr_code_list = \
1009
+ [
1010
+ _code for _code in scr_num_list if _code.split('.')[1] in ['XSHG', 'XSHE']
1011
+ ]
1012
+
1013
+ noexist_code_list = set(scr_code_list) - set(list(CODE_SECURITY_MAP.keys()))
1014
+ if len(noexist_code_list):
1015
+ warnings.warn("函数[scr_num_list]中指定代码不存在[%s]" % ''.join(noexist_code_list))
1016
+ return None
1017
+
1018
+ try:
1019
+ for _scr in scr_num_list:
1020
+ _market_code = _scr.split('.')[1]
1021
+ if CODE_SECURITY_MAP[_scr] == 'STOCK':
1022
+ stk_list.append(_scr)
1023
+ elif CODE_SECURITY_MAP[_scr] == 'BOND':
1024
+ bond_list.append(_scr)
1025
+ elif CODE_SECURITY_MAP[_scr] == 'FUND':
1026
+ fund_list.append(_scr)
1027
+ except IndexError:
1028
+ raise Exception('scr_num_list输入格式异常[%s]' % scr_num_list)
1029
+
1030
+ stk_result = None
1031
+ bond_result = None
1032
+ fund_result = None
1033
+ if len(stk_list) > 0:
1034
+ stk_result = _async_get_hf_data(
1035
+ get_stock_order, stk_list, strt_time, end_time,
1036
+ cols, 'stock'
1037
+ )
1038
+
1039
+ if len(bond_list) > 0:
1040
+ bond_result = _async_get_hf_data(
1041
+ get_bond_order, bond_list, strt_time, end_time,
1042
+ cols, 'bond')
1043
+
1044
+ if len(fund_list) > 0:
1045
+ fund_result = _async_get_hf_data(
1046
+ get_fund_order, fund_list, strt_time, end_time,
1047
+ cols, 'fund')
1048
+
1049
+ final_result = None
1050
+ concat_list = []
1051
+ if stk_result is not None:
1052
+ concat_list.append(stk_result)
1053
+ if bond_result is not None:
1054
+ concat_list.append(bond_result)
1055
+ if fund_result is not None:
1056
+ concat_list.append(fund_result)
1057
+
1058
+ if len(concat_list) > 1:
1059
+ final_result = pd.concat(
1060
+ concat_list,
1061
+ axis=0, sort=False, ignore_index=True
1062
+ )
1063
+ elif len(concat_list) == 1:
1064
+ final_result = concat_list[0]
1065
+
1066
+ final_result = convert_result_data(final_result, rslt_type)
1067
+
1068
+ return final_result
1069
+
1070
+
1071
+ def get_stock_order(scr_num, strt_time=None, end_time=None, cols=None, rslt_type=0):
1072
+ """
1073
+ 批量获取股票代码逐笔委托历史数据查询
1074
+
1075
+ """
1076
+
1077
+ int_param = ['total_num', 'page_num', 'total_pages', 'channel_no']
1078
+ float_param = ['entr_px', 'entr_vol']
1079
+ if cols:
1080
+ fix_cols = \
1081
+ ['scr_num', 'date_time']
1082
+ if isinstance(cols, str):
1083
+ cols = cols.split(',')
1084
+ tmp_cols = fix_cols + cols
1085
+ cols = list(set(tmp_cols))
1086
+ cols.sort(key=tmp_cols.index)
1087
+
1088
+ int_param = list(set(int_param).intersection(set(cols)))
1089
+ float_param = list(set(float_param).intersection(set(cols)))
1090
+
1091
+ if isinstance(cols, list):
1092
+ cols = ','.join(cols)
1093
+
1094
+ if scr_num:
1095
+ params = {
1096
+ "scr_num": scr_num,
1097
+ "strt_time": strt_time,
1098
+ "end_time": end_time,
1099
+ "cols": cols,
1100
+ "page_num": 1,
1101
+ "page_size": CONFIG_PAGE_SIZE, # 20220812 统一改为与期货快照一致
1102
+ "rslt_type": rslt_type,
1103
+ "api_type": "quote",
1104
+ "int_param": int_param,
1105
+ "float_param": float_param
1106
+ }
1107
+
1108
+ # 20220812 统一改为与期货快照一致
1109
+ quote_data = quote_single_part(URL_GET_STK_ORDER, params)
1110
+ return quote_data
1111
+ else:
1112
+ warnings.warn("函数[get_stock_entrust]的参数(scr_num)为必填项")
1113
+ return None
1114
+
1115
+
1116
+ def get_bond_order(scr_num, strt_time=None, end_time=None, cols=None, rslt_type=0):
1117
+ """
1118
+ 批量获取债券代码逐笔委托历史数据查询
1119
+
1120
+ """
1121
+
1122
+ int_param = ['total_num', 'page_num', 'total_pages', 'channel_no']
1123
+ float_param = ['entr_px', 'entr_vol']
1124
+ if cols:
1125
+ fix_cols = \
1126
+ ['scr_num', 'date_time']
1127
+ if isinstance(cols, str):
1128
+ cols = cols.split(',')
1129
+ tmp_cols = fix_cols + cols
1130
+ cols = list(set(tmp_cols))
1131
+ cols.sort(key=tmp_cols.index)
1132
+
1133
+ int_param = list(set(int_param).intersection(set(cols)))
1134
+ float_param = list(set(float_param).intersection(set(cols)))
1135
+
1136
+ if isinstance(cols, list):
1137
+ cols = ','.join(cols)
1138
+
1139
+ if scr_num:
1140
+ params = {
1141
+ "scr_num": scr_num,
1142
+ "strt_time": strt_time,
1143
+ "end_time": end_time,
1144
+ "cols": cols,
1145
+ "page_num": 1,
1146
+ "page_size": CONFIG_PAGE_SIZE, # 20220812 统一改为与期货快照一致
1147
+ "rslt_type": rslt_type,
1148
+ "api_type": "quote",
1149
+ "int_param": int_param,
1150
+ "float_param": float_param
1151
+ }
1152
+
1153
+ # 20220812 统一改为与期货快照一致
1154
+ quote_data = quote_single_part(URL_GET_BND_ORDER, params)
1155
+ return quote_data
1156
+ else:
1157
+ warnings.warn("函数[get_bond_entrust]的参数(scr_num)为必填项")
1158
+ return None
1159
+
1160
+
1161
+ def get_fund_order(scr_num, strt_time=None, end_time=None, cols=None, rslt_type=0):
1162
+ """
1163
+ 批量获取基金代码逐笔委托历史数据查询
1164
+
1165
+ """
1166
+
1167
+ int_param = ['total_num', 'page_num', 'total_pages', 'channel_no']
1168
+ float_param = ['entr_px', 'entr_vol']
1169
+ if cols:
1170
+ fix_cols = \
1171
+ ['scr_num', 'date_time']
1172
+ if isinstance(cols, str):
1173
+ cols = cols.split(',')
1174
+ tmp_cols = fix_cols + cols
1175
+ cols = list(set(tmp_cols))
1176
+ cols.sort(key=tmp_cols.index)
1177
+
1178
+ int_param = list(set(int_param).intersection(set(cols)))
1179
+ float_param = list(set(float_param).intersection(set(cols)))
1180
+
1181
+ if isinstance(cols, list):
1182
+ cols = ','.join(cols)
1183
+
1184
+ if scr_num:
1185
+ params = {
1186
+ "scr_num": scr_num,
1187
+ "strt_time": strt_time,
1188
+ "end_time": end_time,
1189
+ "cols": cols,
1190
+ "page_num": 1,
1191
+ "page_size": CONFIG_PAGE_SIZE, # 20220812 统一改为与期货快照一致
1192
+ "rslt_type": rslt_type,
1193
+ "api_type": "quote",
1194
+ "int_param": int_param,
1195
+ "float_param": float_param
1196
+ }
1197
+
1198
+ # 20220812 统一改为与期货快照一致
1199
+ quote_data = quote_single_part(URL_GET_FND_ORDER, params)
1200
+ return quote_data
1201
+ else:
1202
+ warnings.warn("函数[get_fund_entrust]的参数(scr_num)为必填项")
1203
+ return None
1204
+
1205
+
1206
+ @wait_until_bind()
1207
+ def get_deal(scr_num_list, strt_time, end_time, cols=None, rslt_type=0):
1208
+ """
1209
+
1210
+ """
1211
+
1212
+ if scr_num_list is None or strt_time is None or end_time is None:
1213
+ warnings.warn("函数[get_deal]的参数(scr_num_list, strt_time, end_time)为必填项")
1214
+ return None
1215
+
1216
+ if isinstance(scr_num_list, str):
1217
+ scr_num_list = scr_num_list.split(',')
1218
+
1219
+ stk_list = []
1220
+ bond_list = []
1221
+ fund_list = []
1222
+
1223
+ global CODE_SECURITY_MAP
1224
+ if len(CODE_SECURITY_MAP) == 0:
1225
+ load_basic_info()
1226
+
1227
+ scr_code_list = \
1228
+ [
1229
+ _code for _code in scr_num_list if _code.split('.')[1] in ['XSHG', 'XSHE']
1230
+ ]
1231
+
1232
+ noexist_code_list = set(scr_code_list) - set(list(CODE_SECURITY_MAP.keys()))
1233
+ if len(noexist_code_list):
1234
+ warnings.warn("函数[scr_num_list]中指定代码不存在[%s]" % ''.join(noexist_code_list))
1235
+ return None
1236
+
1237
+ try:
1238
+ for _scr in scr_num_list:
1239
+ _market_code = _scr.split('.')[1]
1240
+ if CODE_SECURITY_MAP[_scr] == 'STOCK':
1241
+ stk_list.append(_scr)
1242
+ elif CODE_SECURITY_MAP[_scr] == 'BOND':
1243
+ bond_list.append(_scr)
1244
+ elif CODE_SECURITY_MAP[_scr] == 'FUND':
1245
+ fund_list.append(_scr)
1246
+ except IndexError:
1247
+ raise Exception('scr_num_list输入格式异常[%s]' % scr_num_list)
1248
+
1249
+ stk_result = None
1250
+ bond_result = None
1251
+ fund_result = None
1252
+ if len(stk_list) > 0:
1253
+ stk_result = _async_get_hf_data(
1254
+ get_stock_deal, stk_list, strt_time, end_time,
1255
+ cols, 'stock'
1256
+ )
1257
+
1258
+ if len(bond_list) > 0:
1259
+ bond_result = _async_get_hf_data(
1260
+ get_bond_deal, bond_list, strt_time, end_time,
1261
+ cols, 'bond')
1262
+
1263
+ if len(fund_list) > 0:
1264
+ fund_result = _async_get_hf_data(
1265
+ get_fund_deal, fund_list, strt_time, end_time,
1266
+ cols, 'fund')
1267
+
1268
+ final_result = None
1269
+ concat_list = []
1270
+ if stk_result is not None:
1271
+ concat_list.append(stk_result)
1272
+ if bond_result is not None:
1273
+ concat_list.append(bond_result)
1274
+ if fund_result is not None:
1275
+ concat_list.append(fund_result)
1276
+
1277
+ if len(concat_list) > 1:
1278
+ final_result = pd.concat(
1279
+ concat_list,
1280
+ axis=0, sort=False, ignore_index=True
1281
+ )
1282
+ elif len(concat_list) == 1:
1283
+ final_result = concat_list[0]
1284
+
1285
+ final_result = convert_result_data(final_result, rslt_type)
1286
+
1287
+ return final_result
1288
+
1289
+
1290
+ def get_stock_deal(scr_num, strt_time=None, end_time=None, cols=None, rslt_type=0):
1291
+ """
1292
+ 批量获取股票代码逐笔委托历史数据查询
1293
+
1294
+ """
1295
+
1296
+ int_param = ['total_num', 'page_num', 'total_pages', 'channel_no']
1297
+ float_param = ['trd_px', 'trd_vol']
1298
+
1299
+ if cols:
1300
+ fix_cols = \
1301
+ ['scr_num', 'date_time']
1302
+ if isinstance(cols, str):
1303
+ cols = cols.split(',')
1304
+ tmp_cols = fix_cols + cols
1305
+ cols = list(set(tmp_cols))
1306
+ cols.sort(key=tmp_cols.index)
1307
+
1308
+ int_param = list(set(int_param).intersection(set(cols)))
1309
+ float_param = list(set(float_param).intersection(set(cols)))
1310
+
1311
+ if isinstance(cols, list):
1312
+ cols = ','.join(cols)
1313
+
1314
+ if scr_num:
1315
+ params = {
1316
+ "scr_num": scr_num,
1317
+ "strt_time": strt_time,
1318
+ "end_time": end_time,
1319
+ "cols": cols,
1320
+ "page_num": 1,
1321
+ "page_size": CONFIG_PAGE_SIZE, # 20220812 统一改为与期货快照一致
1322
+ "rslt_type": rslt_type,
1323
+ "api_type": "quote",
1324
+ "int_param": int_param,
1325
+ "float_param": float_param
1326
+ }
1327
+
1328
+ # 20220812 统一改为与期货快照一致
1329
+ quote_data = quote_single_part(URL_GET_STK_DEAL, params)
1330
+ return quote_data
1331
+ else:
1332
+ warnings.warn("函数[get_stock_deal]的参数(scr_num)为必填项")
1333
+ return None
1334
+
1335
+
1336
+ def get_bond_deal(scr_num, strt_time=None, end_time=None, cols=None, rslt_type=0):
1337
+ """
1338
+ 批量获取债券代码逐笔委托历史数据查询
1339
+
1340
+ """
1341
+
1342
+ int_param = ['total_num', 'page_num', 'total_pages', 'channel_no']
1343
+ float_param = ['trd_px', 'trd_vol']
1344
+
1345
+ if cols:
1346
+ fix_cols = \
1347
+ ['scr_num', 'date_time']
1348
+ if isinstance(cols, str):
1349
+ cols = cols.split(',')
1350
+ tmp_cols = fix_cols + cols
1351
+ cols = list(set(tmp_cols))
1352
+ cols.sort(key=tmp_cols.index)
1353
+
1354
+ int_param = list(set(int_param).intersection(set(cols)))
1355
+ float_param = list(set(float_param).intersection(set(cols)))
1356
+
1357
+ if isinstance(cols, list):
1358
+ cols = ','.join(cols)
1359
+
1360
+ if scr_num:
1361
+ params = {
1362
+ "scr_num": scr_num,
1363
+ "strt_time": strt_time,
1364
+ "end_time": end_time,
1365
+ "cols": cols,
1366
+ "page_num": 1,
1367
+ "page_size": CONFIG_PAGE_SIZE, # 20220812 统一改为与期货快照一致
1368
+ "rslt_type": rslt_type,
1369
+ "api_type": "quote",
1370
+ "int_param": int_param,
1371
+ "float_param": float_param
1372
+ }
1373
+
1374
+ # 20220812 统一改为与期货快照一致
1375
+ quote_data = quote_single_part(URL_GET_BND_DEAL, params)
1376
+ return quote_data
1377
+ else:
1378
+ warnings.warn("函数[get_bond_deal]的参数(scr_num)为必填项")
1379
+ return None
1380
+
1381
+
1382
+ def get_fund_deal(scr_num, strt_time=None, end_time=None, cols=None, rslt_type=0):
1383
+ """
1384
+ 批量获取基金代码逐笔委托历史数据查询
1385
+
1386
+ """
1387
+
1388
+ int_param = ['total_num', 'page_num', 'total_pages', 'channel_no']
1389
+ float_param = ['trd_px', 'trd_vol']
1390
+
1391
+ if cols:
1392
+ fix_cols = \
1393
+ ['scr_num', 'date_time']
1394
+ if isinstance(cols, str):
1395
+ cols = cols.split(',')
1396
+ tmp_cols = fix_cols + cols
1397
+ cols = list(set(tmp_cols))
1398
+ cols.sort(key=tmp_cols.index)
1399
+
1400
+ int_param = list(set(int_param).intersection(set(cols)))
1401
+ float_param = list(set(float_param).intersection(set(cols)))
1402
+
1403
+ if isinstance(cols, list):
1404
+ cols = ','.join(cols)
1405
+
1406
+ if scr_num:
1407
+ params = {
1408
+ "scr_num": scr_num,
1409
+ "strt_time": strt_time,
1410
+ "end_time": end_time,
1411
+ "cols": cols,
1412
+ "page_num": 1,
1413
+ "page_size": CONFIG_PAGE_SIZE, # 20220812 统一改为与期货快照一致
1414
+ "rslt_type": rslt_type,
1415
+ "api_type": "quote",
1416
+ "int_param": int_param,
1417
+ "float_param": float_param
1418
+ }
1419
+
1420
+ # 20220812 统一改为与期货快照一致
1421
+ quote_data = quote_single_part(URL_GET_FND_DEAL, params)
1422
+ return quote_data
1423
+ else:
1424
+ warnings.warn("函数[get_fund_deal]的参数(scr_num)为必填项")
1425
+ return None
1426
+
1427
+
1428
+ def is_product(scr_num):
1429
+ """
1430
+
1431
+ """
1432
+ _result = re.findall(r'\d+', scr_num)
1433
+ if len(_result) == 0:
1434
+ return True
1435
+ else:
1436
+ return False
1437
+
1438
+
1439
+ def get_product(scr_num):
1440
+ _idx_s = 0
1441
+ _idx_e = scr_num.find('.')
1442
+ _prd_type = ''
1443
+ for i in range(len(scr_num)):
1444
+ if scr_num[i].isdecimal() or scr_num[i] == '.':
1445
+ break
1446
+ _prd_type += scr_num[i]
1447
+ _idx_s = i
1448
+
1449
+ _prd_date = scr_num[_idx_s + 1: _idx_e - _idx_s + 1]
1450
+ return _prd_type, _prd_date
1451
+
1452
+
1453
+ def get_future_tick(scr_num, strt_time, end_time, date_type=None, cols=None, rslt_type=0):
1454
+ """
1455
+ 批量获取股票代码快照tick历史数据查询
1456
+
1457
+ """
1458
+
1459
+ int_param = [
1460
+ 'total_num', 'page_num', 'total_pages', 'position', 'preposition'
1461
+ ]
1462
+ float_param = [
1463
+ 'open_px', 'high_px', 'low_px', 'close_px', 'last_px', 'preclose_px',
1464
+ 'up_limit', 'down_limit', 'settlement_px', 'presettlement_px',
1465
+ 'bid_price1', 'bid_price2', 'bid_price3', 'bid_price4', 'bid_price5',
1466
+ 'bid_size1', 'bid_size2', 'bid_size3', 'bid_size4', 'bid_size5',
1467
+ 'offer_price1', 'offer_price2', 'offer_price3', 'offer_price4', 'offer_price5',
1468
+ 'offer_size1', 'offer_size2', 'offer_size3', 'offer_size4', 'offer_size5',
1469
+ 'trd_vol', 'trd_amt', 'delta', 'predelta', 'avg_px'
1470
+ ]
1471
+ if cols:
1472
+ fix_cols = \
1473
+ ['scr_num', 'date_time', 'action_date', 'action_time', 'action_mesc']
1474
+ if isinstance(cols, str):
1475
+ cols = cols.split(',')
1476
+ tmp_cols = fix_cols + cols
1477
+ cols = list(set(tmp_cols))
1478
+ cols.sort(key=tmp_cols.index)
1479
+
1480
+ int_param = list(set(int_param).intersection(set(cols)))
1481
+ float_param = list(set(float_param).intersection(set(cols)))
1482
+
1483
+ if isinstance(cols, list):
1484
+ cols = ','.join(cols)
1485
+
1486
+ if scr_num:
1487
+ _prd_type, _prd_date = get_product(scr_num)
1488
+ contr_type = 'T'
1489
+ if _prd_date is None:
1490
+ contr_type = 'Z'
1491
+
1492
+ params = {
1493
+ "scr_num": scr_num,
1494
+ "strt_time": strt_time,
1495
+ "end_time": end_time,
1496
+ "date_type": date_type,
1497
+ "contr_type": contr_type,
1498
+ "cols": cols,
1499
+ "page_num": 1,
1500
+ "page_size": CONFIG_PAGE_SIZE,
1501
+ "rslt_type": rslt_type,
1502
+ "api_type": "quote",
1503
+ "int_param": int_param,
1504
+ "float_param": float_param,
1505
+ }
1506
+
1507
+ quote_data = quote_single_part(URL_GET_FUT_TICK, params)
1508
+ return quote_data
1509
+
1510
+ else:
1511
+ warnings.warn("函数[get_future_tick]的参数(scr_num)为必填项")
1512
+ return None
1513
+
1514
+
1515
+ def get_future_tick2(scr_num, strt_time, end_time, date_type=None, cols=None, rslt_type=0):
1516
+ """
1517
+ 批量获取股票代码快照tick历史数据查询
1518
+
1519
+ """
1520
+
1521
+ int_param = [
1522
+ 'total_num', 'page_num', 'total_pages', 'position', 'preposition'
1523
+ ]
1524
+ float_param = [
1525
+ 'open_px', 'high_px', 'low_px', 'close_px', 'last_px', 'preclose_px',
1526
+ 'up_limit', 'down_limit', 'settlement_px', 'presettlement_px',
1527
+ 'bid_price1', 'bid_price2', 'bid_price3', 'bid_price4', 'bid_price5',
1528
+ 'bid_size1', 'bid_size2', 'bid_size3', 'bid_size4', 'bid_size5',
1529
+ 'offer_price1', 'offer_price2', 'offer_price3', 'offer_price4', 'offer_price5',
1530
+ 'offer_size1', 'offer_size2', 'offer_size3', 'offer_size4', 'offer_size5',
1531
+ 'trd_vol', 'trd_amt', 'delta', 'predelta', 'avg_px'
1532
+ ]
1533
+ if cols:
1534
+ fix_cols = \
1535
+ ['scr_num', 'date_time', 'action_date', 'action_time', 'action_mesc']
1536
+ if isinstance(cols, str):
1537
+ cols = cols.split(',')
1538
+ tmp_cols = fix_cols + cols
1539
+ cols = list(set(tmp_cols))
1540
+ cols.sort(key=tmp_cols.index)
1541
+
1542
+ int_param = list(set(int_param).intersection(set(cols)))
1543
+ float_param = list(set(float_param).intersection(set(cols)))
1544
+
1545
+ if isinstance(cols, list):
1546
+ cols = ','.join(cols)
1547
+
1548
+ if scr_num:
1549
+ _prd_type, _prd_date = get_product(scr_num)
1550
+ contr_type = 'T'
1551
+ if _prd_date is None:
1552
+ contr_type = 'Z'
1553
+
1554
+ params = {
1555
+ "scr_num": scr_num,
1556
+ "strt_time": strt_time,
1557
+ "end_time": end_time,
1558
+ "date_type": date_type,
1559
+ "contr_type": contr_type,
1560
+ "cols": cols,
1561
+ "page_num": 1,
1562
+ "page_size": CONFIG_PAGE_SIZE,
1563
+ "rslt_type": rslt_type,
1564
+ "api_type": "quote",
1565
+ "int_param": int_param,
1566
+ "float_param": float_param,
1567
+ }
1568
+
1569
+ # 获取期货Level-2快照行情数据
1570
+ quote_data = quote_single_part(URL_GET_FUT_TICK2, params)
1571
+ return quote_data
1572
+
1573
+ else:
1574
+ warnings.warn("函数[get_future_tick2]的参数(scr_num)为必填项")
1575
+ return None
1576
+
1577
+
1578
+ def collect_dfs_parallel(func, _futures):
1579
+ dfs = []
1580
+ if PARALLEL_MODE in {'Coroutine'}:
1581
+ gevent.joinall(_futures)
1582
+ for _f in _futures:
1583
+ try:
1584
+ if _f is None:
1585
+ continue
1586
+ elif _f.value is None:
1587
+ continue
1588
+ elif _f.value['data'] is None:
1589
+ continue
1590
+
1591
+ if len(_f.value['data']) > 0:
1592
+ dfs.append(_f.value['data'])
1593
+ except Exception as ex:
1594
+ warn_str = "并发处理异常,异常函数[%s]" % func
1595
+ warnings.warn(warn_str)
1596
+ # raise Exception("并发处理异常,异常函数[%s]" % func)
1597
+ else:
1598
+ for _f in as_completed(_futures):
1599
+ try:
1600
+ if _f is None:
1601
+ continue
1602
+ elif _f.result() is None:
1603
+ continue
1604
+ elif _f.result()['data'] is None:
1605
+ continue
1606
+
1607
+ if len(_f.result()['data']) > 0:
1608
+ dfs.append(_f.result()['data'])
1609
+ except Exception as ex:
1610
+ import traceback
1611
+ warn_str = "并发处理异常,异常函数[%s],异常信息[%s]" % (func, traceback.format_exc())
1612
+ warnings.warn(warn_str)
1613
+ return dfs
1614
+
1615
+
1616
+ def convert_result_data(df, rslt_type):
1617
+ rslt = None
1618
+ if df is not None:
1619
+ if rslt_type == 0:
1620
+ rslt = df
1621
+ else:
1622
+ rslt = df.values
1623
+ return rslt
1624
+
1625
+
1626
+ def regroup_scr_nums(scr_num_list, group_num):
1627
+ scr_num_list.sort() # 否则合并时可能排序顺序不对
1628
+ new_list = list([','.join(scr_num_list[i:i+group_num]) for i in range(0, len(scr_num_list), group_num)])
1629
+ return new_list
1630
+
1631
+
1632
+ def get_tree_sum(scr_num, qury_time, rslt_type=0):
1633
+ """
1634
+ 获取股票订单簿汇总信息
1635
+
1636
+ """
1637
+
1638
+ int_param = [
1639
+ 'total_num', 'page_num', 'total_pages',
1640
+
1641
+ ]
1642
+ float_param = [
1643
+ 'entr_px', 'minus_volume'
1644
+ ]
1645
+
1646
+ if len(qury_time) != 14:
1647
+ warnings.warn("参数[qury_time]格式不符合规范")
1648
+ return None
1649
+
1650
+ strt_time = '%s091500' % qury_time[0:8]
1651
+ end_time = qury_time
1652
+
1653
+ params = {
1654
+ "scr_num": scr_num,
1655
+ "strt_time": strt_time,
1656
+ "end_time": end_time,
1657
+ "fix_cols": ['entr_px', 'entr_direction', 'entr_volume'],
1658
+ "page_num": 1,
1659
+ "page_size": CONFIG_PAGE_SIZE,
1660
+ "rslt_type": rslt_type,
1661
+ "api_type": "summary",
1662
+ "int_param": int_param,
1663
+ "float_param": float_param
1664
+ }
1665
+
1666
+ # 20220812 统一改为与期货快照一致
1667
+ quote_data = quote_single_part(URL_GET_STK_TREESUM, params)
1668
+ return quote_data['data']
1669
+
1670
+
1671
+ def get_tree_detail(scr_num, qury_time, rslt_type=0):
1672
+ """
1673
+ 获取股票订单簿汇总信息
1674
+
1675
+ """
1676
+
1677
+ int_param = [
1678
+ 'total_num', 'page_num', 'total_pages',
1679
+
1680
+ ]
1681
+ float_param = [
1682
+ 'entr_px', 'minus_volume'
1683
+ ]
1684
+
1685
+ if len(qury_time) != 14:
1686
+ warnings.warn("参数[qury_time]格式不符合规范")
1687
+ return None
1688
+
1689
+ strt_time = '%s091500' % qury_time[0:8]
1690
+ end_time = qury_time
1691
+
1692
+ if scr_num is None or qury_time is None:
1693
+ warnings.warn("函数[get_stock_treesum]的参数(scr_num或qury_time)为必填项")
1694
+ return None
1695
+
1696
+ params = {
1697
+ "scr_num": scr_num,
1698
+ "strt_time": strt_time,
1699
+ "end_time": end_time,
1700
+ "fix_cols": ['entr_px', 'entr_direction', 'entr_orde_no', 'date_time', 'entr_volume'],
1701
+ "page_num": 1,
1702
+ "page_size": CONFIG_PAGE_SIZE,
1703
+ "rslt_type": rslt_type,
1704
+ "api_type": "summary",
1705
+ "int_param": int_param,
1706
+ "float_param": float_param
1707
+ }
1708
+
1709
+ # 20220812 统一改为与期货快照一致
1710
+ quote_data = quote_single_part(URL_GET_STK_TREEDETAIL, params)
1711
+ return quote_data['data']
1712
+
1713
+
1714
+
1715
+ # def check_pid():
1716
+ # global _WARN_MULTI_PROC, BIND_PORT
1717
+ # if platform.system().lower() == 'windows':
1718
+ # return True
1719
+ #
1720
+ # req_pid = os.environ.get('dataquant_sdk_pid', None)
1721
+ # req_pid = int(req_pid) if req_pid else -1
1722
+ # this_pid = os.getpid()
1723
+ # if req_pid == this_pid:
1724
+ # return True
1725
+ #
1726
+ # renew_pid = False
1727
+ # try:
1728
+ # os.kill(req_pid, 0)
1729
+ # except Exception as ex:
1730
+ # renew_pid = True
1731
+ #
1732
+ # if renew_pid:
1733
+ # os.environ['dataquant_sdk_pid'] = str(this_pid)
1734
+ # return True
1735
+ # elif _WARN_MULTI_PROC:
1736
+ # warn_str = '关键功能端口{}绑定失败,请检查端口配置和多开情况,暂不支持多开功能,敬请谅解'.format(BIND_PORT)
1737
+ # warnings.warn(warn_str)
1738
+ # _WARN_MULTI_PROC = False
1739
+ # return False
1740
+ # return False
1741
+
1742
+
1743
+
1744
+