feature(Config): added transformation parameters to Config, removed expanding_window (it's ON now)

This commit is contained in:
Mark Aron Szulyovszky
2022-02-19 14:44:49 +01:00
parent 8dd2d88740
commit 3b9d7f554a
32 changed files with 112 additions and 24289 deletions
+17
View File
@@ -9,6 +9,7 @@ from labeling.eventfilters_map import eventfilters_map
from labeling.labellers_map import labellers_map from labeling.labellers_map import labellers_map
from models.sklearn import SKLearnModel from models.sklearn import SKLearnModel
from sklearn.ensemble import VotingClassifier from sklearn.ensemble import VotingClassifier
from transformations.retrieve import get_pca, get_rfe, get_scaler
def preprocess_config(raw_config: RawConfig) -> Config: def preprocess_config(raw_config: RawConfig) -> Config:
@@ -18,6 +19,7 @@ def preprocess_config(raw_config: RawConfig) -> Config:
config_dict = __preprocess_data_collections_config(config_dict) config_dict = __preprocess_data_collections_config(config_dict)
config_dict = __preprocess_event_filter_config(config_dict) config_dict = __preprocess_event_filter_config(config_dict)
config_dict = __preprocess_event_labeller_config(config_dict) config_dict = __preprocess_event_labeller_config(config_dict)
config_dict = __preprocess_transformations_config(config_dict)
config_dict["no_of_classes"] = "two" config_dict["no_of_classes"] = "two"
config_dict["mode"] = "training" config_dict["mode"] = "training"
@@ -89,3 +91,18 @@ def __preprocess_event_labeller_config(config_dict: dict) -> dict:
config_dict["forecasting_horizon"] config_dict["forecasting_horizon"]
) )
return config_dict return config_dict
def __preprocess_transformations_config(config_dict: dict) -> dict:
transformations = [
get_scaler(config_dict["scaler"]),
get_pca(config_dict["dimensionality_reduction_ratio"], config_dict["sliding_window_size"]),
get_rfe(config_dict["n_features_to_select"]),
]
transformations = [x for x in transformations if x is not None]
config_dict["transformations"] = transformations
config_dict.pop("scaler")
config_dict.pop("dimensionality_reduction_ratio")
config_dict.pop("n_features_to_select")
return config_dict
+12 -43
View File
@@ -1,36 +1,6 @@
from .types import RawConfig, Config from .types import RawConfig, Config
def get_dev_config() -> RawConfig:
classification_models = ["LogisticRegression_two_class"]
return RawConfig(
directional_models_meta=False,
dimensionality_reduction=False,
n_features_to_select=30,
expanding_window_base=False,
expanding_window_meta=False,
sliding_window_size_base=380,
sliding_window_size_meta=1,
retrain_every=20,
scaler="minmax", # 'normalize' 'minmax' 'standardize'
assets=["daily_only_btc"],
target_asset="BTC_USD",
other_assets=[],
exogenous_data=[],
load_non_target_asset=True,
own_features=["level_2", "date_days"],
other_features=["single_mom"],
exogenous_features=["z_score"],
directional_models=classification_models,
meta_models=[],
event_filter="none",
labeling="two_class",
forecasting_horizon=100,
)
def get_default_ensemble_config() -> RawConfig: def get_default_ensemble_config() -> RawConfig:
classification_models = [ classification_models = [
@@ -45,13 +15,9 @@ def get_default_ensemble_config() -> RawConfig:
meta_models = ["LogisticRegression_two_class", "LGBM"] meta_models = ["LogisticRegression_two_class", "LGBM"]
return RawConfig( return RawConfig(
directional_models_meta=True, dimensionality_reduction_ratio=0.5,
dimensionality_reduction=False,
n_features_to_select=30, n_features_to_select=30,
expanding_window_base=False, sliding_window_size=380,
expanding_window_meta=True,
sliding_window_size_base=380,
sliding_window_size_meta=240,
retrain_every=10, retrain_every=10,
scaler="minmax", # 'normalize' 'minmax' 'standardize' scaler="minmax", # 'normalize' 'minmax' 'standardize'
assets=["daily_crypto"], assets=["daily_crypto"],
@@ -72,17 +38,20 @@ def get_default_ensemble_config() -> RawConfig:
def get_lightweight_ensemble_config() -> RawConfig: def get_lightweight_ensemble_config() -> RawConfig:
classification_models = ["LogisticRegression_two_class", "LGBM"] classification_models = [
"LogisticRegression_two_class",
"LDA",
"NB",
"RFC",
"LGBM",
# "StaticMom",
]
meta_models = ["LogisticRegression_two_class", "LGBM"] meta_models = ["LogisticRegression_two_class", "LGBM"]
return RawConfig( return RawConfig(
directional_models_meta=True, dimensionality_reduction_ratio=0.5,
dimensionality_reduction=True,
n_features_to_select=30, n_features_to_select=30,
expanding_window_base=True, sliding_window_size=3800,
expanding_window_meta=True,
sliding_window_size_base=3800,
sliding_window_size_meta=2400,
retrain_every=1000, retrain_every=1000,
scaler="minmax", # 'normalize' 'minmax' 'standardize' scaler="minmax", # 'normalize' 'minmax' 'standardize'
assets=["fivemin_crypto"], assets=["fivemin_crypto"],
+3 -11
View File
@@ -6,28 +6,20 @@ metric:
goal: maximize goal: maximize
name: sharpe name: sharpe
parameters: parameters:
directional_models_meta:
value: True
assets: assets:
value: ['daily_crypto'] value: ['daily_crypto']
other_assets: other_assets:
value: ['daily_etf'] value: ['daily_etf']
exogenous_data: exogenous_data:
value: ['daily_glassnode'] value: ['daily_glassnode']
expanding_window_base: sliding_window_size:
value: True
expanding_window_meta:
value: True
sliding_window_size_base:
value: 380 value: 380
sliding_window_size_meta:
values: [250, 300, 380]
distribution: categorical distribution: categorical
n_features_to_select: n_features_to_select:
values: [40, 50, 60] values: [40, 50, 60]
distribution: categorical distribution: categorical
dimensionality_reduction: dimensionality_reduction_ratio:
value: True value: 0.5
retrain_every: retrain_every:
value: 20 value: 20
scaler: scaler:
-55
View File
@@ -1,55 +0,0 @@
program: run_sweep.py
method: bayes
project: price-forecasting
name: Level-2 models
metric:
goal: maximize
name: sharpe
parameters:
directional_models_meta:
value: True
assets:
value: ['daily_crypto']
other_assets:
value: ['daily_etf']
exogenous_data:
value: ['daily_glassnode']
expanding_window_base:
values: [True, False]
distribution: categorical
expanding_window_meta:
values: [True, False]
distribution: categorical
n_features_to_select:
values: [10, 20, 30]
distribution: categorical
dimensionality_reduction:
value: True
sliding_window_size_base:
values: [180, 280, 380]
distribution: categorical
sliding_window_size_meta:
values: [180, 280, 380]
distribution: categorical
retrain_every:
values: [10, 20, 30]
distribution: categorical
scaler:
value: 'minmax'
no_of_classes:
values: ['two', 'three-balanced', 'three-imbalanced']
distribution: categorical
load_non_target_asset:
values: [True, False]
distribution: categorical
directional_models:
value: ["LogisticRegression_two_class", "LDA", "KNN", "CART", "NB", "AB", "RFC", "StaticMom"]
meta_models:
values: ["LogisticRegression_two_class", "LDA", "KNN", "CART", "NB", "AB", "RFC"]
distribution: categorical
own_features:
values: [['single_mom', 'date_days'], [], ['level_1', 'date_days'], ['date_days', 'level_2']]
distribution: categorical
other_features:
values: [[], ['level_1']]
distribution: categorical
-49
View File
@@ -1,49 +0,0 @@
program: run_sweep.py
method: grid
project: price-forecasting
name: Level-1 models
metric:
goal: maximize
name: sharpe
parameters:
directional_models_meta:
value: True
assets:
value: ['daily_crypto']
other_assets:
value: ['daily_etf']
exogenous_data:
value: ['daily_glassnode']
expanding_window_base:
values: [True, False]
distribution: categorical
expanding_window_meta:
value: False
n_features_to_select:
value: 50
dimensionality_reduction:
value: True
sliding_window_size_base:
value: 380
sliding_window_size_meta:
value: 380
retrain_every:
values: [10, 20, 30]
distribution: categorical
scaler:
value: 'minmax'
no_of_classes:
value: 'two'
load_non_target_asset:
value: True
directional_models:
values: [['LogisticRegression_two_class'], ['SVC'], ['LDA'], ['KNN'], ['CART'], ['MNB'], ['NB'], ['AB'], ['RFC'], ['XGB_two_class'], ['LGBM']]
distribution: categorical
meta_models:
value: ["LGBM", "LogisticRegression_two_class"]
own_features:
value: ['date_days', 'level_2', 'lags_up_to_5']
other_features:
value: ['level_2', 'lags_up_to_5']
exogenous_features:
value: ['z_score']
+6 -14
View File
@@ -7,16 +7,13 @@ from feature_extractors.types import FeatureExtractor, ScalerTypes
from labeling.types import EventFilter, EventLabeller from labeling.types import EventFilter, EventLabeller
from sklearn.base import BaseEstimator from sklearn.base import BaseEstimator
from dataclasses import dataclass from dataclasses import dataclass
from transformations.base import Transformation
# RawConfig is needed to ensure we can declare config presets here with static typing, we then convert it to Config # RawConfig is needed to ensure we can declare config presets here with static typing, we then convert it to Config
class RawConfig(BaseModel): class RawConfig(BaseModel):
directional_models_meta: bool dimensionality_reduction_ratio: float
dimensionality_reduction: bool
n_features_to_select: int n_features_to_select: int
expanding_window_base: bool sliding_window_size: int
expanding_window_meta: bool
sliding_window_size_base: int
sliding_window_size_meta: int
retrain_every: int retrain_every: int
scaler: Literal["normalize", "minmax", "standardize"] scaler: Literal["normalize", "minmax", "standardize"]
@@ -38,15 +35,8 @@ class RawConfig(BaseModel):
@dataclass @dataclass
class Config: class Config:
directional_models_meta: bool sliding_window_size: int
dimensionality_reduction: bool
n_features_to_select: int
expanding_window_base: bool
expanding_window_meta: bool
sliding_window_size_base: int
sliding_window_size_meta: int
retrain_every: int retrain_every: int
scaler: Literal["normalize", "minmax", "standardize"]
assets: DataCollection assets: DataCollection
target_asset: DataSource target_asset: DataSource
@@ -66,6 +56,8 @@ class Config:
directional_model: Model directional_model: Model
meta_model: Model meta_model: Model
transformations: list[Transformation]
@validator("directional_model", "meta_model") @validator("directional_model", "meta_model")
def check_model(cls, v): def check_model(cls, v):
assert isinstance(v, BaseEstimator) assert isinstance(v, BaseEstimator)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -20,5 +20,5 @@ def has_enough_samples_to_train(X: XDataFrame, config: Config) -> bool:
samples_to_train = len(X) - first_valid_index samples_to_train = len(X) - first_valid_index
return ( return (
samples_to_train samples_to_train
> config.sliding_window_size_base + config.sliding_window_size_meta + 100 > (config.sliding_window_size *2) + 100
) )
+14 -13
View File
@@ -5,7 +5,6 @@ from reporting.saving import load_models
from run_pipeline import run_pipeline from run_pipeline import run_pipeline
from config.types import Config, RawConfig from config.types import Config, RawConfig
from config.presets import ( from config.presets import (
get_dev_config,
get_default_ensemble_config, get_default_ensemble_config,
get_lightweight_ensemble_config, get_lightweight_ensemble_config,
) )
@@ -56,24 +55,26 @@ def __inference(config: Config, pipeline_outcome: PipelineOutcome):
# 3. Train directional models # 3. Train directional models
directional_training_outcome = train_directional_model( directional_training_outcome = train_directional_model(
X, X=X,
y, y=y,
forward_returns, forward_returns=forward_returns,
config, config=config,
config.directional_model, model=config.directional_model,
transformations=config.transformations,
from_index=inference_from, from_index=inference_from,
preloaded_training_step=pipeline_outcome.directional_training, preloaded_training_step=pipeline_outcome.directional_training,
) )
# 4. Run bet sizing on primary model's output # 4. Run bet sizing on primary model's output
bet_sizing_outcome = bet_sizing_with_meta_model( bet_sizing_outcome = bet_sizing_with_meta_model(
X, X=X,
directional_training_outcome.training.predictions, input_predictions=directional_training_outcome.training.predictions,
y, y=y,
forward_returns, forward_returns=forward_returns,
config.meta_model, model=config.meta_model,
config, transformations=config.transformations,
"meta", config=config,
model_suffix="meta",
from_index=inference_from, from_index=inference_from,
transformations_over_time=pipeline_outcome.bet_sizing.meta_transformations, transformations_over_time=pipeline_outcome.bet_sizing.meta_transformations,
preloaded_models=pipeline_outcome.bet_sizing.meta_training.model_over_time, preloaded_models=pipeline_outcome.bet_sizing.meta_training.model_over_time,
+2
View File
@@ -81,6 +81,7 @@ def __run_training(config: Config) -> PipelineOutcome:
forward_returns, forward_returns,
config, config,
config.directional_model, config.directional_model,
config.transformations,
from_index=None, from_index=None,
preloaded_training_step=None, preloaded_training_step=None,
) )
@@ -92,6 +93,7 @@ def __run_training(config: Config) -> PipelineOutcome:
y, y,
forward_returns, forward_returns,
config.meta_model, config.meta_model,
config.transformations,
config, config,
"meta", "meta",
None, None,
+5 -18
View File
@@ -9,9 +9,7 @@ from typing import Optional
from config.types import Config from config.types import Config
from .types import BetSizingWithMetaOutcome, ModelOverTime, TransformationsOverTime from .types import BetSizingWithMetaOutcome, ModelOverTime, TransformationsOverTime
from training.walk_forward import walk_forward_process_transformations from training.walk_forward import walk_forward_process_transformations
from transformations.scaler import get_scaler from transformations.base import Transformation
from transformations.rfe import RFETransformation
from transformations.pca import PCATransformation
def bet_sizing_with_meta_model( def bet_sizing_with_meta_model(
@@ -20,6 +18,7 @@ def bet_sizing_with_meta_model(
y: ySeries, y: ySeries,
forward_returns: ForwardReturnSeries, forward_returns: ForwardReturnSeries,
model: Model, model: Model,
transformations: list[Transformation],
config: Config, config: Config,
model_suffix: str, model_suffix: str,
from_index: Optional[pd.Timestamp], from_index: Optional[pd.Timestamp],
@@ -44,21 +43,10 @@ def bet_sizing_with_meta_model(
X=meta_X, X=meta_X,
y=meta_y, y=meta_y,
forward_returns=forward_returns, forward_returns=forward_returns,
expanding_window=config.expanding_window_meta, window_size=config.sliding_window_size,
window_size=config.sliding_window_size_meta,
retrain_every=config.retrain_every, retrain_every=config.retrain_every,
from_index=from_index, from_index=from_index,
transformations=[ transformations=transformations,
get_scaler(config.scaler),
PCATransformation(
ratio_components_to_keep=0.5,
sliding_window_size=config.sliding_window_size_meta,
),
RFETransformation(
n_feature_to_select=40,
model=default_feature_selector_classification,
),
],
) )
meta_outcome = train_model( meta_outcome = train_model(
@@ -67,8 +55,7 @@ def bet_sizing_with_meta_model(
y=meta_y, y=meta_y,
forward_returns=forward_returns, forward_returns=forward_returns,
model=model, model=model,
expanding_window=config.expanding_window_meta, sliding_window_size=config.sliding_window_size,
sliding_window_size=config.sliding_window_size_meta,
retrain_every=config.retrain_every, retrain_every=config.retrain_every,
from_index=from_index, from_index=from_index,
no_of_classes="two", no_of_classes="two",
+6 -20
View File
@@ -1,5 +1,7 @@
import pandas as pd import pandas as pd
from transformations.base import Transformation
from .types import DirectionalTrainingOutcome, TrainingOutcome from .types import DirectionalTrainingOutcome, TrainingOutcome
from training.train_model import train_model from training.train_model import train_model
from training.walk_forward import walk_forward_process_transformations from training.walk_forward import walk_forward_process_transformations
@@ -7,11 +9,6 @@ from training.walk_forward import walk_forward_process_transformations
from typing import Optional from typing import Optional
from config.types import Config from config.types import Config
from models.base import Model from models.base import Model
from models.model_map import default_feature_selector_classification
from transformations.scaler import get_scaler
from transformations.rfe import RFETransformation
from transformations.pca import PCATransformation
def train_directional_model( def train_directional_model(
@@ -20,6 +17,7 @@ def train_directional_model(
forward_returns: pd.Series, forward_returns: pd.Series,
config: Config, config: Config,
model: Model, model: Model,
transformations: list[Transformation],
from_index: Optional[pd.Timestamp], from_index: Optional[pd.Timestamp],
preloaded_training_step: Optional[DirectionalTrainingOutcome] = None, preloaded_training_step: Optional[DirectionalTrainingOutcome] = None,
) -> DirectionalTrainingOutcome: ) -> DirectionalTrainingOutcome:
@@ -30,21 +28,10 @@ def train_directional_model(
X=X, X=X,
y=y, y=y,
forward_returns=forward_returns, forward_returns=forward_returns,
expanding_window=config.expanding_window_base, window_size=config.sliding_window_size,
window_size=config.sliding_window_size_base,
retrain_every=config.retrain_every, retrain_every=config.retrain_every,
from_index=from_index, from_index=from_index,
transformations=[ transformations=transformations,
get_scaler(config.scaler),
PCATransformation(
ratio_components_to_keep=0.5,
sliding_window_size=config.sliding_window_size_base,
),
RFETransformation(
n_feature_to_select=40,
model=default_feature_selector_classification,
),
],
) )
else: else:
transformations_over_time = preloaded_training_step.transformations transformations_over_time = preloaded_training_step.transformations
@@ -55,8 +42,7 @@ def train_directional_model(
y=y, y=y,
forward_returns=forward_returns, forward_returns=forward_returns,
model=model, model=model,
expanding_window=config.expanding_window_base, sliding_window_size=config.sliding_window_size,
sliding_window_size=config.sliding_window_size_base,
retrain_every=config.retrain_every, retrain_every=config.retrain_every,
from_index=from_index, from_index=from_index,
no_of_classes=config.no_of_classes, no_of_classes=config.no_of_classes,
+2 -3
View File
@@ -16,7 +16,6 @@ def train_model(
y: pd.Series, y: pd.Series,
forward_returns: pd.Series, forward_returns: pd.Series,
model: Model, model: Model,
expanding_window: bool,
sliding_window_size: int, sliding_window_size: int,
retrain_every: int, retrain_every: int,
from_index: Optional[pd.Timestamp], from_index: Optional[pd.Timestamp],
@@ -34,7 +33,7 @@ def train_model(
X=X, X=X,
y=y, y=y,
forward_returns=forward_returns, forward_returns=forward_returns,
expanding_window=expanding_window, expanding_window=True,
window_size=sliding_window_size, window_size=sliding_window_size,
retrain_every=retrain_every, retrain_every=retrain_every,
from_index=from_index, from_index=from_index,
@@ -57,7 +56,7 @@ def train_model(
model_over_time=model_over_time, model_over_time=model_over_time,
transformations_over_time=transformations_over_time, transformations_over_time=transformations_over_time,
X=X, X=X,
expanding_window=expanding_window, expanding_window=True,
window_size=sliding_window_size, window_size=sliding_window_size,
retrain_every=retrain_every, retrain_every=retrain_every,
from_index=from_index, from_index=from_index,
@@ -11,7 +11,6 @@ def walk_forward_process_transformations(
X: XDataFrame, X: XDataFrame,
y: ySeries, y: ySeries,
forward_returns: ForwardReturnSeries, forward_returns: ForwardReturnSeries,
expanding_window: bool,
window_size: int, window_size: int,
retrain_every: int, retrain_every: int,
from_index: Optional[pd.Timestamp], from_index: Optional[pd.Timestamp],
@@ -36,11 +35,7 @@ def walk_forward_process_transformations(
iterations_before_retrain = 0 iterations_before_retrain = 0
for index in tqdm(range(train_from, train_till)): for index in tqdm(range(train_from, train_till)):
train_window_start = ( train_window_start = X.index[first_nonzero_return]
X.index[first_nonzero_return]
if expanding_window
else X.index[index - window_size - 1]
)
if iterations_before_retrain <= 0 or pd.isna( if iterations_before_retrain <= 0 or pd.isna(
transformations_over_time[0][index - 1] transformations_over_time[0][index - 1]
@@ -13,7 +13,6 @@ def walk_forward_process_transformations(
X: XDataFrame, X: XDataFrame,
y: ySeries, y: ySeries,
forward_returns: ForwardReturnSeries, forward_returns: ForwardReturnSeries,
expanding_window: bool,
window_size: int, window_size: int,
retrain_every: int, retrain_every: int,
from_index: Optional[pd.Timestamp], from_index: Optional[pd.Timestamp],
@@ -40,7 +39,6 @@ def walk_forward_process_transformations(
preprocess_transformations_window.remote( preprocess_transformations_window.remote(
X, X,
y, y,
expanding_window,
window_size, window_size,
transformations, transformations,
first_nonzero_return, first_nonzero_return,
@@ -63,17 +61,12 @@ def walk_forward_process_transformations(
def preprocess_transformations_window( def preprocess_transformations_window(
X: XDataFrame, X: XDataFrame,
y: ySeries, y: ySeries,
expanding_window: bool,
window_size: int, window_size: int,
transformations: list[Transformation], transformations: list[Transformation],
first_nonzero_return: int, first_nonzero_return: int,
index: int, index: int,
) -> tuple[list[Transformation], int]: ) -> tuple[list[Transformation], int]:
train_window_start = ( train_window_start = X.index[first_nonzero_return]
X.index[first_nonzero_return]
if expanding_window
else X.index[index - window_size - 1]
)
train_window_end = X.index[index - 1] train_window_end = X.index[index - 1]
X_expanding_window = X[train_window_start:train_window_end] X_expanding_window = X[train_window_start:train_window_end]
+42
View File
@@ -0,0 +1,42 @@
from .rfe import RFETransformation
from .pca import PCATransformation
from .sklearn import SKLearnTransformation
from typing import Literal, Optional
from models.model_map import default_feature_selector_classification
from sklearn.preprocessing import MinMaxScaler, Normalizer, StandardScaler
ScalerTypes = Literal["normalize", "minmax", "standardize"]
def get_rfe(n_feature_to_select: int) -> Optional[RFETransformation]:
if n_feature_to_select > 0:
return RFETransformation(
n_feature_to_select=n_feature_to_select,
model=default_feature_selector_classification,
)
else:
return None
def get_pca(
ratio_components_to_keep: float, sliding_window_size: int
) -> Optional[PCATransformation]:
if ratio_components_to_keep > 0:
return PCATransformation(
ratio_components_to_keep=ratio_components_to_keep,
sliding_window_size=sliding_window_size,
)
else:
return None
def get_scaler(type: ScalerTypes) -> SKLearnTransformation:
if type == "normalize":
return SKLearnTransformation(Normalizer())
elif type == "minmax":
return SKLearnTransformation(MinMaxScaler(feature_range=(-1, 1)))
elif type == "standardize":
return SKLearnTransformation(StandardScaler())
else:
raise Exception("Scaler type not supported")
-16
View File
@@ -1,16 +0,0 @@
from sklearn.preprocessing import MinMaxScaler, Normalizer, StandardScaler
from .sklearn import SKLearnTransformation
from typing import Literal
ScalerTypes = Literal["normalize", "minmax", "standardize"]
def get_scaler(type: ScalerTypes) -> SKLearnTransformation:
if type == "normalize":
return SKLearnTransformation(Normalizer())
elif type == "minmax":
return SKLearnTransformation(MinMaxScaler(feature_range=(-1, 1)))
elif type == "standardize":
return SKLearnTransformation(StandardScaler())
else:
raise Exception("Scaler type not supported")