Cross-Sectional Template
Cross-Sectional templates rank a universe of symbols and rebalance into the strongest or weakest names. Use them when your idea depends on relative ranking across many assets.
These templates should express a target portfolio, not a stream of independent fixed-size orders. For live trading, set a basket budget, convert it to target gross exposure with portfolio_value(), then call rebalance_to(...). That keeps each run anchored to the intended exposure and lets the runner send only the delta needed to reach the target state.
Like every multi-symbol strategy, a cross-sectional universe is limited to 20 symbols per strategy. Keep the universe within that limit and use a separate strategy when the research question requires a different basket.
Available Cross-Sectional Templates
| Template | Behavior |
|---|---|
| Momentum Long/Short | Buys relative leaders and shorts relative laggards. |
| Reversal Long/Short | Buys recent laggards and shorts recent leaders. |
Universe and Rebalancing Checks
Build the universe from markets that are actually tradable for the selected account, and exclude symbols with insufficient data or liquidity. A changing universe can introduce survivorship and selection bias, so record how symbols enter and leave the ranking. Test the ranking window, number of selected names, rebalance frequency, maximum symbol weight, and gross exposure on an out-of-sample period.
Rebalancing generates only the difference between current and target weights, but frequent small changes can still create fees and turnover. Use min_change to ignore immaterial adjustments and inspect concentration after cap_weights. Before live trading, verify that both long and short markets are supported, that the basket budget fits available collateral, and that a missing price or failed order cannot leave the portfolio with unintended one-sided exposure.
Momentum Long/Short DSL
scores = {}
for ticker in context['tickers']:
data = get_data(ticker)
ret = data.close / data.close.shift(20) - 1
last_ret = ret[-1]
if last_ret is None:
continue
scores[ticker] = last_ret
scores = drop_nan(scores)
longs = top_n(scores, 5)
shorts = bottom_n(scores, 5)
target_basket_notional = 1000.0
equity = portfolio_value()
target_gross = min(target_basket_notional / equity, 1.0) if equity else 0.0
weights = long_short_equal_weight(longs, shorts, gross=target_gross)
weights = cap_weights(weights, 0.15)
rebalance_to(weights, min_change=0.02)
Reversal Long/Short DSL
scores = {}
for ticker in context['tickers']:
data = get_data(ticker)
ret = data.close / data.close.shift(20) - 1
last_ret = ret[-1]
if last_ret is None:
continue
scores[ticker] = -last_ret
scores = drop_nan(scores)
longs = top_n(scores, 5)
shorts = bottom_n(scores, 5)
target_basket_notional = 1000.0
equity = portfolio_value()
target_gross = min(target_basket_notional / equity, 1.0) if equity else 0.0
weights = long_short_equal_weight(longs, shorts, gross=target_gross)
weights = cap_weights(weights, 0.15)
rebalance_to(weights, min_change=0.02)