Appearance
Simple Moving Average Crossover
Classic trend-following strategy using two moving averages to generate buy and sell signals.
Strategy Overview
Concept: Buy when fast SMA crosses above slow SMA, sell when it crosses below
Complexity: Beginner
Time Frame: Medium-term (days to weeks)
Best For: Learning technical indicators and trend following
How It Works
- Calculate two SMAs: Fast (shorter period) and Slow (longer period)
- Buy Signal: Fast SMA crosses above Slow SMA (golden cross)
- Sell Signal: Fast SMA crosses below Slow SMA (death cross)
- Hold: Stay in position between crossovers
Code Example
python
# Configuration
DEFAULT_SYMBOLS = ["AAPL"]
FAST_PERIOD = 20
SLOW_PERIOD = 50
POSITION_SIZE = 100
def on_strategy_start(portfolio, state):
"""Initialize strategy"""
print("=== SMA Crossover Strategy ===")
print(f"Fast SMA: {FAST_PERIOD} periods")
print(f"Slow SMA: {SLOW_PERIOD} periods")
print(f"Position Size: {POSITION_SIZE} shares")
def on_bar_close(data_contexts, portfolio, state):
"""Main strategy logic"""
# Get data for primary symbol
symbol = DEFAULT_SYMBOLS[0]
data = data_contexts[symbol]
# Calculate moving averages
sma_fast = data.sma(FAST_PERIOD)
sma_slow = data.sma(SLOW_PERIOD)
# Skip if indicators not ready
if sma_fast != sma_fast or sma_slow != sma_slow: # Check for NaN
return None
# Get current position
current_position = portfolio.position(symbol)
# Buy signal: Fast SMA crosses above Slow SMA
if sma_fast > sma_slow and current_position == 0:
print(f"BUY SIGNAL: Fast SMA {sma_fast:.2f} > Slow SMA {sma_slow:.2f}")
return {
"symbol": symbol,
"action": "buy",
"quantity": POSITION_SIZE
}
# Sell signal: Fast SMA crosses below Slow SMA
elif sma_fast < sma_slow and current_position > 0:
print(f"SELL SIGNAL: Fast SMA {sma_fast:.2f} < Slow SMA {sma_slow:.2f}")
return {
"symbol": symbol,
"action": "sell",
"quantity": "all"
}
# No signal
return None
Advanced Version with Crossover Detection
python
def on_bar_close(data_contexts, portfolio, state):
"""Enhanced version with proper crossover detection"""
symbol = 'AAPL'
data = data_contexts[symbol]
# Current SMAs
sma_fast = data.sma(20)
sma_slow = data.sma(50)
# Previous SMAs (from history)
fast_history = [data.sma(20)] # In real implementation, track previous values
slow_history = [data.sma(50)]
# Skip if not enough data
if len(fast_history) < 2 or any(x != x for x in [sma_fast, sma_slow]):
return None
# Detect crossovers
prev_fast = fast_history[-2] if len(fast_history) >= 2 else sma_fast
prev_slow = slow_history[-2] if len(slow_history) >= 2 else sma_slow
# Golden Cross: Fast crosses above Slow
golden_cross = (prev_fast <= prev_slow) and (sma_fast > sma_slow)
# Death Cross: Fast crosses below Slow
death_cross = (prev_fast >= prev_slow) and (sma_fast < sma_slow)
current_pos = portfolio.position(symbol)
if golden_cross and current_pos == 0:
print(f"GOLDEN CROSS: Buying at ${data.close:.2f}")
return {'symbol': symbol, 'action': 'buy', 'quantity': 100}
elif death_cross and current_pos > 0:
print(f"DEATH CROSS: Selling at ${data.close:.2f}")
return {'symbol': symbol, 'action': 'sell', 'quantity': 'all'}
return None
Parameter Optimization
Common SMA Combinations
- Conservative: 50/200 (long-term trends)
- Balanced: 20/50 (medium-term trends)
- Aggressive: 10/20 (short-term trends)
- Very Fast: 5/10 (day trading)
Testing Different Parameters
python
# Test these combinations
PARAMS = [
(10, 20), # Fast signals, more trades
(20, 50), # Balanced approach
(50, 200), # Slow signals, fewer trades
]
FAST_PERIOD, SLOW_PERIOD = PARAMS[1] # Choose configuration
Expected Results
Typical Performance:
- Win Rate: 40-60% (depends on market conditions)
- Profit Factor: 1.1-1.8
- Best Markets: Trending markets
- Worst Markets: Choppy, sideways markets
Trade Frequency: Medium (10-50 trades per year)
Pros and Cons
Advantages ✅
- Simple to understand and implement
- Works well in trending markets
- Automatic trend detection
- No complex parameters
Disadvantages ❌
- Generates false signals in choppy markets
- Late entry/exit (lagging indicators)
- Whipsaws during consolidation periods
- No risk management built-in
Improvements
Add Volume Filter
python
if sma_fast > sma_slow and current_position == 0:
# Only buy on high volume
avg_volume = sum(data.history('volume', 20)) / 20
if data.volume > avg_volume * 1.5:
return buy_order
Add RSI Filter
python
if sma_fast > sma_slow and current_position == 0:
# Only buy when not overbought
if data.rsi(14) < 70:
return buy_order
Position Sizing
python
# Risk-based position sizing
def calculate_position_size(portfolio, price, stop_loss_pct=0.05):
risk_per_trade = portfolio.equity * 0.02 # 2% risk per trade
risk_per_share = price * stop_loss_pct
shares = int(risk_per_trade / risk_per_share)
return min(shares, int(portfolio.cash / price))
Next Steps
- RSI Strategy - Add momentum confirmation
- Bollinger Bounce - Mean reversion approach
- Risk Management - Add stops and sizing
Master this strategy before moving to more complex indicators.