Initialisation de l'environnement et ingestion des données
L'opérationnalisation d'une infrastructure de recherche quantitative exige une configuration rigoureuse des dépendances et le chargement standardisé des séries temporelles financières. Le framework Qlib impose un format de stockage binaire spécifique et nécessite la déclaration explicite de la région géographique ciblée avant toute interaction.
import qlib
import pandas as pd
from qlib.constant import REG_CN
from qlib.utils import exists_qlib_data, init_instance_by_config
from qlib.workflow import R
from qlib.workflow.record_temp import SignalRecord, PortAnaRecord
from qlib.utils import flatten_dict
repertoire_donnees = "~/.qlib/qlib_data/cn_data"
if not exists_qlib_data(repertoire_donnees):
print(f"Aucune base historique détectée dans {repertoire_donnees}. Téléchargement requis.")
# Exécution du script officiel : python scripts/get_data.py qlib_data --target_dir ~/.qlib/qlib_data/cn_data --region cn
qlib.init(provider_uri=repertoire_donnees, region=REG_CN)
Configuration du pipeline d'apprentissage automatique
La préparation des caractéristiques techniques et la définition des hyperparamètres s'orchestrent via des structures de configuration. Cette section initialise l'ensemble de facteurs Alpha158 et un modèle de régression basé sur les arbres décisionnels.
fenetre_temporelle = {
"start_time": "2008-01-01",
"end_time": "2020-08-01",
"fit_start_time": "2008-01-01",
"fit_end_time": "2014-12-31",
"instruments": "csi300",
}
schema_experimentale = {
"model": {
"class": "LGBModel",
"module_path": "qlib.contrib.model.gbdt",
"kwargs": {
"loss": "mse",
"colsample_bytree": 0.8879,
"learning_rate": 0.0421,
"n_estimators": 100,
"subsample": 0.9
}
},
"dataset": {
"class": "DatasetH",
"module_path": "qlib.data.dataset",
"kwargs": {
"handler": {
"class": "Alpha158",
"module_path": "qlib.contrib.data.handler",
"kwargs": fenetre_temporelle,
},
"segments": {
"train": ("2008-01-01", "2014-12-31"),
"valid": ("2015-01-01", "2016-12-31"),
"test": ("2017-01-01", "2020-08-01"),
}
}
}
}
L'instanciation des composants et le cycle d'entraînement sont gérés par le système de traçabilité natif, permettant le versionnage des paramètres et des artefacts produits.
def executer_entrainement(config):
instance_modele = init_instance_by_config(config["model"])
instance_dataset = init_instance_by_config(config["dataset"])
with R.start(experiment_name="phase_apprentissage"):
R.log_params(**flatten_dict(config))
instance_modele.fit(instance_dataset)
R.save_objects(modele_entraine=instance_modele)
return R.get_recorder().id, instance_modele, instance_dataset
id_session, modele_actif, dataset_actif = executer_entrainement(schema_experimentale)
Simulation de backtesting et exécution stratégique
La validation historique repose sur la définition d'un exécuteur de simulation, d'une politique d'allocation dynamique et des contraintes de marché réalistes (frais de transaction, limites de prix, slippage).
parametres_backtest = {
"executor": {
"class": "SimulatorExecutor",
"module_path": "qlib.backtest.executor",
"kwargs": {
"time_per_step": "day",
"generate_portfolio_metrics": True,
}
},
"strategy": {
"class": "TopkDropoutStrategy",
"module_path": "qlib.contrib.strategy.signal_strategy",
"kwargs": {
"signal": None,
"topk": 50,
"n_drop": 5,
}
},
"backtest": {
"start_time": "2017-01-01",
"end_time": "2020-08-01",
"account": 1e8,
"benchmark": "SH000300",
"exchange_kwargs": {
"freq": "day",
"limit_threshold": 0.095,
"deal_price": "close",
"open_cost": 0.0005,
"close_cost": 0.0015,
"min_cost": 5,
}
}
}
def lancer_simulation(modele_ref, donnees_ref, config_sim):
config_sim["strategy"]["kwargs"]["model"] = modele_ref
config_sim["strategy"]["kwargs"]["dataset"] = donnees_ref
with R.start(experiment_name="phase_backtest"):
recordeur = R.get_recorder()
id_sim = recordeur.id
generateur = SignalRecord(modele_ref, donnees_ref, recordeur)
generateur.generate()
analyseur = PortAnaRecord(recordeur, config_sim, "day")
analyseur.generate()
return id_sim
id_resultats = lancer_simulation(modele_actif, dataset_actif, parametres_backtest)
Extraction des métriques et visualisation des performances
L'exploitation des artefacts de backtesting permet de reconstruire les prédictions, les pondérations historiques et les indicateurs de risque agrégés. Les modules d'analyse calculent la corrélation croisée (IC) entre les signaux générés et les rendements observés.
from qlib.contrib.report import analysis_model, analysis_position
session_finale = R.get_recorder(recorder_id=id_resultats, experiment_name="phase_backtest")
predictions_brutes = session_finale.load_object("pred.pkl")
bilan_journalier = session_finale.load_object("portfolio_analysis/report_normal_1day.pkl")
allocations_historiques = session_finale.load_object("portfolio_analysis/positions_normal_1day.pkl")
synthese_risques = session_finale.load_object("portfolio_analysis/port_analysis_1day.pkl")
analysis_position.report_graph(bilan_journalier)
analysis_position.risk_analysis_graph(synthese_risques, bilan_journalier)
cibles = dataset_actif.prepare("test", col_set="label")
cibles.columns = ["rendement_reel"]
fusion_donnees = pd.concat([cibles, predictions_brutes], axis=1, sort=True).reindex(cibles.index)
analysis_position.score_ic_graph(fusion_donnees)
analysis_model.model_performance_graph(fusion_donnees)