Indicators & Signals
Indicators are mathematical calculations based on price, volume, or other market data. They form the building blocks of trading signals.
What is a Signal?
A signal is a trigger that suggests a trading action. Signals are typically generated from indicators.
Indicator → Condition → Signal → Action
↓ ↓ ↓ ↓
RSI RSI < 30 Buy Open long
Trend Indicators
Moving Averages
Smooth price data to identify trends.
Simple Moving Average (SMA)
def sma(prices, window):
return sum(prices[-window:]) / window
# Usage
sma_20 = sma(close_prices, 20)
sma_50 = sma(close_prices, 50)
# Signal: Golden Cross (SMA20 crosses above SMA50)
if sma_20 > sma_50 and prev_sma_20 <= prev_sma_50:
signal = "BUY"
Exponential Moving Average (EMA)
Gives more weight to recent prices.
def ema(prices, window):
multiplier = 2 / (window + 1)
ema = prices[0]
for price in prices[1:]:
ema = (price - ema) * multiplier + ema
return ema
# VecAlpha built-in
ema_20 = self.ema(20) # In strategy class
Average Directional Index (ADX)
Measures trend strength (not direction).
| ADX Value | Trend Strength |
|---|---|
| 0-25 | Weak or no trend |
| 25-50 | Strong trend |
| 50-75 | Very strong trend |
| 75-100 | Extremely strong |
adx = self.adx(14)
if adx > 25:
# Strong trend - use trend-following strategies
else:
# Weak trend - consider mean-reversion
Momentum Indicators
Relative Strength Index (RSI)
Measures speed and magnitude of price changes.
RSI = 100 - (100 / (1 + RS))
RS = Average Gain / Average Loss
| RSI Range | Interpretation |
|---|---|
| > 70 | Overbought |
| 50-70 | Bullish |
| 30-50 | Bearish |
| < 30 | Oversold |
rsi = self.rsi(14)
# Mean reversion signals
if rsi < 30:
signal = "BUY" # Oversold
elif rsi > 70:
signal = "SELL" # Overbought
MACD (Moving Average Convergence Divergence)
Combines trend and momentum.
macd, signal_line, histogram = self.macd(12, 26, 9)
# Crossover signal
if macd > signal_line and prev_macd <= prev_signal:
signal = "BUY"
elif macd < signal_line and prev_macd >= prev_signal:
signal = "SELL"
Volatility Indicators
Bollinger Bands
Volatility-based envelopes around price.
Middle Band = 20 SMA
Upper Band = Middle + (2 × Standard Deviation)
Lower Band = Middle - (2 × Standard Deviation)
upper, middle, lower = self.bollinger(20, 2)
# Mean reversion signal
if price < lower:
signal = "BUY" # Price at lower band
elif price > upper:
signal = "SELL" # Price at upper band
Average True Range (ATR)
Measures volatility.
atr = self.atr(14)
# Use for position sizing
stop_loss_distance = 2 * atr
position_size = risk_amount / stop_loss_distance
Volume Indicators
On-Balance Volume (OBV)
Tracks cumulative volume flow.
obv = self.obv()
# Divergence detection
if price_making_new_highs and obv_not_making_new_highs:
signal = "POTENTIAL_REVERSAL"
Volume Weighted Average Price (VWAP)
Average price weighted by volume.
vwap = self.vwap()
# Mean reversion
if price < vwap:
# Price below VWAP - potential buy
signal = "BUY"
Combining Indicators
Single indicators are rarely enough. Combine multiple for confirmation:
Example: Trend + Momentum
def generate_signal(self):
# Trend filter
trend = self.sma(50) > self.sma(200)
# Momentum signal
rsi = self.rsi(14)
# Combined signal
if trend and rsi < 40: # Uptrend + oversold
return "BUY"
elif not trend and rsi > 60: # Downtrend + overbought
return "SELL"
return "HOLD"
Example: Multiple Timeframes
def multi_timeframe_signal(self):
# Higher timeframe trend
daily_trend = self.sma(50, timeframe='1d') > self.sma(200, timeframe='1d')
# Lower timeframe entry
hourly_rsi = self.rsi(14, timeframe='1h')
if daily_trend and hourly_rsi < 30:
return "BUY"
return "HOLD"
Indicator Best Practices
1. Understand What You're Measuring
| Category | What It Measures | Example Indicators |
|---|---|---|
| Trend | Direction of price movement | SMA, EMA, ADX |
| Momentum | Speed of price change | RSI, MACD, Stochastic |
| Volatility | Magnitude of price swings | ATR, Bollinger Bands |
| Volume | Trading activity | OBV, VWAP, Volume |
2. Avoid Redundancy
Don't use multiple indicators that measure the same thing:
# REDUNDANT: All measure momentum
indicators = ['RSI', 'Stochastic', 'CCI']
# BETTER: Different aspects
indicators = ['RSI', 'MACD', 'ATR'] # Momentum + Trend + Volatility
3. Match Indicators to Strategy Type
| Strategy Type | Best Indicators |
|---|---|
| Trend Following | SMA, EMA, ADX, MACD |
| Mean Reversion | RSI, Bollinger Bands, Stochastic |
| Breakout | ATR, Donchian Channels |
| Pairs Trading | Cointegration, Correlation |
4. Parameter Selection
Common parameter ranges:
| Indicator | Default | Range to Test |
|---|---|---|
| SMA/EMA | 20, 50, 200 | 10-200 |
| RSI | 14 | 7-21 |
| MACD | 12, 26, 9 | Various |
| Bollinger | 20, 2 | 15-25, 1.5-2.5 |
Custom Indicators
Create your own in VecAlpha:
from vecalpha import Indicator
class CustomIndicator(Indicator):
"""Custom momentum indicator."""
def __init__(self, window=14):
self.window = window
def calculate(self, data):
closes = data['close']
volumes = data['volume']
# Custom calculation
momentum = closes.pct_change(self.window)
volume_weight = volumes / volumes.rolling(self.window).mean()
return momentum * volume_weight
# Use in strategy
custom = CustomIndicator(14)
signal_value = custom.calculate(self.data)
Next Steps
- Backtesting Guide - Test your signals properly
- Optimization - Fine-tune parameters