quantlab.portfolio.predefined.top_n.TopNConstructor#
- class quantlab.portfolio.predefined.top_n.TopNConstructor(config)[source]#
Bases:
PortfolioConstructorEqual-weight top-n selection on each rebalance bar.
A locked position (held, not tradable at the bar) keeps its current weight and is never picked. The picks come from the other symbols that are tradable with a finite score, ranked with a stable sort, so ties resolve by symbol order. When the cut of a book that picks falls inside a group of equal scores, the row’s
attrs["events"]["tie_at_cutoff"]counts the tied symbols left out, so a run records how often its picks were decided by symbol order rather than by the scores. Withdirection="long_only"each of thekhighest-scoring picks gets(1 - L) / k,Lthe sum of the locked weights; with nothing locked that is1/k, for a gross exposure of 100%. Withdirection="long_short"the topkget(0.5 - L_long) / keach and the bottomkget-(0.5 - L_short) / keach,L_longandL_shortthe locked long and short exposure; the two books never share a symbol. A side with no budget left adds nothing.kistop_n, reduced when fewer symbols can be picked, and a warning names the bar. With no pick the row is the locked positions and 0.0 elsewhere.- Parameters:
- configTopNConfig
direction,top_nandscore_label, the label ranking the symbols (Nonefor the predictor’s first label).
- Raises:
- ValueError
If
directionis not"long_only"or"long_short", ortop_nis smaller than 1.
Examples
>>> import numpy as np, pandas as pd, xarray as xr >>> from quantlab.portfolio.config import TopNConfig >>> from quantlab.portfolio.base import PortfolioContext >>> rule = TopNConstructor(TopNConfig(direction="long_only", top_n=2)) >>> symbols = ["AAA", "BBB", "CCC", "DDD"] >>> context = PortfolioContext( ... timestamp=pd.Timestamp("2024-01-02"), ... predictions=xr.Dataset({"ret": ("symbol", [0.3, 0.1, np.nan, 0.2])}, ... coords={"symbol": symbols}), ... tradable=xr.DataArray([True, True, True, True], dims="symbol", ... coords={"symbol": symbols}), ... current_weights=xr.DataArray(np.zeros(4), dims="symbol", ... coords={"symbol": symbols}), ... ) >>> rule.construct(context).values array([0.5, 0. , 0. , 0.5])
- bind(labels)[source]#
Refuse empty label specs, or specs without
score_label.Any scale ranks, so a spec’s
scaleis not read.- Raises:
- ValueError
If
labelsis empty, orscore_labelis set and not one of them; the message lists the labels.
Examples
>>> from quantlab.runs.prediction_panel import LabelSpec >>> specs = [LabelSpec("ret_5", "raw", 1, 5), LabelSpec("ret_1", "raw", 1, 1)] >>> rule = TopNConstructor(TopNConfig(direction="long_only", top_n=2, score_label="ret_20")) >>> rule.bind(specs) Traceback (most recent call last): ValueError: score_label 'ret_20' is not one of the predicted labels ['ret_5', 'ret_1']
- config_cls#
alias of
TopNConfig
- construct(context)[source]#
Return the equal-weight top-n book of the context’s bar.
See the class docstring for the rule and an example.
- Parameters:
- contextPortfolioContext
The bar’s predictions, tradability and current weights.
- Returns:
- xr.DataArray
One finite weight per symbol of
context.symbols.
Examples
>>> rule = TopNConstructor(TopNConfig(direction="long_short", top_n=1)) >>> rule.construct(context).values array([ 0.5, -0.5, 0. , 0. ])