mirror of
https://github.com/webclinic017/drift.git
synced 2026-08-20 14:28:09 +00:00
feat(Core): ensemble models, correct forward returns calculation, scaling, only train from when asset returns are available, major bug fixed in walk_forward_train_test (#35)
* fix(Core): correct forward returns calculation, classifiers are now working again, only train from when asset returns are available * feat(Utils): added get_first_valid_return_index() * feat(Ensemble): return models from `run_whole_pipeline` * feat(Ensemble): added ensemble step, fixed walk_forward_train_test predictions index confusion, * chore(Pipeline): remove unnecessary extra ensemble results dataframe * refactor(Core): removed unnecessary ensemble_train_predict, moved run_single_asset_trainig_pipeline to a separate file * feat(Training): added scaling on expanding window (the past) to walk_forward_train_test(), now printing out mean sharpe ratio * feat(CI): added environment.yml file * chore(Environment): update env.yml * feat(CI): added testing workflow * fix(CI): renamed enviroment.yml * fix(Tests): added missing new parameter to walk_forward_train_test()
This commit is contained in:
+84
-82
@@ -1,12 +1,10 @@
|
||||
from logging import log
|
||||
from typing import Literal
|
||||
from sklearnex import patch_sklearn
|
||||
patch_sklearn()
|
||||
|
||||
from load_data import get_crypto_assets, get_etf_assets, load_data
|
||||
from utils.evaluate import evaluate_predictions
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from sklearn.linear_model import LinearRegression, Lasso, BayesianRidge, LogisticRegression, Ridge
|
||||
from sklearn.tree import DecisionTreeClassifier
|
||||
from sklearn.neighbors import KNeighborsRegressor, KNeighborsClassifier
|
||||
@@ -15,106 +13,110 @@ from sklearn.svm import SVR
|
||||
from sklearn.naive_bayes import GaussianNB
|
||||
from sklearn.neural_network import MLPRegressor, MLPClassifier
|
||||
from sklearn.ensemble import AdaBoostRegressor, RandomForestRegressor, ExtraTreesRegressor, AdaBoostClassifier, GradientBoostingClassifier, RandomForestClassifier, ExtraTreesClassifier
|
||||
from sklearn.preprocessing import MinMaxScaler
|
||||
|
||||
from utils.walk_forward import walk_forward_train_test
|
||||
from training.pipeline import run_single_asset_trainig_pipeline
|
||||
|
||||
|
||||
# Parameters
|
||||
regression_models = [
|
||||
# ('LR', LinearRegression(n_jobs=-1)),
|
||||
('Lasso', Lasso(alpha=0.1, max_iter=10000)),
|
||||
('Lasso', Lasso(alpha=1.0, max_iter=10000)),
|
||||
('Ridge', Ridge(alpha=1.0)),
|
||||
('BayesianRidge', BayesianRidge()),
|
||||
('KNN', KNeighborsRegressor(n_neighbors=15)),
|
||||
# ('AB', AdaBoostRegressor()),
|
||||
# ('LR', LinearRegression(n_jobs=-1)),
|
||||
# ('MLP', MLPRegressor(hidden_layer_sizes=(100,20), max_iter=1000)),
|
||||
('AB', AdaBoostRegressor()),
|
||||
# ('RF', RandomForestRegressor(n_jobs=-1)),
|
||||
# ('SVR', SVR(kernel='rbf', C=1e3, gamma=0.1))
|
||||
]
|
||||
ensemble_model = [('Ensemble - Lasso', Lasso(alpha=1.0, max_iter=10000, positive=True))]
|
||||
|
||||
classification_models = [
|
||||
('LR', LogisticRegression(n_jobs=-1)),
|
||||
('LDA', LinearDiscriminantAnalysis()),
|
||||
('KNN', KNeighborsClassifier()),
|
||||
('CART', DecisionTreeClassifier()),
|
||||
('NB', GaussianNB()),
|
||||
('AB', AdaBoostClassifier()),
|
||||
('RF', RandomForestClassifier(n_jobs=-1))
|
||||
# ('LDA', LinearDiscriminantAnalysis()),
|
||||
# ('KNN', KNeighborsClassifier()),
|
||||
# ('CART', DecisionTreeClassifier()),
|
||||
# ('NB', GaussianNB()),
|
||||
# ('AB', AdaBoostClassifier()),
|
||||
# ('RF', RandomForestClassifier(n_jobs=-1))
|
||||
]
|
||||
|
||||
|
||||
path = 'data/'
|
||||
all_assets = get_crypto_assets(path)
|
||||
|
||||
def run_whole_pipeline(
|
||||
ticker_to_predict: str,
|
||||
load_data_args: dict,
|
||||
models,
|
||||
method: Literal['regression', 'classification'],
|
||||
sliding_window_size: int,
|
||||
retrain_every: int,
|
||||
scaling: bool,
|
||||
):
|
||||
print('--------\nPredicting: ', ticker_to_predict)
|
||||
sliding_window_size = 200
|
||||
retrain_every = 100
|
||||
scaler = 'none' # 'normalize' 'minmax' 'standardize' 'none'
|
||||
include_original_data_in_ensemble = True
|
||||
method = 'regression'
|
||||
data_parameters = dict(path=path,
|
||||
target_asset_lags= [1,2,3,4,5,6,8,10,15],
|
||||
load_other_assets= True,
|
||||
other_asset_lags= [],
|
||||
log_returns= True,
|
||||
add_date_features= True,
|
||||
own_technical_features= 'level2',
|
||||
other_technical_features= 'none',
|
||||
exogenous_features= 'none',
|
||||
index_column= 'int',
|
||||
method= method,
|
||||
)
|
||||
|
||||
X, y = load_data(**load_data_args)
|
||||
|
||||
if scaling:
|
||||
# TODO: should move scaling to an expanding window compomenent, probably worth not turning it on for now
|
||||
feature_scaler = MinMaxScaler(feature_range= (-1, 1))
|
||||
X = pd.DataFrame(feature_scaler.fit_transform(X), columns=X.columns, index=X.index)
|
||||
# TODO: should scale y as well probably
|
||||
|
||||
results = pd.DataFrame()
|
||||
|
||||
for model_name, model in models:
|
||||
|
||||
model_over_time, preds = walk_forward_train_test(
|
||||
model_name=model_name,
|
||||
model = model,
|
||||
X = X,
|
||||
y = y,
|
||||
window_size = sliding_window_size,
|
||||
retrain_every = retrain_every
|
||||
)
|
||||
result = evaluate_predictions(
|
||||
model_name = model_name,
|
||||
y_true = y,
|
||||
y_pred = preds,
|
||||
sliding_window_size = sliding_window_size,
|
||||
method = method,
|
||||
)
|
||||
column_name = ticker_to_predict + "_" + model_name
|
||||
results[column_name] = result
|
||||
|
||||
return results
|
||||
# Run pipeline
|
||||
|
||||
results = pd.DataFrame()
|
||||
all_assets = get_crypto_assets('data/')
|
||||
|
||||
|
||||
|
||||
for asset in all_assets:
|
||||
for method in ['regression']:
|
||||
load_data_args = dict(path='data/',
|
||||
target_asset= asset,
|
||||
target_asset_lags= [1,2,3,4,5,6,8,10,15],
|
||||
load_other_assets= False,
|
||||
other_asset_lags= [],
|
||||
log_returns= True,
|
||||
add_date_features= True,
|
||||
own_technical_features= 'level2',
|
||||
other_technical_features= 'none',
|
||||
exogenous_features= 'none',
|
||||
index_column= 'int',
|
||||
method= method,
|
||||
)
|
||||
print('--------\nPredicting: ', asset)
|
||||
all_predictions = pd.DataFrame()
|
||||
|
||||
current_result = run_whole_pipeline(
|
||||
ticker_to_predict = asset,
|
||||
load_data_args = load_data_args,
|
||||
models = regression_models if method == 'regression' else classification_models,
|
||||
method = method,
|
||||
sliding_window_size = 120,
|
||||
retrain_every = 50,
|
||||
scaling = False
|
||||
)
|
||||
results = pd.concat([results, current_result], axis=1)
|
||||
# 1. Load data
|
||||
data_params = data_parameters.copy()
|
||||
data_params['target_asset'] = asset
|
||||
|
||||
results.to_csv('results.csv')
|
||||
X, y, target_returns = load_data(**data_params)
|
||||
|
||||
# 2. Train Level-1 models
|
||||
current_result, current_predictions = run_single_asset_trainig_pipeline(
|
||||
ticker_to_predict = asset,
|
||||
X = X,
|
||||
y = y,
|
||||
target_returns = target_returns,
|
||||
models = regression_models if method == 'regression' else classification_models,
|
||||
method = method,
|
||||
sliding_window_size = sliding_window_size,
|
||||
retrain_every = retrain_every,
|
||||
scaler = scaler
|
||||
)
|
||||
results = pd.concat([results, current_result], axis=1)
|
||||
all_predictions = pd.concat([all_predictions, current_predictions], axis=1)
|
||||
|
||||
# 3. Train Level-2 (Ensemble) model
|
||||
ensemble_X = all_predictions
|
||||
if include_original_data_in_ensemble:
|
||||
ensemble_X = pd.concat([ensemble_X, X], axis=1)
|
||||
|
||||
ensemble_result, ensemble_preds = run_single_asset_trainig_pipeline(
|
||||
ticker_to_predict = asset,
|
||||
X = ensemble_X,
|
||||
y = target_returns,
|
||||
target_returns = target_returns,
|
||||
models = ensemble_model,
|
||||
method = 'regression',
|
||||
sliding_window_size = sliding_window_size,
|
||||
retrain_every = retrain_every,
|
||||
scaler = scaler
|
||||
)
|
||||
|
||||
results = pd.concat([results, ensemble_result], axis=1)
|
||||
all_predictions = pd.concat([all_predictions, ensemble_preds], axis=1)
|
||||
|
||||
results.to_csv('results.csv')
|
||||
|
||||
level1_columns = results[[column for column in results.columns if 'Ensemble' not in column]]
|
||||
ensemble_columns = results[[column for column in results.columns if 'Ensemble' in column]]
|
||||
|
||||
print("Mean Sharpe ratio for Level-1 models: ", level1_columns.loc['sharpe'].mean())
|
||||
print("Mean Sharpe ratio for Level-2 (Ensemble) models: ", ensemble_columns.loc['sharpe'].mean())
|
||||
Reference in New Issue
Block a user