mirror of
https://github.com/microsoft/qlib.git
synced 2026-07-10 14:26:56 +08:00
Adjust rolling api (#1594)
* Intermediate version * Fix yaml template & Successfully run rolling * Be compatible with benchmark * Get same results with previous linear model * Black formatting * Update black * Update the placeholder mechanism * Update CI * Update CI * Upgrade Black * Fix CI and simplify code * Fix CI * Move the data processing caching mechanism into utils. * Adjusting DDG-DA * Organize import
This commit is contained in:
@@ -130,7 +130,6 @@ class MTSDatasetH(DatasetH):
|
||||
input_size=None,
|
||||
**kwargs,
|
||||
):
|
||||
|
||||
assert num_states == 0 or horizon > 0, "please specify `horizon` to avoid data leakage"
|
||||
assert memory_mode in ["sample", "daily"], "unsupported memory mode"
|
||||
assert memory_mode == "sample" or batch_size < 0, "daily memory requires daily sampling (`batch_size < 0`)"
|
||||
@@ -153,7 +152,6 @@ class MTSDatasetH(DatasetH):
|
||||
super().__init__(handler, segments, **kwargs)
|
||||
|
||||
def setup_data(self, handler_kwargs: dict = None, **kwargs):
|
||||
|
||||
super().setup_data(**kwargs)
|
||||
|
||||
if handler_kwargs is not None:
|
||||
@@ -288,7 +286,6 @@ class MTSDatasetH(DatasetH):
|
||||
daily_count = [] # store number of samples for each day
|
||||
|
||||
for j in indices[i : i + batch_size]:
|
||||
|
||||
# normal sampling: self.batch_size > 0 => slices is a list => slices_subset is a slice
|
||||
# daily sampling: self.batch_size < 0 => slices is a nested list => slices_subset is a list
|
||||
slices_subset = slices[j]
|
||||
@@ -297,7 +294,6 @@ class MTSDatasetH(DatasetH):
|
||||
# each slices_subset contains a list of slices for multiple stocks
|
||||
# NOTE: daily sampling is used in 1) eval mode, 2) train mode with self.batch_size < 0
|
||||
if self.batch_size < 0:
|
||||
|
||||
# store daily index
|
||||
idx = self._daily_index.index[j] # daily_index.index is the index of the original data
|
||||
daily_index.append(idx)
|
||||
@@ -320,7 +316,6 @@ class MTSDatasetH(DatasetH):
|
||||
slices_subset = [slices_subset]
|
||||
|
||||
for slc in slices_subset:
|
||||
|
||||
# legacy support for Alpha360 data by `input_size`
|
||||
if self.input_size:
|
||||
data.append(self._data[slc.stop - 1].reshape(self.input_size, -1).T)
|
||||
|
||||
@@ -17,7 +17,6 @@ class HighFreqHandler(DataHandlerLP):
|
||||
fit_end_time=None,
|
||||
drop_raw=True,
|
||||
):
|
||||
|
||||
infer_processors = check_transform_proc(infer_processors, fit_start_time, fit_end_time)
|
||||
learn_processors = check_transform_proc(learn_processors, fit_start_time, fit_end_time)
|
||||
|
||||
@@ -318,7 +317,6 @@ class HighFreqOrderHandler(DataHandlerLP):
|
||||
inst_processors=None,
|
||||
drop_raw=True,
|
||||
):
|
||||
|
||||
infer_processors = check_transform_proc(infer_processors, fit_start_time, fit_end_time)
|
||||
learn_processors = check_transform_proc(learn_processors, fit_start_time, fit_end_time)
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ class HighFreqNorm(Processor):
|
||||
feature_save_dir: str,
|
||||
norm_groups: Dict[str, int],
|
||||
):
|
||||
|
||||
self.fit_start_time = fit_start_time
|
||||
self.fit_end_time = fit_end_time
|
||||
self.feature_save_dir = feature_save_dir
|
||||
|
||||
@@ -49,6 +49,8 @@ class InternalData:
|
||||
|
||||
# 1) prepare the prediction of proxy models
|
||||
perf_task_tpl = deepcopy(self.task_tpl) # this task is supposed to contains no complicated objects
|
||||
# The only thing we want to save is the prediction
|
||||
perf_task_tpl["record"] = ["qlib.workflow.record_temp.SignalRecord"]
|
||||
|
||||
trainer = auto_filter_kwargs(trainer)(experiment_name=self.exp_name, **trainer_kwargs)
|
||||
# NOTE:
|
||||
|
||||
@@ -246,7 +246,6 @@ class ADARNN(Model):
|
||||
evals_result=dict(),
|
||||
save_path=None,
|
||||
):
|
||||
|
||||
df_train, df_valid = dataset.prepare(
|
||||
["train", "valid"],
|
||||
col_set=["feature", "label"],
|
||||
@@ -318,7 +317,6 @@ class ADARNN(Model):
|
||||
preds = []
|
||||
|
||||
for begin in range(sample_num)[:: self.batch_size]:
|
||||
|
||||
if sample_num - begin < self.batch_size:
|
||||
end = sample_num
|
||||
else:
|
||||
|
||||
@@ -146,7 +146,6 @@ class ALSTM(Model):
|
||||
raise ValueError("unknown loss `%s`" % self.loss)
|
||||
|
||||
def metric_fn(self, pred, label):
|
||||
|
||||
mask = torch.isfinite(label)
|
||||
|
||||
if self.metric in ("", "loss"):
|
||||
@@ -155,7 +154,6 @@ class ALSTM(Model):
|
||||
raise ValueError("unknown metric `%s`" % self.metric)
|
||||
|
||||
def train_epoch(self, x_train, y_train):
|
||||
|
||||
x_train_values = x_train.values
|
||||
y_train_values = np.squeeze(y_train.values)
|
||||
|
||||
@@ -165,7 +163,6 @@ class ALSTM(Model):
|
||||
np.random.shuffle(indices)
|
||||
|
||||
for i in range(len(indices))[:: self.batch_size]:
|
||||
|
||||
if len(indices) - i < self.batch_size:
|
||||
break
|
||||
|
||||
@@ -181,7 +178,6 @@ class ALSTM(Model):
|
||||
self.train_optimizer.step()
|
||||
|
||||
def test_epoch(self, data_x, data_y):
|
||||
|
||||
# prepare training data
|
||||
x_values = data_x.values
|
||||
y_values = np.squeeze(data_y.values)
|
||||
@@ -194,7 +190,6 @@ class ALSTM(Model):
|
||||
indices = np.arange(len(x_values))
|
||||
|
||||
for i in range(len(indices))[:: self.batch_size]:
|
||||
|
||||
if len(indices) - i < self.batch_size:
|
||||
break
|
||||
|
||||
@@ -217,7 +212,6 @@ class ALSTM(Model):
|
||||
evals_result=dict(),
|
||||
save_path=None,
|
||||
):
|
||||
|
||||
df_train, df_valid, df_test = dataset.prepare(
|
||||
["train", "valid", "test"],
|
||||
col_set=["feature", "label"],
|
||||
@@ -282,7 +276,6 @@ class ALSTM(Model):
|
||||
preds = []
|
||||
|
||||
for begin in range(sample_num)[:: self.batch_size]:
|
||||
|
||||
if sample_num - begin < self.batch_size:
|
||||
end = sample_num
|
||||
else:
|
||||
|
||||
@@ -156,7 +156,6 @@ class ALSTM(Model):
|
||||
raise ValueError("unknown loss `%s`" % self.loss)
|
||||
|
||||
def metric_fn(self, pred, label):
|
||||
|
||||
mask = torch.isfinite(label)
|
||||
|
||||
if self.metric in ("", "loss"):
|
||||
@@ -165,10 +164,9 @@ class ALSTM(Model):
|
||||
raise ValueError("unknown metric `%s`" % self.metric)
|
||||
|
||||
def train_epoch(self, data_loader):
|
||||
|
||||
self.ALSTM_model.train()
|
||||
|
||||
for (data, weight) in data_loader:
|
||||
for data, weight in data_loader:
|
||||
feature = data[:, :, 0:-1].to(self.device)
|
||||
label = data[:, -1, -1].to(self.device)
|
||||
|
||||
@@ -181,14 +179,12 @@ class ALSTM(Model):
|
||||
self.train_optimizer.step()
|
||||
|
||||
def test_epoch(self, data_loader):
|
||||
|
||||
self.ALSTM_model.eval()
|
||||
|
||||
scores = []
|
||||
losses = []
|
||||
|
||||
for (data, weight) in data_loader:
|
||||
|
||||
for data, weight in data_loader:
|
||||
feature = data[:, :, 0:-1].to(self.device)
|
||||
# feature[torch.isnan(feature)] = 0
|
||||
label = data[:, -1, -1].to(self.device)
|
||||
@@ -295,7 +291,6 @@ class ALSTM(Model):
|
||||
preds = []
|
||||
|
||||
for data in test_loader:
|
||||
|
||||
feature = data[:, :, 0:-1].to(self.device)
|
||||
|
||||
with torch.no_grad():
|
||||
|
||||
@@ -154,7 +154,6 @@ class GATs(Model):
|
||||
raise ValueError("unknown loss `%s`" % self.loss)
|
||||
|
||||
def metric_fn(self, pred, label):
|
||||
|
||||
mask = torch.isfinite(label)
|
||||
|
||||
if self.metric in ("", "loss"):
|
||||
@@ -175,7 +174,6 @@ class GATs(Model):
|
||||
return daily_index, daily_count
|
||||
|
||||
def train_epoch(self, x_train, y_train):
|
||||
|
||||
x_train_values = x_train.values
|
||||
y_train_values = np.squeeze(y_train.values)
|
||||
self.GAT_model.train()
|
||||
@@ -197,7 +195,6 @@ class GATs(Model):
|
||||
self.train_optimizer.step()
|
||||
|
||||
def test_epoch(self, data_x, data_y):
|
||||
|
||||
# prepare training data
|
||||
x_values = data_x.values
|
||||
y_values = np.squeeze(data_y.values)
|
||||
@@ -230,7 +227,6 @@ class GATs(Model):
|
||||
evals_result=dict(),
|
||||
save_path=None,
|
||||
):
|
||||
|
||||
df_train, df_valid, df_test = dataset.prepare(
|
||||
["train", "valid", "test"],
|
||||
col_set=["feature", "label"],
|
||||
|
||||
@@ -32,7 +32,6 @@ class DailyBatchSampler(Sampler):
|
||||
self.daily_index[0] = 0
|
||||
|
||||
def __iter__(self):
|
||||
|
||||
for idx, count in zip(self.daily_index, self.daily_count):
|
||||
yield np.arange(idx, idx + count)
|
||||
|
||||
@@ -173,7 +172,6 @@ class GATs(Model):
|
||||
raise ValueError("unknown loss `%s`" % self.loss)
|
||||
|
||||
def metric_fn(self, pred, label):
|
||||
|
||||
mask = torch.isfinite(label)
|
||||
|
||||
if self.metric in ("", "loss"):
|
||||
@@ -194,11 +192,9 @@ class GATs(Model):
|
||||
return daily_index, daily_count
|
||||
|
||||
def train_epoch(self, data_loader):
|
||||
|
||||
self.GAT_model.train()
|
||||
|
||||
for data in data_loader:
|
||||
|
||||
data = data.squeeze()
|
||||
feature = data[:, :, 0:-1].to(self.device)
|
||||
label = data[:, -1, -1].to(self.device)
|
||||
@@ -212,14 +208,12 @@ class GATs(Model):
|
||||
self.train_optimizer.step()
|
||||
|
||||
def test_epoch(self, data_loader):
|
||||
|
||||
self.GAT_model.eval()
|
||||
|
||||
scores = []
|
||||
losses = []
|
||||
|
||||
for data in data_loader:
|
||||
|
||||
data = data.squeeze()
|
||||
feature = data[:, :, 0:-1].to(self.device)
|
||||
# feature[torch.isnan(feature)] = 0
|
||||
@@ -240,7 +234,6 @@ class GATs(Model):
|
||||
evals_result=dict(),
|
||||
save_path=None,
|
||||
):
|
||||
|
||||
dl_train = dataset.prepare("train", col_set=["feature", "label"], data_key=DataHandlerLP.DK_L)
|
||||
dl_valid = dataset.prepare("valid", col_set=["feature", "label"], data_key=DataHandlerLP.DK_L)
|
||||
if dl_train.empty or dl_valid.empty:
|
||||
@@ -329,7 +322,6 @@ class GATs(Model):
|
||||
preds = []
|
||||
|
||||
for data in test_loader:
|
||||
|
||||
data = data.squeeze()
|
||||
feature = data[:, :, 0:-1].to(self.device)
|
||||
|
||||
|
||||
@@ -146,7 +146,6 @@ class GRU(Model):
|
||||
raise ValueError("unknown loss `%s`" % self.loss)
|
||||
|
||||
def metric_fn(self, pred, label):
|
||||
|
||||
mask = torch.isfinite(label)
|
||||
|
||||
if self.metric in ("", "loss"):
|
||||
@@ -155,7 +154,6 @@ class GRU(Model):
|
||||
raise ValueError("unknown metric `%s`" % self.metric)
|
||||
|
||||
def train_epoch(self, x_train, y_train):
|
||||
|
||||
x_train_values = x_train.values
|
||||
y_train_values = np.squeeze(y_train.values)
|
||||
|
||||
@@ -165,7 +163,6 @@ class GRU(Model):
|
||||
np.random.shuffle(indices)
|
||||
|
||||
for i in range(len(indices))[:: self.batch_size]:
|
||||
|
||||
if len(indices) - i < self.batch_size:
|
||||
break
|
||||
|
||||
@@ -181,7 +178,6 @@ class GRU(Model):
|
||||
self.train_optimizer.step()
|
||||
|
||||
def test_epoch(self, data_x, data_y):
|
||||
|
||||
# prepare training data
|
||||
x_values = data_x.values
|
||||
y_values = np.squeeze(data_y.values)
|
||||
@@ -194,7 +190,6 @@ class GRU(Model):
|
||||
indices = np.arange(len(x_values))
|
||||
|
||||
for i in range(len(indices))[:: self.batch_size]:
|
||||
|
||||
if len(indices) - i < self.batch_size:
|
||||
break
|
||||
|
||||
@@ -217,7 +212,6 @@ class GRU(Model):
|
||||
evals_result=dict(),
|
||||
save_path=None,
|
||||
):
|
||||
|
||||
df_train, df_valid, df_test = dataset.prepare(
|
||||
["train", "valid", "test"],
|
||||
col_set=["feature", "label"],
|
||||
@@ -282,7 +276,6 @@ class GRU(Model):
|
||||
preds = []
|
||||
|
||||
for begin in range(sample_num)[:: self.batch_size]:
|
||||
|
||||
if sample_num - begin < self.batch_size:
|
||||
end = sample_num
|
||||
else:
|
||||
|
||||
@@ -154,7 +154,6 @@ class GRU(Model):
|
||||
raise ValueError("unknown loss `%s`" % self.loss)
|
||||
|
||||
def metric_fn(self, pred, label):
|
||||
|
||||
mask = torch.isfinite(label)
|
||||
|
||||
if self.metric in ("", "loss"):
|
||||
@@ -163,10 +162,9 @@ class GRU(Model):
|
||||
raise ValueError("unknown metric `%s`" % self.metric)
|
||||
|
||||
def train_epoch(self, data_loader):
|
||||
|
||||
self.GRU_model.train()
|
||||
|
||||
for (data, weight) in data_loader:
|
||||
for data, weight in data_loader:
|
||||
feature = data[:, :, 0:-1].to(self.device)
|
||||
label = data[:, -1, -1].to(self.device)
|
||||
|
||||
@@ -179,14 +177,12 @@ class GRU(Model):
|
||||
self.train_optimizer.step()
|
||||
|
||||
def test_epoch(self, data_loader):
|
||||
|
||||
self.GRU_model.eval()
|
||||
|
||||
scores = []
|
||||
losses = []
|
||||
|
||||
for (data, weight) in data_loader:
|
||||
|
||||
for data, weight in data_loader:
|
||||
feature = data[:, :, 0:-1].to(self.device)
|
||||
# feature[torch.isnan(feature)] = 0
|
||||
label = data[:, -1, -1].to(self.device)
|
||||
@@ -293,7 +289,6 @@ class GRU(Model):
|
||||
preds = []
|
||||
|
||||
for data in test_loader:
|
||||
|
||||
feature = data[:, :, 0:-1].to(self.device)
|
||||
|
||||
with torch.no_grad():
|
||||
|
||||
@@ -160,7 +160,6 @@ class HIST(Model):
|
||||
raise ValueError("unknown loss `%s`" % self.loss)
|
||||
|
||||
def metric_fn(self, pred, label):
|
||||
|
||||
mask = torch.isfinite(label)
|
||||
|
||||
if self.metric == "ic":
|
||||
@@ -189,7 +188,6 @@ class HIST(Model):
|
||||
return daily_index, daily_count
|
||||
|
||||
def train_epoch(self, x_train, y_train, stock_index):
|
||||
|
||||
stock2concept_matrix = np.load(self.stock2concept)
|
||||
x_train_values = x_train.values
|
||||
y_train_values = np.squeeze(y_train.values)
|
||||
@@ -214,7 +212,6 @@ class HIST(Model):
|
||||
self.train_optimizer.step()
|
||||
|
||||
def test_epoch(self, data_x, data_y, stock_index):
|
||||
|
||||
# prepare training data
|
||||
stock2concept_matrix = np.load(self.stock2concept)
|
||||
x_values = data_x.values
|
||||
|
||||
@@ -153,7 +153,6 @@ class IGMTF(Model):
|
||||
raise ValueError("unknown loss `%s`" % self.loss)
|
||||
|
||||
def metric_fn(self, pred, label):
|
||||
|
||||
mask = torch.isfinite(label)
|
||||
|
||||
if self.metric == "ic":
|
||||
@@ -201,7 +200,6 @@ class IGMTF(Model):
|
||||
return train_hidden, train_hidden_day
|
||||
|
||||
def train_epoch(self, x_train, y_train, train_hidden, train_hidden_day):
|
||||
|
||||
x_train_values = x_train.values
|
||||
y_train_values = np.squeeze(y_train.values)
|
||||
|
||||
@@ -222,7 +220,6 @@ class IGMTF(Model):
|
||||
self.train_optimizer.step()
|
||||
|
||||
def test_epoch(self, data_x, data_y, train_hidden, train_hidden_day):
|
||||
|
||||
# prepare training data
|
||||
x_values = data_x.values
|
||||
y_values = np.squeeze(data_y.values)
|
||||
@@ -254,7 +251,6 @@ class IGMTF(Model):
|
||||
evals_result=dict(),
|
||||
save_path=None,
|
||||
):
|
||||
|
||||
df_train, df_valid = dataset.prepare(
|
||||
["train", "valid"],
|
||||
col_set=["feature", "label"],
|
||||
|
||||
@@ -46,7 +46,6 @@ class LocalformerModel(Model):
|
||||
seed=None,
|
||||
**kwargs
|
||||
):
|
||||
|
||||
# set hyper-parameters.
|
||||
self.d_model = d_model
|
||||
self.dropout = dropout
|
||||
@@ -96,7 +95,6 @@ class LocalformerModel(Model):
|
||||
raise ValueError("unknown loss `%s`" % self.loss)
|
||||
|
||||
def metric_fn(self, pred, label):
|
||||
|
||||
mask = torch.isfinite(label)
|
||||
|
||||
if self.metric in ("", "loss"):
|
||||
@@ -105,7 +103,6 @@ class LocalformerModel(Model):
|
||||
raise ValueError("unknown metric `%s`" % self.metric)
|
||||
|
||||
def train_epoch(self, x_train, y_train):
|
||||
|
||||
x_train_values = x_train.values
|
||||
y_train_values = np.squeeze(y_train.values)
|
||||
|
||||
@@ -115,7 +112,6 @@ class LocalformerModel(Model):
|
||||
np.random.shuffle(indices)
|
||||
|
||||
for i in range(len(indices))[:: self.batch_size]:
|
||||
|
||||
if len(indices) - i < self.batch_size:
|
||||
break
|
||||
|
||||
@@ -131,7 +127,6 @@ class LocalformerModel(Model):
|
||||
self.train_optimizer.step()
|
||||
|
||||
def test_epoch(self, data_x, data_y):
|
||||
|
||||
# prepare training data
|
||||
x_values = data_x.values
|
||||
y_values = np.squeeze(data_y.values)
|
||||
@@ -144,7 +139,6 @@ class LocalformerModel(Model):
|
||||
indices = np.arange(len(x_values))
|
||||
|
||||
for i in range(len(indices))[:: self.batch_size]:
|
||||
|
||||
if len(indices) - i < self.batch_size:
|
||||
break
|
||||
|
||||
@@ -167,7 +161,6 @@ class LocalformerModel(Model):
|
||||
evals_result=dict(),
|
||||
save_path=None,
|
||||
):
|
||||
|
||||
df_train, df_valid, df_test = dataset.prepare(
|
||||
["train", "valid", "test"],
|
||||
col_set=["feature", "label"],
|
||||
@@ -232,7 +225,6 @@ class LocalformerModel(Model):
|
||||
preds = []
|
||||
|
||||
for begin in range(sample_num)[:: self.batch_size]:
|
||||
|
||||
if sample_num - begin < self.batch_size:
|
||||
end = sample_num
|
||||
else:
|
||||
|
||||
@@ -44,7 +44,6 @@ class LocalformerModel(Model):
|
||||
seed=None,
|
||||
**kwargs
|
||||
):
|
||||
|
||||
# set hyper-parameters.
|
||||
self.d_model = d_model
|
||||
self.dropout = dropout
|
||||
@@ -96,7 +95,6 @@ class LocalformerModel(Model):
|
||||
raise ValueError("unknown loss `%s`" % self.loss)
|
||||
|
||||
def metric_fn(self, pred, label):
|
||||
|
||||
mask = torch.isfinite(label)
|
||||
|
||||
if self.metric in ("", "loss"):
|
||||
@@ -105,7 +103,6 @@ class LocalformerModel(Model):
|
||||
raise ValueError("unknown metric `%s`" % self.metric)
|
||||
|
||||
def train_epoch(self, data_loader):
|
||||
|
||||
self.model.train()
|
||||
|
||||
for data in data_loader:
|
||||
@@ -121,14 +118,12 @@ class LocalformerModel(Model):
|
||||
self.train_optimizer.step()
|
||||
|
||||
def test_epoch(self, data_loader):
|
||||
|
||||
self.model.eval()
|
||||
|
||||
scores = []
|
||||
losses = []
|
||||
|
||||
for data in data_loader:
|
||||
|
||||
feature = data[:, :, 0:-1].to(self.device)
|
||||
label = data[:, -1, -1].to(self.device)
|
||||
|
||||
@@ -148,7 +143,6 @@ class LocalformerModel(Model):
|
||||
evals_result=dict(),
|
||||
save_path=None,
|
||||
):
|
||||
|
||||
dl_train = dataset.prepare("train", col_set=["feature", "label"], data_key=DataHandlerLP.DK_L)
|
||||
dl_valid = dataset.prepare("valid", col_set=["feature", "label"], data_key=DataHandlerLP.DK_L)
|
||||
if dl_train.empty or dl_valid.empty:
|
||||
|
||||
@@ -142,7 +142,6 @@ class LSTM(Model):
|
||||
raise ValueError("unknown loss `%s`" % self.loss)
|
||||
|
||||
def metric_fn(self, pred, label):
|
||||
|
||||
mask = torch.isfinite(label)
|
||||
|
||||
if self.metric in ("", "loss"):
|
||||
@@ -151,7 +150,6 @@ class LSTM(Model):
|
||||
raise ValueError("unknown metric `%s`" % self.metric)
|
||||
|
||||
def train_epoch(self, x_train, y_train):
|
||||
|
||||
x_train_values = x_train.values
|
||||
y_train_values = np.squeeze(y_train.values)
|
||||
|
||||
@@ -161,7 +159,6 @@ class LSTM(Model):
|
||||
np.random.shuffle(indices)
|
||||
|
||||
for i in range(len(indices))[:: self.batch_size]:
|
||||
|
||||
if len(indices) - i < self.batch_size:
|
||||
break
|
||||
|
||||
@@ -177,7 +174,6 @@ class LSTM(Model):
|
||||
self.train_optimizer.step()
|
||||
|
||||
def test_epoch(self, data_x, data_y):
|
||||
|
||||
# prepare training data
|
||||
x_values = data_x.values
|
||||
y_values = np.squeeze(data_y.values)
|
||||
@@ -190,7 +186,6 @@ class LSTM(Model):
|
||||
indices = np.arange(len(x_values))
|
||||
|
||||
for i in range(len(indices))[:: self.batch_size]:
|
||||
|
||||
if len(indices) - i < self.batch_size:
|
||||
break
|
||||
|
||||
@@ -212,7 +207,6 @@ class LSTM(Model):
|
||||
evals_result=dict(),
|
||||
save_path=None,
|
||||
):
|
||||
|
||||
df_train, df_valid, df_test = dataset.prepare(
|
||||
["train", "valid", "test"],
|
||||
col_set=["feature", "label"],
|
||||
|
||||
@@ -150,7 +150,6 @@ class LSTM(Model):
|
||||
raise ValueError("unknown loss `%s`" % self.loss)
|
||||
|
||||
def metric_fn(self, pred, label):
|
||||
|
||||
mask = torch.isfinite(label)
|
||||
|
||||
if self.metric in ("", "loss"):
|
||||
@@ -159,10 +158,9 @@ class LSTM(Model):
|
||||
raise ValueError("unknown metric `%s`" % self.metric)
|
||||
|
||||
def train_epoch(self, data_loader):
|
||||
|
||||
self.LSTM_model.train()
|
||||
|
||||
for (data, weight) in data_loader:
|
||||
for data, weight in data_loader:
|
||||
feature = data[:, :, 0:-1].to(self.device)
|
||||
label = data[:, -1, -1].to(self.device)
|
||||
|
||||
@@ -175,14 +173,12 @@ class LSTM(Model):
|
||||
self.train_optimizer.step()
|
||||
|
||||
def test_epoch(self, data_loader):
|
||||
|
||||
self.LSTM_model.eval()
|
||||
|
||||
scores = []
|
||||
losses = []
|
||||
|
||||
for (data, weight) in data_loader:
|
||||
|
||||
for data, weight in data_loader:
|
||||
feature = data[:, :, 0:-1].to(self.device)
|
||||
# feature[torch.isnan(feature)] = 0
|
||||
label = data[:, -1, -1].to(self.device)
|
||||
@@ -288,7 +284,6 @@ class LSTM(Model):
|
||||
preds = []
|
||||
|
||||
for data in test_loader:
|
||||
|
||||
feature = data[:, :, 0:-1].to(self.device)
|
||||
|
||||
with torch.no_grad():
|
||||
|
||||
@@ -306,7 +306,6 @@ class SFM(Model):
|
||||
return self.device != torch.device("cpu")
|
||||
|
||||
def test_epoch(self, data_x, data_y):
|
||||
|
||||
# prepare training data
|
||||
x_values = data_x.values
|
||||
y_values = np.squeeze(data_y.values)
|
||||
@@ -319,7 +318,6 @@ class SFM(Model):
|
||||
indices = np.arange(len(x_values))
|
||||
|
||||
for i in range(len(indices))[:: self.batch_size]:
|
||||
|
||||
if len(indices) - i < self.batch_size:
|
||||
break
|
||||
|
||||
@@ -336,7 +334,6 @@ class SFM(Model):
|
||||
return np.mean(losses), np.mean(scores)
|
||||
|
||||
def train_epoch(self, x_train, y_train):
|
||||
|
||||
x_train_values = x_train.values
|
||||
y_train_values = np.squeeze(y_train.values)
|
||||
|
||||
@@ -346,7 +343,6 @@ class SFM(Model):
|
||||
np.random.shuffle(indices)
|
||||
|
||||
for i in range(len(indices))[:: self.batch_size]:
|
||||
|
||||
if len(indices) - i < self.batch_size:
|
||||
break
|
||||
|
||||
@@ -367,7 +363,6 @@ class SFM(Model):
|
||||
evals_result=dict(),
|
||||
save_path=None,
|
||||
):
|
||||
|
||||
df_train, df_valid = dataset.prepare(
|
||||
["train", "valid"],
|
||||
col_set=["feature", "label"],
|
||||
@@ -431,7 +426,6 @@ class SFM(Model):
|
||||
raise ValueError("unknown loss `%s`" % self.loss)
|
||||
|
||||
def metric_fn(self, pred, label):
|
||||
|
||||
mask = torch.isfinite(label)
|
||||
|
||||
if self.metric in ("", "loss"):
|
||||
|
||||
@@ -256,7 +256,6 @@ class TabnetModel(Model):
|
||||
indices = np.arange(len(x_values))
|
||||
|
||||
for i in range(len(indices))[:: self.batch_size]:
|
||||
|
||||
if len(indices) - i < self.batch_size:
|
||||
break
|
||||
feature = x_values[indices[i : i + self.batch_size]].float().to(self.device)
|
||||
@@ -283,7 +282,6 @@ class TabnetModel(Model):
|
||||
np.random.shuffle(indices)
|
||||
|
||||
for i in range(len(indices))[:: self.batch_size]:
|
||||
|
||||
if len(indices) - i < self.batch_size:
|
||||
break
|
||||
|
||||
@@ -308,7 +306,6 @@ class TabnetModel(Model):
|
||||
self.tabnet_decoder.train()
|
||||
|
||||
for i in range(len(indices))[:: self.batch_size]:
|
||||
|
||||
if len(indices) - i < self.batch_size:
|
||||
break
|
||||
|
||||
@@ -339,7 +336,6 @@ class TabnetModel(Model):
|
||||
losses = []
|
||||
|
||||
for i in range(len(indices))[:: self.batch_size]:
|
||||
|
||||
if len(indices) - i < self.batch_size:
|
||||
break
|
||||
|
||||
|
||||
@@ -154,7 +154,6 @@ class TCN(Model):
|
||||
raise ValueError("unknown loss `%s`" % self.loss)
|
||||
|
||||
def metric_fn(self, pred, label):
|
||||
|
||||
mask = torch.isfinite(label)
|
||||
|
||||
if self.metric in ("", "loss"):
|
||||
@@ -163,7 +162,6 @@ class TCN(Model):
|
||||
raise ValueError("unknown metric `%s`" % self.metric)
|
||||
|
||||
def train_epoch(self, x_train, y_train):
|
||||
|
||||
x_train_values = x_train.values
|
||||
y_train_values = np.squeeze(y_train.values)
|
||||
|
||||
@@ -173,7 +171,6 @@ class TCN(Model):
|
||||
np.random.shuffle(indices)
|
||||
|
||||
for i in range(len(indices))[:: self.batch_size]:
|
||||
|
||||
if len(indices) - i < self.batch_size:
|
||||
break
|
||||
|
||||
@@ -200,7 +197,6 @@ class TCN(Model):
|
||||
indices = np.arange(len(x_values))
|
||||
|
||||
for i in range(len(indices))[:: self.batch_size]:
|
||||
|
||||
if len(indices) - i < self.batch_size:
|
||||
break
|
||||
|
||||
@@ -223,7 +219,6 @@ class TCN(Model):
|
||||
evals_result=dict(),
|
||||
save_path=None,
|
||||
):
|
||||
|
||||
df_train, df_valid, df_test = dataset.prepare(
|
||||
["train", "valid", "test"],
|
||||
col_set=["feature", "label"],
|
||||
@@ -286,7 +281,6 @@ class TCN(Model):
|
||||
preds = []
|
||||
|
||||
for begin in range(sample_num)[:: self.batch_size]:
|
||||
|
||||
if sample_num - begin < self.batch_size:
|
||||
end = sample_num
|
||||
else:
|
||||
|
||||
@@ -155,7 +155,6 @@ class TCN(Model):
|
||||
raise ValueError("unknown loss `%s`" % self.loss)
|
||||
|
||||
def metric_fn(self, pred, label):
|
||||
|
||||
mask = torch.isfinite(label)
|
||||
|
||||
if self.metric in ("", "loss"):
|
||||
@@ -164,7 +163,6 @@ class TCN(Model):
|
||||
raise ValueError("unknown metric `%s`" % self.metric)
|
||||
|
||||
def train_epoch(self, data_loader):
|
||||
|
||||
self.TCN_model.train()
|
||||
|
||||
for data in data_loader:
|
||||
@@ -181,7 +179,6 @@ class TCN(Model):
|
||||
self.train_optimizer.step()
|
||||
|
||||
def test_epoch(self, data_loader):
|
||||
|
||||
self.TCN_model.eval()
|
||||
|
||||
scores = []
|
||||
@@ -277,7 +274,6 @@ class TCN(Model):
|
||||
preds = []
|
||||
|
||||
for data in test_loader:
|
||||
|
||||
feature = data[:, :, 0:-1].to(self.device)
|
||||
|
||||
with torch.no_grad():
|
||||
|
||||
@@ -119,7 +119,6 @@ class TCTS(Model):
|
||||
)
|
||||
|
||||
def loss_fn(self, pred, label, weight):
|
||||
|
||||
if self.mode == "hard":
|
||||
loc = torch.argmax(weight, 1)
|
||||
loss = (pred - label[np.arange(weight.shape[0]), loc]) ** 2
|
||||
@@ -157,7 +156,6 @@ class TCTS(Model):
|
||||
|
||||
for i in range(self.steps):
|
||||
for i in range(len(indices))[:: self.batch_size]:
|
||||
|
||||
if len(indices) - i < self.batch_size:
|
||||
break
|
||||
|
||||
@@ -191,7 +189,6 @@ class TCTS(Model):
|
||||
|
||||
# fix forecasting model and valid weight model
|
||||
for i in range(len(indices))[:: self.batch_size]:
|
||||
|
||||
if len(indices) - i < self.batch_size:
|
||||
break
|
||||
|
||||
@@ -212,7 +209,6 @@ class TCTS(Model):
|
||||
self.weight_optimizer.step()
|
||||
|
||||
def test_epoch(self, data_x, data_y):
|
||||
|
||||
# prepare training data
|
||||
x_values = data_x.values
|
||||
y_values = np.squeeze(data_y.values)
|
||||
@@ -224,7 +220,6 @@ class TCTS(Model):
|
||||
indices = np.arange(len(x_values))
|
||||
|
||||
for i in range(len(indices))[:: self.batch_size]:
|
||||
|
||||
if len(indices) - i < self.batch_size:
|
||||
break
|
||||
|
||||
@@ -282,7 +277,6 @@ class TCTS(Model):
|
||||
verbose=True,
|
||||
save_path=None,
|
||||
):
|
||||
|
||||
self.fore_model = GRUModel(
|
||||
d_feat=self.d_feat,
|
||||
hidden_size=self.hidden_size,
|
||||
@@ -366,7 +360,6 @@ class TCTS(Model):
|
||||
preds = []
|
||||
|
||||
for begin in range(sample_num)[:: self.batch_size]:
|
||||
|
||||
if sample_num - begin < self.batch_size:
|
||||
end = sample_num
|
||||
else:
|
||||
|
||||
@@ -84,7 +84,6 @@ class TRAModel(Model):
|
||||
transport_method="none",
|
||||
memory_mode="sample",
|
||||
):
|
||||
|
||||
self.logger = get_module_logger("TRA")
|
||||
|
||||
assert memory_mode in ["sample", "daily"], "invalid memory mode"
|
||||
@@ -136,7 +135,6 @@ class TRAModel(Model):
|
||||
self._init_model()
|
||||
|
||||
def _init_model(self):
|
||||
|
||||
self.logger.info("init TRAModel...")
|
||||
|
||||
self.model = eval(self.model_type)(**self.model_config).to(device)
|
||||
@@ -176,7 +174,6 @@ class TRAModel(Model):
|
||||
self.global_step = -1
|
||||
|
||||
def train_epoch(self, epoch, data_set, is_pretrain=False):
|
||||
|
||||
self.model.train()
|
||||
self.tra.train()
|
||||
data_set.train()
|
||||
@@ -274,7 +271,6 @@ class TRAModel(Model):
|
||||
return total_loss
|
||||
|
||||
def test_epoch(self, epoch, data_set, return_pred=False, prefix="test", is_pretrain=False):
|
||||
|
||||
self.model.eval()
|
||||
self.tra.eval()
|
||||
data_set.eval()
|
||||
@@ -360,7 +356,6 @@ class TRAModel(Model):
|
||||
return metrics, preds, probs, P_all
|
||||
|
||||
def _fit(self, train_set, valid_set, test_set, evals_result, is_pretrain=True):
|
||||
|
||||
best_score = -1
|
||||
best_epoch = 0
|
||||
stop_rounds = 0
|
||||
@@ -419,7 +414,6 @@ class TRAModel(Model):
|
||||
return best_score
|
||||
|
||||
def fit(self, dataset, evals_result=dict()):
|
||||
|
||||
assert isinstance(dataset, MTSDatasetH), "TRAModel only supports `qlib.contrib.data.dataset.MTSDatasetH`"
|
||||
|
||||
train_set, valid_set, test_set = dataset.prepare(["train", "valid", "test"])
|
||||
@@ -503,7 +497,6 @@ class TRAModel(Model):
|
||||
json.dump(info, f)
|
||||
|
||||
def predict(self, dataset, segment="test"):
|
||||
|
||||
assert isinstance(dataset, MTSDatasetH), "TRAModel only supports `qlib.contrib.data.dataset.MTSDatasetH`"
|
||||
|
||||
if not self.fitted:
|
||||
@@ -571,7 +564,6 @@ class RNN(nn.Module):
|
||||
self.output_size = hidden_size
|
||||
|
||||
def forward(self, x):
|
||||
|
||||
if self.input_proj is not None:
|
||||
x = self.input_proj(x)
|
||||
|
||||
@@ -647,7 +639,6 @@ class Transformer(nn.Module):
|
||||
self.output_size = hidden_size
|
||||
|
||||
def forward(self, x):
|
||||
|
||||
x = x.permute(1, 0, 2).contiguous() # the first dim need to be time
|
||||
x = self.pe(x)
|
||||
|
||||
@@ -713,7 +704,6 @@ class TRA(nn.Module):
|
||||
child.reset_parameters()
|
||||
|
||||
def forward(self, hidden, hist_loss):
|
||||
|
||||
preds = self.predictors(hidden)
|
||||
|
||||
if self.num_states == 1: # no need for router when having only one prediction
|
||||
|
||||
@@ -45,7 +45,6 @@ class TransformerModel(Model):
|
||||
seed=None,
|
||||
**kwargs
|
||||
):
|
||||
|
||||
# set hyper-parameters.
|
||||
self.d_model = d_model
|
||||
self.dropout = dropout
|
||||
@@ -95,7 +94,6 @@ class TransformerModel(Model):
|
||||
raise ValueError("unknown loss `%s`" % self.loss)
|
||||
|
||||
def metric_fn(self, pred, label):
|
||||
|
||||
mask = torch.isfinite(label)
|
||||
|
||||
if self.metric in ("", "loss"):
|
||||
@@ -104,7 +102,6 @@ class TransformerModel(Model):
|
||||
raise ValueError("unknown metric `%s`" % self.metric)
|
||||
|
||||
def train_epoch(self, x_train, y_train):
|
||||
|
||||
x_train_values = x_train.values
|
||||
y_train_values = np.squeeze(y_train.values)
|
||||
|
||||
@@ -114,7 +111,6 @@ class TransformerModel(Model):
|
||||
np.random.shuffle(indices)
|
||||
|
||||
for i in range(len(indices))[:: self.batch_size]:
|
||||
|
||||
if len(indices) - i < self.batch_size:
|
||||
break
|
||||
|
||||
@@ -130,7 +126,6 @@ class TransformerModel(Model):
|
||||
self.train_optimizer.step()
|
||||
|
||||
def test_epoch(self, data_x, data_y):
|
||||
|
||||
# prepare training data
|
||||
x_values = data_x.values
|
||||
y_values = np.squeeze(data_y.values)
|
||||
@@ -143,7 +138,6 @@ class TransformerModel(Model):
|
||||
indices = np.arange(len(x_values))
|
||||
|
||||
for i in range(len(indices))[:: self.batch_size]:
|
||||
|
||||
if len(indices) - i < self.batch_size:
|
||||
break
|
||||
|
||||
@@ -166,7 +160,6 @@ class TransformerModel(Model):
|
||||
evals_result=dict(),
|
||||
save_path=None,
|
||||
):
|
||||
|
||||
df_train, df_valid, df_test = dataset.prepare(
|
||||
["train", "valid", "test"],
|
||||
col_set=["feature", "label"],
|
||||
@@ -231,7 +224,6 @@ class TransformerModel(Model):
|
||||
preds = []
|
||||
|
||||
for begin in range(sample_num)[:: self.batch_size]:
|
||||
|
||||
if sample_num - begin < self.batch_size:
|
||||
end = sample_num
|
||||
else:
|
||||
|
||||
@@ -43,7 +43,6 @@ class TransformerModel(Model):
|
||||
seed=None,
|
||||
**kwargs
|
||||
):
|
||||
|
||||
# set hyper-parameters.
|
||||
self.d_model = d_model
|
||||
self.dropout = dropout
|
||||
@@ -93,7 +92,6 @@ class TransformerModel(Model):
|
||||
raise ValueError("unknown loss `%s`" % self.loss)
|
||||
|
||||
def metric_fn(self, pred, label):
|
||||
|
||||
mask = torch.isfinite(label)
|
||||
|
||||
if self.metric in ("", "loss"):
|
||||
@@ -102,7 +100,6 @@ class TransformerModel(Model):
|
||||
raise ValueError("unknown metric `%s`" % self.metric)
|
||||
|
||||
def train_epoch(self, data_loader):
|
||||
|
||||
self.model.train()
|
||||
|
||||
for data in data_loader:
|
||||
@@ -118,14 +115,12 @@ class TransformerModel(Model):
|
||||
self.train_optimizer.step()
|
||||
|
||||
def test_epoch(self, data_loader):
|
||||
|
||||
self.model.eval()
|
||||
|
||||
scores = []
|
||||
losses = []
|
||||
|
||||
for data in data_loader:
|
||||
|
||||
feature = data[:, :, 0:-1].to(self.device)
|
||||
label = data[:, -1, -1].to(self.device)
|
||||
|
||||
@@ -145,7 +140,6 @@ class TransformerModel(Model):
|
||||
evals_result=dict(),
|
||||
save_path=None,
|
||||
):
|
||||
|
||||
dl_train = dataset.prepare("train", col_set=["feature", "label"], data_key=DataHandlerLP.DK_L)
|
||||
dl_valid = dataset.prepare("valid", col_set=["feature", "label"], data_key=DataHandlerLP.DK_L)
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ class XGBModel(Model, FeatureInt):
|
||||
reweighter=None,
|
||||
**kwargs
|
||||
):
|
||||
|
||||
df_train, df_valid = dataset.prepare(
|
||||
["train", "valid"],
|
||||
col_set=["feature", "label"],
|
||||
|
||||
@@ -30,7 +30,6 @@ class CombFeaAna(FeaAnalyser):
|
||||
"""The statistics of features are finished in the underlying analysers"""
|
||||
|
||||
def plot_all(self, *args, **kwargs):
|
||||
|
||||
ax_gen = iter(sub_fig_generator(row_n=len(self._fea_ana_l), *args, **kwargs))
|
||||
|
||||
for col in self._dataset:
|
||||
|
||||
@@ -28,7 +28,6 @@ class FeaAnalyser:
|
||||
return False
|
||||
|
||||
def plot_all(self, *args, **kwargs):
|
||||
|
||||
ax_gen = iter(sub_fig_generator(*args, **kwargs))
|
||||
for col in self._dataset:
|
||||
if not self.skip(col):
|
||||
|
||||
@@ -15,7 +15,6 @@ from plotly.figure_factory import create_distplot
|
||||
|
||||
|
||||
class BaseGraph:
|
||||
|
||||
_name = None
|
||||
|
||||
def __init__(
|
||||
|
||||
7
qlib/contrib/rolling/__init__.py
Normal file
7
qlib/contrib/rolling/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
"""
|
||||
The difference between me and the scripts in examples/benchmarks/benchmarks_dynamic
|
||||
- This module only focus provide a general rolling implementation.
|
||||
Anything specific that benchmark is placed in examples/benchmarks/benchmarks_dynamic
|
||||
"""
|
||||
16
qlib/contrib/rolling/__main__.py
Normal file
16
qlib/contrib/rolling/__main__.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import fire
|
||||
from qlib import auto_init
|
||||
from qlib.contrib.rolling.base import Rolling
|
||||
from qlib.utils.mod import find_all_classes
|
||||
|
||||
if __name__ == "__main__":
|
||||
sub_commands = {}
|
||||
for cls in find_all_classes("qlib.contrib.rolling", Rolling):
|
||||
sub_commands[cls.__module__.split(".")[-1]] = cls
|
||||
# The sub_commands will be like
|
||||
# {'base': <class 'qlib.contrib.rolling.base.Rolling'>, ...}
|
||||
# So the you can run it with commands like command below
|
||||
# - `python -m qlib.contrib.rolling base --conf_path <path to the yaml> run`
|
||||
# - base can be replace with other module names
|
||||
auto_init()
|
||||
fire.Fire(sub_commands)
|
||||
246
qlib/contrib/rolling/base.py
Normal file
246
qlib/contrib/rolling/base.py
Normal file
@@ -0,0 +1,246 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
from copy import deepcopy
|
||||
from pathlib import Path
|
||||
from typing import List, Optional, Union
|
||||
|
||||
import fire
|
||||
import pandas as pd
|
||||
import yaml
|
||||
|
||||
from qlib import auto_init
|
||||
from qlib.log import get_module_logger
|
||||
from qlib.model.ens.ensemble import RollingEnsemble
|
||||
from qlib.model.trainer import TrainerR
|
||||
from qlib.utils import get_cls_kwargs, init_instance_by_config
|
||||
from qlib.utils.data import update_config
|
||||
from qlib.workflow import R
|
||||
from qlib.workflow.record_temp import SignalRecord
|
||||
from qlib.workflow.task.collect import RecorderCollector
|
||||
from qlib.workflow.task.gen import RollingGen, task_generator
|
||||
from qlib.workflow.task.utils import replace_task_handler_with_cache
|
||||
|
||||
|
||||
class Rolling:
|
||||
"""
|
||||
The motivation of Rolling Module
|
||||
- It only focus **offlinely** turn a specific task to rollinng
|
||||
- To make the implementation easier, following factors are ignored.
|
||||
- The tasks is dependent (e.g. time series).
|
||||
|
||||
Related modules and difference from me:
|
||||
- MetaController: It is learning how to handle a task (e.g. learning to learn).
|
||||
- But rolling is about how to split a single task into tasks in time series and run them.
|
||||
- OnlineStrategy: It is focusing on serving a model, the model can be updated time dependently in time.
|
||||
- Rolling is much simpler and is only for testing rolling models offline. It does not want to share the interface with OnlineStrategy.
|
||||
|
||||
The code about rolling is shared in `task_generator` & `RollingGen` level between me and the above modules
|
||||
But it is for different purpose, so other parts are not shared.
|
||||
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
# here is an typical use case of the module.
|
||||
python -m qlib.contrib.rolling.base --conf_path <path to the yaml> run
|
||||
|
||||
**NOTE**
|
||||
before running the example, please clean your previous results with following command
|
||||
- `rm -r mlruns`
|
||||
- Because it is very hard to permanently delete a experiment (it will be moved into .trash and raise error when creating experiment with same name).
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
conf_path: Union[str, Path],
|
||||
exp_name: Optional[str] = None,
|
||||
horizon: Optional[int] = 20,
|
||||
step: int = 20,
|
||||
h_path: Optional[str] = None,
|
||||
train_start: Optional[str] = None,
|
||||
test_end: Optional[str] = None,
|
||||
task_ext_conf: Optional[dict] = None,
|
||||
rolling_exp: Optional[str] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
conf_path : str
|
||||
Path to the config for rolling.
|
||||
exp_name : Optional[str]
|
||||
The exp name of the outputs (Output is a record which contains the concatenated predictions of rolling records).
|
||||
horizon: Optional[int] = 20,
|
||||
The horizon of the prediction target.
|
||||
This is used to override the prediction horizon of the file.
|
||||
h_path : Optional[str]
|
||||
the dumped data handler;
|
||||
It may come from other data source. It will override the data handler in the config.
|
||||
test_end : Optional[str]
|
||||
the test end for the data. It is typically used together with the handler
|
||||
You can do the same thing with task_ext_conf in a more complicated way
|
||||
train_start : Optional[str]
|
||||
the train start for the data. It is typically used together with the handler.
|
||||
You can do the same thing with task_ext_conf in a more complicated way
|
||||
task_ext_conf : Optional[dict]
|
||||
some option to update the task config.
|
||||
rolling_exp : Optional[str]
|
||||
The name for the experiments for rolling.
|
||||
It will contains a lot of record in an experiment. Each record corresponds to a specific rolling.
|
||||
Please note that it is different from the final experiments
|
||||
"""
|
||||
self.logger = get_module_logger("Rolling")
|
||||
self.conf_path = Path(conf_path)
|
||||
self.exp_name = exp_name
|
||||
self._rid = None # the final combined recorder id in `exp_name`
|
||||
|
||||
self.step = step
|
||||
assert horizon is not None, "Current version does not support extracting horizon from the underlying dataset"
|
||||
self.horizon = horizon
|
||||
if rolling_exp is None:
|
||||
datetime_suffix = pd.Timestamp.now().strftime("%Y%m%d%H%M%S")
|
||||
self.rolling_exp = f"rolling_models_{datetime_suffix}"
|
||||
else:
|
||||
self.rolling_exp = rolling_exp
|
||||
self.logger.warning(
|
||||
"Using user specifiied name for rolling models. So the experiment names duplicateds. "
|
||||
"Please manually remove your experiment for rolling model with command like `rm -r mlruns`."
|
||||
" Otherwise it will prevents the creating of experimen with same name"
|
||||
)
|
||||
self.train_start = train_start
|
||||
self.test_end = test_end
|
||||
self.task_ext_conf = task_ext_conf
|
||||
self.h_path = h_path
|
||||
|
||||
# FIXME:
|
||||
# - the qlib_init section will be ignored by me.
|
||||
# - So we have to design a priority mechanism to solve this issue.
|
||||
|
||||
def _raw_conf(self) -> dict:
|
||||
with self.conf_path.open("r") as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
def _replace_hanler_with_cache(self, task: dict):
|
||||
"""
|
||||
Due to the data processing part in original rolling is slow. So we have to
|
||||
This class tries to add more feature
|
||||
"""
|
||||
if self.h_path is not None:
|
||||
h_path = Path(self.h_path)
|
||||
task["dataset"]["kwargs"]["handler"] = f"file://{h_path}"
|
||||
else:
|
||||
task = replace_task_handler_with_cache(task, self.conf_path.parent)
|
||||
return task
|
||||
|
||||
def _update_start_end_time(self, task: dict):
|
||||
if self.train_start is not None:
|
||||
seg = task["dataset"]["kwargs"]["segments"]["train"]
|
||||
task["dataset"]["kwargs"]["segments"]["train"] = pd.Timestamp(self.train_start), seg[1]
|
||||
|
||||
if self.test_end is not None:
|
||||
seg = task["dataset"]["kwargs"]["segments"]["test"]
|
||||
task["dataset"]["kwargs"]["segments"]["test"] = seg[0], pd.Timestamp(self.test_end)
|
||||
return task
|
||||
|
||||
def basic_task(self, enable_handler_cache: Optional[bool] = True):
|
||||
"""
|
||||
The basic task may not be the exactly same as the config from `conf_path` from __init__ due to
|
||||
- some parameters could be overriding by some parameters from __init__
|
||||
- user could implementing sublcass to change it for higher performance
|
||||
"""
|
||||
task: dict = self._raw_conf()["task"]
|
||||
task = deepcopy(task)
|
||||
|
||||
# modify dataset horizon
|
||||
# NOTE:
|
||||
# It assumpts that the label can be modifiled in the handler's kwargs
|
||||
# But is not always a valid. It is only valid in the predefined dataset `Alpha158` & `Alpha360`
|
||||
if self.horizon is None:
|
||||
# TODO:
|
||||
# - get horizon automatically from the expression!!!!
|
||||
raise NotImplementedError(f"This type of input is not supported")
|
||||
else:
|
||||
self.logger.info("The prediction horizon is overrided")
|
||||
task["dataset"]["kwargs"]["handler"]["kwargs"]["label"] = [
|
||||
"Ref($close, -{}) / Ref($close, -1) - 1".format(self.horizon + 1)
|
||||
]
|
||||
|
||||
if enable_handler_cache:
|
||||
task = self._replace_hanler_with_cache(task)
|
||||
task = self._update_start_end_time(task)
|
||||
|
||||
if self.task_ext_conf is not None:
|
||||
task = update_config(task, self.task_ext_conf)
|
||||
self.logger.info(task)
|
||||
return task
|
||||
|
||||
def get_task_list(self) -> List[dict]:
|
||||
"""return a batch of tasks for rolling."""
|
||||
task = self.basic_task()
|
||||
task_l = task_generator(
|
||||
task, RollingGen(step=self.step, trunc_days=self.horizon + 1)
|
||||
) # the last two days should be truncated to avoid information leakage
|
||||
for t in task_l:
|
||||
# when we rolling tasks. No further analyis is needed.
|
||||
# analyis are postponed to the final ensemble.
|
||||
t["record"] = ["qlib.workflow.record_temp.SignalRecord"]
|
||||
return task_l
|
||||
|
||||
def _train_rolling_tasks(self):
|
||||
task_l = self.get_task_list()
|
||||
self.logger.info("Deleting previous Rolling results")
|
||||
try:
|
||||
# TODO: mlflow does not support permanently delete experiment
|
||||
# it will be moved to .trash and prevents creating the experiments with the same name
|
||||
R.delete_exp(experiment_name=self.rolling_exp) # We should remove the rolling experiments.
|
||||
except ValueError:
|
||||
self.logger.info("No previous rolling results")
|
||||
trainer = TrainerR(experiment_name=self.rolling_exp)
|
||||
trainer(task_l)
|
||||
|
||||
def _ens_rolling(self):
|
||||
rc = RecorderCollector(
|
||||
experiment=self.rolling_exp,
|
||||
artifacts_key=["pred", "label"],
|
||||
process_list=[RollingEnsemble()],
|
||||
# rec_key_func=lambda rec: (self.COMB_EXP, rec.info["id"]),
|
||||
artifacts_path={"pred": "pred.pkl", "label": "label.pkl"},
|
||||
)
|
||||
res = rc()
|
||||
with R.start(experiment_name=self.exp_name):
|
||||
R.log_params(exp_name=self.rolling_exp)
|
||||
R.save_objects(**{"pred.pkl": res["pred"], "label.pkl": res["label"]})
|
||||
self._rid = R.get_recorder().id
|
||||
|
||||
def _update_rolling_rec(self):
|
||||
"""
|
||||
Evaluate the combined rolling results
|
||||
"""
|
||||
rec = R.get_recorder(experiment_name=self.exp_name, recorder_id=self._rid)
|
||||
# Follow the original analyser
|
||||
records = self._raw_conf()["task"].get("record", [])
|
||||
if isinstance(records, dict): # prevent only one dict
|
||||
records = [records]
|
||||
for record in records:
|
||||
if issubclass(get_cls_kwargs(record)[0], SignalRecord):
|
||||
# skip the signal record.
|
||||
continue
|
||||
r = init_instance_by_config(
|
||||
record,
|
||||
recorder=rec,
|
||||
default_module="qlib.workflow.record_temp",
|
||||
)
|
||||
r.generate()
|
||||
print(f"Your evaluation results can be found in the experiment named `{self.exp_name}`.")
|
||||
|
||||
def run(self):
|
||||
# the results will be save in mlruns.
|
||||
# 1) each rolling task is saved in rolling_models
|
||||
self._train_rolling_tasks()
|
||||
# 2) combined rolling tasks and evaluation results are saved in rolling
|
||||
self._ens_rolling()
|
||||
self._update_rolling_rec()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
auto_init()
|
||||
fire.Fire(Rolling)
|
||||
343
qlib/contrib/rolling/ddgda.py
Normal file
343
qlib/contrib/rolling/ddgda.py
Normal file
@@ -0,0 +1,343 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
from pathlib import Path
|
||||
import pickle
|
||||
from typing import Optional, Union
|
||||
|
||||
import pandas as pd
|
||||
import yaml
|
||||
|
||||
from qlib.contrib.meta.data_selection.dataset import InternalData, MetaDatasetDS
|
||||
from qlib.contrib.meta.data_selection.model import MetaModelDS
|
||||
from qlib.data.dataset.handler import DataHandlerLP
|
||||
from qlib.model.meta.task import MetaTask
|
||||
from qlib.model.trainer import TrainerR
|
||||
from qlib.typehint import Literal
|
||||
from qlib.utils import init_instance_by_config
|
||||
from qlib.workflow import R
|
||||
from qlib.workflow.task.utils import replace_task_handler_with_cache
|
||||
|
||||
from .base import Rolling
|
||||
|
||||
# LGBM is designed for feature importance & similarity
|
||||
LGBM_MODEL = """
|
||||
class: LGBModel
|
||||
module_path: qlib.contrib.model.gbdt
|
||||
kwargs:
|
||||
loss: mse
|
||||
colsample_bytree: 0.8879
|
||||
learning_rate: 0.2
|
||||
subsample: 0.8789
|
||||
lambda_l1: 205.6999
|
||||
lambda_l2: 580.9768
|
||||
max_depth: 8
|
||||
num_leaves: 210
|
||||
num_threads: 20
|
||||
"""
|
||||
# covnert the yaml to dict
|
||||
LGBM_MODEL = yaml.load(LGBM_MODEL, Loader=yaml.FullLoader)
|
||||
|
||||
LINEAR_MODEL = """
|
||||
class: LinearModel
|
||||
module_path: qlib.contrib.model.linear
|
||||
kwargs:
|
||||
estimator: ridge
|
||||
alpha: 0.05
|
||||
"""
|
||||
LINEAR_MODEL = yaml.load(LINEAR_MODEL, Loader=yaml.FullLoader)
|
||||
|
||||
PROC_ARGS = """
|
||||
infer_processors:
|
||||
- class: RobustZScoreNorm
|
||||
kwargs:
|
||||
fields_group: feature
|
||||
clip_outlier: true
|
||||
- class: Fillna
|
||||
kwargs:
|
||||
fields_group: feature
|
||||
learn_processors:
|
||||
- class: DropnaLabel
|
||||
- class: CSRankNorm
|
||||
kwargs:
|
||||
fields_group: label
|
||||
"""
|
||||
PROC_ARGS = yaml.load(PROC_ARGS, Loader=yaml.FullLoader)
|
||||
|
||||
UTIL_MODEL_TYPE = Literal["linear", "gbdt"]
|
||||
|
||||
|
||||
class DDGDA(Rolling):
|
||||
"""
|
||||
It is a rolling based on DDG-DA
|
||||
|
||||
**NOTE**
|
||||
before running the example, please clean your previous results with following command
|
||||
- `rm -r mlruns`
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
sim_task_model: UTIL_MODEL_TYPE = "gbdt",
|
||||
meta_1st_train_end: Optional[str] = None,
|
||||
alpha: float = 0.01,
|
||||
working_dir: Optional[Union[str, Path]] = None,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
|
||||
Parameters
|
||||
----------
|
||||
sim_task_model: Literal["linear", "gbdt"] = "gbdt",
|
||||
The model for calculating similarity between data.
|
||||
meta_1st_train_end: Optional[str]
|
||||
the datetime of training end of the first meta_task
|
||||
alpha: float
|
||||
Setting the L2 regularization for ridge
|
||||
The `alpha` is only passed to MetaModelDS (it is not passed to sim_task_model currently..)
|
||||
"""
|
||||
# NOTE:
|
||||
# the horizon must match the meaning in the base task template
|
||||
self.meta_exp_name = "DDG-DA"
|
||||
self.sim_task_model: UTIL_MODEL_TYPE = sim_task_model # The model to capture the distribution of data.
|
||||
self.alpha = alpha
|
||||
self.meta_1st_train_end = meta_1st_train_end
|
||||
super().__init__(**kwargs)
|
||||
self.working_dir = self.conf_path.parent if working_dir is None else Path(working_dir)
|
||||
self.proxy_hd = self.working_dir / "handler_proxy.pkl"
|
||||
|
||||
def _adjust_task(self, task: dict, astype: UTIL_MODEL_TYPE):
|
||||
"""
|
||||
some task are use for special purpose.
|
||||
For example:
|
||||
- GBDT for calculating feature importance
|
||||
- Linear or GBDT for calculating similarity
|
||||
- Datset (well processed) that aligned to Linear that for meta learning
|
||||
"""
|
||||
# NOTE: here is just for aligning with previous implementation
|
||||
# It is not necessary for the current implementation
|
||||
handler = task["dataset"].setdefault("kwargs", {}).setdefault("handler", {})
|
||||
if astype == "gbdt":
|
||||
task["model"] = LGBM_MODEL
|
||||
if isinstance(handler, dict):
|
||||
for k in ["infer_processors", "learn_processors"]:
|
||||
if k in handler.setdefault("kwargs", {}):
|
||||
handler["kwargs"].pop(k)
|
||||
elif astype == "linear":
|
||||
task["model"] = LINEAR_MODEL
|
||||
handler["kwargs"].update(PROC_ARGS)
|
||||
else:
|
||||
raise ValueError(f"astype not supported: {astype}")
|
||||
return task
|
||||
|
||||
def _get_feature_importance(self):
|
||||
# this must be lightGBM, because it needs to get the feature importance
|
||||
task = self.basic_task(enable_handler_cache=False)
|
||||
task = self._adjust_task(task, astype="gbdt")
|
||||
task = replace_task_handler_with_cache(task, self.working_dir)
|
||||
|
||||
with R.start(experiment_name="feature_importance"):
|
||||
model = init_instance_by_config(task["model"])
|
||||
dataset = init_instance_by_config(task["dataset"])
|
||||
model.fit(dataset)
|
||||
|
||||
fi = model.get_feature_importance()
|
||||
# Because the model use numpy instead of dataframe for training lightgbm
|
||||
# So the we must use following extra steps to get the right feature importance
|
||||
df = dataset.prepare(segments=slice(None), col_set="feature", data_key=DataHandlerLP.DK_R)
|
||||
cols = df.columns
|
||||
fi_named = {cols[int(k.split("_")[1])]: imp for k, imp in fi.to_dict().items()}
|
||||
|
||||
return pd.Series(fi_named)
|
||||
|
||||
def _dump_data_for_proxy_model(self):
|
||||
"""
|
||||
Dump data for training meta model.
|
||||
The meta model will be trained upon the proxy forecasting model.
|
||||
This dataset is for the proxy forecasting model.
|
||||
"""
|
||||
topk = 30
|
||||
fi = self._get_feature_importance()
|
||||
col_selected = fi.nlargest(topk)
|
||||
# NOTE: adjusting to `self.sim_task_model` just for aligning with previous implementation.
|
||||
task = self._adjust_task(self.basic_task(enable_handler_cache=False), self.sim_task_model)
|
||||
task = replace_task_handler_with_cache(task, self.working_dir)
|
||||
|
||||
dataset = init_instance_by_config(task["dataset"])
|
||||
prep_ds = dataset.prepare(slice(None), col_set=["feature", "label"], data_key=DataHandlerLP.DK_L)
|
||||
|
||||
feature_df = prep_ds["feature"]
|
||||
label_df = prep_ds["label"]
|
||||
|
||||
feature_selected = feature_df.loc[:, col_selected.index]
|
||||
|
||||
feature_selected = feature_selected.groupby("datetime", group_keys=False).apply(
|
||||
lambda df: (df - df.mean()).div(df.std())
|
||||
)
|
||||
feature_selected = feature_selected.fillna(0.0)
|
||||
|
||||
df_all = {
|
||||
"label": label_df.reindex(feature_selected.index),
|
||||
"feature": feature_selected,
|
||||
}
|
||||
df_all = pd.concat(df_all, axis=1)
|
||||
df_all.to_pickle(self.working_dir / "fea_label_df.pkl")
|
||||
|
||||
# dump data in handler format for aligning the interface
|
||||
handler = DataHandlerLP(
|
||||
data_loader={
|
||||
"class": "qlib.data.dataset.loader.StaticDataLoader",
|
||||
"kwargs": {"config": self.working_dir / "fea_label_df.pkl"},
|
||||
}
|
||||
)
|
||||
handler.to_pickle(self.working_dir / self.proxy_hd, dump_all=True)
|
||||
|
||||
@property
|
||||
def _internal_data_path(self):
|
||||
return self.working_dir / f"internal_data_s{self.step}.pkl"
|
||||
|
||||
def _dump_meta_ipt(self):
|
||||
"""
|
||||
Dump data for training meta model.
|
||||
This function will dump the input data for meta model
|
||||
"""
|
||||
# According to the experiments, the choice of the model type is very important for achieving good results
|
||||
sim_task = self._adjust_task(self.basic_task(enable_handler_cache=False), astype=self.sim_task_model)
|
||||
sim_task = replace_task_handler_with_cache(sim_task, self.working_dir)
|
||||
|
||||
if self.sim_task_model == "gbdt":
|
||||
sim_task["model"].setdefault("kwargs", {}).update({"early_stopping_rounds": None, "num_boost_round": 150})
|
||||
|
||||
exp_name_sim = f"data_sim_s{self.step}"
|
||||
|
||||
internal_data = InternalData(sim_task, self.step, exp_name=exp_name_sim)
|
||||
internal_data.setup(trainer=TrainerR)
|
||||
|
||||
with self._internal_data_path.open("wb") as f:
|
||||
pickle.dump(internal_data, f)
|
||||
|
||||
def _train_meta_model(self, fill_method="max"):
|
||||
"""
|
||||
training a meta model based on a simplified linear proxy model;
|
||||
"""
|
||||
|
||||
# 1) leverage the simplified proxy forecasting model to train meta model.
|
||||
# - Only the dataset part is important, in current version of meta model will integrate the
|
||||
|
||||
# the train_start for training meta model does not necessarily align with final rolling
|
||||
train_start = "2008-01-01" if self.train_start is None else self.train_start
|
||||
train_end = "2010-12-31" if self.meta_1st_train_end is None else self.meta_1st_train_end
|
||||
test_start = (pd.Timestamp(train_end) + pd.Timedelta(days=1)).strftime("%Y-%m-%d")
|
||||
proxy_forecast_model_task = {
|
||||
# "model": "qlib.contrib.model.linear.LinearModel",
|
||||
"dataset": {
|
||||
"class": "qlib.data.dataset.DatasetH",
|
||||
"kwargs": {
|
||||
"handler": f"file://{(self.working_dir / self.proxy_hd).absolute()}",
|
||||
"segments": {
|
||||
"train": (train_start, train_end),
|
||||
"test": (test_start, self.basic_task()["dataset"]["kwargs"]["segments"]["test"][1]),
|
||||
},
|
||||
},
|
||||
},
|
||||
# "record": ["qlib.workflow.record_temp.SignalRecord"]
|
||||
}
|
||||
# the proxy_forecast_model_task will be used to create meta tasks.
|
||||
# The test date of first task will be 2011-01-01. Each test segment will be about 20days
|
||||
# The tasks include all training tasks and test tasks.
|
||||
|
||||
# 2) preparing meta dataset
|
||||
kwargs = dict(
|
||||
task_tpl=proxy_forecast_model_task,
|
||||
step=self.step,
|
||||
segments=0.62, # keep test period consistent with the dataset yaml
|
||||
trunc_days=1 + self.horizon,
|
||||
hist_step_n=30,
|
||||
fill_method=fill_method,
|
||||
rolling_ext_days=0,
|
||||
)
|
||||
# NOTE:
|
||||
# the input of meta model (internal data) are shared between proxy model and final forecasting model
|
||||
# but their task test segment are not aligned! It worked in my previous experiment.
|
||||
# So the misalignment will not affect the effectiveness of the method.
|
||||
with self._internal_data_path.open("rb") as f:
|
||||
internal_data = pickle.load(f)
|
||||
|
||||
md = MetaDatasetDS(exp_name=internal_data, **kwargs)
|
||||
|
||||
# 3) train and logging meta model
|
||||
with R.start(experiment_name=self.meta_exp_name):
|
||||
R.log_params(**kwargs)
|
||||
mm = MetaModelDS(
|
||||
step=self.step, hist_step_n=kwargs["hist_step_n"], lr=0.001, max_epoch=30, seed=43, alpha=self.alpha
|
||||
)
|
||||
mm.fit(md)
|
||||
R.save_objects(model=mm)
|
||||
|
||||
@property
|
||||
def _task_path(self):
|
||||
return self.working_dir / f"tasks_s{self.step}.pkl"
|
||||
|
||||
def get_task_list(self):
|
||||
"""
|
||||
Leverage meta-model for inference:
|
||||
- Given
|
||||
- baseline tasks
|
||||
- input for meta model(internal data)
|
||||
- meta model (its learnt knowledge on proxy forecasting model is expected to transfer to normal forecasting model)
|
||||
"""
|
||||
# 1) get meta model
|
||||
exp = R.get_exp(experiment_name=self.meta_exp_name)
|
||||
rec = exp.list_recorders(rtype=exp.RT_L)[0]
|
||||
meta_model: MetaModelDS = rec.load_object("model")
|
||||
|
||||
# 2)
|
||||
# we are transfer to knowledge of meta model to final forecasting tasks.
|
||||
# Create MetaTaskDataset for the final forecasting tasks
|
||||
# Aligning the setting of it to the MetaTaskDataset when training Meta model is necessary
|
||||
|
||||
# 2.1) get previous config
|
||||
param = rec.list_params()
|
||||
trunc_days = int(param["trunc_days"])
|
||||
step = int(param["step"])
|
||||
hist_step_n = int(param["hist_step_n"])
|
||||
fill_method = param.get("fill_method", "max")
|
||||
|
||||
task_l = super().get_task_list()
|
||||
|
||||
# 2.2) create meta dataset for final dataset
|
||||
kwargs = dict(
|
||||
task_tpl=task_l,
|
||||
step=step,
|
||||
segments=0.0, # all the tasks are for testing
|
||||
trunc_days=trunc_days,
|
||||
hist_step_n=hist_step_n,
|
||||
fill_method=fill_method,
|
||||
task_mode=MetaTask.PROC_MODE_TRANSFER,
|
||||
)
|
||||
|
||||
with self._internal_data_path.open("rb") as f:
|
||||
internal_data = pickle.load(f)
|
||||
mds = MetaDatasetDS(exp_name=internal_data, **kwargs)
|
||||
|
||||
# 3) meta model make inference and get new qlib task
|
||||
new_tasks = meta_model.inference(mds)
|
||||
with self._task_path.open("wb") as f:
|
||||
pickle.dump(new_tasks, f)
|
||||
return new_tasks
|
||||
|
||||
def run(self):
|
||||
# prepare the meta model for rolling ---------
|
||||
# 1) file: handler_proxy.pkl (self.proxy_hd)
|
||||
self._dump_data_for_proxy_model()
|
||||
# 2)
|
||||
# file: internal_data_s20.pkl
|
||||
# mlflow: data_sim_s20, models for calculating meta_ipt
|
||||
self._dump_meta_ipt()
|
||||
# 3) meta model will be stored in `DDG-DA`
|
||||
self._train_meta_model()
|
||||
|
||||
# Run rolling --------------------------------
|
||||
# 4) new_tasks are saved in "tasks_s20.pkl" (reweighter is added)
|
||||
# - the meta inference are done when calling `get_task_list`
|
||||
# 5) load the saved tasks and train model
|
||||
super().run()
|
||||
@@ -112,7 +112,6 @@ class PortfolioOptimizer(BaseOptimizer):
|
||||
return w
|
||||
|
||||
def _optimize(self, S: np.ndarray, r: Optional[np.ndarray] = None, w0: Optional[np.ndarray] = None) -> np.ndarray:
|
||||
|
||||
# inverse volatility
|
||||
if self.method == self.OPT_INV:
|
||||
if r is not None:
|
||||
|
||||
@@ -522,7 +522,6 @@ class ACStrategy(BaseStrategy):
|
||||
_order_amount = min(_order_amount, self.trade_amount[order.stock_id])
|
||||
|
||||
if _order_amount > 1e-5:
|
||||
|
||||
_order = Order(
|
||||
stock_id=order.stock_id,
|
||||
amount=_order_amount,
|
||||
|
||||
@@ -435,7 +435,6 @@ class EnhancedIndexingStrategy(WeightStrategyBase):
|
||||
self._riskdata_cache = {}
|
||||
|
||||
def get_risk_data(self, date):
|
||||
|
||||
if date in self._riskdata_cache:
|
||||
return self._riskdata_cache[date]
|
||||
|
||||
@@ -462,7 +461,6 @@ class EnhancedIndexingStrategy(WeightStrategyBase):
|
||||
return self._riskdata_cache[date]
|
||||
|
||||
def generate_target_weight_position(self, score, current, trade_start_time, trade_end_time):
|
||||
|
||||
trade_date = trade_start_time
|
||||
pre_date = get_pre_trading_date(trade_date, future=True) # previous trade date
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import os
|
||||
|
||||
class TunerConfigManager:
|
||||
def __init__(self, config_path):
|
||||
|
||||
if not config_path:
|
||||
raise ValueError("Config path is invalid.")
|
||||
self.config_path = config_path
|
||||
@@ -58,7 +57,6 @@ class PipelineExperimentConfig:
|
||||
|
||||
class OptimizationConfig:
|
||||
def __init__(self, config, TUNER_CONFIG_MANAGER):
|
||||
|
||||
self.report_type = config.get("report_type", "pred_long")
|
||||
if self.report_type not in [
|
||||
"pred_long",
|
||||
|
||||
@@ -15,11 +15,9 @@ from ...utils import get_module_by_module_path
|
||||
|
||||
|
||||
class Pipeline:
|
||||
|
||||
GLOBAL_BEST_PARAMS_NAME = "global_best_params.json"
|
||||
|
||||
def __init__(self, tuner_config_manager):
|
||||
|
||||
self.logger = get_module_logger("Pipeline", sh_level=logging.INFO)
|
||||
|
||||
self.tuner_config_manager = tuner_config_manager
|
||||
@@ -37,7 +35,6 @@ class Pipeline:
|
||||
self.best_tuner_index = None
|
||||
|
||||
def run(self):
|
||||
|
||||
TimeInspector.set_time_mark()
|
||||
for tuner_index, tuner_config in enumerate(self.pipeline_config):
|
||||
tuner = self.init_tuner(tuner_index, tuner_config)
|
||||
@@ -77,7 +74,6 @@ class Pipeline:
|
||||
return tuner_class(tuner_config, self.optim_config)
|
||||
|
||||
def save_tuner_exp_info(self):
|
||||
|
||||
TimeInspector.set_time_mark()
|
||||
save_path = os.path.join(self.pipeline_ex_config.tuner_ex_dir, Pipeline.GLOBAL_BEST_PARAMS_NAME)
|
||||
with open(save_path, "w") as fp:
|
||||
|
||||
@@ -24,7 +24,6 @@ from hyperopt import STATUS_OK, STATUS_FAIL
|
||||
|
||||
class Tuner:
|
||||
def __init__(self, tuner_config, optim_config):
|
||||
|
||||
self.logger = get_module_logger("Tuner", sh_level=logging.INFO)
|
||||
|
||||
self.tuner_config = tuner_config
|
||||
@@ -42,7 +41,6 @@ class Tuner:
|
||||
self.space = self.setup_space()
|
||||
|
||||
def tune(self):
|
||||
|
||||
TimeInspector.set_time_mark()
|
||||
fmin(
|
||||
fn=self.objective,
|
||||
@@ -84,7 +82,6 @@ class Tuner:
|
||||
|
||||
|
||||
class QLibTuner(Tuner):
|
||||
|
||||
ESTIMATOR_CONFIG_NAME = "estimator_config.yaml"
|
||||
EXP_INFO_NAME = "exp_info.json"
|
||||
EXP_RESULT_DIR = "sacred/{}"
|
||||
@@ -92,7 +89,6 @@ class QLibTuner(Tuner):
|
||||
LOCAL_BEST_PARAMS_NAME = "local_best_params.json"
|
||||
|
||||
def objective(self, params):
|
||||
|
||||
# 1. Setup an config for a specific estimator process
|
||||
estimator_path = self.setup_estimator_config(params)
|
||||
self.logger.info("Searching params: {} ".format(params))
|
||||
@@ -120,7 +116,6 @@ class QLibTuner(Tuner):
|
||||
return {"loss": res, "status": status}
|
||||
|
||||
def fetch_result(self):
|
||||
|
||||
# 1. Get experiment information
|
||||
exp_info_path = os.path.join(self.ex_dir, QLibTuner.EXP_INFO_NAME)
|
||||
with open(exp_info_path) as fp:
|
||||
@@ -155,7 +150,6 @@ class QLibTuner(Tuner):
|
||||
return np.abs(res.values[0] - 1)
|
||||
|
||||
def setup_estimator_config(self, params):
|
||||
|
||||
estimator_config = copy.deepcopy(self.tuner_config)
|
||||
estimator_config["model"].update({"args": params["model_space"]})
|
||||
estimator_config["strategy"].update({"args": params["strategy_space"]})
|
||||
@@ -212,7 +206,6 @@ class QLibTuner(Tuner):
|
||||
return space
|
||||
|
||||
def save_local_best_params(self):
|
||||
|
||||
TimeInspector.set_time_mark()
|
||||
local_best_params_path = os.path.join(self.ex_dir, QLibTuner.LOCAL_BEST_PARAMS_NAME)
|
||||
with open(local_best_params_path, "w") as fp:
|
||||
|
||||
Reference in New Issue
Block a user