Skip to main content

Overview

The TechnicalIndicators class provides static methods for calculating common technical analysis indicators used in cryptocurrency trading. All methods are optimized for pandas DataFrames and return vectorized calculations.

Class: TechnicalIndicators

Location: source/utils/indicators.py:8 A collection of static methods for technical analysis. No instantiation required.

Indicator Methods

calculate_rsi

Location: source/utils/indicators.py:12
@staticmethod
calculate_rsi(data: pd.Series, periods: int = 14) -> pd.Series
Calculates the Relative Strength Index (RSI), a momentum oscillator that measures the speed and magnitude of price changes.

Parameters

ParameterTypeDefaultDescription
datapd.SeriesRequiredPrice data (typically closing prices)
periodsint14Number of periods for RSI calculation

Returns

Type: pd.Series Returns a pandas Series with RSI values ranging from 0 to 100:
  • Above 70: Overbought condition (potential sell signal)
  • Below 30: Oversold condition (potential buy signal)
  • 50: Neutral momentum

Example

import pandas as pd
from utils.indicators import TechnicalIndicators
from data.collectors import CryptoDataCollector

# Fetch price data
collector = CryptoDataCollector('binance')
df = collector.fetch_ohlcv('BTC/USDT', timeframe='1h', limit=200)

# Calculate RSI
rsi = TechnicalIndicators.calculate_rsi(df['close'], periods=14)

print(f"Current RSI: {rsi.iloc[-1]:.2f}")

if rsi.iloc[-1] > 70:
    print("⚠️ Overbought condition")
elif rsi.iloc[-1] < 30:
    print("✓ Oversold condition - potential buy")

# Calculate with different period
rsi_9 = TechnicalIndicators.calculate_rsi(df['close'], periods=9)
print(f"RSI(9): {rsi_9.iloc[-1]:.2f}")

calculate_macd

Location: source/utils/indicators.py:24
@staticmethod
calculate_macd(
    data: pd.Series, 
    fast: int = 12, 
    slow: int = 26, 
    signal: int = 9
) -> Tuple[pd.Series, pd.Series, pd.Series]
Calculates the Moving Average Convergence Divergence (MACD), a trend-following momentum indicator.

Parameters

ParameterTypeDefaultDescription
datapd.SeriesRequiredPrice data (typically closing prices)
fastint12Fast EMA period
slowint26Slow EMA period
signalint9Signal line period

Returns

Type: Tuple[pd.Series, pd.Series, pd.Series] Returns a tuple containing three pandas Series:
  1. MACD line: Difference between fast and slow EMAs
  2. Signal line: EMA of the MACD line
  3. Histogram: Difference between MACD and signal line
Trading signals:
  • MACD crosses above signal line: Bullish signal
  • MACD crosses below signal line: Bearish signal
  • Histogram positive: Bullish momentum
  • Histogram negative: Bearish momentum

Example

from utils.indicators import TechnicalIndicators
from data.collectors import CryptoDataCollector

collector = CryptoDataCollector('kraken')
df = collector.fetch_ohlcv('ETH/USDT', timeframe='4h', limit=300)

# Calculate MACD
macd, signal, histogram = TechnicalIndicators.calculate_macd(
    df['close'], 
    fast=12, 
    slow=26, 
    signal=9
)

print(f"MACD: {macd.iloc[-1]:.4f}")
print(f"Signal: {signal.iloc[-1]:.4f}")
print(f"Histogram: {histogram.iloc[-1]:.4f}")

# Detect crossovers
if macd.iloc[-2] < signal.iloc[-2] and macd.iloc[-1] > signal.iloc[-1]:
    print("🚀 Bullish crossover detected!")
elif macd.iloc[-2] > signal.iloc[-2] and macd.iloc[-1] < signal.iloc[-1]:
    print("⚠️ Bearish crossover detected!")

# Custom parameters for faster signals
fast_macd, fast_signal, fast_hist = TechnicalIndicators.calculate_macd(
    df['close'], fast=8, slow=17, signal=5
)

calculate_bollinger_bands

Location: source/utils/indicators.py:36
@staticmethod
calculate_bollinger_bands(
    data: pd.Series, 
    period: int = 20, 
    std_dev: float = 2
) -> Tuple[pd.Series, pd.Series, pd.Series]
Calculates Bollinger Bands, which provide a relative definition of high and low prices.

Parameters

ParameterTypeDefaultDescription
datapd.SeriesRequiredPrice data (typically closing prices)
periodint20Moving average period
std_devfloat2Number of standard deviations for bands

