quantlab.factor.predefined.barra.BarraStyle#
- class quantlab.factor.predefined.barra.BarraStyle(config)[source]#
Bases:
FactorKunQuantUSE4-style exposures: standardized descriptors and style factors.
Reads the panel variables of
BarraStyleParameters.panel_columns: the adjusted close, the market cap, the risk-free rate, the raw close, raw volume, cash dividend and split factor, eight SF1 fundamentals and the fiscal-year EPS and sales-per-share history. They usually come from a merge of a Sharadar price dataset, the DAILY dataset, the SF1 ART fundamentals dataset (whose balance-sheet items equal ARQ’s on every filing), the fiscal-year history dataset and a risk-free series broadcast across symbols, and the share-class firm of each symbol. On every bart:a secondary share class (
firm_columnnaming another symbol) takes that symbol’s market cap, fundamentals and fiscal-year history before anything else is computed;the estimation universe is the
estimation_universe_sizesymbols with the largest market cap att-1, secondary share classes left out;the market return is the universe’s return, weighted by the market cap at
t-1;returns are in excess of the risk-free rate of the bar before (the rate of a day is published the next business day);
excessisr - r_fandlog_excessislog(1 + r) - log(1 + r_f);LNCAP is
log(marketcap[t]);BETA is the slope of an exponentially weighted least-squares fit of
excesson the market’s excess return over the lastbeta_windowbars (half-lifebeta_half_life), and HSIGMA the weighted standard deviation of that fit’s residual;RSTR is the exponentially weighted sum of
log_excessovermomentum_windowbars endingmomentum_lagbars ago (half-lifemomentum_half_life), the weights not normalized (Empirical Notes eq. A2, p.52); a bar without a return adds nothing;DASTD is the exponentially weighted standard deviation of
excessoverdastd_windowbars (half-lifedastd_half_life);CMRA is
log(1 + max Z) - log(1 + min Z),Z(T)the sum oflog_excessover the lastTmonths ofcmra_month_lengthbars,T = 1 .. cmra_months; NaN whenmin Z <= -1;a day’s turnover is
volume * close / marketcap, its dollar volume over its market cap: the share countmarketcap / closeand the volume are on that day’s share basis, so a split moves neither. A secondary share class’s turnover is its dollar volume over its firm’s market cap, as Sharadar gives no per-class share count. STOM, STOQ and STOA arelog(L * mean turnover)over the last 1,stoq_monthsandstoa_monthsmonths ofL = liquidity_month_lengthbars, which is USE4’slog(sum over a month)andlog(mean over months of exp(STOM))when every day has a turnover; a day without one is left out of the mean (our choice), and a window of zero volume only is NaN;YILD is the cash dividends of the last
dividend_windowbars, each converted to today’s share basis through the splits since its ex-date, divided by today’s raw close;fundamentals are converted to USD by
fx_columnwhere they meet the market cap. BTOP is book equity over market cap; ETOP is trailing net income to common over market cap and CETOP the same plus depreciation and amortization; with long-term debtLD(the total debt where the long-term part is not reported), MLEV is1 + LD / market cap, BLEV1 + LD / book equity(NaN for book equity at or below 0, our choice) and DTOA(LD + current liabilities) / assetsover the non-current debt only (NaN where it or the current liabilities are not reported);EGRO is the slope of a least-squares fit of the fiscal-year EPS
eps_fy0 .. eps_fy<growth_years - 1>on time, each year placed at its fiscal year end in years before the latest’s (so a missing year or a moved year end keeps the spacing true), over the years known, divided by their mean absolute value; NaN with fewer thanmin_growth_yearsknown years or a mean of 0. SGRO is the same with sales per share. Dividing by the mean absolute value, not the signed mean of the text, is our choice;a windowed descriptor is NaN with fewer valid values in its window than its
*_min_observations(liquidity_min_fractionof the window for Liquidity);each descriptor has its outliers treated, then is standardized: with
mandsthe equally weighted mean and standard deviation of the raw descriptor over the universe, a value beyondm +- data_error_sigma * sbecomes NaN and the rest are clipped tom +- clip_sigma * s(Methodology Notes §2.2, p.8); the result is standardized over the universe (market cap att-1weighted mean 0, equally weighted standard deviation 1; §2.3, p.9, eq. 2.4);Size, Beta and Momentum are their one descriptor standardized again;
Residual Volatility is
0.75 DASTD + 0.15 CMRA + 0.10 HSIGMA(*_weight) over the descriptors present, standardized, orthogonalized against Beta and standardized again (Empirical Notes p.16, p.52);Liquidity is
0.35 STOM + 0.35 STOQ + 0.30 STOAover the descriptors present, standardized; Dividend Yield is YILD standardized again; Book-to-Price is BTOP standardized again; Earnings Yield is0.15 CETOP + 0.10 ETOP, Leverage0.75 MLEV + 0.15 DTOA + 0.10 BLEVand Growth0.20 EGRO + 0.10 SGRO, each over the descriptors present, standardized;a style still missing for a symbol with a market cap on the bar is imputed: the style is regressed, with
imputation_weightingweights over the universe, on one intercept per industry and a slope on the Size exposure (imputation_regressors; Size itself on the industry alone), and the symbol gets its industry’s intercept plus the slope times its Size. A symbol keeps NaN when its industry has no fitted member or a regressor of its own is missing. Every style is then standardized once more, imputed values included;the NLSIZE descriptor is the cube of the Size exposure (before imputation), orthogonalized against Size, then outlier-treated and standardized like any descriptor; Non-linear Size is it standardized again. NLBETA and Non-linear Beta are the same with Beta. This is the text’s order (Empirical Notes p.55), so the outlier step can leave a small weighted correlation with the regressor;
an orthogonalization is a weighted least-squares regression with an intercept, weighted by
orthogonalization_weightingand fitted in the universe; every symbol gets its residual, a symbol missing a regressor being taken at that regressor’s weighted mean (our choice). Standardizing afterwards keeps the weighted correlation with the regressors at zero;last, a symbol whose
price_columnis NaN on the bar gets NaN in every descriptor and style (notindustryorestu). Only the outputs are masked, so nothing above sees the mask: a windowed descriptor, which would stay defined while its window holds enough past returns, and carried fundamentals end with the prices. This reads bartonly, with no look-ahead; a halted stock has no exposure on its halt days.
Outputs, all on
(timestamp, symbol):desc_lncap,desc_beta,desc_rstr,desc_dastd,desc_cmra,desc_hsigma,desc_nlsize,desc_nlbeta,desc_stom,desc_stoq,desc_stoa,desc_yild,desc_btop,desc_etop,desc_cetop,desc_mlev,desc_dtoa,desc_blev,desc_egro,desc_sgro: the outlier-treated, standardized descriptors;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 style exposures;industry: the industry code, unchanged, on every bar;estu: 1 inside the estimation universe of the bar, 0 outside.
The graph runs in double precision. On 1000 bars of 3008 symbols, with a 252-bar BETA window of half-life 63, float32 runs 2x faster but leaves errors of 4e-7 in BETA and 6.6e-5 in standardized LNCAP against a float64 reference, where double leaves 4e-15 and 3e-10. The factor is batch only:
mode="stream"is refused.- Parameters:
- configFactorConfig
modemust be"batch",data_columnsmust name exactlyBarraStyleParameters.panel_columns,factor_namesmay pick any subset of the outputs, andwarmup_barsshould be at leastBarraStyleParameters.warmup_bars(526 by default). Withrisk_free_symbolthe warm-up is counted in bars where some symbol has a price: a date only the rate has (FRED’s DTB3 has one on 2025-01-09, when NYSE was closed) is dropped before the graph runs, so it does not count.
- Raises:
- ValueError
If
modeis not"batch",data_columnsdoes not match the parameters,factor_namesnames an unknown output, orkwargsholds an unknown or invalid parameter.
Examples
pricesis aSharadarStockDataset,dailyaSharadarDailyDatasetandrisk_freea dataset holding therisk_freevariable on the same symbols.>>> factor = BarraStyle(FactorConfig( ... warmup_bars=526, ... dataset=[prices, daily, risk_free], ... mode="batch", ... data_columns=("adjClose", "marketcap", "risk_free", "close", "volume", ... "divCash", "splitFactor"), ... file_path="barra_style.zarr", ... )) >>> factor.get_factor_names()[:4] ('desc_lncap', 'desc_beta', 'desc_rstr', 'desc_dastd')