quantlab.portfolio.base.PortfolioConstructor#

class quantlab.portfolio.base.PortfolioConstructor(config)[source]#

Bases: _Configured, ABC

Base class of every rule that turns one bar’s scores into target weights.

Subclass it, set config_cls to a dataclass of the rule’s parameters and implement construct; override bind to check the label specs and read what the rule needs from them, and declared_inputs when it reads a return window, factor panels or a factor risk model’s exposures. decide makes the one-bar decision on a context DecisionInputs assembles. get_config and from_config serialise the rule as its config’s fields plus the class’s import path under "name", which a backtest’s config.json records.

Parameters:
configdataclass instance

An instance of config_cls.

Raises:
TypeError

If config is not a config_cls instance.

Examples

>>> from quantlab.portfolio.config import TopNConfig
>>> from quantlab.portfolio.predefined.top_n import TopNConstructor
>>> rule = TopNConstructor(TopNConfig(direction="long_only", top_n=2))
>>> isinstance(rule, PortfolioConstructor), rule.config.top_n
(True, 2)
axis_factors()[source]#

The declared factor outputs the rule reads on symbols it has no prediction for.

A run decides on the symbols with a prediction on some bar of its window, the only ones a rule can buy, plus the symbols with a value of one of these outputs on some bar (DecisionInputs.decision_symbols): a benchmark’s or the market’s weights, which the rule reads on symbols it never holds. None by default.

Examples

>>> rule.axis_factors()
()
bind(labels)[source]#

Check the label specs and read from them what the rule needs.

Called once when a backtest is constructed, before any data is read or model trained, with the specs of the predictor’s labels; and by DecisionInputs.from_run with the specs a run’s prediction panel records. A rule reading a label’s span resolves it here. The specs are the only metadata a rule may read about a prediction. The default accepts any specs.

Parameters:
labelsSequence[LabelSpec]

One spec per prediction variable, in the predictor’s order.

Raises:
ValueError

If the rule cannot use the labels.

Examples

>>> rule.bind([LabelSpec(name="ret_5", scale="raw", delay=1, span=5)]) is None
True
abstractmethod construct(context)[source]#

Return the weights to hold after the context’s bar.

Parameters:
contextPortfolioContext

What is known at the bar.

Returns:
xr.DataArray

One weight per symbol of context.symbols, on symbol: finite, 0.0 where nothing is held, gross exposure at most one; or all NaN to hold the current position.

Raises:
PortfolioConstructionError

If the bar cannot be decided (the loop then holds it).

Examples

>>> rule.construct(context).values  # CCC is locked at 0.6
array([0.2, 0.2, 0.6])
decide(context)[source]#

Decide one bar: run construct, check its row and hold a bar it cannot solve.

The one-bar decision, shared by DecisionInputs.weights and event-driven executors. A PortfolioConstructionError from construct becomes a hold (all-NaN weights) carrying the error’s message, with a warning. A row that breaks the weights contract is a bug in the rule, not a hold, and raises. decide is not meant to be overridden: a rule implements construct.

Parameters:
contextPortfolioContext

The bar’s context, from DecisionInputs or built by hand.

Returns:
Decision

The weights on context.symbols (all NaN to hold), the failure message or None, and the row’s events.

Raises:
ValueError

If construct returns weights on other symbols than the context’s, a row mixing NaN and finite values, a changed locked position (held, not tradable) or weight on a symbol neither tradable nor held.

Examples

>>> decision = rule.decide(context)
>>> decision.weights.values, decision.failure, decision.events
(array([0.2, 0.2, 0.6]), None, {})
declared_inputs()[source]#

What the rule reads at each bar besides its predictions and holdings.

DecisionInputs builds each context from it: the return window and the prices behind it, the factors’ values and the risk model’s exposures. None of them by default; a rule holding a covariance estimator declares the estimator’s, merged with its own (InputDeclaration.merged).

Examples

>>> rule.declared_inputs()
InputDeclaration(lookback_bars=0, history_bars=1, factors=(), risk_model=None)
keeps_every_symbol()[source]#

Whether a run keeps every symbol of its price axis rather than those it decides on.

True when a decision reads symbols beyond those with a prediction and those of axis_factors, as a covariance estimated over every symbol the context holds does (CovarianceEstimator.pools_symbols). False by default.

Examples

>>> rule.keeps_every_symbol()
False
ranking_label(names)[source]#

Return the predicted label the rule ranks the symbols by.

A run’s attribution ranks its universe and score groups by it. The default is the first label; a rule reading one label by name answers with that label.

Parameters:
namesSequence[str]

The predicted labels, in the predictor’s order.

Returns:
str

Examples

>>> rule.ranking_label(["ret_5", "ret_1"])
'ret_5'