Returns

Type: Tuple[pd.Series, pd.Series, pd.Series] Returns a tuple containing three pandas Series:
  1. Upper band: Middle band + (standard deviation × std_dev)
  2. Middle band: Simple moving average
  3. Lower band: Middle band - (standard deviation × std_dev)
Trading signals:
  • Price touches upper band: Overbought, potential reversal
  • Price touches lower band: Oversold, potential bounce
  • Price breaks above upper band: Strong momentum, potential continuation
  • Bands squeeze: Low volatility, potential breakout coming

Example

from utils.indicators import TechnicalIndicators
from data.collectors import CryptoDataCollector

collector = CryptoDataCollector('binance')
df = collector.fetch_ohlcv('BTC/USDT', timeframe='1d', limit=100)

# Calculate Bollinger Bands
upper, middle, lower = TechnicalIndicators.calculate_bollinger_bands(
    df['close'], 
    period=20, 
    std_dev=2
)

current_price = df['close'].iloc[-1]
print(f"Current price: ${current_price:,.2f}")
print(f"Upper band: ${upper.iloc[-1]:,.2f}")
print(f"Middle band: ${middle.iloc[-1]:,.2f}")
print(f"Lower band: ${lower.iloc[-1]:,.2f}")

# Check position relative to bands
if current_price > upper.iloc[-1]:
    print("⚠️ Price above upper band - potential overbought")
elif current_price < lower.iloc[-1]:
    print("✓ Price below lower band - potential oversold")
else:
    position = (current_price - lower.iloc[-1]) / (upper.iloc[-1] - lower.iloc[-1])
    print(f"Price position in bands: {position * 100:.1f}%")

# Calculate band width (volatility measure)
band_width = (upper - lower) / middle * 100
print(f"Current band width: {band_width.iloc[-1]:.2f}%")

# Detect squeeze
if band_width.iloc[-1] < band_width.rolling(50).quantile(0.2).iloc[-1]:
    print("🔥 Bollinger Band squeeze detected - breakout likely!")

calculate_ema

Location: source/utils/indicators.py:47
@staticmethod
calculate_ema(data: pd.Series, period: int) -> pd.Series
Calculates the Exponential Moving Average (EMA), which gives more weight to recent prices.

Parameters

ParameterTypeDescription
datapd.SeriesPrice data (typically closing prices)
periodintNumber of periods for EMA calculation

Returns

Type: pd.Series Returns a pandas Series with EMA values. Common periods:
  • EMA(9): Very short-term trend
  • EMA(21): Short-term trend
  • EMA(50): Medium-term trend
  • EMA(200): Long-term trend

Example

from utils.indicators import TechnicalIndicators
from data.collectors import CryptoDataCollector

collector = CryptoDataCollector('kraken')
df = collector.fetch_ohlcv('ETH/USDT', timeframe='1h', limit=500)

# Calculate multiple EMAs
ema_9 = TechnicalIndicators.calculate_ema(df['close'], 9)
ema_21 = TechnicalIndicators.calculate_ema(df['close'], 21)
ema_50 = TechnicalIndicators.calculate_ema(df['close'], 50)
ema_200 = TechnicalIndicators.calculate_ema(df['close'], 200)

current_price = df['close'].iloc[-1]

print(f"Current price: ${current_price:,.2f}")
print(f"EMA(9): ${ema_9.iloc[-1]:,.2f}")
print(f"EMA(21): ${ema_21.iloc[-1]:,.2f}")
print(f"EMA(50): ${ema_50.iloc[-1]:,.2f}")
print(f"EMA(200): ${ema_200.iloc[-1]:,.2f}")

# Check trend alignment
if (ema_9.iloc[-1] > ema_21.iloc[-1] > ema_50.iloc[-1] > ema_200.iloc[-1]):
    print("🚀 Strong uptrend - all EMAs aligned")
elif (ema_9.iloc[-1] < ema_21.iloc[-1] < ema_50.iloc[-1] < ema_200.iloc[-1]):
    print("⚠️ Strong downtrend - all EMAs aligned")

# Detect golden cross (bullish) or death cross (bearish)
if ema_50.iloc[-2] < ema_200.iloc[-2] and ema_50.iloc[-1] > ema_200.iloc[-1]:
    print("🌟 Golden Cross detected! (EMA 50 crossed above EMA 200)")
elif ema_50.iloc[-2] > ema_200.iloc[-2] and ema_50.iloc[-1] < ema_200.iloc[-1]:
    print("💀 Death Cross detected! (EMA 50 crossed below EMA 200)")

