book_vol_target

Defined in fynance.portfolio.sizing

book_vol_target(W, X, target_vol=0.15, period=252, w=21, max_leverage=5.0)[source]

Causal volatility-targeting leverage series for a multi-asset book.

Multi-asset counterpart of vol_target: scales a whole position book so its own trailing realized volatility targets target_vol, instead of scaling a single price series.

Parameters:
Warray_like

Weights held at each step, shape (T, N) (e.g. the w_mat returned by fynance.portfolio.allocation.rolling_allocation). A 1-D input is reshaped to (T, 1).

Xarray_like

Price/level panel, shape (T, N), same convention as vol_target (prices, not returns). A 1-D input is reshaped to (T, 1).

target_volfloat, optional

Target annualized volatility. Default 0.15.

periodint, optional

Annualization factor. Default 252.

wint, optional

Rolling window for the realized volatility. Default 21.

max_leveragefloat, optional

Cap on the leverage. Default 5.0.

Returns:
np.ndarray

Leverage series, shape (T,) (0 where volatility is not yet defined).

Raises:
ValueError

If W and X do not share the same shape once 1-D inputs have been reshaped to (T, 1).

See also

vol_target

single-asset causal volatility-targeting leverage.

Notes

Strictly causal, no-lookahead construction of the book:

\[r_t = X_t / X_{t-1} - 1, \quad r_0 = 0\]
\[rb_t = \sum_i W_{t-1, i} \cdot r_{t, i}, \quad rb_0 = 0\]

i.e. the weights decided at t - 1 (the last full step of information available before t) earn the asset returns realized over (t - 1, t] — the same convention as the training/holding split of fynance.portfolio.allocation.rolling_allocation. The book level L = 100 * cumprod(1 + rb) is then fed to fynance.features.indicators.realized_volatility and the leverage is derived and clipped exactly as in vol_target.

With a single asset (N = 1) and constant unit weight, rb reduces to the asset’s own returns, so book_vol_target reduces to vol_target on the same price series.

Examples

>>> import numpy as np
>>> X = np.array([[100.0, 100.0],
...               [102.0,  98.0],
...               [104.0,  96.5],
...               [101.0,  99.0],
...               [105.0, 101.0]])
>>> W = np.full((5, 2), 0.5)
>>> lev = book_vol_target(W, X, target_vol=0.10, w=2, max_leverage=3.0)
>>> lev.shape
(5,)
>>> lev[0], lev[1]
(0.0, 0.0)
>>> bool((lev >= 0.0).all() and (lev <= 3.0).all())
True