quantlab.model.predefined.xgb.XGBoostRegressor#

class quantlab.model.predefined.xgb.XGBoostRegressor(config)[source]#

Bases: LibraryModel

Predict future returns with an XGBoost Booster.

Training calls xgb.train on the rows LibraryModel builds: one per cell with a valid training target, NaN features kept (xgboost treats them as missing; the default _transform_feature turns infinities into NaN). The label a tree fits is the training target, so overriding _transform_target trains on a per-bar rank or z-score while the headline metrics still score the raw primary label, index 0. Each label is one output of a multi-output regression.

The training objective is the pooled CCC loss 1 - ccc (see ccc_objective), applied to every label column on its own. Unless base_score is given, the Booster starts from the mean of the training target (written to the run summary as base_score) instead of xgboost’s 0.5, because the CCC gradient barely corrects a constant offset while ccc is near zero. Setting objective in the hyperparameters switches back to that built-in xgboost objective.

Hyperparameters come from config.hyperparameters, without the keys LibraryModel reads itself (LIBRARY_RESERVED_HYPERPARAMETERS). num_boost_round (default 1000) is taken out separately; every other key overrides the matching entry of DEFAULT_PARAMS, and seed defaults to config.random_seed. Unless device is given, training runs on "cuda" when the installed xgboost is a CUDA build and a CUDA device is visible, and on "cpu" otherwise, never on Apple MPS; the check does not import torch. scikit-learn style aliases such as learning_rate or n_estimators are rewritten to the native names first; giving both an alias and its native name raises ValueError. The user’s dict is never modified, and the parameters actually used are recorded under resolved_hyperparameters in the trained unit’s run.json and in the run config.

With hyperparameters["early_stopping"] set and validation rows, xgb.callback.EarlyStopping watches the validation rmse; rmse is appended to a user eval_metric that lacks it. Every other metric, and the ccc_loss curve of the training objective, is logged only. Patience counts boosting rounds. save_best=True means the returned Booster is already truncated to best_iteration + 1 trees, so the .joblib checkpoint is the best model, and best_iteration and best_score (an RMSE) are written to the run summary. Without a usable validation segment a warning is logged and all rounds are trained.

After training, per-factor importance (weight, gain and total_gain) is written to the run summary as importance_{type}/{factor} and logged as a sorted table plus a top-30 bar chart. This is best effort: a Booster that cannot report an importance type only logs a warning, and the checkpoint is never lost because of it.

train_cv (rolling walk-forward cross-validation) is inherited. Each fold does its own native early stopping and writes its own .joblib. The folds train one after another.

Parameters:
configModelConfig

Factors, labels, date ranges, early-stopping settings and hyperparameters. See ModelConfig.

Examples

>>> config = ModelConfig(
...     factors=[alpha],            # factor objects
...     labels=[fwd_return],        # label objects
...     model_save_dir="checkpoints",
...     factor_data_strategy="read",
...     label_data_strategy="read",
...     train_start="2024-01-01", train_end="2024-02-09",
...     test_start="2024-02-10", test_end="2024-02-29",
...     hyperparameters={
...         "early_stopping": True, "early_stopping_patience": 5,
...         "num_boost_round": 20, "max_depth": 3,
...     },
... )
>>> model = XGBoostRegressor(config)
>>> checkpoint = model.collect().train()
>>> checkpoint.name
'XGBoostRegressor_total.joblib'
>>> model.predict(np.zeros((5, 2, 3), dtype="float32")).shape
(5, 2, 1)
>>> model.train_cv(train_periods=500)
EARLY_STOPPING_METRIC = 'rmse'#

Metric watched by early stopping on the validation segment.