Weights
How target portfolio exposure is produced and transformed into realized portfolio state.
Weights are target portfolio exposure. They are not orders, fills or final positions. The engine later shifts, risk-adjusts, rebalances, charges costs and converts realized exposure into positions.
Source
backtester/core.py + backtester/portfolio/rebalance.pyLayerPortfolio target exposure
ModeWeight mode only
Inputsignals, scores, constraints and portfolio rules
Outputtarget weights DataFrame indexed by date and instrument
Primary API
_compute_weights(self) -> pd.DataFrameMain caveatTarget weights are shifted and rebalanced before becoming realized exposure.
Engine Contract
The weight hook answers: "What exposure do I want?" It does not answer: "What order filled?", "What shares do I own?" or "What price did I trade?" Those are later accounting and execution questions.
signals -> target_weights
-> shift(1) to avoid same-bar look-ahead
-> cash buffer
-> optional risk model
-> RebalanceEngine
-> actual realized weights
-> positions and NAV
Data Contract
| Contract item | Requirement |
|---|---|
| Shape | dates x instruments DataFrame |
| Index | aligned to engine trading dates |
| Columns | engine universe instruments |
| Values | portfolio weights, for example 0.25, 0.0, -0.10 |
| Row sum | can be less than 1.0 for cash; can exceed 1.0 only if leverage is intentionally modeled |
| NaN policy | fill explicitly before returning |
| Timing | raw weights are shifted by the performance path before returns are earned |
Level 1: Equal Weight Active Signals
This is the canonical long/cash allocation pattern.
Equal weight active names
python
class EqualWeightSignals(Backtester):
def _compute_weights(self) -> pd.DataFrame:
signals = self.instruments_data.get_feature(
"strategies", self.strategy_name, "signals"
)
active = signals == 1
n_active = active.sum(axis=1)
weights = active.div(n_active, axis=0).fillna(0.0)
return weightsLevel 2: Capped Exposure With Cash Sleeve
Use this when you want residual cash instead of forcing 100 percent exposure.
Cap each position and preserve cash
python
class CappedWeights(Backtester):
def _compute_weights(self) -> pd.DataFrame:
signals = self.instruments_data.get_feature(
"strategies", self.strategy_name, "signals"
)
active = signals == 1
n_active = active.sum(axis=1)
raw = active.div(n_active, axis=0).fillna(0.0)
capped = raw.clip(upper=float(self.max_position_size))
# Do not renormalize. Unallocated weight remains cash.
return capped.fillna(0.0)Level 3: Rank-Based Top-N Portfolio
Continuous signals can be transformed into top-N weights.
Top 10 ranked names
python
class TopNWeights(Backtester):
def _compute_weights(self) -> pd.DataFrame:
score = self.instruments_data.get_feature(
"strategies", self.strategy_name, "signals"
)
top_n = score.rank(axis=1, ascending=False) <= 10
weights = top_n.astype(float)
weights = weights.div(weights.sum(axis=1), axis=0)
return weights.fillna(0.0)Level 4: Long/Short Dollar-Neutral Weights
Long/short weights should make gross and net exposure explicit.
Top long / bottom short
python
class LongShortWeights(Backtester):
def _compute_weights(self) -> pd.DataFrame:
score = self.instruments_data.get_feature(
"strategies", self.strategy_name, "signals"
)
long_mask = score.rank(axis=1, ascending=False) <= 10
short_mask = score.rank(axis=1, ascending=True) <= 10
longs = long_mask.astype(float).div(long_mask.sum(axis=1), axis=0) * 0.50
shorts = short_mask.astype(float).div(short_mask.sum(axis=1), axis=0) * -0.50
return (longs.fillna(0.0) + shorts.fillna(0.0)).fillna(0.0)What Happens After Weights Are Returned?
- The engine stores the raw target weights.
- Weight-mode performance shifts targets by one bar.
- The engine applies the cash buffer.
- A configured risk model can adjust weights.
RebalanceEnginedecides when target weights become actual weights.- Between rebalance dates, actual weights drift with returns.
- Positions are derived from actual weights, NAV and prices.
- Transaction cost is charged on rebalance-day turnover.
Failure Modes
- Normalizing capped weights back to 100 percent when the strategy intended cash.
- Forgetting that raw target weights are not the final realized weights.
- Returning NaNs from dates before enough signal history exists.
- Treating weight-mode rebalancing as a market/limit order simulation.
- Assuming
RebalanceAt.OPENorVWAP_WINDOWimplies full open/VWAP fill simulation unless implemented in the performance path.
Audit Checklist
- Inspect raw target weights and realized portfolio weights separately.
- Check row sums for cash, leverage and short exposure.
- Inspect
portfolio_data.rebalance_flags. - Compare turnover before and after changing rebalance policy.
- Confirm transaction costs appear only on rebalance days.