quantlab.runs.backtest_stats.relative_stats#

quantlab.runs.backtest_stats.relative_stats(returns, benchmark_returns, *, bar_interval, year_freq, ranges)[source]#

Return a strategy’s statistics relative to its benchmark over ranges.

These are the rows of the relative blocks of metrics.json (with win_rates against the benchmark). Both series are cut to ranges and the bars where either is NaN are dropped. The relative NAV compounds (1 + r) / (1 + b) bar by bar (r the strategy’s return, b the benchmark’s); over a whole window it equals the strategy’s value divided by the benchmark’s, the excess-return curve the report draws. ann is year_freq / bar_interval, and every key ending in [%] is in percent:

  • Strategy Total Return [%] / Benchmark Total Return [%]: compounded returns of each series;

  • Excess Return [%]: relative NAV at the end minus 1, the geometric excess; Annualized Excess Return [%]: the same compounded to one year;

  • Total Return Difference [%]: the arithmetic difference of the two total returns;

  • Excess Max Drawdown [%]: the deepest fall of the relative NAV from its running peak (starting at 1), negative or 0;

  • Tracking Error [%]: standard deviation (ddof 1) of r - b times sqrt(ann); Information Ratio: mean of r - b times ann over the tracking error;

  • Beta and Correlation of r on b, and CAPM Alpha [%], the annualized intercept mean(r) - beta * mean(b);

  • Win Rate vs Benchmark [%]: share of bars with r > b;

  • Bars: number of bars used.

A statistic that is undefined (fewer than two bars, a flat benchmark, zero tracking error) is NaN.

Parameters:
returns, benchmark_returnsxarray.DataArray

Per-bar returns of the strategy and the benchmark, on the same timestamp axis.

bar_interval

The bar spacing, anything pd.Timedelta accepts.

year_freqpd.Timedelta

One year, as year_freq returns it.

rangessequence of (str, str)

Inclusive bar-label ranges of the slice.

Returns:
dict

Thirteen rows, in the order listed above.

Raises:
ValueError

If the two series are not on the same bars.

Examples

>>> bars = pd.bdate_range("2024-01-01", periods=3)
>>> strategy = xr.DataArray([0.02, -0.01, 0.03], dims="timestamp", coords={"timestamp": bars})
>>> benchmark = xr.DataArray([0.01, 0.0, 0.01], dims="timestamp", coords={"timestamp": bars})
>>> stats = relative_stats(
...     strategy, benchmark, bar_interval="1D",
...     year_freq=year_freq("1D", 252, 390), ranges=[("2024-01-01", "2024-01-03")],
... )
>>> round(stats["Total Return Difference [%]"], 4), stats["Bars"]
(1.9994, 3)
>>> round(stats["Win Rate vs Benchmark [%]"], 2)
66.67