microsoft/qlib · error · ValueError
Both trade_end_time and bench_value is None, benchmark is no
Error message
Both trade_end_time and bench_value is None, benchmark is not usable.
What it means
In PortfolioMetric.fill, the benchmark value for the step is either passed in (bench_value) or sampled from self.bench between trade_start_time and trade_end_time. If both are None there is no way to compute the benchmark leg, so ValueError('Both trade_end_time and bench_value is None, benchmark is not usable.') is raised before any data is recorded.
Source
Thrown at qlib/backtest/report.py:185
# check data
if None in [
trade_start_time,
account_value,
cash,
return_rate,
total_turnover,
turnover_rate,
total_cost,
cost_rate,
stock_value,
]:
raise ValueError(
"None in [trade_start_time, account_value, cash, return_rate, total_turnover, turnover_rate, "
"total_cost, cost_rate, stock_value]",
)
if trade_end_time is None and bench_value is None:
raise ValueError("Both trade_end_time and bench_value is None, benchmark is not usable.")
elif bench_value is None:
bench_value = self._sample_benchmark(self.bench, trade_start_time, trade_end_time)
# update pm data
self.accounts[trade_start_time] = account_value
self.returns[trade_start_time] = return_rate
self.total_turnovers[trade_start_time] = total_turnover
self.turnovers[trade_start_time] = turnover_rate
self.total_costs[trade_start_time] = total_cost
self.costs[trade_start_time] = cost_rate
self.values[trade_start_time] = stock_value
self.cashes[trade_start_time] = cash
self.benches[trade_start_time] = bench_value
# update pm
self.latest_pm_time = trade_start_time
# finish pm update in each step
def generate_portfolio_metrics_dataframe(self) -> pd.DataFrame:View on GitHub (pinned to 79633dd950)
Solutions
- Pass trade_end_time (even equal to trade_start_time) whenever a benchmark is configured
- Or pass bench_value explicitly (e.g. 0.0) for steps where no benchmark sampling applies
- If you do not want benchmark comparison, construct the metric without benchmark_config so the bench path is skipped
Example fix
# before pm.fill(trade_start_time=t, account_value=v, cash=c, ..., bench_value=None) # after pm.fill(trade_start_time=t, trade_end_time=t, account_value=v, cash=c, ..., bench_value=0.0)
Defensive patterns
Strategy: validation
Validate before calling
if trade_end_time is None and bench_value is None:
bench_value = 0.0 # or pass trade_end_time=trade_start_time
# choose based on whether benchmark comparison matters for this step Try / catch
try:
pm.fill(..., trade_end_time=trade_end_time, bench_value=bench_value)
except ValueError as e:
if "benchmark is not usable" in str(e):
pm.fill(..., bench_value=0.0) # retry without benchmark sampling Prevention
- Always pass trade_end_time to fill
- Pass an explicit bench_value for single-timestamp steps
- Drop benchmark_config entirely if you do not report benchmark-relative metrics
When it happens
Trigger: Calling fill without trade_end_time and without bench_value while a benchmark series is configured; or with trade_end_time=None and bench=None (no benchmark was given at init, so _sample_benchmark returns None and the passed bench_value is None).
Common situations: Single-timestamp fills (intraday steps with no end time) that still carry a benchmark config; benchmark_config set to None so init produced self.bench = None, while fill still expects a bench value.
Related errors
- benchmark freq can't be None!
- The benchmark {_codes} does not exist. Please provide the ri
- None in [trade_start_time, account_value, cash, return_rate,
- trade_calendar is necessary for getting TradeRangeByTime.
- The decision didn't provide an index range
AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15).
Data as JSON: /api/errors/4cdbb92268c6e924.
Report an issue: GitHub.