quantlab.factor.predefined.neutralized.NeutralizedFactor#

class quantlab.factor.predefined.neutralized.NeutralizedFactor(config)[source]#

Bases: Factor

The outputs of config.factor, neutralized against industry and size.

On every bar, each output y of the wrapped factor becomes:

ZScore(y - intercept[industry] - b * log(marketcap))

the residual of an ordinary least-squares regression over every symbol with a finite y and present exposures, z-scored across symbols (ddof=1). config.regressors picks the regression: one intercept per industry plus a slope on size (the default), the industry means alone, or one intercept plus the size slope. The wrapped stock alphas already z-score their outputs, so the full chain is z-score, neutralize, z-score; the outputs are again in cross-sectional standard deviations.

The exposures are read from config.dataset on the bars of the wrapped factor’s panel, bar t’s market cap and industry for bar t. A symbol the exposures lack on a bar, or whose market cap is not positive, or whose industry code is missing, is NaN there (only the regressors in use count). A secondary share class that carries no market cap of its own (Sharadar’s GOOG beside GOOGL) is therefore NaN under size neutralization. The panel is float32, like a KunQuant factor’s.

The wrapper has a store of its own (config.file_path), written by build and extend and read by read, so a model that reads its factors from their stores reads the neutralized values. It is batch only: there is no stream mode, and it cannot be resampled.

Parameters:
configNeutralizedConfig

The factor config: the wrapped factor, the exposures dataset and the column names and regressors.

Examples

With alpha158 an Alpha158Stock over Sharadar prices on the permaticker axis, daily a SharadarDailyDataset and industry a SharadarIndustryDataset:

>>> neutral = NeutralizedFactor(NeutralizedConfig(
...     factor=alpha158, dataset=[daily, industry],
...     file_path="factor/alpha158_neutral.zarr",
... ))
>>> neutral.get_factor_names() == alpha158.get_factor_names()
True
>>> neutral.build("2012-01-01", "2024-12-31")
>>> panel = neutral.read("2020-01-01", "2020-12-31")
bar_after(date, n)[source]#

Return the bar n bars after date on the wrapped factor’s calendar.

See Factor.bar_after for the rules.

Examples

>>> neutral.bar_after("2020-01-03", 1) == alpha158.bar_after("2020-01-03", 1)
True
bar_before(date, n)[source]#

Return the bar n bars before date on the wrapped factor’s calendar.

See Factor.bar_before for the rules and errors.

Examples

>>> neutral.bar_before("2020-01-06", 1) == alpha158.bar_before("2020-01-06", 1)
True
calendar(start, end)[source]#

Return the wrapped factor’s bars from start to end, both inclusive.

The neutralized panel is on the wrapped factor’s bars, not on the exposures dataset’s; see Factor.calendar.

Examples

>>> neutral.calendar("2020-01-01", "2020-12-31").equals(alpha158.calendar("2020-01-01", "2020-12-31"))
True
compute(start, end)[source]#

Compute the neutralized outputs from start to end, both inclusive.

The wrapped factor is computed over the range with its own warm-up; the exposures are read over the same range and placed on its bars and symbols. See Factor.compute for the arguments.

Raises:
ValueError

If start is after end.

KeyError

If the exposures dataset has no variable named by size_column or industry_column where that regressor is in use.

Examples

>>> panel = neutral.compute("2024-02-01", "2024-02-29")
>>> tuple(panel.data_vars) == neutral.get_factor_names()
True
config_cls#

The config class from_config rebuilds this factor with.

alias of NeutralizedConfig

keep_compiled()[source]#

Keep the wrapped factor’s compiled graph inside the block.

Examples

>>> with neutral.keep_compiled():
...     january = neutral.compute("2024-01-01", "2024-01-31")
...     february = neutral.compute("2024-02-01", "2024-02-29")
output_symbols()[source]#

Return the wrapped factor’s output symbols, not the exposures’.

Examples

>>> neutral.output_symbols() == alpha158.output_symbols()
True