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
| Parameter | Type | Default | Description |
|---|
data | pd.Series | Required | Price data (typically closing prices) |
periods | int | 14 | Number 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
| Parameter | Type | Default | Description |
|---|
data | pd.Series | Required | Price data (typically closing prices) |
fast | int | 12 | Fast EMA period |
slow | int | 26 | Slow EMA period |
signal | int | 9 | Signal line period |
Returns
Type: Tuple[pd.Series, pd.Series, pd.Series]
Returns a tuple containing three pandas Series:
- MACD line: Difference between fast and slow EMAs
- Signal line: EMA of the MACD line
- 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
| Parameter | Type | Default | Description |
|---|
data | pd.Series | Required | Price data (typically closing prices) |
period | int | 20 | Moving average period |
std_dev | float | 2 | Number of standard deviations for bands |
Returns
Type: Tuple[pd.Series, pd.Series, pd.Series]
Returns a tuple containing three pandas Series:
- Upper band: Middle band + (standard deviation × std_dev)
- Middle band: Simple moving average
- 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
| Parameter | Type | Description |
|---|
data | pd.Series | Price data (typically closing prices) |
period | int | Number 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
| Parameter | Type | Description |
|---|
df | pd.DataFrame | DataFrame with OHLCV data (must have ‘close’ column) |
Returns
Type: pd.DataFrame
Returns a new DataFrame with all original columns plus:
| Column | Description |
|---|
rsi | RSI with 14 periods |
macd | MACD line |
macd_signal | MACD signal line |
macd_hist | MACD histogram |
bb_upper | Bollinger Band upper |
bb_middle | Bollinger Band middle |
bb_lower | Bollinger Band lower |
ema_9 | 9-period EMA |
ema_21 | 21-period EMA |
ema_50 | 50-period EMA |
ema_200 | 200-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
| Parameter | Type | Description |
|---|
df | pd.DataFrame | DataFrame with calculated indicators (use add_all_indicators first) |
Returns
Type: dict
Returns a dictionary with trading signals:
| Key | Possible Values | Description |
|---|
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