From 88f7550f5f65f90acd8181fb36a41531b9f5bcf9 Mon Sep 17 00:00:00 2001 From: dajinzhu <41352123+dajinzhu@users.noreply.github.com> Date: Wed, 19 Aug 2026 20:39:30 +0800 Subject: [PATCH] docs: clarify benchmark weighting semantics --- qlib/backtest/report.py | 8 +++-- tests/backtest/test_benchmark_weighting.py | 35 ++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 tests/backtest/test_benchmark_weighting.py diff --git a/qlib/backtest/report.py b/qlib/backtest/report.py index f1016e24e2a..b553b5f712f 100644 --- a/qlib/backtest/report.py +++ b/qlib/backtest/report.py @@ -60,8 +60,12 @@ def __init__(self, freq: str = "day", benchmark_config: dict = {}) -> None: 2017-01-09 0.006874 2017-01-10 -0.003350 - If `benchmark` is list, will use the daily average change of the stock pool in the list as the - 'bench'. - - If `benchmark` is str, will use the daily change as the 'bench'. + 'bench' (an equal-weighted benchmark). + - If `benchmark` is str, will use the daily change as the 'bench' for that instrument. + + Note that an equal-weighted strategy compared with a string benchmark (for example, a + cap-weighted index) includes a weighting effect in its excess return. Use a benchmark with + the same weighting convention when the goal is to isolate strategy alpha. benchmark code, default is SH000300 CSI300 - start_time : Union[str, pd.Timestamp], optional - If `benchmark` is pd.Series, it will be ignored diff --git a/tests/backtest/test_benchmark_weighting.py b/tests/backtest/test_benchmark_weighting.py new file mode 100644 index 00000000000..4ea399630ad --- /dev/null +++ b/tests/backtest/test_benchmark_weighting.py @@ -0,0 +1,35 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +from unittest.mock import patch + +import pandas as pd + +from qlib.backtest.report import PortfolioMetrics + + +class TestBenchmarkWeighting: + @patch("qlib.backtest.report.get_higher_eq_freq_feature") + def test_list_benchmark_is_equal_weighted(self, get_feature): + dates = pd.to_datetime(["2026-01-02", "2026-01-02", "2026-01-05", "2026-01-05"]) + instruments = ["A", "B", "A", "B"] + index = pd.MultiIndex.from_arrays( + [instruments, dates], names=["instrument", "datetime"] + ) + returns = pd.DataFrame( + {"$close/Ref($close,1)-1": [0.10, 0.00, -0.05, 0.04]}, + index=index, + ) + get_feature.return_value = (returns, None) + + benchmark = PortfolioMetrics._cal_benchmark( + {"benchmark": ["A", "B"]}, freq="day" + ) + + expected = pd.Series( + [0.05, -0.005], + index=pd.to_datetime(["2026-01-02", "2026-01-05"]), + name="$close/Ref($close,1)-1", + ) + expected.index.name = "datetime" + pd.testing.assert_series_equal(benchmark, expected)