1
0
mirror of https://github.com/microsoft/qlib.git synced 2026-07-17 17:34:35 +08:00

update handler & fix some bugs

This commit is contained in:
Young
2020-11-05 10:22:42 +00:00
parent 918a2b8a38
commit e37950d636
6 changed files with 78 additions and 33 deletions

View File

@@ -160,7 +160,7 @@ class Position:
def save_position(self, path, last_trade_date): def save_position(self, path, last_trade_date):
path = pathlib.Path(path) path = pathlib.Path(path)
p = copy.deepcopy(self.position) p = copy.deepcopy(self.position)
cash = pd.Series() cash = pd.Series(dtype=np.float)
cash["init_cash"] = self.init_cash cash["init_cash"] = self.init_cash
cash["cash"] = p["cash"] cash["cash"] = p["cash"]
cash["today_account_value"] = p["today_account_value"] cash["today_account_value"] = p["today_account_value"]

View File

@@ -7,7 +7,7 @@ from __future__ import print_function
import socketio import socketio
from .. import __version__ import qlib
from ..log import get_module_logger from ..log import get_module_logger
import pickle import pickle
@@ -59,7 +59,7 @@ class Client(object):
msg_queue: Queue msg_queue: Queue
The queue to pass the messsage after callback The queue to pass the messsage after callback
""" """
head_info = {"version": __version__} head_info = {"version": qlib.__version__}
def request_callback(*args): def request_callback(*args):
"""callback_wrapper """callback_wrapper

View File

@@ -640,7 +640,7 @@ class LocalFeatureProvider(FeatureProvider):
uri_data = self._uri_data.format(instrument.lower(), field, freq) uri_data = self._uri_data.format(instrument.lower(), field, freq)
if not os.path.exists(uri_data): if not os.path.exists(uri_data):
get_module_logger("data").warning("WARN: data not found for %s.%s" % (instrument, field)) get_module_logger("data").warning("WARN: data not found for %s.%s" % (instrument, field))
return pd.Series() return pd.Series(dtype=np.float32)
# raise ValueError('uri_data not found: ' + uri_data) # raise ValueError('uri_data not found: ' + uri_data)
# load # load
series = read_bin(uri_data, start_index, end_index) series = read_bin(uri_data, start_index, end_index)

View File