Batch Operations

add_all_indicators

Location: source/utils/indicators.py:52
@staticmethod
add_all_indicators(df: pd.DataFrame) -> pd.DataFrame
Adds all technical indicators to a DataFrame in a single operation.

Parameters

ParameterTypeDescription
dfpd.DataFrameDataFrame with OHLCV data (must have ‘close’ column)

Returns

Type: pd.DataFrame Returns a new DataFrame with all original columns plus:
ColumnDescription
rsiRSI with 14 periods
macdMACD line
macd_signalMACD signal line
macd_histMACD histogram
bb_upperBollinger Band upper
bb_middleBollinger Band middle
bb_lowerBollinger Band lower
ema_99-period EMA
ema_2121-period EMA
ema_5050-period EMA
ema_200200-period EMA

Example

from utils.indicators import TechnicalIndicators
from data.collectors import CryptoDataCollector

collector = CryptoDataCollector('binance')
df = collector.fetch_ohlcv('BTC/USDT', timeframe='1h', limit=300)

# Add all indicators at once
df_with_indicators = TechnicalIndicators.add_all_indicators(df)

# Display latest values
latest = df_with_indicators.iloc[-1]
print(f"Price: ${latest['close']:,.2f}")
print(f"RSI: {latest['rsi']:.2f}")
print(f"MACD: {latest['macd']:.4f}")
print(f"BB Upper: ${latest['bb_upper']:,.2f}")
print(f"BB Lower: ${latest['bb_lower']:,.2f}")
print(f"EMA(50): ${latest['ema_50']:,.2f}")

# Export to CSV with all indicators
df_with_indicators.to_csv('btc_analysis.csv')

# Use for visualization
import matplotlib.pyplot as plt

fig, axes = plt.subplots(3, 1, figsize=(12, 10))

# Price and Bollinger Bands
axes[0].plot(df_with_indicators.index, df_with_indicators['close'], label='Price')
axes[0].plot(df_with_indicators.index, df_with_indicators['bb_upper'], '--', label='BB Upper')
axes[0].plot(df_with_indicators.index, df_with_indicators['bb_lower'], '--', label='BB Lower')
axes[0].legend()

# RSI
axes[1].plot(df_with_indicators.index, df_with_indicators['rsi'])
axes[1].axhline(70, color='r', linestyle='--')
axes[1].axhline(30, color='g', linestyle='--')
axes[1].set_ylabel('RSI')

# MACD
axes[2].plot(df_with_indicators.index, df_with_indicators['macd'], label='MACD')
axes[2].plot(df_with_indicators.index, df_with_indicators['macd_signal'], label='Signal')
axes[2].bar(df_with_indicators.index, df_with_indicators['macd_hist'], label='Histogram')
axes[2].legend()

plt.tight_layout()
plt.savefig('technical_analysis.png')

get_signals

Location: source/utils/indicators.py:78
@staticmethod
get_signals(df: pd.DataFrame) -> dict
Generates trading signals based on technical indicators.

Parameters

ParameterTypeDescription
dfpd.DataFrameDataFrame with calculated indicators (use add_all_indicators first)

Returns

Type: dict Returns a dictionary with trading signals:
KeyPossible ValuesDescription
rsi_signal'overbought', 'oversold', 'neutral'RSI-based signal
macd_signal'bullish', 'bearish', 'neutral'MACD-based signal
bb_signal'neutral'Bollinger Band signal (currently not implemented)
overall'buy', 'sell', 'neutral'Combined signal from all indicators
Signal Logic:
  • Overall = ‘buy’: At least 2 bullish signals (RSI oversold + MACD bullish)
  • Overall = ‘sell’: At least 2 bearish signals (RSI overbought + MACD bearish)
  • Overall = ‘neutral’: Mixed or insufficient signals

Example

from utils.indicators import TechnicalIndicators
from data.collectors import CryptoDataCollector

collector = CryptoDataCollector('binance')
df = collector.fetch_ohlcv('BTC/USDT', timeframe='1h', limit=200)

# Add all indicators
df = TechnicalIndicators.add_all_indicators(df)

# Get trading signals
signals = TechnicalIndicators.get_signals(df)

print(f"RSI Signal: {signals['rsi_signal']}")
print(f"MACD Signal: {signals['macd_signal']}")
print(f"Overall Signal: {signals['overall']}")

