DOCUMENTATION
Build deterministic trading strategies with the Podium Strategy SDK.
First Backtest
Step-by-step tutorial: clone a template, run a backtest, and interpret results.
Risk Limits
Every guardrail check explained with numeric examples of what happens on breach.
Long-Short Strategies
Gross vs net exposure, borrow costs, long/short attribution, and archetypes.
Leaderboard Scoring
How the composite score works, component weights, eligibility, and deduplication.
STRATEGY SDK
All strategies extend Strategy and implement universe() and signal() to return target portfolio weights.
REQUIRED & OPTIONAL METHODS
initialize(ctx) — optional
Called once on the first tick. Use for setting parameters or loading state.
universe(ctx) -> list[str] — required
Return the list of symbols to consider. Called before each signal() invocation.
signal(ctx) -> dict[str, float] — required
Return target weights as a dict mapping symbol to weight (0.0-1.0). Weights are normalized to sum to 1.0 for long-only strategies.
risk_limits() -> dict — optional
Override default risk limits per strategy. See Risk Limits docs for all available parameters.
STRATEGY CONTEXT
Every method receives a StrategyContext with:
# StrategyContext fields available in every method call:
ctx.date # Current trading date (datetime)
ctx.portfolio # Current portfolio state
ctx.data # DataAccessor for market data
ctx.config # StrategyConfig from strategy.json
ctx.security_master # Security master lookupDATA ACCESSOR
# DataAccessor methods:
ctx.data.returns(lookback=126) -> pd.DataFrame
# Daily returns for all universe symbols
ctx.data.prices(lookback=126) -> pd.DataFrame
# Daily close prices
ctx.data.volume(lookback=126) -> pd.DataFrame
# Daily volumeRISK LIMITS OVERRIDE
# Optional: override default risk limits
def risk_limits(self) -> dict:
return {
"max_position_pct": 0.10, # 10% max per position
"max_sector_pct": 0.35, # 35% max per sector
"max_drawdown_pct": 0.20, # 20% max drawdown
"min_positions": 5, # Minimum 5 positions
}COMPLETE WORKING EXAMPLE — MOMENTUM RANKING
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 or state.
pass
def universe(self, ctx: StrategyContext) -> list[str]:
# Return the list of symbols to consider.
# Called before each signal() call.
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}.
# Weights are normalized to sum to 1.0.
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}USER GUIDES
In-depth manuals for backtesting, strategy development, and deployment