quantlab.runs.backtest_stats.turnover_stats#

quantlab.runs.backtest_stats.turnover_stats(turnover, rebalances, timestamps, *, ranges, bar_interval, year_freq)[source]#

Summarize the rebalance turnover of a slice as the three percent-valued metric rows.

Total Turnover [%] is the sum of turnover over the fill bars in ranges. Turnover per Rebalance [%] is that total over the decided rebalances whose fill bar (the bar after the rebalance) is in ranges, so a rebalance that placed no order counts as zero; NaN without one. Annualized Turnover [%] is the total over the slice’s years: its bars over the bars of a year (year_freq / bar_interval); NaN for a slice without a bar.

Parameters:
turnoverxarray.DataArray

turnover’s output over the run (rebalance fills only).

rebalancesarray_like of datetime64

The bars of the decided rebalances (the rebalance record’s decided bars).

timestampsarray_like of datetime64

The run’s bars: the axis a rebalance’s fill bar and the slice’s length are counted on.

rangessequence of (str, str)

Inclusive bar-label ranges of the slice.

bar_interval

The bar spacing, anything pd.Timedelta accepts.

year_freqpd.Timedelta

One year, as year_freq returns it.

Returns:
dict

The three rows.

Examples

Decided on bars 0, 2 and 4 of six; the last decision placed no order:

>>> bars = pd.bdate_range("2024-01-01", periods=6)
>>> flows = xr.DataArray([1.0, 0.5], dims="timestamp", coords={"timestamp": bars[[1, 3]]})
>>> stats = turnover_stats(
...     flows, bars[[0, 2, 4]].values, bars.values, ranges=[("2024-01-01", "2024-01-08")],
...     bar_interval="1D", year_freq=year_freq("1D", 252, 390),
... )
>>> {key: round(value, 6) for key, value in stats.items()}
{'Turnover per Rebalance [%]': 50.0, 'Total Turnover [%]': 150.0, 'Annualized Turnover [%]': 6300.0}