quantlab.risk.config.Use4RiskConfig#
- class quantlab.risk.config.Use4RiskConfig(*, exposures, dataset, exposure_data_strategy, regression_path=None, estu_name='estu', price_column='adjClose', market_cap_column='marketcap', risk_free_column='risk_free', risk_free_symbol=None, estimate_path=None, name=None, style_names=('style_size', 'style_beta', 'style_momentum', 'style_residual_volatility', 'style_nonlinear_size', 'style_nonlinear_beta', 'style_liquidity', 'style_dividend_yield', 'style_book_to_price', 'style_earnings_yield', 'style_leverage', 'style_growth'), country=True, industry_name='industry', industries=(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48), weighting='sqrt_cap', min_industry_members=5, return_outlier_sigma=5.0, volatility_half_life=84.0, volatility_window=252, correlation_half_life=504.0, correlation_window=1512, specific_half_life=84.0, specific_window=252, volatility_lags=5, volatility_autocorrelation_half_life=504.0, volatility_autocorrelation_window=1512, correlation_lags=2, eigen_simulations=1000, eigen_seed=0, eigen_scale=None, eigen_fit_skip=15, vra_half_life=42.0, vra_window=126, specific_lags=0, specific_autocorrelation_half_life=252.0, specific_autocorrelation_window=756, structural_model='fill', structural_fit='series', structural_bias='smearing', structural_history_window=756, blending_min_observations=60, blending_ramp=120, blending_outlier_bound=10.0, shrinkage=0.1, shrinkage_groups=10, njobs=1, min_observations=21)[source]#
Bases:
FactorRiskConfigConfig of
quantlab.risk.predefined.use4.Use4RiskModel: USE4’s factor set and method.The factor set defaults to USE4’s on the
BarraStyleoutputs: a country factor, the Fama-French 48 industries ofindustry, the 12 styles, fitted onestuwith square-root-of-cap weights; any factor outputs can stand in (style_names,industry_name,industries,country). The column fields default toBarraStyleParameters’ and must match the ones the exposures were computed with. The half-lives default to USE4S’s (Table 4.1, Table 5.1); USE4L isvolatility_half_life=252,specific_half_life=252(and windows of three half-lives, 756). The Newey-West lags of the factor volatilities and correlations are USE4’s (5 and 2); two defaults are our choice, measured on the Sharadar history with bias statistics over 1- and 21-bar returns (#197): the factor volatilities’ autocorrelations use a 504-bar half-life (with the volatilities’ own 84 the multiplier is so noisy that calibration worsens), and the specific volatilities have no Newey-West adjustment (USE4: 5 lags, half-life 252; it worsened the specific bias at both horizons).specific_lags=5,specific_autocorrelation_half_life=252andvolatility_autocorrelation_half_life=Nonegive USE4’s.Examples
With
styleaBarraStyleandpricesthe dataset it reads:>>> cfg = Use4RiskConfig( ... exposures=style, dataset=prices, exposure_data_strategy="read", ... regression_path="risk/use4_regression.zarr", ... ) >>> len(cfg.style_names), len(cfg.industries), cfg.estu_name (12, 48, 'estu')
A model of two style factors and a country factor over every symbol, with
signalsa factor outputtingvalueandquality:>>> cfg = Use4RiskConfig( ... exposures=signals, dataset=prices, exposure_data_strategy="cal", ... style_names=("value", "quality"), industry_name=None, industries=(), ... estu_name=None, regression_path="risk/two_style_regression.zarr", ... ) >>> cfg.country, cfg.weighting (True, 'sqrt_cap')
- blending_min_observations = 60#
The blending coefficient is
min(1, max(0, (h - m) / r)) * min(1, max(0, exp(1 - Z)))withhthe specific returns in the lastspecific_windowbars,mthis value andrblending_ramp;Z = |s / s_robust - 1|,s_robustthe interquartile range over 1.35 andsthe standard deviation of the returns clipped toblending_outlier_boundtimess_robust. USE4 publishes only that it is 1 for few missing returns and thin tails and 0 for many or fat; these values (60, 120, 10) are third-party replications’ (our choice).
- blending_outlier_bound = 10.0#
See
blending_min_observations.
- blending_ramp = 120#
See
blending_min_observations.
- correlation_half_life = 504.0#
Half-life, in bars, of the exponential weights of the factor correlations (USE4S and USE4L: 504 trading days).
- correlation_lags = 2#
Newey-West lags of the factor correlations (USE4S and USE4L: 2); 0 for none.
- correlation_window = 1512#
Bars of factor returns the factor correlations are estimated from. Our choice: three half-lives.
- country = True#
Whether the model has a country (market) factor, an exposure of 1 for every symbol.
- eigen_fit_skip = 15#
Eigenfactors, from the lowest-volatility one, the parabola of the scaled adjustment gives no weight (USE4: 15).
- eigen_scale = None#
aof USE4’s scaled adjustment (eq. B8, USE4: 1.4): the simulated volatility biases are fitted with a parabola in the eigenfactor number and scaled toa (v_P - 1) + 1.Noneis USE4’s simulated adjustment (eq. B7), which the USE4 model uses.
- eigen_seed = 0#
Seed of the simulations. A bar’s draws come from
numpy.random.default_rng([eigen_seed, bar]),barits timestamp in nanoseconds modulo2**64, so a row does not depend on the rows computed with it.
- eigen_simulations = 1000#
Simulated factor-return histories of the eigenfactor risk adjustment (USE4 §4.2, Appendix B); 0 for none. Each bar’s covariance is adjusted from simulations of its own: complete histories as long as the longest window the covariance reads (our choice), normal with that covariance, estimated with the same windows, half-lives and Newey-West lags. USE4 publishes no count. Our choice: 1000.
- estu_name = 'estu'#
The exposures variable that is 1 inside the estimation universe, the symbols the model is fitted on;
Nonefits on every symbol.
- industries = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48)#
The industry codes, one factor each; a symbol with another code has no industry exposure and is left out. Used only with
industry_name.
- industry_name = 'industry'#
The exposures variable holding each symbol’s industry code;
Nonefor a model without industry factors.
- min_industry_members = 5#
Fewest fitted members an industry needs on a bar to be in that bar’s regression. Our choice.
- min_observations = 21#
Fewest observations in its window for a factor variance, a pair’s correlation, a symbol’s specific volatility or a lagged autocorrelation to be estimated; NaN with fewer (a lag term: none). Also the fewest bias statistics a volatility regime multiplier needs (1 with fewer). Our choice.
- njobs = 1#
Processes the estimate rows are computed in; 1 computes them in this one. The rows do not depend on it.
- return_outlier_sigma = 5.0#
Robust distance from the cross-sectional median, in standard deviations estimated as 1.4826 times the median absolute deviation, beyond which a fitted return is trimmed to that bound for the fit. Our choice.
- shrinkage = 0.1#
Bayesian shrinkage parameter
qof the specific volatilities toward the cap-weighted mean of their size group (USE4 §5.2, eqs. 5.6-5.9; USE4S and USE4L: 0.1); 0 for none. On the Sharadar history a largerqflattened the 21-bar bias statistics across forecast-volatility deciles but worsened the one-bar ones and the random active portfolios’, and 0 left the lowest decile underpredicted, so USE4’s value stands (#203).
- shrinkage_groups = 10#
Size groups of the shrinkage, equal-count market-cap groups of the symbols with a specific volatility and a market cap at the bar (USE4: deciles).
- specific_autocorrelation_half_life = 252.0#
Half-life, in bars, of the exponential weights of the specific returns’ autocorrelations behind the specific Newey-West adjustment (USE4S and USE4L: 252 trading days).
- specific_autocorrelation_window = 756#
Bars of specific returns the autocorrelations are estimated from. Our choice: three half-lives.
- specific_half_life = 84.0#
Half-life, in bars, of the exponential weights of the specific volatilities (USE4S: 84 trading days; USE4L: 252).
- specific_lags = 0#
Newey-West lags of the specific volatilities; 0 for none. USE4S and USE4L: 5. Our choice: 0, as the adjustment worsened the specific bias statistics over 1- and 21-bar returns on the Sharadar history (#197).
- specific_window = 252#
Bars of specific returns the specific volatilities are estimated from. Our choice: three half-lives.
- structural_bias = 'smearing'#
E_0of USE4 eq. 5.4, “slightly greater than 1”, which removes the bias of exponentiating the residuals: a number, or"smearing", each bar’s mean ofexp(residual)over the fitted symbols (Duan’s 1983 smearing estimate, unweighted). USE4 publishes no value; third-party replications use 1.05. Our choice:"smearing"; with the two choices below, 1.05 left the symbols the fill covers slightly more underpredicted on the Sharadar history (#203).
- structural_fit = 'series'#
The symbols the structural regression is fitted on:
"blending", those whose blending coefficient is 1 (USE4 §5.1);"series", every symbol with a time-series value. Our choice:"series". The well-behaved symbols of USE4’s fit underpredict the short-history and fat-tailed symbols the structural value is given to; fitting on every series lowered the bias statistic of the symbols the fill covers on the Sharadar history (#203).
- structural_history_window = 756#
Bars of the history-length regressor of the structural regression:
log(1 + h / 252),hthe symbol’s specific returns in the last this many bars (years of daily bars, at most this window), so a new listing gets the higher volatility short histories have.Nonefor none, USE4’s regression on the exposures alone. Our choice: 756, three years (#203: on the Sharadar history the symbols the fill covers had a 21-bar bias statistic of 1.36 without it, 1.09 with it and the two choices above). A row reads this many bars of specific returns.
- structural_model = 'fill'#
How the specific volatilities use a structural model (USE4 §5.1, eqs. 5.3-5.5): each bar, the log time-series volatility of the symbols
structural_fitnames is regressed on their exposures and, withstructural_history_window, the history-length regressor (weighted asweighting), and a symbol’s structural volatility isstructural_biastimes the exponential of its fitted value."blend"is USE4’s,gammatimes the time series plus1 - gammatimes the structural value;"fill"gives the structural value only to the symbols without a time-series value (fewer thanmin_observationsreturns in the window) and keeps every other symbol’s time series;"off"uses none. Either of the first two gives every symbol with exposures a specific risk (a new listing included) whenever enough symbols are fitted. Our choice:"fill", for that coverage; on the Sharadar history the blend worsened the specific bias statistics, with USE4’s structural model (#199) and with the calibrated one (#203).
- style_names = ('style_size', 'style_beta', 'style_momentum', 'style_residual_volatility', 'style_nonlinear_size', 'style_nonlinear_beta', 'style_liquidity', 'style_dividend_yield', 'style_book_to_price', 'style_earnings_yield', 'style_leverage', 'style_growth')#
The continuous exposures regressed on, one factor each.
- volatility_autocorrelation_half_life = 504.0#
Half-life, in bars, of the exponential weights of the factor returns’ autocorrelations behind the volatilities’ Newey-West adjustment;
Noneforvolatility_half_life, as USE4 (which publishes no separate one). Our choice: 504, as the correlations, so the multiplier is estimated from more bars (#197).
- volatility_autocorrelation_window = 1512#
Bars of factor returns those autocorrelations are estimated from;
Noneforvolatility_window. Our choice: three half-lives.
- volatility_half_life = 84.0#
Half-life, in bars, of the exponential weights of the factor volatilities (USE4S: 84 trading days; USE4L: 252).
- volatility_lags = 5#
Newey-West lags of the factor volatilities (USE4S and USE4L: 5); 0 for none.
- volatility_window = 252#
Bars of factor returns the factor volatilities are estimated from, the exponential weights truncated there. Our choice: three half-lives.
- vra_half_life = 42.0#
Half-life, in bars, of the volatility regime adjustment (USE4 §4.3, §5.3; USE4S: 42 trading days, USE4L: 168), the same for the factor volatilities and the specific risk, as USE4;
Nonefor none. Each bar’s forecasts are scaled bysqrt(sum_t w_t B_t^2),B_tthe cross-sectional bias statistic of bart’s returns against the forecasts of the bar before (the factors’ root mean square over the factors with a forecast and a return, where USE4 divides by every factor; the specific returns’ weighted by the market caps of the bar before over its estimation universe, where USE4 names no bar or universe: our choices),w_texponential weights normalised to 1 over the lastvra_windowbars; the factor covariance by its square, so the correlations do not move. With fewer thanmin_observationsbias statistics in the window a multiplier is 1 (our choice).
- vra_window = 126#
Bars of bias statistics the multipliers are estimated from. Our choice: three half-lives.
- weighting = 'sqrt_cap'#
Regression weights: the square root of the previous bar’s market cap, the market cap, or equal.