@@ -5,6 +5,7 @@
import abc import abc
import bisect import bisect
import logging import logging
import warnings
from typing import Union, Tuple, List, Iterator, Optional from typing import Union, Tuple, List, Iterator, Optional
import pandas as pd import pandas as pd
@@ -61,7 +62,9 @@ class DataHandler(Serializable):
# Setup data loader # Setup data loader
assert data_loader is not None # to make start_time end_time could have None default value assert data_loader is not None # to make start_time end_time could have None default value
self.data_loader = init_instance_by_config(data_loader, data_loader_module, accept_types=DataLoader) self.data_loader = init_instance_by_config(data_loader,
None if 'module_path' in data_loader else data_loader_module,
accept_types=DataLoader)
self.instruments = instruments self.instruments = instruments
self.start_time = start_time self.start_time = start_time
@@ -224,12 +227,12 @@ class DataHandlerLP(DataHandler):
# process type # process type
PTYPE_I = "independent" PTYPE_I = "independent"
# - _proc_infer_df will processed by infer_processors # - self._infer will processed by infer_processors
# - _proc_learn_df will be processed by learn_processors # - self._learn will be processed by learn_processors
PTYPE_A = "append" PTYPE_A = "append"
# - _proc_infer_df will processed by infer_processors # - self._infer will processed by infer_processors
# - _proc_learn_df will be processed by infer_processors + learn_processors # - self._learn will be processed by infer_processors + learn_processors
# - (e.g. _proc_infer_df processed by learn_processors ) # - (e.g. self._infer processed by learn_processors )
def __init__( def __init__(
self, self,
@@ -265,12 +268,12 @@ class DataHandlerLP(DataHandler):
process_type: str process_type: str
PTYPE_I = 'independent' PTYPE_I = 'independent'
- _proc_infer_df will processed by infer_processors - self._infer will processed by infer_processors
- _proc_learn_df will be processed by learn_processors - self._learn will be processed by learn_processors
PTYPE_A = 'append' PTYPE_A = 'append'
- _proc_infer_df will processed by infer_processors - self._infer will processed by infer_processors
- _proc_learn_df will be processed by infer_processors + learn_processors - self._learn will be processed by infer_processors + learn_processors
- (e.g. _proc_infer_df processed by learn_processors ) - (e.g. self._infer processed by learn_processors )
""" """
# Setup preprocessor # Setup preprocessor

View File

@@ -14,7 +14,6 @@ class DataLoader(abc.ABC):
""" """
DataLoader is designed for loading raw data from original data source. DataLoader is designed for loading raw data from original data source.
""" """
@abc.abstractmethod @abc.abstractmethod
def load(self, instruments, start_time=None, end_time=None) -> pd.DataFrame: def load(self, instruments, start_time=None, end_time=None) -> pd.DataFrame:
""" """
@@ -48,10 +47,13 @@ class DataLoader(abc.ABC):
pass pass
class QlibDataLoader(DataLoader): class DLWParser(DataLoader):
"""Same as QlibDataLoader. The fields can be define by config""" """
(D)ata(L)oader (W)ith (P)arser for features and names
def __init__(self, config: Tuple[list, tuple, dict], filter_pipe=None): Extracting this class so that QlibDataLoader and other dataloaders(such as QdbDataLoader) can share the fields
"""
def __init__(self, config: Tuple[list, tuple, dict]):
""" """
Parameters Parameters
---------- ----------
@@ -74,8 +76,6 @@ class QlibDataLoader(DataLoader):
else: else:
self.fields = self._parse_fields_info(config) self.fields = self._parse_fields_info(config)
self.filter_pipe = filter_pipe
def _parse_fields_info(self, fields_info: Tuple[list, tuple]) -> Tuple[list, list]: def _parse_fields_info(self, fields_info: Tuple[list, tuple]) -> Tuple[list, list]:
if isinstance(fields_info, list): if isinstance(fields_info, list):
exprs = names = fields_info exprs = names = fields_info
@@ -85,21 +85,62 @@ class QlibDataLoader(DataLoader):
raise NotImplementedError(f"This type of input is not supported") raise NotImplementedError(f"This type of input is not supported")
return exprs, names return exprs, names
def load(self, instruments, start_time=None, end_time=None) -> pd.DataFrame: @abc.abstractmethod
def load_group_df(self, instruments, exprs: list, names: list, start_time=None, end_time=None) -> pd.DataFrame:
"""
load the dataframe for specific group
Parameters
----------
instruments :
the instruments
exprs : list
The expressions to describe the content of the data
names : list
The name of the data
Returns
-------
pd.DataFrame:
the queried dataframe
"""
pass
def load(self, instruments=None, start_time=None, end_time=None) -> pd.DataFrame:
if self.is_group:
df = pd.concat(
{
grp: self.load_group_df(instruments, exprs, names, start_time, end_time)
for grp, (exprs, names) in self.fields.items()
},
axis=1)
else:
exprs, names = self.fields
df = self.load_group_df(instruments, exprs, names, start_time, end_time)
return df
class QlibDataLoader(DLWParser):
"""Same as QlibDataLoader. The fields can be define by config"""
def __init__(self, config: Tuple[list, tuple, dict], filter_pipe=None):
"""
Parameters
----------
config : Tuple[list, tuple, dict]
Please refer to the doc of DLWParser
filter_pipe :
Filter pipe for the instruments
"""
self.filter_pipe = filter_pipe
super().__init__(config)
def load_group_df(self, instruments, exprs: list, names: list, start_time=None, end_time=None) -> pd.DataFrame:
if isinstance(instruments, str): if isinstance(instruments, str):
instruments = D.instruments(instruments, filter_pipe=self.filter_pipe) instruments = D.instruments(instruments, filter_pipe=self.filter_pipe)
elif self.filter_pipe is not None: elif self.filter_pipe is not None:
warnings.warn("`filter_pipe` is not None, but it will not be used with `instruments` as list") warnings.warn("`filter_pipe` is not None, but it will not be used with `instruments` as list")
def _get_df(exprs, names): df = D.features(instruments, exprs, start_time, end_time)
df = D.features(instruments, exprs, start_time, end_time) df.columns = names
df.columns = names
return df
if self.is_group:
df = pd.concat({grp: _get_df(exprs, names) for grp, (exprs, names) in self.fields.items()}, axis=1)
else:
exprs, names = self.fields
df = _get_df(exprs, names)
df = df.swaplevel().sort_index() # NOTE: always return <datetime, instrument> df = df.swaplevel().sort_index() # NOTE: always return <datetime, instrument>
return df return df

View File

@@ -44,7 +44,7 @@ def read_bin(file_path, start_index, end_index):
ref_start_index = int(np.frombuffer(f.read(4), dtype="<f")[0]) ref_start_index = int(np.frombuffer(f.read(4), dtype="<f")[0])
si = max(ref_start_index, start_index) si = max(ref_start_index, start_index)
if si > end_index: if si > end_index:
return pd.Series() return pd.Series(np.float32)
# calculate offset # calculate offset
f.seek(4 * (si - ref_start_index) + 4) f.seek(4 * (si - ref_start_index) + 4)
# read nbytes # read nbytes
@@ -213,6 +213,7 @@ def init_instance_by_config(
"ClassName": getattr(module, config)() will be used. "ClassName": getattr(module, config)() will be used.
module : Python module module : Python module
Optional. It should be a python module. Optional. It should be a python module.
NOTE: the "module_path" will be override by `module` arguments
accept_types: Union[type, Tuple[type]] accept_types: Union[type, Tuple[type]]
Optional. If the config is a instance of specific type, return the config directly. Optional. If the config is a instance of specific type, return the config directly.