# Act on signals
if signals['overall'] == 'buy':
    print("🟢 BUY signal generated")
    print("Consider entering a long position")
elif signals['overall'] == 'sell':
    print("🔴 SELL signal generated")
    print("Consider closing longs or entering short")
else:
    print("⚪ NEUTRAL - Wait for clearer signals")

# Monitor multiple assets
symbols = ['BTC/USDT', 'ETH/USDT', 'SOL/USDT', 'AVAX/USDT']

for symbol in symbols:
    df = collector.fetch_ohlcv(symbol, timeframe='4h', limit=200)
    df = TechnicalIndicators.add_all_indicators(df)
    signals = TechnicalIndicators.get_signals(df)
    
    if signals['overall'] == 'buy':
        print(f"🟢 {symbol}: BUY signal")
    elif signals['overall'] == 'sell':
        print(f"🔴 {symbol}: SELL signal")

Complete Usage Example

Here’s a comprehensive example combining all indicators:
from data.collectors import CryptoDataCollector
from utils.indicators import TechnicalIndicators
import pandas as pd

# Initialize data collector
collector = CryptoDataCollector('binance')

# Fetch data
print("Fetching market data...")
df = collector.fetch_ohlcv('BTC/USDT', timeframe='1h', limit=500)

if df.empty:
    print("Failed to fetch data")
    exit(1)

# Add all technical indicators
print("Calculating indicators...")
df = TechnicalIndicators.add_all_indicators(df)

# Display current market state
latest = df.iloc[-1]
print(f"\n{'='*60}")
print(f"Bitcoin (BTC/USDT) - Technical Analysis")
print(f"{'='*60}")
print(f"\nCurrent Price: ${latest['close']:,.2f}")

print(f"\nMomentum Indicators:")
print(f"  RSI(14): {latest['rsi']:.2f}")
print(f"  MACD: {latest['macd']:.4f}")
print(f"  MACD Signal: {latest['macd_signal']:.4f}")
print(f"  MACD Histogram: {latest['macd_hist']:.4f}")

print(f"\nMoving Averages:")
print(f"  EMA(9): ${latest['ema_9']:,.2f}")
print(f"  EMA(21): ${latest['ema_21']:,.2f}")
print(f"  EMA(50): ${latest['ema_50']:,.2f}")
print(f"  EMA(200): ${latest['ema_200']:,.2f}")

print(f"\nBollinger Bands (20,2):")
print(f"  Upper: ${latest['bb_upper']:,.2f}")
print(f"  Middle: ${latest['bb_middle']:,.2f}")
print(f"  Lower: ${latest['bb_lower']:,.2f}")

# Generate and display signals
print(f"\n{'='*60}")
print("Trading Signals")
print(f"{'='*60}")

signals = TechnicalIndicators.get_signals(df)

for key, value in signals.items():
    print(f"  {key.replace('_', ' ').title()}: {value.upper()}")

# Additional analysis
print(f"\n{'='*60}")
print("Analysis")
print(f"{'='*60}")

if latest['close'] > latest['ema_200']:
    print("✓ Price above 200 EMA - Long-term uptrend")
else:
    print("✗ Price below 200 EMA - Long-term downtrend")

if latest['rsi'] > 70:
    print("⚠️  RSI overbought - potential correction")
elif latest['rsi'] < 30:
    print("✓ RSI oversold - potential bounce")

if latest['close'] > latest['bb_upper']:
    print("⚠️  Price above upper Bollinger Band - extended move")
elif latest['close'] < latest['bb_lower']:
    print("✓ Price below lower Bollinger Band - potential reversal")

if latest['macd'] > latest['macd_signal'] and latest['macd_hist'] > 0:
    print("✓ MACD bullish - positive momentum")
elif latest['macd'] < latest['macd_signal'] and latest['macd_hist'] < 0:
    print("⚠️  MACD bearish - negative momentum")

print(f"\n{'='*60}")

Best Practices

Combine Multiple Indicators: Don’t rely on a single indicator. Use get_signals() to get a consensus view across multiple technical indicators.
Lookback Period: Ensure you fetch enough data points for indicators to be meaningful. For example:
  • RSI needs at least 14+ periods
  • Bollinger Bands need 20+ periods
  • EMA(200) needs 200+ periods
Performance Tip: Use add_all_indicators() when you need multiple indicators instead of calling individual methods repeatedly. This is more efficient and cleaner.

Dependencies

  • pandas - Data manipulation and Series operations
  • numpy - Numerical computations

See Also

Build docs developers (and LLMs) love