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 models.sklearn import SKLearnModel
from sklearn.ensemble import VotingClassifier
from transformations.retrieve import get_pca, get_rfe, get_scaler
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_event_filter_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["mode"] = "training"
@@ -89,3 +91,18 @@ def __preprocess_event_labeller_config(config_dict: dict) -> dict:
config_dict["forecasting_horizon"]
)
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
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:
classification_models = [
@@ -45,13 +15,9 @@ def get_default_ensemble_config() -> RawConfig:
meta_models = ["LogisticRegression_two_class", "LGBM"]
return RawConfig(
directional_models_meta=True,
dimensionality_reduction=False,
dimensionality_reduction_ratio=0.5,
n_features_to_select=30,
expanding_window_base=False,
expanding_window_meta=True,
sliding_window_size_base=380,
sliding_window_size_meta=240,
sliding_window_size=380,
retrain_every=10,
scaler="minmax", # 'normalize' 'minmax' 'standardize'
assets=["daily_crypto"],
@@ -72,17 +38,20 @@ def get_default_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"]
return RawConfig(
directional_models_meta=True,
dimensionality_reduction=True,
dimensionality_reduction_ratio=0.5,
n_features_to_select=30,
expanding_window_base=True,
expanding_window_meta=True,
sliding_window_size_base=3800,
sliding_window_size_meta=2400,
sliding_window_size=3800,
retrain_every=1000,
scaler="minmax", # 'normalize' 'minmax' 'standardize'
assets=["fivemin_crypto"],
+3 -11
View File
@@ -6,28 +6,20 @@ 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:
value: True
expanding_window_meta:
value: True
sliding_window_size_base:
sliding_window_size:
value: 380
sliding_window_size_meta:
values: [250, 300, 380]
distribution: categorical
n_features_to_select:
values: [40, 50, 60]
distribution: categorical
dimensionality_reduction:
value: True
dimensionality_reduction_ratio:
value: 0.5
retrain_every:
value: 20
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 sklearn.base import BaseEstimator
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
class RawConfig(BaseModel):
directional_models_meta: bool
dimensionality_reduction: bool
dimensionality_reduction_ratio: float
n_features_to_select: int
expanding_window_base: bool
expanding_window_meta: bool
sliding_window_size_base: int
sliding_window_size_meta: int
sliding_window_size: int
retrain_every: int
scaler: Literal["normalize", "minmax", "standardize"]
@@ -38,15 +35,8 @@ class RawConfig(BaseModel):
@dataclass
class Config:
directional_models_meta: bool
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
sliding_window_size: int
retrain_every: int
scaler: Literal["normalize", "minmax", "standardize"]
assets: DataCollection
target_asset: DataSource
@@ -66,6 +56,8 @@ class Config:
directional_model: Model
meta_model: Model
transformations: list[Transformation]
@validator("directional_model", "meta_model")
def check_model(cls, v):
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
return (
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 config.types import Config, RawConfig
from config.presets import (
get_dev_config,
get_default_ensemble_config,
get_lightweight_ensemble_config,
)
@@ -56,24 +55,26 @@ def __inference(config: Config, pipeline_outcome: PipelineOutcome):
# 3. Train directional models
directional_training_outcome = train_directional_model(
X,
y,
forward_returns,
config,
config.directional_model,
X=X,
y=y,
forward_returns=forward_returns,
config=config,
model=config.directional_model,
transformations=config.transformations,
from_index=inference_from,
preloaded_training_step=pipeline_outcome.directional_training,
)
# 4. Run bet sizing on primary model's output
bet_sizing_outcome = bet_sizing_with_meta_model(
X,
directional_training_outcome.training.predictions,
y,
forward_returns,
config.meta_model,
config,
"meta",
X=X,
input_predictions=directional_training_outcome.training.predictions,
y=y,
forward_returns=forward_returns,
model=config.meta_model,
transformations=config.transformations,
config=config,
model_suffix="meta",
from_index=inference_from,
transformations_over_time=pipeline_outcome.bet_sizing.meta_transformations,
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,
config,
config.directional_model,
config.transformations,
from_index=None,
preloaded_training_step=None,
)
@@ -92,6 +93,7 @@ def __run_training(config: Config) -> PipelineOutcome:
y,
forward_returns,
config.meta_model,
config.transformations,
config,
"meta",
None,
+5 -18
View File
@@ -9,9 +9,7 @@ from typing import Optional
from config.types import Config
from .types import BetSizingWithMetaOutcome, ModelOverTime, TransformationsOverTime
from training.walk_forward import walk_forward_process_transformations
from transformations.scaler import get_scaler
from transformations.rfe import RFETransformation
from transformations.pca import PCATransformation
from transformations.base import Transformation
def bet_sizing_with_meta_model(
@@ -20,6 +18,7 @@ def bet_sizing_with_meta_model(
y: ySeries,
forward_returns: ForwardReturnSeries,
model: Model,
transformations: list[Transformation],
config: Config,
model_suffix: str,
from_index: Optional[pd.Timestamp],
@@ -44,21 +43,10 @@ def bet_sizing_with_meta_model(
X=meta_X,
y=meta_y,
forward_returns=forward_returns,
expanding_window=config.expanding_window_meta,
window_size=config.sliding_window_size_meta,
window_size=config.sliding_window_size,
retrain_every=config.retrain_every,
from_index=from_index,
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,
),
],
transformations=transformations,
)
meta_outcome = train_model(
@@ -67,8 +55,7 @@ def bet_sizing_with_meta_model(
y=meta_y,
forward_returns=forward_returns,
model=model,
expanding_window=config.expanding_window_meta,
sliding_window_size=config.sliding_window_size_meta,
sliding_window_size=config.sliding_window_size,
retrain_every=config.retrain_every,
from_index=from_index,
no_of_classes="two",
+6 -20
View File
@@ -1,5 +1,7 @@
import pandas as pd
from transformations.base import Transformation
from .types import DirectionalTrainingOutcome, TrainingOutcome
from training.train_model import train_model
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 config.types import Config
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(
@@ -20,6 +17,7 @@ def train_directional_model(
forward_returns: pd.Series,
config: Config,
model: Model,
transformations: list[Transformation],
from_index: Optional[pd.Timestamp],
preloaded_training_step: Optional[DirectionalTrainingOutcome] = None,
) -> DirectionalTrainingOutcome:
@@ -30,21 +28,10 @@ def train_directional_model(
X=X,
y=y,
forward_returns=forward_returns,
expanding_window=config.expanding_window_base,
window_size=config.sliding_window_size_base,
window_size=config.sliding_window_size,
retrain_every=config.retrain_every,
from_index=from_index,
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,
),
],
transformations=transformations,
)
else:
transformations_over_time = preloaded_training_step.transformations
@@ -55,8 +42,7 @@ def train_directional_model(
y=y,
forward_returns=forward_returns,
model=model,
expanding_window=config.expanding_window_base,
sliding_window_size=config.sliding_window_size_base,
sliding_window_size=config.sliding_window_size,
retrain_every=config.retrain_every,
from_index=from_index,
no_of_classes=config.no_of_classes,
+2 -3
View File
@@ -16,7 +16,6 @@ def train_model(
y: pd.Series,
forward_returns: pd.Series,
model: Model,
expanding_window: bool,
sliding_window_size: int,
retrain_every: int,
from_index: Optional[pd.Timestamp],
@@ -34,7 +33,7 @@ def train_model(
X=X,
y=y,
forward_returns=forward_returns,
expanding_window=expanding_window,
expanding_window=True,
window_size=sliding_window_size,
retrain_every=retrain_every,
from_index=from_index,
@@ -57,7 +56,7 @@ def train_model(
model_over_time=model_over_time,
transformations_over_time=transformations_over_time,
X=X,
expanding_window=expanding_window,
expanding_window=True,
window_size=sliding_window_size,
retrain_every=retrain_every,
from_index=from_index,
@@ -11,7 +11,6 @@ def walk_forward_process_transformations(
X: XDataFrame,
y: ySeries,
forward_returns: ForwardReturnSeries,
expanding_window: bool,
window_size: int,
retrain_every: int,
from_index: Optional[pd.Timestamp],
@@ -36,11 +35,7 @@ def walk_forward_process_transformations(
iterations_before_retrain = 0
for index in tqdm(range(train_from, train_till)):
train_window_start = (
X.index[first_nonzero_return]
if expanding_window
else X.index[index - window_size - 1]
)
train_window_start = X.index[first_nonzero_return]
if iterations_before_retrain <= 0 or pd.isna(
transformations_over_time[0][index - 1]
@@ -13,7 +13,6 @@ def walk_forward_process_transformations(
X: XDataFrame,
y: ySeries,
forward_returns: ForwardReturnSeries,
expanding_window: bool,
window_size: int,
retrain_every: int,
from_index: Optional[pd.Timestamp],
@@ -40,7 +39,6 @@ def walk_forward_process_transformations(
preprocess_transformations_window.remote(
X,
y,
expanding_window,
window_size,
transformations,
first_nonzero_return,
@@ -63,17 +61,12 @@ def walk_forward_process_transformations(
def preprocess_transformations_window(
X: XDataFrame,
y: ySeries,
expanding_window: bool,
window_size: int,
transformations: list[Transformation],
first_nonzero_return: int,
index: int,
) -> tuple[list[Transformation], int]:
train_window_start = (
X.index[first_nonzero_return]
if expanding_window
else X.index[index - window_size - 1]
)
train_window_start = X.index[first_nonzero_return]
train_window_end = X.index[index - 1]
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")