feat(Config): feature extractors are enabled one-by-one with a bool, added previous model to model.fit() (#77)

* feat(Config): feature extractors are enabled one-by-one with a bool, added previous model to model.fit()

* fix(Sweep): removed unused `other_features` parameter that fails sweep

* feat(Config): using preset names for defining feature extractors again

* fix(Tests): fixed model stub classes
This commit is contained in:
Mark Aron Szulyovszky
2021-12-23 10:35:20 +01:00
committed by GitHub
parent 6ae8acf70e
commit b6cd6b14fe
14 changed files with 78 additions and 44 deletions
+4 -3
View File
@@ -1,5 +1,6 @@
from collections import defaultdict
from utils.load_data import get_crypto_assets
import feature_extractors.feature_extractor_presets as feature_extractor_presets
from feature_extractors.feature_extractor_presets import presets
from models.model_map import model_names_classification, model_names_regression
def get_default_config() -> tuple[dict, dict, dict]:
@@ -18,8 +19,8 @@ def get_default_config() -> tuple[dict, dict, dict]:
load_other_assets= False,
log_returns= True,
forecasting_horizon = 1,
own_features= feature_extractor_presets.date + feature_extractor_presets.level1,
other_features= [],
own_features = ['level_1', 'date_days'],
other_features = [],
index_column= 'int',
method= 'classification',
)
+42 -17
View File
@@ -1,24 +1,49 @@
from feature_extractors.feature_extractors import feature_lag, feature_mom, feature_ROC, feature_RSI, feature_STOD, feature_STOK, feature_vol, feature_day_of_month, feature_day_of_week, feature_month, feature_debug_future_lookahead
from utils.typing import FeatureExtractorConfig
from utils.helpers import flatten
debug_future_lookahead = [('debug_future', feature_debug_future_lookahead, [1])]
__presets = dict(
debug_future_lookahead = [('debug_future', feature_debug_future_lookahead, [1])],
single_mom = [('mom', feature_mom, [30])],
single_vol = [('vol', feature_vol, [30])],
mom = [('mom', feature_mom, [10, 20, 30, 60, 90])],
vol = [('vol', feature_vol, [10, 20, 30, 60])],
lags_up_to_5 = [('lag', feature_lag, [1,2,3,4,5])],
lags_up_to_10 = [('lag', feature_lag, [1,2,3,4,5,6,7,8,9,10])],
date_all = [
('day_of_week', feature_day_of_week, [0]),
('day_of_month', feature_day_of_month, [0]),
('month', feature_month, [0])],
date_days = [
('day_of_week', feature_day_of_week, [0]),
('day_of_month', feature_day_of_month, [0]),
],
roc = [('roc', feature_ROC, [10, 30])],
rsi = [('rsi', feature_ROC, [10, 30, 100])],
stod = [('stod', feature_STOD, [10, 30, 200])],
stok = [('stok', feature_STOK, [10, 30, 200])],
)
lags = [('lag', feature_lag, [1,2,3,4,5,6,7,8,9])]
presets = __presets | dict(
level_1 = __presets["mom"] + __presets["vol"],
level_2 = __presets["mom"] + __presets["vol"] + __presets["roc"] + __presets["rsi"] + __presets["stod"] + __presets["stok"],
)
only_mom = [('mom', feature_mom, [30])]
def preprocess_feature_extractors_config(data_dict: dict) -> dict:
keys = ['own_features', 'other_features']
for key in keys:
preset_names = data_dict[key]
data_dict[key] = flatten([presets[preset_name] for preset_name in preset_names])
return data_dict
date = [
('day_of_week', feature_day_of_week, [0]),
('day_of_month', feature_day_of_month, [0]),
('month', feature_month, [0])]
level1 = [
('mom', feature_mom, [10, 20, 30, 60, 90]),
('vol', feature_vol, [10, 20, 30, 60]),
]
# Use this if ever we want to create an independent boolean for each featureextractor
# def preprocess_feature_extractors_config(data_dict: dict) -> dict:
# prefixes = ['own_features', 'other_features']
# features_dict = dict()
# for prefix in prefixes:
# features_to_include = [key.replace(prefix + "_", "") for key, value in data_dict.items() if key.startswith(prefix) and value == True]
# features_dict[prefix] = flatten([presets[feature_name] for feature_name in features_to_include])
level2 = level1 + [
('roc', feature_ROC, [10, 30]),
('rsi', feature_RSI, [10, 30, 100]),
('stod', feature_STOD, [10, 30, 200]),
('stok', feature_STOK, [10, 30, 200]),
]
# data_dict = {k: v for k, v in data_dict.items() if not (k.startswith(prefixes[0]) or k.startswith(prefixes[1]))}
# return (data_dict | features_dict)
+1 -1
View File
@@ -10,7 +10,7 @@ class StaticAverageModel(Model):
data_scaling = 'unscaled'
only_column = 'model_'
def fit(self, X, y):
def fit(self, X, y, prev_model):
# This is a static model, it can' learn anything
pass
+2 -2
View File
@@ -11,7 +11,7 @@ class Model(ABC):
only_column: Optional[str]
@abstractmethod
def fit(self, X, y):
def fit(self, X, y, prev_model):
pass
@abstractmethod
@@ -32,7 +32,7 @@ class SKLearnModel(Model):
def __init__(self, model):
self.model = model
def fit(self, X, y):
def fit(self, X, y, prev_model):
self.model.fit(X, y)
def predict(self, X):
+1 -1
View File
@@ -14,7 +14,7 @@ class StaticMomentumModel(Model):
super().__init__()
self.allow_short = allow_short
def fit(self, X, y):
def fit(self, X, y, prev_model):
# This is a static model, it can' learn anything
pass
+1 -1
View File
@@ -10,7 +10,7 @@ class StaticNaiveModel(Model):
data_scaling = 'unscaled'
only_column = None
def fit(self, X, y):
def fit(self, X, y, prev_model):
# This is a static model, it can' learn anything
pass
+7 -6
View File
@@ -15,15 +15,16 @@ def launch_wandb(project_name:str, default_config:dict, sweep:bool=False):
return wandb
def seperate_configs(wandb: Optional[object], model_config:dict, training_config:dict, data_config:dict) -> tuple[dict,dict,dict]:
def register_config_with_wandb(wandb: Optional[object], model_config:dict, training_config:dict, data_config:dict):
config: dict = wandb.config
if type(wandb) is not type(None):
for k in training_config: training_config[k] = config[k]
for k in model_config: model_config[k] = config[k]
# for k in data_config: data_config[k] = config[k]
return model_config, training_config, data_config
for k in training_config:
training_config[k] = config[k]
for k in model_config:
model_config[k] = config[k]
for k in data_config:
data_config[k] = config[k]
def send_report_to_wandb(results: pd.DataFrame, wandb:Optional[object], project_name: str, model_name: str):
+4 -2
View File
@@ -1,8 +1,9 @@
from utils.load_data import load_data
import pandas as pd
from training.training import run_single_asset_trainig
from reporting.wandb import launch_wandb, send_report_to_wandb, seperate_configs
from reporting.wandb import launch_wandb, send_report_to_wandb, register_config_with_wandb
from models.model_map import map_model_name_to_function
from feature_extractors.feature_extractor_presets import preprocess_feature_extractors_config
from config import get_default_config, validate_config, get_model_name
def setup_pipeline(project_name:str, with_wandb: bool, sweep: bool):
@@ -11,9 +12,10 @@ def setup_pipeline(project_name:str, with_wandb: bool, sweep: bool):
wandb = None
if with_wandb:
wandb = launch_wandb(project_name=project_name, default_config=dict(**model_config, **training_config, **data_config), sweep=sweep)
model_config, training_config, data_config = seperate_configs(wandb, model_config, training_config, data_config)
register_config_with_wandb(wandb, model_config, training_config, data_config)
model_config = map_model_name_to_function(model_config, data_config['method'])
data_config = preprocess_feature_extractors_config(data_config)
pipeline(project_name, wandb, sweep, model_config, training_config, data_config)
+7 -7
View File
@@ -15,10 +15,10 @@ parameters:
values: [True, False]
distribution: categorical
sliding_window_size:
values: [90, 130, 160, 180, 280, 380]
values: [180, 280, 380]
distribution: categorical
retrain_every:
values: [14, 30, 60, 100]
values: [10, 20, 30]
distribution: categorical
scaler:
values: ['minmax', 'normalize', 'minmax', 'standardize', 'none']
@@ -37,10 +37,6 @@ parameters:
log_returns:
values: [True, False]
distribution: categorical
own_features:
value: []
other_features:
value: []
index_column:
value: 'int'
level_1_models:
@@ -49,4 +45,8 @@ parameters:
level_2_models:
value: []
distribution: constant
own_features:
values: [['only_mom', 'date_days'], [], ['level_1', 'date_days'], ['level_1', 'date_days', 'level_2']]
distribution: categorical
other_features:
value: []
+1 -1
View File
@@ -42,7 +42,7 @@ class EvenOddStubModel(Model):
super().__init__()
self.window_length = window_length
def fit(self, X, y):
def fit(self, X, y, prev_model):
assert len(X) == self.window_length
for i in range(len(X)):
assert y[i] == -1 if X[i][0] == 1 else 1
+1 -1
View File
@@ -40,7 +40,7 @@ class IncrementingStubModel(Model):
super().__init__()
self.window_length = window_length
def fit(self, X, y):
def fit(self, X, y, prev_model):
assert len(X) == self.window_length
for i in range(len(X)):
assert X[i][0] + 1 == y[i]
+1 -1
View File
@@ -58,7 +58,7 @@ def walk_forward_train_test(
X_slice = X_slice.to_numpy()
current_model = model.clone()
current_model.fit(X_slice, y_slice.to_numpy())
current_model.fit(X_slice, y_slice.to_numpy(), models[index-1])
iterations_before_retrain = retrain_every
else:
current_model = models[index-1]
+3
View File
@@ -4,3 +4,6 @@ import numpy as np
def get_first_valid_return_index(series: pd.Series) -> int:
return np.where(np.logical_and(series != 0, np.logical_not(np.isnan(series))))[0][0]
def flatten(list_of_lists: list) -> list:
return [item for sublist in list_of_lists for item in sublist]
+3 -1
View File
@@ -4,4 +4,6 @@ import pandas as pd
Period = int
IsLogReturn = bool
FeatureExtractor = Callable[[pd.DataFrame, Period, IsLogReturn], Union[pd.DataFrame, pd.Series]]
FeatureExtractor = Callable[[pd.DataFrame, Period, IsLogReturn], Union[pd.DataFrame, pd.Series]]
Name = str
FeatureExtractorConfig = tuple[Name, FeatureExtractor, list[Period]]