microsoft/qlib · error · QlibException
MultiPassPortAnaRecord require the passed in strategy to be
Error message
MultiPassPortAnaRecord require the passed in strategy to be a dict
What it means
MultiPassPortAnaRecord.__init__ deep-copies the strategy portion of the port analysis config (self.strategy_config) and requires it to be a dict, because it must mutate strategy['kwargs']['signal'] between passes (replacing the prediction dataframe for random-init scoring). If the copied strategy is not a dict, the constructor raises QlibException immediately — the record cannot do multi-pass backtests on a non-dict strategy spec.
Source
Thrown at qlib/workflow/record_temp.py:613
"""
Parameters
----------
recorder : Recorder
The recorder used to save the backtest results.
pass_num : int
The number of backtest passes.
shuffle_init_score : bool
Whether to shuffle the prediction score of the first backtest date.
"""
self.pass_num = pass_num
self.shuffle_init_score = shuffle_init_score
super().__init__(recorder, **kwargs)
# Save original strategy so that pred df can be replaced in next generate
self.original_strategy = deepcopy_basic_type(self.strategy_config)
if not isinstance(self.original_strategy, dict):
raise QlibException("MultiPassPortAnaRecord require the passed in strategy to be a dict")
if "signal" not in self.original_strategy.get("kwargs", {}):
raise QlibException("MultiPassPortAnaRecord require the passed in strategy to have signal as a parameter")
def random_init(self):
pred_df = self.load("pred.pkl")
all_pred_dates = pred_df.index.get_level_values("datetime")
bt_start_date = pd.to_datetime(self.backtest_config.get("start_time"))
if bt_start_date is None:
first_bt_pred_date = all_pred_dates.min()
else:
first_bt_pred_date = all_pred_dates[all_pred_dates >= bt_start_date].min()
# Shuffle the first backtest date's pred score
first_date_score = pred_df.loc[first_bt_pred_date]["score"]
np.random.shuffle(first_date_score.values)
# Use shuffled signal as the strategy signalView on GitHub (pinned to 79633dd950)
Solutions
- Make the strategy parameter a full dict spec, e.g. {'class': 'TopkDropoutStrategy', 'module_path': 'qlib.contrib.strategy.signal_strategy', 'kwargs': {...}}
- Cross-check against a working PortAnaRecord/MultiPassPortAnaRecord example config in qlib's examples (benchmarks/workflows) and fix yaml indentation so strategy stays a mapping
- If you genuinely need an object strategy, use plain PortAnaRecord, which does not rewrite the signal between passes
Example fix
# before
record = MultiPassPortAnaRecord(
recorder=rec,
config={'strategy': 'TopkDropout', 'backtest': bt}, # string -> QlibException
)
# after
record = MultiPassPortAnaRecord(
recorder=rec,
config={
'strategy': {
'class': 'TopkDropoutStrategy',
'module_path': 'qlib.contrib.strategy.signal_strategy',
'kwargs': {'signal': <PRED>, 'topk': 50},
},
'backtest': bt,
},
) Defensive patterns
Strategy: validation
Validate before calling
def validate_multipass_config(config: dict):
strategy = config.get('strategy')
if not isinstance(strategy, dict):
raise TypeError(
f"strategy must be a dict spec, got {type(strategy).__name__}; "
"expected {{'class': ..., 'module_path': ..., 'kwargs': {{...}}}}"
)
if 'signal' not in strategy.get('kwargs', {}):
raise TypeError('strategy["kwargs"] must contain "signal"')
return config Type guard
from typing import Any, Dict
def is_strategy_dict_spec(strategy: Any) -> bool:
return (
isinstance(strategy, dict)
and isinstance(strategy.get('kwargs'), dict)
and 'signal' in strategy['kwargs']
) Try / catch
try:
MultiPassPortAnaRecord(recorder=rec, config=config)
except QlibException as e:
raise ValueError(f'invalid multi-pass port analysis config: {e}; see {validate_multipass_config.__name__}') from e Prevention
- Validate the strategy block before constructing the record; fail at config-load time
- Start from a working example config from qlib/examples and edit values only
- Watch yaml indentation: strategy must stay a nested mapping, not a scalar
When it happens
Trigger: Passing a strategy config that is a string, list, or instantiated strategy object where a dict config is expected, e.g. MultiPassPortAnaRecord(recorder, strategy='TopkDropout', ...) or a yaml section where 'strategy:' parsed to a scalar; building the port_analysis record block by hand instead of mirroring the standard PortAnaRecord config shape {class: ..., module_path: ..., kwargs: {...}}.
Common situations: Hand-writing the 'task.record' config for portfolio analysis and omitting the nested dict structure; mixing up the constructor order and passing backtest config where strategy belongs; yaml indentation collapsing the strategy mapping into a string.
Related errors
- MultiPassPortAnaRecord require the passed in strategy to hav
- method {method} is not supported!
- This type of input {rtype} is not supported
- Can't find the BASE_CONFIG file: {base_config_path}
- Invalid Qlib configuration (note: the global config has alre
AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15).
Data as JSON: /api/errors/2d83a04fea90b835.
Report an issue: GitHub.