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 REFERENCE
Full SDK docs — quickstart, architecture, data, execution, API reference, examples
Alpha Operators
40 formulaic operators via ctx.ops — rank, decay, correlation, indicators.
Skill Bundles
Author SKILL.md + sandboxed scripts callable from signal().
Strategy Templates
8 long-only and long-short templates to clone and customize.
Deployment & Paper Trading
Deploy, daily ticks, monitoring, and T+1 reconciliation.
Data Sources
Databento OHLCV + FMP fundamentals: coverage and fields.
Security Model
Sandbox isolation, AST scanning, and env restrictions.
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