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