Intermediate

Technical Indicators

Calculate key technical indicators: RSI, MACD, Bollinger Bands, moving averages, and volume-based indicators that serve as features for the LSTM model.

Technical Indicator Calculations

# app/indicators.py
import pandas as pd
import numpy as np

class TechnicalIndicators:
    @staticmethod
    def rsi(series: pd.Series, period: int = 14) -> pd.Series:
        delta = series.diff()
        gain = delta.where(delta > 0, 0).rolling(period).mean()
        loss = (-delta.where(delta < 0, 0)).rolling(period).mean()
        rs = gain / loss
        return 100 - (100 / (1 + rs))

    @staticmethod
    def macd(series: pd.Series, fast=12, slow=26, signal=9):
        ema_fast = series.ewm(span=fast).mean()
        ema_slow = series.ewm(span=slow).mean()
        macd_line = ema_fast - ema_slow
        signal_line = macd_line.ewm(span=signal).mean()
        histogram = macd_line - signal_line
        return macd_line, signal_line, histogram

    @staticmethod
    def bollinger_bands(series: pd.Series, period=20, std_dev=2):
        sma = series.rolling(period).mean()
        std = series.rolling(period).std()
        upper = sma + (std * std_dev)
        lower = sma - (std * std_dev)
        width = (upper - lower) / sma
        return upper, sma, lower, width

    @staticmethod
    def moving_averages(series: pd.Series):
        return {
            "sma_10": series.rolling(10).mean(),
            "sma_20": series.rolling(20).mean(),
            "sma_50": series.rolling(50).mean(),
            "ema_12": series.ewm(span=12).mean(),
            "ema_26": series.ewm(span=26).mean(),
        }

    def add_all(self, df: pd.DataFrame) -> pd.DataFrame:
        close = df["Close"]
        df["rsi"] = self.rsi(close)
        df["macd"], df["macd_signal"], df["macd_hist"] = self.macd(close)
        df["bb_upper"], df["bb_mid"], df["bb_lower"], df["bb_width"] = self.bollinger_bands(close)
        mas = self.moving_averages(close)
        for name, series in mas.items():
            df[name] = series
        df["price_sma_ratio"] = close / df["sma_20"]
        return df.dropna()

Testing Indicators

from app.data_collector import StockDataCollector
from app.indicators import TechnicalIndicators

collector = StockDataCollector()
df = collector.fetch_history("AAPL")

ti = TechnicalIndicators()
df = ti.add_all(df)
print(f"Features: {list(df.columns)}")
print(f"RSI range: {df['rsi'].min():.1f} - {df['rsi'].max():.1f}")
print(f"Shape after indicators: {df.shape}")
💡
Feature selection: Not all indicators improve predictions. Use correlation analysis and feature importance from a simple model to select the most predictive indicators for your target stock.

Key Takeaways

  • RSI measures momentum: above 70 is overbought, below 30 is oversold.
  • MACD captures trend direction and momentum through moving average crossovers.
  • Bollinger Bands measure volatility: narrow bands suggest a breakout is coming.
  • Combining multiple indicators as features gives the LSTM model richer context.