Simple Moving Average Strategy
A classic trend-following strategy using moving average crossovers.
Strategy Overview
This strategy uses two simple moving averages (SMAs) to generate buy and sell signals:
- Short SMA: 10-day moving average
- Long SMA: 30-day moving average
- Buy Signal: Short SMA crosses above Long SMA
- Sell Signal: Short SMA crosses below Long SMA
Complete Strategy Code
strategy.pypython
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
from podium_sdk import Strategy, StrategyContext
from podium_sdk.types import RiskLimits
class SmaCrossover(Strategy):
"""
Trend-following SMA crossover.
Hold (equal weight) the names whose 10-day SMA is above their
30-day SMA; exit the rest. Strategies return target weights —
the engine derives orders and fills at the next open (delay-1).
"""
SHORT_WINDOW = 10
LONG_WINDOW = 30
SYMBOLS = [
"AAPL", "MSFT", "GOOGL", "AMZN", "META",
"NVDA", "TSLA", "JPM", "V", "WMT",
]
def universe(self, ctx: StrategyContext) -> list[str]:
return self.SYMBOLS
def signal(self, ctx: StrategyContext) -> dict[str, float]:
# Close-price panel: rows = dates, columns = symbols.
close = ctx.data.close(symbols=self.SYMBOLS, lookback=self.LONG_WINDOW + 5)
if close.empty:
return {}
short_sma = close.rolling(self.SHORT_WINDOW).mean().iloc[-1]
long_sma = close.rolling(self.LONG_WINDOW).mean().iloc[-1]
# Go long names in an uptrend (short SMA above long SMA).
longs = [s for s in close.columns if short_sma[s] > long_sma[s]]
if not longs:
return {} # all cash
weight = 1.0 / len(longs)
return {s: round(weight, 6) for s in longs}
def risk_limits(self, ctx: StrategyContext) -> RiskLimits:
return RiskLimits(max_position_pct=0.10, max_drawdown_pct=0.20)How It Works
1
Fetch Data
Retrieve 40 days of historical price data for all symbols in the universe.
2
Calculate Indicators
Compute 10-day and 30-day simple moving averages using pandas rolling windows.
3
Detect Crossovers
Compare current and previous SMA values to identify crossover events.
4
Generate Weights
Return equal target weights for names in an uptrend. The engine derives orders from the weight changes and fills at the next open.
Performance Note: Simple moving average strategies can underperform in choppy or range-bound markets. Consider adding filters like volume confirmation or trend strength indicators to improve performance.
Strategy Variations
Exponential Moving Average (EMA)
Use EMA instead of SMA for faster reaction to price changes:close.ewm(span=10).mean()
Triple Crossover
Add a third SMA for additional confirmation. Buy only when all three are aligned bullish.
Adaptive Windows
Adjust SMA periods based on market volatility or trend strength.
Next Steps
- Backtest this strategy to see historical performance
- Build a multi-factor model for more sophisticated signals
- Explore the Strategy API for more configuration options