refactor: unify qlib experiment configs, runners, and templates (#1320)

* refactor: unify qlib experiment configs, runners, and templates

* fix: use PropSetting instances instead of class attributes in qlib runners

* docs: add configurable train/valid/test time segments for fintech scenarios
This commit is contained in:
Linlang
2026-01-07 12:49:15 +08:00
committed by GitHub
parent 99a71bf533
commit 19dbeb93e9
13 changed files with 244 additions and 52 deletions
+54
View File
@@ -28,6 +28,24 @@ class ModelBasePropSetting(BasePropSetting):
evolving_n: int = 10
"""Number of evolutions"""
train_start: str = "2008-01-01"
"""Start date of the training segment"""
train_end: str = "2014-12-31"
"""End date of the training segment"""
valid_start: str = "2015-01-01"
"""Start date of the validation segment"""
valid_end: str = "2016-12-31"
"""End date of the validation segment"""
test_start: str = "2017-01-01"
"""Start date of the test / backtest segment"""
test_end: str = "2020-12-31"
"""End date of the test / backtest segment"""
class FactorBasePropSetting(BasePropSetting):
model_config = SettingsConfigDict(env_prefix="QLIB_FACTOR_", protected_namespaces=())
@@ -54,6 +72,24 @@ class FactorBasePropSetting(BasePropSetting):
evolving_n: int = 10
"""Number of evolutions"""
train_start: str = "2008-01-01"
"""Start date of the training segment"""
train_end: str = "2014-12-31"
"""End date of the training segment"""
valid_start: str = "2015-01-01"
"""Start date of the validation segment"""
valid_end: str = "2016-12-31"
"""End date of the validation segment"""
test_start: str = "2017-01-01"
"""Start date of the test / backtest segment"""
test_end: str = "2020-12-31"
"""End date of the test / backtest segment"""
class FactorFromReportPropSetting(FactorBasePropSetting):
# 1) override the scen attribute
@@ -113,6 +149,24 @@ class QuantBasePropSetting(BasePropSetting):
action_selection: str = "bandit"
"""Action selection strategy: 'bandit' for bandit-based selection, 'llm' for LLM-based selection, 'random' for random selection"""
train_start: str = "2008-01-01"
"""Start date of the training segment"""
train_end: str = "2014-12-31"
"""End date of the training segment"""
valid_start: str = "2015-01-01"
"""Start date of the validation segment"""
valid_end: str = "2016-12-31"
"""End date of the validation segment"""
test_start: str = "2017-01-01"
"""Start date of the test / backtest segment"""
test_end: str = "2020-12-31"
"""End date of the test / backtest segment"""
FACTOR_PROP_SETTING = FactorBasePropSetting()
FACTOR_FROM_REPORT_PROP_SETTING = FactorFromReportPropSetting()
@@ -8,6 +8,7 @@ from rdagent.core.utils import cache_with_pickle
pandarallel.initialize(verbose=1)
from rdagent.app.qlib_rd_loop.conf import FactorBasePropSetting
from rdagent.components.runner import CachedRunner
from rdagent.core.exception import FactorEmptyError
from rdagent.log import rdagent_logger as logger
@@ -80,6 +81,17 @@ class QlibFactorRunner(CachedRunner[QlibFactorExperiment]):
logger.info(f"Baseline experiment execution ...")
exp.based_experiments[-1] = self.develop(exp.based_experiments[-1])
fbps = FactorBasePropSetting()
env_to_use = {
"PYTHONPATH": "./",
"train_start": fbps.train_start,
"train_end": fbps.train_end,
"valid_start": fbps.valid_start,
"valid_end": fbps.valid_end,
"test_start": fbps.test_start,
"test_end": fbps.test_end,
}
if exp.based_experiments:
SOTA_factor = None
# Filter and retain only QlibFactorExperiment instances
@@ -136,7 +148,6 @@ class QlibFactorRunner(CachedRunner[QlibFactorExperiment]):
exp.experiment_workspace.inject_files(
**{"model.py": sota_model_exp.sub_workspace_list[0].file_dict["model.py"]}
)
env_to_use = {"PYTHONPATH": "./"}
sota_training_hyperparameters = sota_model_exp.sub_tasks[0].training_hyperparameters
if sota_training_hyperparameters:
env_to_use.update(
@@ -165,14 +176,16 @@ class QlibFactorRunner(CachedRunner[QlibFactorExperiment]):
result, stdout = exp.experiment_workspace.execute(
qlib_config_name=(
f"conf_baseline.yaml" if len(exp.based_experiments) == 0 else "conf_combined_factors.yaml"
)
),
run_env=env_to_use,
)
else:
logger.info(f"Experiment execution ...")
result, stdout = exp.experiment_workspace.execute(
qlib_config_name=(
f"conf_baseline.yaml" if len(exp.based_experiments) == 0 else "conf_combined_factors.yaml"
)
),
run_env=env_to_use,
)
if result is None:
@@ -1,5 +1,6 @@
import pandas as pd
from rdagent.app.qlib_rd_loop.conf import ModelBasePropSetting
from rdagent.components.runner import CachedRunner
from rdagent.core.conf import RD_AGENT_SETTINGS
from rdagent.core.exception import ModelEmptyError
@@ -58,7 +59,16 @@ class QlibModelRunner(CachedRunner[QlibModelExperiment]):
# to replace & inject code
exp.experiment_workspace.inject_files(**{"model.py": exp.sub_workspace_list[0].file_dict["model.py"]})
env_to_use = {"PYTHONPATH": "./"}
mbps = ModelBasePropSetting()
env_to_use = {
"PYTHONPATH": "./",
"train_start": mbps.train_start,
"train_end": mbps.train_end,
"valid_start": mbps.valid_start,
"valid_end": mbps.valid_end,
"test_start": mbps.test_start,
"test_end": mbps.test_end,
}
training_hyperparameters = exp.sub_tasks[0].training_hyperparameters
if training_hyperparameters:
@@ -1,6 +1,7 @@
from copy import deepcopy
from pathlib import Path
from rdagent.app.qlib_rd_loop.conf import FACTOR_PROP_SETTING
from rdagent.components.coder.factor_coder.config import get_factor_env
from rdagent.components.coder.factor_coder.factor import (
FactorExperiment,
@@ -36,7 +37,16 @@ class QlibFactorScenario(Scenario):
self._strategy = deepcopy(T(".prompts:qlib_factor_strategy").r())
self._simulator = deepcopy(T(".prompts:qlib_factor_simulator").r())
self._rich_style_description = deepcopy(T(".prompts:qlib_factor_rich_style_description").r())
self._experiment_setting = deepcopy(T(".prompts:qlib_factor_experiment_setting").r())
self._experiment_setting = deepcopy(
T(".prompts:qlib_factor_experiment_setting").r(
train_start=FACTOR_PROP_SETTING.train_start,
train_end=FACTOR_PROP_SETTING.train_end,
valid_start=FACTOR_PROP_SETTING.valid_start,
valid_end=FACTOR_PROP_SETTING.valid_end,
test_start=FACTOR_PROP_SETTING.test_start,
test_end=FACTOR_PROP_SETTING.test_end,
)
)
@property
def background(self) -> str:
@@ -6,10 +6,10 @@ market: &market csi300
benchmark: &benchmark SH000300
data_handler_config: &data_handler_config
start_time: 2008-01-01
end_time: 2020-08-01
fit_start_time: 2008-01-01
fit_end_time: 2014-12-31
start_time: {{ train_start | default("2008-01-01", true) }}
end_time: {{ test_end | default("2020-08-01", true) }}
fit_start_time: {{ train_start | default("2008-01-01", true) }}
fit_end_time: {{ train_end | default("2014-12-31", true) }}
instruments: *market
infer_processors:
- class: FilterCol
@@ -42,8 +42,8 @@ port_analysis_config: &port_analysis_config
topk: 50
n_drop: 5
backtest:
start_time: 2017-01-01
end_time: 2020-08-01
start_time: {{ test_start | default("2017-01-01", true) }}
end_time: {{ test_end | default("2020-08-01", true) }}
account: 100000000
benchmark: *benchmark
exchange_kwargs:
@@ -75,9 +75,9 @@ task:
module_path: qlib.contrib.data.handler
kwargs: *data_handler_config
segments:
train: [2008-01-01, 2014-12-31]
valid: [2015-01-01, 2016-12-31]
test: [2017-01-01, 2020-08-01]
train: [{{ train_start | default("2008-01-01", true) }}, {{ train_end | default("2014-12-31", true) }}]
valid: [{{ valid_start | default("2015-01-01", true) }}, {{ valid_end | default("2016-12-31", true) }}]
test: [{{ test_start | default("2017-01-01", true) }}, {{ test_end | default("2020-08-01", true) }}]
record:
- class: SignalRecord
module_path: qlib.workflow.record_temp
@@ -6,8 +6,8 @@ market: &market csi300
benchmark: &benchmark SH000300
data_handler_config: &data_handler_config
start_time: 2008-01-01
end_time: 2022-08-01
start_time: {{ train_start | default("2008-01-01", true) }}
end_time: {{ test_end | default("2020-08-01", true) }}
instruments: *market
data_loader:
class: NestedDataLoader
@@ -50,8 +50,8 @@ port_analysis_config: &port_analysis_config
topk: 50
n_drop: 5
backtest:
start_time: 2017-01-01
end_time: 2020-08-01
start_time: {{ test_start | default("2017-01-01", true) }}
end_time: {{ test_end | default("2020-08-01", true) }}
account: 100000000
benchmark: *benchmark
exchange_kwargs:
@@ -84,9 +84,9 @@ task:
module_path: qlib.contrib.data.handler
kwargs: *data_handler_config
segments:
train: [2008-01-01, 2014-12-31]
valid: [2015-01-01, 2016-12-31]
test: [2017-01-01, 2020-08-01]
train: [{{ train_start | default("2008-01-01", true) }}, {{ train_end | default("2014-12-31", true) }}]
valid: [{{ valid_start | default("2015-01-01", true) }}, {{ valid_end | default("2016-12-31", true) }}]
test: [{{ test_start | default("2017-01-01", true) }}, {{ test_end | default("2020-08-01", true) }}]
record:
- class: SignalRecord
module_path: qlib.workflow.record_temp
@@ -6,8 +6,8 @@ market: &market csi300
benchmark: &benchmark SH000300
data_handler_config: &data_handler_config
start_time: 2008-01-01
end_time: 2022-08-01
start_time: {{ train_start | default("2008-01-01", true) }}
end_time: {{ test_end | default("2020-08-01", true) }}
instruments: *market
data_loader:
class: NestedDataLoader
@@ -40,8 +40,8 @@ data_handler_config: &data_handler_config
kwargs:
fields_group: feature
clip_outlier: true
fit_start_time: 2008-01-01
fit_end_time: 2014-12-31
fit_start_time: {{ train_start | default("2008-01-01", true) }}
fit_end_time: {{ train_end | default("2014-12-31", true) }}
- class: Fillna
kwargs:
fields_group: feature
@@ -60,8 +60,8 @@ port_analysis_config: &port_analysis_config
topk: 50
n_drop: 5
backtest:
start_time: 2017-01-01
end_time: 2020-08-01
start_time: {{ test_start | default("2017-01-01", true) }}
end_time: {{ test_end | default("2020-08-01", true) }}
account: 100000000
benchmark: *benchmark
exchange_kwargs:
@@ -98,9 +98,9 @@ task:
module_path: qlib.contrib.data.handler
kwargs: *data_handler_config
segments:
train: [2008-01-01, 2014-12-31]
valid: [2015-01-01, 2016-12-31]
test: [2017-01-01, 2020-08-01]
train: [{{ train_start | default("2008-01-01", true) }}, {{ train_end | default("2014-12-31", true) }}]
valid: [{{ valid_start | default("2015-01-01", true) }}, {{ valid_end | default("2016-12-31", true) }}]
test: [{{ test_start | default("2017-01-01", true) }}, {{ test_end | default("2020-08-01", true) }}]
{% if step_len %}step_len: {{ step_len }}{% endif %}
record:
- class: SignalRecord
@@ -1,6 +1,7 @@
from copy import deepcopy
from pathlib import Path
from rdagent.app.qlib_rd_loop.conf import MODEL_PROP_SETTING
from rdagent.components.coder.model_coder.conf import get_model_env
from rdagent.components.coder.model_coder.model import (
ModelExperiment,
@@ -33,7 +34,16 @@ class QlibModelScenario(Scenario):
self._interface = deepcopy(T(".prompts:qlib_model_interface").r())
self._simulator = deepcopy(T(".prompts:qlib_model_simulator").r())
self._rich_style_description = deepcopy(T(".prompts:qlib_model_rich_style_description").r())
self._experiment_setting = deepcopy(T(".prompts:qlib_model_experiment_setting").r())
self._experiment_setting = deepcopy(
T(".prompts:qlib_model_experiment_setting").r(
train_start=MODEL_PROP_SETTING.train_start,
train_end=MODEL_PROP_SETTING.train_end,
valid_start=MODEL_PROP_SETTING.valid_start,
valid_end=MODEL_PROP_SETTING.valid_end,
test_start=MODEL_PROP_SETTING.test_start,
test_end=MODEL_PROP_SETTING.test_end,
)
)
@property
def background(self) -> str:
@@ -4,10 +4,10 @@ qlib_init:
market: &market csi300
benchmark: &benchmark SH000300
data_handler_config: &data_handler_config
start_time: 2008-01-01
end_time: 2020-08-01
fit_start_time: 2008-01-01
fit_end_time: 2014-12-31
start_time: {{ train_start | default("2008-01-01", true) }}
end_time: {{ test_end | default("2020-08-01", true) }}
fit_start_time: {{ train_start | default("2008-01-01", true) }}
fit_end_time: {{ train_end | default("2014-12-31", true) }}
instruments: *market
infer_processors:
- class: FilterCol
@@ -40,8 +40,8 @@ port_analysis_config: &port_analysis_config
topk: 50
n_drop: 5
backtest:
start_time: 2017-01-01
end_time: 2020-08-01
start_time: {{ test_start | default("2017-01-01", true) }}
end_time: {{ test_end | default("2020-08-01", true) }}
account: 100000000
benchmark: *benchmark
exchange_kwargs:
@@ -78,9 +78,9 @@ task:
module_path: qlib.contrib.data.handler
kwargs: *data_handler_config
segments:
train: [2008-01-01, 2014-12-31]
valid: [2015-01-01, 2016-12-31]
test: [2017-01-01, 2020-08-01]
train: [{{ train_start | default("2008-01-01", true) }}, {{ train_end | default("2014-12-31", true) }}]
valid: [{{ valid_start | default("2015-01-01", true) }}, {{ valid_end | default("2016-12-31", true) }}]
test: [{{ test_start | default("2017-01-01", true) }}, {{ test_end | default("2020-08-01", true) }}]
{% if step_len %}step_len: {{ step_len }}{% endif %}
record:
- class: SignalRecord
@@ -6,8 +6,8 @@ market: &market csi300
benchmark: &benchmark SH000300
data_handler_config: &data_handler_config
start_time: 2008-01-01
end_time: 2022-08-01
start_time: {{ train_start | default("2008-01-01", true) }}
end_time: {{ test_end | default("2020-08-01", true) }}
instruments: *market
data_loader:
class: NestedDataLoader
@@ -40,8 +40,8 @@ data_handler_config: &data_handler_config
kwargs:
fields_group: feature
clip_outlier: true
fit_start_time: 2008-01-01
fit_end_time: 2014-12-31
fit_start_time: {{ train_start | default("2008-01-01", true) }}
fit_end_time: {{ train_end | default("2014-12-31", true) }}
- class: Fillna
kwargs:
fields_group: feature
@@ -60,8 +60,8 @@ port_analysis_config: &port_analysis_config
topk: 50
n_drop: 5
backtest:
start_time: 2017-01-01
end_time: 2020-08-01
start_time: {{ test_start | default("2017-01-01", true) }}
end_time: {{ test_end | default("2020-08-01", true) }}
account: 100000000
benchmark: *benchmark
exchange_kwargs:
@@ -98,9 +98,9 @@ task:
module_path: qlib.contrib.data.handler
kwargs: *data_handler_config
segments:
train: [2008-01-01, 2014-12-31]
valid: [2015-01-01, 2016-12-31]
test: [2017-01-01, 2020-08-01]
train: [{{ train_start | default("2008-01-01", true) }}, {{ train_end | default("2014-12-31", true) }}]
valid: [{{ valid_start | default("2015-01-01", true) }}, {{ valid_end | default("2016-12-31", true) }}]
test: [{{ test_start | default("2017-01-01", true) }}, {{ test_end | default("2020-08-01", true) }}]
{% if step_len %}step_len: {{ step_len }}{% endif %}
record:
- class: SignalRecord
@@ -161,7 +161,7 @@ qlib_factor_from_report_rich_style_description : |-
qlib_factor_experiment_setting: |-
| Dataset 📊 | Model 🤖 | Factors 🌟 | Data Split 🧮 |
|---------|----------|---------------|-------------------------------------------------|
| CSI300 | LGBModel | Alpha158 Plus | Train: 2008-01-01 to 2014-12-31 <br> Valid: 2015-01-01 to 2016-12-31 <br> Test &nbsp;: 2017-01-01 to 2020-08-01 |
| CSI300 | LGBModel | Alpha158 Plus | Train: {{ train_start }} to {{ train_end }} <br> Valid: {{ valid_start }} to {{ valid_end }} <br> Test &nbsp;: {{ test_start }} to {{ test_end }} |
qlib_model_background: |-
@@ -256,4 +256,4 @@ qlib_model_rich_style_description: |-
qlib_model_experiment_setting: |-
| Dataset 📊 | Model 🤖 | Factors 🌟 | Data Split 🧮 |
|---------|----------|---------------|-------------------------------------------------|
| CSI300 | RDAgent-dev | 20 factors (Alpha158) | Train: 2008-01-01 to 2014-12-31 <br> Valid: 2015-01-01 to 2016-12-31 <br> Test &nbsp;: 2017-01-01 to 2020-08-01 |
| CSI300 | RDAgent-dev | 20 factors (Alpha158) | Train: {{ train_start }} to {{ train_end }} <br> Valid: {{ valid_start }} to {{ valid_end }} <br> Test &nbsp;: {{ test_start }} to {{ test_end }} |
@@ -1,6 +1,8 @@
from copy import deepcopy
from pathlib import Path
from rdagent.app.qlib_rd_loop.conf import QUANT_PROP_SETTING
# Factor
from rdagent.components.coder.factor_coder.config import get_factor_env
from rdagent.components.coder.factor_coder.factor import (
@@ -42,7 +44,16 @@ class QlibQuantScenario(Scenario):
self._source_data = deepcopy(get_data_folder_intro())
self._rich_style_description = deepcopy(T(".prompts:qlib_factor_rich_style_description").r())
self._experiment_setting = deepcopy(T(".prompts:qlib_factor_experiment_setting").r())
self._experiment_setting = deepcopy(
T(".prompts:qlib_factor_experiment_setting").r(
train_start=QUANT_PROP_SETTING.train_start,
train_end=QUANT_PROP_SETTING.train_end,
valid_start=QUANT_PROP_SETTING.valid_start,
valid_end=QUANT_PROP_SETTING.valid_end,
test_start=QUANT_PROP_SETTING.test_start,
test_end=QUANT_PROP_SETTING.test_end,
)
)
def background(self, tag=None) -> str:
assert tag in [None, "factor", "model"]