Strategy Development Guide

Build deterministic trading strategies with the Podium Strategy SDK. Strategies extend the Strategy base class and implement universe() and signal() to return target portfolio weights.

Strategy Lifecycle

Every strategy extends Strategy and implements up to 4 methods. The engine calls these in order on each trading day.

initialize(ctx) — Optional

Called once on the first tick. Use for setting parameters, loading state, or pre-computing values that don't change daily.

universe(ctx) → list[str] — Required

Called before each signal(). Return the list of symbols your strategy considers. Dynamic — can change daily based on data availability or screening criteria.

signal(ctx) → dict[str, float] — Required

Return target weights as a dict mapping symbol to weight. Positive weights = long, negative weights = short. The engine derives orders from weight changes (delay-1: signal at T close, fill at T+1 open).

risk_limits() → dict — Optional

Override default risk limits. See Risk Limits docs for all available parameters and what happens on breach.

Complete Example — Momentum Ranking

python
from podium_sdk import Strategy, StrategyContext

class MomentumRanking(Strategy):
    """6-month momentum, top 20 by trailing return, equal weight."""
    TOP_N = 20
    LOOKBACK_DAYS = 126

    def initialize(self, ctx: StrategyContext) -> None:
        # Called once on first tick. Use for loading parameters.
        pass

    def universe(self, ctx: StrategyContext) -> list[str]:
        # Return the list of symbols to consider.
        returns = ctx.data.returns(lookback=self.LOOKBACK_DAYS + 5)
        if returns.empty:
            return []
        return list(returns.columns)

    def signal(self, ctx: StrategyContext) -> dict[str, float]:
        # Return target weights {symbol: weight}.
        returns = ctx.data.returns(lookback=self.LOOKBACK_DAYS)
        if returns.empty:
            return {}
        cum_return = (1 + returns).prod() - 1
        ranked = cum_return.sort_values(ascending=False)
        top_n = ranked.head(self.TOP_N)
        if len(top_n) == 0:
            return {}
        weight = 1.0 / len(top_n)
        return {sym: round(weight, 6) for sym in top_n.index}

    def risk_limits(self) -> dict:
        return {
            "max_position_pct": 0.10,
            "max_sector_pct": 0.35,
            "max_drawdown_pct": 0.20,
            "min_positions": 5,
        }

StrategyContext API

The ctx object is passed to every lifecycle method. It provides access to the current date, portfolio state, market data, and configuration.

python
# StrategyContext — available in every method:

ctx.date              # Current trading date (datetime)
ctx.portfolio         # PortfolioState (positions, cash, equity, drawdown)
ctx.data              # DataAccessor (returns, prices, volume, fundamentals)
ctx.config            # Strategy configuration dict
ctx.security_master   # Security master lookup (sector, market cap)

# DataAccessor methods:
ctx.data.returns(lookback=126)       # pd.DataFrame of daily returns
ctx.data.prices(lookback=126)        # pd.DataFrame of close prices
ctx.data.volume(lookback=126)        # pd.DataFrame of daily volume
ctx.data.fundamentals()              # Dict of PE, ROE, market cap per symbol
ctx.data.sector()                    # Dict of symbol -> sector

# PortfolioState fields:
ctx.portfolio.positions              # Dict of symbol -> {weight, quantity, value}
ctx.portfolio.cash                   # Available cash
ctx.portfolio.equity                 # Total portfolio value
ctx.portfolio.drawdown               # Current drawdown from peak

Long-Short Strategy Example

Use negative weights in signal() to short stocks. The engine handles borrow costs (1.5% annualized) and separate book accounting automatically.

python
from podium_sdk import Strategy, StrategyContext

class LongShortMomentum(Strategy):
    """130/30 momentum: long top 20, short bottom 10."""

    def universe(self, ctx: StrategyContext) -> list[str]:
        returns = ctx.data.returns(lookback=130)
        return list(returns.columns)

    def signal(self, ctx: StrategyContext) -> dict[str, float]:
        returns = ctx.data.returns(lookback=126)
        if returns.empty:
            return {}
        cum_return = (1 + returns).prod() - 1
        ranked = cum_return.sort_values(ascending=False)

        # Long top 20 at ~6.5% each = 130% gross long
        longs = ranked.head(20)
        # Short bottom 10 at ~3% each = 30% gross short
        shorts = ranked.tail(10)

        weights = {}
        for sym in longs.index:
            weights[sym] = 0.065   # Positive = long
        for sym in shorts.index:
            weights[sym] = -0.03   # Negative = short

        return weights

    def risk_limits(self) -> dict:
        return {
            "max_position_pct": 0.10,
            "max_gross_exposure": 1.7,
            "max_net_exposure": 1.2,
            "max_short_position_pct": 0.05,
            "max_drawdown_pct": 0.25,
        }

See Long-Short Strategies for detailed documentation on exposure, borrow costs, and archetypes.

Execution Model

Backtest Execution

  1. Engine calls universe() → gets list of symbols
  2. Engine calls signal() → gets target weights
  3. Guardrails enforce risk limits (scale down or reject)
  4. Orders derived from weight changes
  5. Fill at T+1 open price (delay-1 model)
  6. Portfolio marked to market at T+1 close
  7. Metrics computed (Sharpe, Sortino, drawdown, etc.)

Paper Trading Execution

  1. Daily cron triggers after market close (9:33 PM ET)
  2. Strategy code uploaded to Azure sandbox
  3. StrategyContext serialized (portfolio, OHLCV, fundamentals)
  4. signal() executed in sandbox (~500ms)
  5. Guardrails applied to target weights
  6. Orders filled at estimated close price
  7. T+1 reconciliation adjusts to actual open price

Available Python Packages

Strategies execute in a sandboxed Python environment with these pre-installed packages:

Data Science

  • numpy
  • pandas
  • scipy
  • statsmodels

Machine Learning

  • scikit-learn
  • xgboost
  • lightgbm

Technical Analysis

  • ta
  • pandas-ta

Next Steps