rlquantdata 0.1.0__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.

Potentially problematic release.


This version of rlquantdata might be problematic. Click here for more details.

@@ -0,0 +1,665 @@
1
+ import datetime
2
+ import datetime as dt
3
+ import gc
4
+ import hashlib
5
+ import json
6
+ import os
7
+ import re
8
+ import threading
9
+ import time
10
+ from io import BytesIO
11
+
12
+ import numpy as np
13
+ import pandas as pd
14
+ import requests
15
+ from pandas import Index
16
+
17
+ from src.rlquantdata.utilities import utils
18
+
19
+ exec('from numpy import nan')
20
+
21
+
22
+ class DayBarStore():
23
+ """规定一些标准数据加载模块,目前数据格式同因子平台数据,columns: code, index: time"""
24
+
25
+ def __init__(self, f, include930, key, start_time='2015-01-01', end_time=None):
26
+ '''freq: 文件子目录'''
27
+ t1 = time.time()
28
+ freq, field = key.split('/')
29
+ if start_time is None:
30
+ start_time = '2007-01-01'
31
+ if freq == '01min':
32
+ start_time = '2009-01-01'
33
+ self.start_time = start_time
34
+ self.end_time = end_time
35
+ self._data = utils.get_feather(f, include930, key, start_time, end_time, stocks=None, freq='price' + freq)
36
+ self._data = self._data.sort_index(axis=1)
37
+ if end_time is None:
38
+ self.end_time = self._data.index[-1].strftime('%Y-%m-%d')
39
+ print('load {}/price{} from {} to {}, consume {:.4}s'.format(f, key,
40
+ start_time, end_time, time.time() - t1))
41
+
42
+ def get_datas(self, trading_dates, start_time, end_time, bar_counts, codes):
43
+ # 合并 trading_dates 和 self._data.index,防止类似于指数000001.fea数据,会提前录入
44
+ trading_dates = pd.to_datetime(trading_dates).union(self._data.index).sort_values()
45
+ days = utils.trade_time(trading_dates, start_time=start_time, end_time=end_time, bar_counts=bar_counts)
46
+ if len(days) == 0:
47
+ raise ValueError('days did not provide')
48
+ if isinstance(days, str):
49
+ days = pd.to_datetime(days)
50
+ elif isinstance(days, int):
51
+ days = pd.to_datetime(str(days))
52
+ elif isinstance(days, list):
53
+ days = [pd.to_datetime((day)) for day in days]
54
+ else:
55
+ raise ValueError('days type:{} not match.'.format(type(days)))
56
+
57
+ if isinstance(codes, (list, np.ndarray)):
58
+ codes = [int(code) for code in codes]
59
+ return self._data.reindex(index=days, columns=codes)
60
+ elif isinstance(codes, Index):
61
+ if codes.dtype != 'int64':
62
+ raise ValueError('not support for codes type:{}'.format(codes.dtype))
63
+ return self._data.loc[start_time:end_time].reindex(columns=codes)
64
+ elif isinstance(codes, str):
65
+ codes = [int(codes)]
66
+ return self._data.reindex(index=days, columns=codes)
67
+ elif codes is None:
68
+ return self._data.reindex(index=days)
69
+ else:
70
+ raise ValueError('not support for codes:{}'.format(codes))
71
+
72
+ def get_range_datas(self, start_time, end_time, codes):
73
+ if isinstance(codes, (list, np.ndarray)):
74
+ codes = [int(code) for code in codes]
75
+ return self._data.loc[start_time:end_time].reindex(columns=codes)
76
+ elif isinstance(codes, Index):
77
+ if codes.dtype != 'int64':
78
+ raise ValueError('not support for codes type:{}'.format(codes.dtype))
79
+ return self._data.loc[start_time:end_time].reindex(columns=codes)
80
+ elif isinstance(codes, str):
81
+ codes = [int(codes)]
82
+ return self._data.loc[start_time:end_time].reindex(columns=codes)
83
+ elif codes is None:
84
+ return self._data.loc[start_time:end_time, :]
85
+ else:
86
+ raise ValueError('not support for codes:{}'.format(codes))
87
+
88
+
89
+ def get_last_not_nan(df: pd.DataFrame):
90
+ sub_v = df.values
91
+ ix = len(df) - 1 - (~pd.isna(sub_v))[::-1].argmax(0)
92
+ return pd.Series(data=sub_v[ix, range(df.shape[1])], index=df.columns, dtype=object)
93
+
94
+
95
+ class BaseClient(object):
96
+ """加载数据key格式统一:fre/field"""
97
+
98
+ def __init__(self, path,
99
+ include930=True,
100
+ day_with_1500=False,
101
+ username=None,
102
+ password=None,
103
+ remote_servers='factorlib.irongliang.com',
104
+ https=True,
105
+ enable_remote=False,
106
+ format_column=None
107
+ ):
108
+ if isinstance(path, str) or path is None:
109
+ self.path = {'public': path}
110
+ elif isinstance(path, dict):
111
+ self.path = path
112
+ else:
113
+ raise ValueError('not support for path:{}'.format(path))
114
+ self.include930 = include930
115
+ self.day_with_1500 = day_with_1500
116
+ self.format_column = format_column
117
+ self._datas = {}
118
+ for key, path in self.path.items():
119
+ if path != '' and path is not None:
120
+ if not os.path.exists(path):
121
+ raise RuntimeError('data path {} not exist'.format(os.path.abspath(path)))
122
+ self.load_time = 0
123
+ self.load_count = 0
124
+ self.get_datas_time = 0
125
+ self.get_datas_count = 0
126
+
127
+ now_time = datetime.datetime.now()
128
+ if now_time.hour < 22:
129
+ self.validate_time = datetime.datetime.combine(now_time - datetime.timedelta(days=1),
130
+ datetime.time(10, 0, 0))
131
+ else:
132
+ self.validate_time = datetime.datetime.combine(now_time, datetime.time(10, 0, 0))
133
+ # load bar
134
+
135
+ self.enable_remote = enable_remote
136
+ if enable_remote:
137
+ none_num = [username, password, remote_servers].count(None)
138
+ if 1 < none_num < 3:
139
+ raise Exception('开启远程请求,必须提供 remove_servers、username、password 参数')
140
+ self.username = username
141
+ self.password = hashlib.md5(password.encode('utf-8')).hexdigest()
142
+ self.__server_list = []
143
+ self.__lock = threading.Lock()
144
+ self.__valid_url_index = 0
145
+ self.__token = None
146
+ protocol = 'https' if https else 'http'
147
+ for remote_server in remote_servers.split(','):
148
+ split = remote_server.split(':')
149
+ if len(split) != 2 and len(split) != 1:
150
+ raise Exception(f'错误的服务列表 {remote_server}')
151
+ ip = split[0]
152
+ if len(split) == 2:
153
+ port = split[1]
154
+ else:
155
+ port = '443' if https else '80'
156
+ if not port.isdigit():
157
+ raise Exception(f'错误的端口 {port}')
158
+ self.__server_list.append(f'{protocol}://{ip}:{port}')
159
+
160
+ close_data = self.get_data('2007-01-01', dt.datetime.today().strftime('%Y-%m-%d'), None, '01d/close', None, 1,
161
+ format_column=False)
162
+ # 按close时间戳作为交易日列表
163
+ self.trading_dates = pd.to_datetime(close_data.index, unit='ns').values
164
+
165
+ self.full_ticks = {}
166
+ # # 初始化daily,分钟的后续取数时再添加
167
+ freq = '01d'
168
+ self.full_ticks[freq] = utils.get_full_trade_tick(self.trading_dates, freq, include930=include930)
169
+
170
+ if format_column is not None:
171
+ code_columns = self.get_data('2007-01-01', dt.datetime.today().strftime('%Y-%m-%d'), None,
172
+ f'01d/{format_column}', None, 1, format_column=False)
173
+ self.codes = code_columns.columns.astype(int).tolist()
174
+ else:
175
+ self.codes = close_data.columns.astype(int).tolist()
176
+
177
+ def set_include930(self, include930):
178
+ self.include930 = include930
179
+
180
+ def check_validata_time(self):
181
+ now_time = datetime.datetime.now()
182
+ if (now_time - self.validate_time).total_seconds() > 24 * 3600:
183
+ self._datas.clear()
184
+ self.validate_time = datetime.datetime.combine(now_time, datetime.time(10, 0, 0))
185
+
186
+ def preloading(self, start_time, end_time, subscribe_data):
187
+ for freq, fields in subscribe_data.items():
188
+ t1 = time.time()
189
+ for field_in in fields:
190
+ splite_field = field_in.split('.')
191
+ if len(splite_field) == 2:
192
+ custom_key = splite_field[0]
193
+ field = splite_field[1]
194
+ else:
195
+ custom_key = 'public'
196
+ field = field_in
197
+ data_key = '{}/{}'.format(freq, field)
198
+ bar_key = '{}/{}'.format(freq, field_in)
199
+ self._datas[bar_key] = DayBarStore(self.path[custom_key], self.include930, data_key, start_time,
200
+ end_time)
201
+ self.load_time += time.time() - t1
202
+ self.load_count += 1
203
+
204
+ def check_time_limit(self, start_time, end_time, bar_store):
205
+ if start_time is not None:
206
+ if pd.to_datetime(start_time) < pd.to_datetime(bar_store.start_time):
207
+ return True
208
+ if end_time is not None:
209
+ if pd.to_datetime(end_time) > pd.to_datetime(bar_store.end_time):
210
+ return True
211
+
212
+ # if end_time is None:
213
+ # end_time = dt.datetime.today().strftime('%Y-%m-%d 15:00:00')
214
+ # else:
215
+ # end_time = pd.to_datetime(end_time).strftime('%Y-%m-%d 15:00:00')
216
+ # if start_time is None:
217
+ # start_time = '2007-01-01'
218
+ # if pd.to_datetime(start_time) < pd.to_datetime(bar_store.start_time):
219
+ # return True
220
+ # if pd.to_datetime(end_time) > pd.to_datetime(bar_store.end_time):
221
+ # return True
222
+ return False
223
+
224
+ def get_fin_data(self, start_time, end_time, field, quarter_shift=0, quarter=None, interval=1, codes=None,
225
+ publish_merge=None, quarter_merge=None, format_column=True, universe=None):
226
+ universe = self.__apply_universe(start_time, end_time, codes, universe)
227
+ fields = field
228
+ file_list = []
229
+ if not isinstance(field, list):
230
+ fields = [field]
231
+ for field in fields:
232
+ file_exist = False
233
+ for _, msg_path in self.path.items():
234
+ filepath = "{}/{}/{}.fea".format(msg_path, 'finance', field)
235
+ if not os.path.exists(filepath):
236
+ try:
237
+ filepath = utils.search_files(msg_path, 'finance', '{}.fea'.format(field))
238
+ file_list.append((field, filepath))
239
+ file_exist = True
240
+ break
241
+ except FileNotFoundError:
242
+ pass
243
+ else:
244
+ file_list.append((field, filepath))
245
+ file_exist = True
246
+ break
247
+ if not file_exist:
248
+ if self.enable_remote:
249
+ for field in fields:
250
+ df = self.__get_remote_fin_data(start_time, end_time, field, quarter_shift, quarter,
251
+ interval)
252
+ file_list.append((df, None))
253
+ else:
254
+ raise FileNotFoundError('{}.fea'.format(field))
255
+
256
+ merge_publish_df = None
257
+ merge_quarter_df = None
258
+ if publish_merge is not None:
259
+ if isinstance(publish_merge, pd.DataFrame):
260
+ merge_publish_df = publish_merge
261
+ else:
262
+ merge_publish_df = self.get_data('1993-01-01', datetime.datetime.now().strftime('%Y-%m-%d'), codes=None,
263
+ field=f'01d/{publish_merge}')
264
+ merge_publish_df.index = merge_publish_df.index.strftime('%Y-%m-%d')
265
+ elif quarter_merge is not None:
266
+ if isinstance(quarter_merge, pd.DataFrame):
267
+ merge_quarter_df = quarter_merge
268
+ else:
269
+ merge_quarter_df = self.get_data('1993-01-31', datetime.datetime.now().strftime('%Y-%m-%d'), codes=None,
270
+ field=f'01d/{quarter_merge}')
271
+ merge_quarter_df.index = merge_quarter_df.index.strftime('%Y-%m-%d')
272
+
273
+ result = None
274
+ for file_tuple in file_list:
275
+ if file_tuple[1] is not None:
276
+ single_df = self._get_fin_data(file_tuple[0], file_tuple[1], quarter_shift, start_time, quarter,
277
+ merge_publish_df, merge_quarter_df)
278
+ else:
279
+ single_df = file_tuple[0]
280
+ if result is None:
281
+ result = single_df
282
+ else:
283
+ union_columns = result.columns.union(single_df.columns)
284
+ result = result.reindex(columns=union_columns)
285
+ single_df = single_df.reindex(columns=union_columns)
286
+ result = single_df.fillna(result)
287
+ result = result.ffill()
288
+ result = result.loc[start_time:end_time, :]
289
+ result = result.bfill()
290
+ if interval > 1:
291
+ result = result[::interval]
292
+
293
+ if merge_publish_df is not None:
294
+ merge_publish_df.index = pd.to_datetime(merge_publish_df.index)
295
+ elif merge_quarter_df is not None:
296
+ merge_quarter_df.index = pd.to_datetime(merge_quarter_df.index)
297
+
298
+ if universe is not None:
299
+ result = result.reindex(columns=universe.columns)
300
+ universe = universe.reindex(index=result.index)
301
+ return result * universe
302
+ elif format_column and self.format_column is not None and codes is None:
303
+ # 如果需要按照统一codes进行format,在此执行
304
+ return result.reindex(columns=self.codes)
305
+
306
+ if isinstance(codes, (list, np.ndarray)):
307
+ codes = [int(code) for code in codes]
308
+ elif isinstance(codes, Index):
309
+ if codes.dtype != 'int64':
310
+ raise ValueError('not support for codes type:{}'.format(codes.dtype))
311
+ pass
312
+ elif isinstance(codes, str):
313
+ codes = [int(codes)]
314
+ elif codes is None:
315
+ return result
316
+ else:
317
+ raise ValueError('not support for codes:{}'.format(codes))
318
+
319
+ return result.reindex(columns=codes)
320
+
321
+ def _get_fin_data(self, field, filepath, quarter_shift, start_time, quarter, merge_publish_df, merge_quarter_df):
322
+ if field in self._datas.keys():
323
+ df = self._datas[field]
324
+ else:
325
+ df: pd.DataFrame = pd.read_feather(filepath, use_threads=False).set_index('trade_date')
326
+ df.columns = df.columns.astype(int)
327
+ df = df.sort_index(axis=1)
328
+ df.columns.name = 'code'
329
+ self._datas[field] = df
330
+
331
+ values = df.tail(1).squeeze()
332
+
333
+ quarter_map = {1: '0331', 2: '0630', 3: '0930', 4: '1231'}
334
+ fin_date = quarter_map[quarter] if quarter in quarter_map else None
335
+
336
+ path = self.get_path()
337
+ if isinstance(path, str):
338
+ trade_date_path = f'{path}/price01d/trade_date.fea'
339
+ else:
340
+ trade_date_path = f'{path["public"]}/price01d/trade_date.fea'
341
+ from src.rlquantdata.utilities.trade_date_utils import TradeDateUtils
342
+ tdu = TradeDateUtils(trade_date_path, self)
343
+
344
+ def at_data(data_df, label, trade_date):
345
+ if label not in data_df.columns:
346
+ return np.nan
347
+ trade_date = trade_date[:4] + "-" + trade_date[4:6] + "-" + trade_date[6:]
348
+ if not tdu.isBizDay('china.sse', trade_date):
349
+ trade_date = tdu.advanceDateByCalendar('china.sse', trade_date, '-1b').strftime('%Y-%m-%d')
350
+ if trade_date in data_df.index:
351
+ return data_df.at[trade_date, label]
352
+ else:
353
+ return np.nan
354
+
355
+ def get_fin(vv, label, fin_date=None, shift=0):
356
+ split_array = vv.split(':')
357
+ for i in range(len(split_array), 0, -2):
358
+ if fin_date is None:
359
+ idx = i - 1 - shift * 2
360
+ if idx < 0:
361
+ return np.nan
362
+ if merge_quarter_df is not None:
363
+ return at_data(merge_quarter_df, label, split_array[idx - 1])
364
+ return float(split_array[idx]) if split_array[idx] != '' else np.nan
365
+ elif split_array[i - 2].endswith(fin_date):
366
+ idx = i - 1 - shift * 2 * 4
367
+ if idx < 0:
368
+ return np.nan
369
+ if merge_quarter_df is not None:
370
+ return at_data(merge_quarter_df, label, split_array[idx - 1])
371
+ return float(split_array[idx]) if split_array[idx] != '' else np.nan
372
+
373
+ start_time = start_time.replace('-', '')
374
+ main_dict = {}
375
+ for label, item in values.items():
376
+ if item is None:
377
+ main_dict[label] = {}
378
+ continue
379
+
380
+ array = item.split(',')
381
+ label_dict = {}
382
+ for i in range(len(array), 0, -2):
383
+ cur_date = array[i - 2]
384
+ if merge_publish_df is not None:
385
+ label_dict[cur_date] = at_data(merge_publish_df, label, cur_date)
386
+ else:
387
+ label_dict[cur_date] = get_fin(array[i - 1], label, fin_date, quarter_shift)
388
+ if cur_date < start_time and not pd.isna(label_dict[cur_date]):
389
+ break
390
+
391
+ main_dict[label] = label_dict
392
+
393
+ # 生成完整的 df,可能包含节假日
394
+ result = pd.DataFrame(main_dict)
395
+ result.index = pd.to_datetime(result.index)
396
+ union_index = result.index.union(df.index)
397
+ result = result.reindex(union_index, fill_value=pd.NA)
398
+
399
+ # 获取节假日的 index
400
+ hol_index = result.index.difference(df.index)
401
+
402
+ # 获取连续节假日的第一天,得到第一天的索引,由该索引获得连续节假日的第一天的上一个交易日
403
+ start_of_continuous_segments = hol_index[
404
+ result.index.difference(df.index).to_series().diff() > pd.Timedelta(days=1)]
405
+ start_of_continuous_segments = pd.DatetimeIndex([hol_index[0]]).union(start_of_continuous_segments)
406
+ position = union_index.searchsorted(start_of_continuous_segments)
407
+ head_hol_size = 0
408
+ # 移除连续节假日
409
+ while True:
410
+ if position[head_hol_size] == head_hol_size:
411
+ head_hol_size += 1
412
+ else:
413
+ break
414
+ position = position[head_hol_size:] - 1
415
+ pre_biz_index = union_index[position]
416
+
417
+ # 将连续节假日的数据,先 ffill,再取连续节假日的最后一天,使得后面节假日的数据优先级高于前面的节假日
418
+ hol_df = result.loc[hol_index]
419
+ index_diff = hol_df.index.to_series().diff().dt.days.ne(1).cumsum()
420
+ hol_df['index_diff'] = index_diff
421
+ hol_df = hol_df.groupby('index_diff').ffill()
422
+ hol_df['index_diff'] = index_diff
423
+ hol_df = hol_df.groupby('index_diff').tail(1)
424
+ hol_df = hol_df.drop('index_diff', axis=1)
425
+
426
+ if head_hol_size != 0:
427
+ # 头几天为节假日,移除后再合并
428
+ # 将合并的节假日数据,与交易日数据合并,并给与交易日的 index
429
+ head_hol_row_df = hol_df.head(head_hol_size)
430
+ hol_df = hol_df.drop(hol_df.index[:head_hol_size])
431
+ hol_df = pd.DataFrame(
432
+ np.where(pd.isna(hol_df), result.loc[pre_biz_index], hol_df),
433
+ index=pre_biz_index,
434
+ columns=hol_df.columns
435
+ )
436
+ result = pd.concat([head_hol_row_df, result.reindex(df.index)])
437
+ result.update(hol_df)
438
+ result.iloc[:head_hol_size + 1] = result.iloc[:head_hol_size + 1].ffill()
439
+ result = result.drop(result.index[:head_hol_size])
440
+ result.index.name = 'trade_date'
441
+ else:
442
+ hol_df = pd.DataFrame(
443
+ np.where(pd.isna(hol_df), result.loc[pre_biz_index], hol_df),
444
+ index=pre_biz_index,
445
+ columns=hol_df.columns
446
+ )
447
+ result = result.reindex(df.index)
448
+ result.update(hol_df)
449
+
450
+ return result
451
+
452
+ def get_data(self, start_time=None, end_time=None,
453
+ codes=None, field='01d/close', bar_counts=None, interval=1, format_column=True, universe=None):
454
+ universe = self.__apply_universe(start_time, end_time, codes, universe)
455
+ t1 = time.time()
456
+ freq, fld = field.split('/', 1)
457
+ fre = re.sub(r'[^a-zA-Z]', '', freq)
458
+ try:
459
+ if field not in self._datas.keys():
460
+ print('no {} data source, now load it'.format(field))
461
+ self.preloading(start_time, end_time, {freq: [fld]})
462
+ elif self.check_time_limit(start_time, end_time, self._datas[field]):
463
+ self.preloading(start_time, end_time, {freq: [fld]})
464
+ except FileNotFoundError as e:
465
+ if self.enable_remote:
466
+ return self.__get_remote_data(start_time, end_time, codes, field, bar_counts, interval)
467
+ raise e
468
+
469
+ start_time, end_time = utils.format_time(start_time, end_time, fre)
470
+
471
+ # if end_time == None:
472
+ # end_time = dt.datetime.today().strftime('%Y%m%d')
473
+ if (start_time is None) or (end_time is None):
474
+ if freq.endswith('min'):
475
+ bars = self._datas[field].get_datas([], start_time, end_time, bar_counts, codes)
476
+ else:
477
+ bars = self._datas[field].get_datas(self.trading_dates, start_time, end_time, bar_counts, codes)
478
+ else:
479
+ if freq == '01d':
480
+ start_time_int = pd.to_datetime(pd.to_datetime(start_time).strftime('%Y-%m-%d 00:00:00'))
481
+ end_time_int = pd.to_datetime(pd.to_datetime(end_time).strftime('%Y-%m-%d 00:00:00'))
482
+ else:
483
+ start_time_int = pd.to_datetime(start_time)
484
+ end_time_int = pd.to_datetime(end_time)
485
+ bars = self._datas[field].get_range_datas(start_time_int, end_time_int, codes)
486
+ self.get_datas_time += time.time() - t1
487
+ self.get_datas_count += 1
488
+ if bar_counts is None and interval > 1:
489
+ bars = bars[::interval]
490
+
491
+ if universe is not None:
492
+ bars = bars.reindex(columns=universe.columns)
493
+ universe = universe.reindex(index=bars.index)
494
+ return bars * universe
495
+ # 如果需要按照统一codes进行format,在此执行
496
+ elif format_column and self.format_column is not None and codes is None:
497
+ bars = bars.reindex(columns=self.codes)
498
+ return bars
499
+
500
+ def del_data(self, field):
501
+ if field in self._datas.keys():
502
+ del self._datas[field]
503
+ gc.collect()
504
+
505
+ def get_path(self):
506
+ return self.path
507
+
508
+ def get_remote_hol_dates(self, from_date=None):
509
+ if self.enable_remote:
510
+ return self.__get_remote_hol_dates(from_date)
511
+ else:
512
+ return None
513
+
514
+ def __get_remote_hol_dates(self, from_date, relogin=False):
515
+ token = self.__login(relogin)
516
+ if token is None:
517
+ return None
518
+ if from_date is None:
519
+ from_date = ''
520
+ response = self.__request(f'api/factor/data/get_hol_dates/{from_date}', None, headers={'Authorization': token})
521
+ if response is not None:
522
+ stream_data = BytesIO()
523
+ for chunk in response.iter_content(chunk_size=1024):
524
+ if chunk:
525
+ stream_data.write(chunk)
526
+ stream_data.seek(0)
527
+ result = json.loads(stream_data.getvalue())
528
+ if result.get('code', 0) == -1 and not relogin:
529
+ return self.__get_remote_hol_dates(from_date, True)
530
+ elif result.get('code', None) is None:
531
+ return result.get('value')
532
+ else:
533
+ raise Exception(f'get data error, message: {result["msg"]}')
534
+ return None
535
+
536
+ def __get_remote_data(self, start_time, end_time, codes, field, bar_counts, interval, relogin=False, universe=None):
537
+ token = self.__login(relogin)
538
+ if token is None:
539
+ return None
540
+ t1 = time.time()
541
+ data_json = {
542
+ 'start_time': start_time, 'end_time': end_time, 'codes': codes, 'field': field,
543
+ 'bar_counts': bar_counts, 'interval': interval,
544
+ }
545
+ if isinstance(universe, str):
546
+ data_json['universe'] = universe
547
+ response = self.__request('api/factor/data/get_data', data_json, {'Authorization': token}, True)
548
+ if response is not None:
549
+ with BytesIO() as stream_data:
550
+ for chunk in response.iter_content(chunk_size=1024):
551
+ # 处理数据块
552
+ if chunk:
553
+ # 在这里自定义处理数据块的逻辑
554
+ stream_data.write(chunk)
555
+ stream_data.seek(0)
556
+ if stream_data.getvalue()[0] == 65:
557
+ data = pd.read_feather(stream_data).set_index('trade_date')
558
+ data.columns = data.columns.astype(int)
559
+ print(
560
+ f'get field {field} from remote, start time {start_time}, end time {end_time}, cost {time.time() - t1}')
561
+ else:
562
+ result = json.loads(stream_data.getvalue())
563
+ if result.get('code', 0) == -1 and not relogin:
564
+ data = self.__get_remote_data(start_time, end_time, codes, field, bar_counts, interval, True,
565
+ universe)
566
+ else:
567
+ raise Exception(f'get data error, message: {result["msg"]}')
568
+ if universe is not None and not isinstance(universe, str):
569
+ if codes is not None:
570
+ universe = universe.reindex(columns=codes)
571
+ data = data.reindex(columns=universe.columns)
572
+ universe = universe.reindex(index=data.index)
573
+ return data * universe
574
+ return data
575
+ return None
576
+
577
+ def __get_remote_fin_data(self, start_time, end_time, field, quarter_shift, quarter, interval, relogin=False,
578
+ universe=None):
579
+ token = self.__login(relogin)
580
+ if token is None:
581
+ return None
582
+ t1 = time.time()
583
+ data_json = {
584
+ 'start_time': start_time, 'end_time': end_time, 'field': field, 'quarter_shift': quarter_shift,
585
+ 'quarter': quarter, 'interval': interval,
586
+ }
587
+ if isinstance(universe, str):
588
+ data_json['universe'] = universe
589
+ response = self.__request('api/factor/data/get_fin_data', data_json, {'Authorization': token})
590
+ if response is not None:
591
+ with BytesIO() as stream_data:
592
+ for chunk in response.iter_content(chunk_size=1024):
593
+ # 处理数据块
594
+ if chunk:
595
+ # 在这里自定义处理数据块的逻辑
596
+ stream_data.write(chunk)
597
+ stream_data.seek(0)
598
+ if stream_data.getvalue()[0] == 65:
599
+ data = pd.read_feather(stream_data).set_index('trade_date')
600
+ data.columns = data.columns.astype(int)
601
+ print(
602
+ f'get field {field} from remote, start time {start_time}, end time {end_time}, cost {time.time() - t1}')
603
+ else:
604
+ result = json.loads(stream_data.getvalue())
605
+ if result.get('code', 0) == -1 and not relogin:
606
+ data = self.__get_remote_fin_data(start_time, end_time, field, quarter_shift, quarter, interval,
607
+ True, universe)
608
+ else:
609
+ raise Exception(f'get fin data error, message: {result["msg"]}')
610
+ if universe is not None and not isinstance(universe, str):
611
+ data = data.reindex(columns=universe.columns)
612
+ universe = universe.reindex(index=data.index)
613
+ return data * universe
614
+ return None
615
+
616
+ def __login(self, relogin=False):
617
+ if self.__token is None or relogin:
618
+ result = self.__request('api/user/login', {
619
+ 'username': self.username,
620
+ 'password': self.password,
621
+ })
622
+ if result is not None:
623
+ result = json.loads(result.text)
624
+ if result['success']:
625
+ self.__token = result['value']['token']
626
+ print(f'login success, role : {result["value"]["role"]}')
627
+ else:
628
+ print(f'login error, message: {result["msg"]}')
629
+
630
+ return self.__token
631
+
632
+ def __request(self, url_suffix, json, headers=None, stream=False):
633
+ self.__lock.acquire()
634
+ size = len(self.__server_list)
635
+ firs_index = self.__valid_url_index
636
+ resp = None
637
+ while True:
638
+ url_prefix = self.__server_list[self.__valid_url_index]
639
+ try:
640
+ resp = requests.post(f'{url_prefix}' + f'/{url_suffix}'.replace('//', '/'), json=json, headers=headers,
641
+ stream=stream)
642
+ if resp.status_code == 200:
643
+ return resp
644
+ else:
645
+ print(f'request error, http status {resp.status_code}')
646
+ return None
647
+ except Exception as e:
648
+ print(f'{self.__server_list[self.__valid_url_index]} invalid')
649
+ if firs_index != (self.__valid_url_index + 1) % size:
650
+ self.__valid_url_index = (self.__valid_url_index + 1) % size
651
+ else:
652
+ break
653
+ finally:
654
+ self.__lock.release()
655
+ return resp
656
+
657
+ def __apply_universe(self, start_time, end_time, codes, universe):
658
+ if universe is None:
659
+ return universe
660
+ if isinstance(universe, pd.DataFrame):
661
+ if codes is not None:
662
+ universe = universe.reindex(columns=codes)
663
+ return universe
664
+ if isinstance(universe, str):
665
+ return self.get_data(start_time, end_time, field=universe)
File without changes