Ollama Technical Analysis: RSI, MACD, and Bollinger Bands Automation

Automate RSI, MACD, and Bollinger Bands analysis with Ollama. Build intelligent trading signals and save hours of manual chart analysis daily.

Picture this: You're hunched over your computer at 2 AM, squinting at candlestick charts while your coffee has gone cold for the third time. Your eyes burn from analyzing RSI divergences, MACD crossovers, and Bollinger Band squeezes. Sound familiar?

What if I told you that Ollama could automate this entire process, turning your sleepless nights into automated insights?

Traditional technical analysis requires constant monitoring and manual interpretation. Traders spend hours analyzing price movements, calculating indicators, and identifying patterns. This manual approach leads to missed opportunities, emotional decisions, and trader fatigue.

Ollama changes this game entirely. By automating RSI calculations, MACD signal detection, and Bollinger Band analysis, you transform from a chart-watching zombie into a strategic decision-maker. This guide shows you exactly how to build an intelligent technical analysis system that works while you sleep.

What Makes Ollama Perfect for Technical Analysis Automation

Ollama excels at pattern recognition and data interpretation - exactly what technical analysis requires. Unlike traditional trading bots that follow rigid rules, Ollama understands context and adapts to market conditions.

Key Advantages of Ollama for Trading Automation

Advanced Pattern Recognition: Ollama identifies complex chart patterns that simple algorithms miss. It recognizes head and shoulders formations, triangle breakouts, and support/resistance levels with remarkable accuracy.

Natural Language Processing: Convert technical analysis results into plain English explanations. Instead of raw numbers, receive clear insights like "Strong bullish divergence detected on RSI with MACD confirming upward momentum."

Multi-Timeframe Analysis: Ollama processes data across multiple timeframes simultaneously, providing comprehensive market context that manual analysis often overlooks.

Contextual Decision Making: Unlike mechanical trading systems, Ollama considers market sentiment, news events, and broader economic factors when interpreting technical indicators.

Setting Up Your Ollama Technical Analysis Environment

Before automating technical indicators, establish your development environment with proper data sources and Ollama integration.

Installing Required Dependencies

# Install essential packages for technical analysis automation
pip install ollama yfinance pandas numpy ta-lib matplotlib plotly

# Verify Ollama installation
import ollama
print(ollama.list())

Data Source Configuration

import yfinance as yf
import pandas as pd
import numpy as np
import talib
from datetime import datetime, timedelta

class MarketDataProvider:
    def __init__(self):
        self.symbols = []
        
    def fetch_price_data(self, symbol, period="1y", interval="1d"):
        """
        Fetch historical price data for technical analysis
        
        Args:
            symbol: Stock/crypto symbol (e.g., 'AAPL', 'BTC-USD')
            period: Data period ('1d', '5d', '1mo', '3mo', '6mo', '1y', '2y', '5y', '10y', 'ytd', 'max')
            interval: Data interval ('1m', '2m', '5m', '15m', '30m', '60m', '90m', '1h', '1d', '5d', '1wk', '1mo', '3mo')
        """
        try:
            ticker = yf.Ticker(symbol)
            data = ticker.history(period=period, interval=interval)
            
            if data.empty:
                raise ValueError(f"No data found for symbol {symbol}")
                
            # Clean and prepare data
            data = data.dropna()
            data.columns = data.columns.str.lower()
            
            return data
            
        except Exception as e:
            print(f"Error fetching data for {symbol}: {str(e)}")
            return None

Ollama Model Selection

# Configure Ollama for technical analysis tasks
OLLAMA_MODEL = "llama3.1:8b"  # Recommended for financial analysis
OLLAMA_ENDPOINT = "http://localhost:11434"

def initialize_ollama():
    """Initialize Ollama client and verify model availability"""
    try:
        client = ollama.Client(host=OLLAMA_ENDPOINT)
        
        # Check if model is available
        models = client.list()
        model_names = [model['name'] for model in models['models']]
        
        if OLLAMA_MODEL not in model_names:
            print(f"Pulling {OLLAMA_MODEL} model...")
            client.pull(OLLAMA_MODEL)
            
        return client
        
    except Exception as e:
        print(f"Failed to initialize Ollama: {str(e)}")
        return None

Automating RSI Analysis with Ollama

The Relative Strength Index (RSI) measures momentum and identifies overbought/oversold conditions. Automating RSI analysis with Ollama adds intelligent interpretation to raw calculations.

RSI Calculation and Signal Detection

class RSIAnalyzer:
    def __init__(self, period=14):
        self.period = period
        self.overbought_threshold = 70
        self.oversold_threshold = 30
        
    def calculate_rsi(self, price_data):
        """Calculate RSI using TA-Lib for accuracy"""
        close_prices = price_data['close'].values
        rsi_values = talib.RSI(close_prices, timeperiod=self.period)
        
        # Add RSI to dataframe
        price_data['rsi'] = rsi_values
        
        return price_data
    
    def detect_rsi_signals(self, price_data):
        """Detect RSI-based trading signals"""
        signals = []
        rsi_values = price_data['rsi'].values
        
        for i in range(1, len(rsi_values)):
            current_rsi = rsi_values[i]
            previous_rsi = rsi_values[i-1]
            
            # Overbought to normal transition (sell signal)
            if previous_rsi > self.overbought_threshold and current_rsi <= self.overbought_threshold:
                signals.append({
                    'date': price_data.index[i],
                    'type': 'sell',
                    'indicator': 'rsi',
                    'value': current_rsi,
                    'reason': 'RSI dropped below overbought threshold'
                })
            
            # Oversold to normal transition (buy signal)
            elif previous_rsi < self.oversold_threshold and current_rsi >= self.oversold_threshold:
                signals.append({
                    'date': price_data.index[i],
                    'type': 'buy',
                    'indicator': 'rsi',
                    'value': current_rsi,
                    'reason': 'RSI rose above oversold threshold'
                })
                
        return signals
    
    def find_rsi_divergences(self, price_data):
        """Detect bullish/bearish RSI divergences"""
        divergences = []
        prices = price_data['close'].values
        rsi_values = price_data['rsi'].values
        
        # Look for divergences in recent data (last 20 periods)
        lookback = min(20, len(prices))
        recent_prices = prices[-lookback:]
        recent_rsi = rsi_values[-lookback:]
        
        # Find local highs and lows
        price_highs = self._find_peaks(recent_prices)
        price_lows = self._find_troughs(recent_prices)
        rsi_highs = self._find_peaks(recent_rsi)
        rsi_lows = self._find_troughs(recent_rsi)
        
        # Check for bearish divergence (price makes higher highs, RSI makes lower highs)
        if len(price_highs) >= 2 and len(rsi_highs) >= 2:
            if (recent_prices[price_highs[-1]] > recent_prices[price_highs[-2]] and 
                recent_rsi[rsi_highs[-1]] < recent_rsi[rsi_highs[-2]]):
                divergences.append({
                    'type': 'bearish',
                    'strength': 'strong' if abs(recent_rsi[rsi_highs[-1]] - recent_rsi[rsi_highs[-2]]) > 5 else 'weak'
                })
        
        # Check for bullish divergence (price makes lower lows, RSI makes higher lows)
        if len(price_lows) >= 2 and len(rsi_lows) >= 2:
            if (recent_prices[price_lows[-1]] < recent_prices[price_lows[-2]] and 
                recent_rsi[rsi_lows[-1]] > recent_rsi[rsi_lows[-2]]):
                divergences.append({
                    'type': 'bullish',
                    'strength': 'strong' if abs(recent_rsi[rsi_lows[-1]] - recent_rsi[rsi_lows[-2]]) > 5 else 'weak'
                })
                
        return divergences
    
    def _find_peaks(self, data, min_distance=3):
        """Find local peaks in data"""
        peaks = []
        for i in range(min_distance, len(data) - min_distance):
            is_peak = True
            for j in range(i - min_distance, i + min_distance + 1):
                if j != i and data[j] >= data[i]:
                    is_peak = False
                    break
            if is_peak:
                peaks.append(i)
        return peaks
    
    def _find_troughs(self, data, min_distance=3):
        """Find local troughs in data"""
        troughs = []
        for i in range(min_distance, len(data) - min_distance):
            is_trough = True
            for j in range(i - min_distance, i + min_distance + 1):
                if j != i and data[j] <= data[i]:
                    is_trough = False
                    break
            if is_trough:
                troughs.append(i)
        return troughs

Intelligent RSI Interpretation with Ollama

def analyze_rsi_with_ollama(price_data, signals, divergences, ollama_client):
    """Use Ollama to provide intelligent RSI analysis"""
    
    current_rsi = price_data['rsi'].iloc[-1]
    rsi_trend = "increasing" if price_data['rsi'].iloc[-1] > price_data['rsi'].iloc[-2] else "decreasing"
    
    # Prepare context for Ollama
    context = f"""
    Current RSI Analysis:
    - Current RSI: {current_rsi:.2f}
    - RSI Trend: {rsi_trend}
    - Recent Signals: {len(signals)} signals in last 20 periods
    - Divergences: {len(divergences)} divergences detected
    
    Signal Details:
    {'; '.join([f"{s['type']} signal on {s['date'].strftime('%Y-%m-%d')} (RSI: {s['value']:.2f})" for s in signals[-3:]])}
    
    Divergence Details:
    {'; '.join([f"{d['type']} divergence ({d['strength']} strength)" for d in divergences])}
    """
    
    prompt = f"""
    Analyze this RSI data and provide actionable trading insights:
    
    {context}
    
    Please provide:
    1. Current momentum assessment
    2. Probability of trend continuation or reversal
    3. Recommended trading actions
    4. Risk management considerations
    5. Timeframe for next significant move
    
    Keep the analysis practical and specific. Avoid generic advice.
    """
    
    try:
        response = ollama_client.generate(model=OLLAMA_MODEL, prompt=prompt)
        return response['response']
    except Exception as e:
        return f"Error generating RSI analysis: {str(e)}"

MACD Automation for Trend Analysis

The Moving Average Convergence Divergence (MACD) identifies trend changes and momentum shifts. Automating MACD analysis provides reliable trend-following signals.

MACD Calculation and Signal Generation

class MACDAnalyzer:
    def __init__(self, fast_period=12, slow_period=26, signal_period=9):
        self.fast_period = fast_period
        self.slow_period = slow_period
        self.signal_period = signal_period
        
    def calculate_macd(self, price_data):
        """Calculate MACD line, signal line, and histogram"""
        close_prices = price_data['close'].values
        
        # Calculate MACD components using TA-Lib
        macd_line, signal_line, histogram = talib.MACD(
            close_prices, 
            fastperiod=self.fast_period,
            slowperiod=self.slow_period, 
            signalperiod=self.signal_period
        )
        
        # Add to dataframe
        price_data['macd'] = macd_line
        price_data['macd_signal'] = signal_line
        price_data['macd_histogram'] = histogram
        
        return price_data
    
    def detect_macd_crossovers(self, price_data):
        """Detect MACD line and signal line crossovers"""
        crossovers = []
        macd_values = price_data['macd'].values
        signal_values = price_data['macd_signal'].values
        
        for i in range(1, len(macd_values)):
            if np.isnan(macd_values[i]) or np.isnan(signal_values[i]):
                continue
                
            current_diff = macd_values[i] - signal_values[i]
            previous_diff = macd_values[i-1] - signal_values[i-1]
            
            # Bullish crossover (MACD crosses above signal)
            if previous_diff <= 0 and current_diff > 0:
                crossovers.append({
                    'date': price_data.index[i],
                    'type': 'bullish_crossover',
                    'macd': macd_values[i],
                    'signal': signal_values[i],
                    'strength': abs(current_diff)
                })
            
            # Bearish crossover (MACD crosses below signal)
            elif previous_diff >= 0 and current_diff < 0:
                crossovers.append({
                    'date': price_data.index[i],
                    'type': 'bearish_crossover',
                    'macd': macd_values[i],
                    'signal': signal_values[i],
                    'strength': abs(current_diff)
                })
                
        return crossovers
    
    def analyze_macd_momentum(self, price_data):
        """Analyze MACD momentum and trend strength"""
        histogram = price_data['macd_histogram'].values
        
        # Recent histogram trend (last 5 periods)
        recent_histogram = histogram[-5:]
        increasing_momentum = all(recent_histogram[i] >= recent_histogram[i-1] for i in range(1, len(recent_histogram)))
        decreasing_momentum = all(recent_histogram[i] <= recent_histogram[i-1] for i in range(1, len(recent_histogram)))
        
        # Momentum strength based on histogram magnitude
        current_histogram = histogram[-1] if not np.isnan(histogram[-1]) else 0
        momentum_strength = abs(current_histogram)
        
        # Trend direction
        macd_current = price_data['macd'].iloc[-1]
        signal_current = price_data['macd_signal'].iloc[-1]
        
        trend_direction = "bullish" if macd_current > signal_current else "bearish"
        
        return {
            'trend_direction': trend_direction,
            'momentum_increasing': increasing_momentum,
            'momentum_decreasing': decreasing_momentum,
            'momentum_strength': momentum_strength,
            'current_histogram': current_histogram
        }
    
    def detect_macd_divergences(self, price_data):
        """Detect MACD divergences with price action"""
        divergences = []
        prices = price_data['close'].values
        macd_values = price_data['macd'].values
        
        # Analyze last 20 periods for divergences
        lookback = min(20, len(prices))
        recent_prices = prices[-lookback:]
        recent_macd = macd_values[-lookback:]
        
        # Remove NaN values
        valid_indices = ~(np.isnan(recent_prices) | np.isnan(recent_macd))
        recent_prices = recent_prices[valid_indices]
        recent_macd = recent_macd[valid_indices]
        
        if len(recent_prices) < 10:  # Need enough data for analysis
            return divergences
        
        # Find peaks and troughs
        price_peaks = self._find_peaks(recent_prices)
        price_troughs = self._find_troughs(recent_prices)
        macd_peaks = self._find_peaks(recent_macd)
        macd_troughs = self._find_troughs(recent_macd)
        
        # Check for bearish divergence
        if len(price_peaks) >= 2 and len(macd_peaks) >= 2:
            last_price_peak = price_peaks[-1]
            prev_price_peak = price_peaks[-2]
            last_macd_peak = macd_peaks[-1]
            prev_macd_peak = macd_peaks[-2]
            
            if (recent_prices[last_price_peak] > recent_prices[prev_price_peak] and
                recent_macd[last_macd_peak] < recent_macd[prev_macd_peak]):
                divergences.append({
                    'type': 'bearish',
                    'strength': abs(recent_macd[last_macd_peak] - recent_macd[prev_macd_peak])
                })
        
        # Check for bullish divergence
        if len(price_troughs) >= 2 and len(macd_troughs) >= 2:
            last_price_trough = price_troughs[-1]
            prev_price_trough = price_troughs[-2]
            last_macd_trough = macd_troughs[-1]
            prev_macd_trough = macd_troughs[-2]
            
            if (recent_prices[last_price_trough] < recent_prices[prev_price_trough] and
                recent_macd[last_macd_trough] > recent_macd[prev_macd_trough]):
                divergences.append({
                    'type': 'bullish',
                    'strength': abs(recent_macd[last_macd_trough] - recent_macd[prev_macd_trough])
                })
                
        return divergences

Advanced MACD Analysis with Ollama

def analyze_macd_with_ollama(price_data, crossovers, momentum_data, divergences, ollama_client):
    """Generate intelligent MACD analysis using Ollama"""
    
    current_macd = price_data['macd'].iloc[-1]
    current_signal = price_data['macd_signal'].iloc[-1]
    current_histogram = price_data['macd_histogram'].iloc[-1]
    
    context = f"""
    MACD Technical Analysis:
    
    Current Values:
    - MACD Line: {current_macd:.4f}
    - Signal Line: {current_signal:.4f}
    - Histogram: {current_histogram:.4f}
    
    Trend Analysis:
    - Current Trend: {momentum_data['trend_direction']}
    - Momentum Increasing: {momentum_data['momentum_increasing']}
    - Momentum Strength: {momentum_data['momentum_strength']:.4f}
    
    Recent Crossovers:
    {'; '.join([f"{co['type']} on {co['date'].strftime('%Y-%m-%d')}" for co in crossovers[-3:]])}
    
    Divergences:
    {'; '.join([f"{div['type']} divergence (strength: {div['strength']:.4f})" for div in divergences])}
    
    Price Context:
    - Current Price: ${price_data['close'].iloc[-1]:.2f}
    - 20-day Price Change: {((price_data['close'].iloc[-1] / price_data['close'].iloc[-20]) - 1) * 100:.2f}%
    """
    
    prompt = f"""
    As an expert technical analyst, interpret this MACD data and provide specific trading guidance:
    
    {context}
    
    Provide detailed analysis covering:
    1. Current trend strength and direction
    2. Probability of trend continuation vs reversal
    3. Optimal entry and exit points
    4. Stop-loss recommendations
    5. Expected timeframe for next significant move
    6. Risk-reward assessment
    
    Be specific with price levels and avoid generic technical analysis language.
    """
    
    try:
        response = ollama_client.generate(model=OLLAMA_MODEL, prompt=prompt)
        return response['response']
    except Exception as e:
        return f"Error generating MACD analysis: {str(e)}"

Bollinger Bands Automation for Volatility Analysis

Bollinger Bands measure volatility and identify potential support/resistance levels. Automating Bollinger Band analysis helps identify breakouts and mean reversion opportunities.

Bollinger Bands Calculation and Analysis

class BollingerBandsAnalyzer:
    def __init__(self, period=20, std_dev=2):
        self.period = period
        self.std_dev = std_dev
        
    def calculate_bollinger_bands(self, price_data):
        """Calculate Bollinger Bands using TA-Lib"""
        close_prices = price_data['close'].values
        
        # Calculate Bollinger Bands
        upper_band, middle_band, lower_band = talib.BBANDS(
            close_prices,
            timeperiod=self.period,
            nbdevup=self.std_dev,
            nbdevdn=self.std_dev,
            matype=0  # Simple Moving Average
        )
        
        # Add to dataframe
        price_data['bb_upper'] = upper_band
        price_data['bb_middle'] = middle_band
        price_data['bb_lower'] = lower_band
        
        # Calculate additional metrics
        price_data['bb_width'] = (upper_band - lower_band) / middle_band * 100
        price_data['bb_position'] = (close_prices - lower_band) / (upper_band - lower_band) * 100
        
        return price_data
    
    def detect_bb_signals(self, price_data):
        """Detect Bollinger Band trading signals"""
        signals = []
        
        close_prices = price_data['close'].values
        upper_band = price_data['bb_upper'].values
        lower_band = price_data['bb_lower'].values
        bb_position = price_data['bb_position'].values
        
        for i in range(1, len(close_prices)):
            if np.isnan(upper_band[i]) or np.isnan(lower_band[i]):
                continue
                
            # Bollinger Band squeeze (low volatility)
            current_width = price_data['bb_width'].iloc[i]
            avg_width = price_data['bb_width'].iloc[max(0, i-20):i].mean()
            
            if current_width < avg_width * 0.7:  # Width 30% below average
                signals.append({
                    'date': price_data.index[i],
                    'type': 'squeeze',
                    'description': 'Bollinger Band squeeze detected - low volatility',
                    'bb_width': current_width
                })
            
            # Price touching upper band (potential resistance)
            if close_prices[i] >= upper_band[i] * 0.995:  # Within 0.5% of upper band
                signals.append({
                    'date': price_data.index[i],
                    'type': 'upper_band_touch',
                    'description': 'Price reached upper Bollinger Band',
                    'bb_position': bb_position[i]
                })
            
            # Price touching lower band (potential support)
            elif close_prices[i] <= lower_band[i] * 1.005:  # Within 0.5% of lower band
                signals.append({
                    'date': price_data.index[i],
                    'type': 'lower_band_touch',
                    'description': 'Price reached lower Bollinger Band',
                    'bb_position': bb_position[i]
                })
            
            # Bollinger Band expansion (high volatility)
            if current_width > avg_width * 1.5:  # Width 50% above average
                signals.append({
                    'date': price_data.index[i],
                    'type': 'expansion',
                    'description': 'Bollinger Band expansion - high volatility',
                    'bb_width': current_width
                })
                
        return signals
    
    def analyze_bb_breakouts(self, price_data):
        """Analyze Bollinger Band breakouts"""
        breakouts = []
        
        close_prices = price_data['close'].values
        upper_band = price_data['bb_upper'].values
        lower_band = price_data['bb_lower'].values
        volume = price_data['volume'].values if 'volume' in price_data.columns else None
        
        for i in range(1, len(close_prices)):
            if np.isnan(upper_band[i]) or np.isnan(lower_band[i]):
                continue
                
            # Upward breakout
            if (close_prices[i-1] <= upper_band[i-1] and 
                close_prices[i] > upper_band[i]):
                
                volume_confirmation = True
                if volume is not None:
                    avg_volume = np.mean(volume[max(0, i-10):i])
                    volume_confirmation = volume[i] > avg_volume * 1.2
                
                breakouts.append({
                    'date': price_data.index[i],
                    'type': 'upward_breakout',
                    'price': close_prices[i],
                    'band_level': upper_band[i],
                    'volume_confirmed': volume_confirmation
                })
            
            # Downward breakout
            elif (close_prices[i-1] >= lower_band[i-1] and 
                  close_prices[i] < lower_band[i]):
                
                volume_confirmation = True
                if volume is not None:
                    avg_volume = np.mean(volume[max(0, i-10):i])
                    volume_confirmation = volume[i] > avg_volume * 1.2
                
                breakouts.append({
                    'date': price_data.index[i],
                    'type': 'downward_breakout',
                    'price': close_prices[i],
                    'band_level': lower_band[i],
                    'volume_confirmed': volume_confirmation
                })
                
        return breakouts
    
    def calculate_bb_statistics(self, price_data):
        """Calculate Bollinger Band statistics"""
        bb_position = price_data['bb_position'].dropna()
        bb_width = price_data['bb_width'].dropna()
        
        # Position statistics
        avg_position = bb_position.mean()
        position_std = bb_position.std()
        
        # Width statistics
        avg_width = bb_width.mean()
        current_width = bb_width.iloc[-1]
        width_percentile = (bb_width <= current_width).mean() * 100
        
        # Recent behavior
        recent_positions = bb_position.tail(10)
        trending_up = (recent_positions > 70).any()  # Recently in upper region
        trending_down = (recent_positions < 30).any()  # Recently in lower region
        
        return {
            'average_position': avg_position,
            'current_position': bb_position.iloc[-1],
            'position_volatility': position_std,
            'average_width': avg_width,
            'current_width': current_width,
            'width_percentile': width_percentile,
            'recently_overbought': trending_up,
            'recently_oversold': trending_down
        }

Intelligent Bollinger Bands Analysis with Ollama

def analyze_bollinger_bands_with_ollama(price_data, signals, breakouts, statistics, ollama_client):
    """Generate comprehensive Bollinger Bands analysis using Ollama"""
    
    current_price = price_data['close'].iloc[-1]
    current_position = statistics['current_position']
    current_width = statistics['current_width']
    
    context = f"""
    Bollinger Bands Analysis:
    
    Current Market Position:
    - Price: ${current_price:.2f}
    - BB Position: {current_position:.1f}% (0% = lower band, 100% = upper band)
    - Band Width: {current_width:.2f}% (percentile: {statistics['width_percentile']:.0f}%)
    
    Recent Signals:
    {'; '.join([f"{s['type']} on {s['date'].strftime('%Y-%m-%d')}" for s in signals[-5:]])}
    
    Breakout Analysis:
    {'; '.join([f"{b['type']} on {b['date'].strftime('%Y-%m-%d')} (volume confirmed: {b['volume_confirmed']})" for b in breakouts[-3:]])}
    
    Statistical Context:
    - Average Position: {statistics['average_position']:.1f}%
    - Position Volatility: {statistics['position_volatility']:.1f}%
    - Recently Overbought: {statistics['recently_overbought']}
    - Recently Oversold: {statistics['recently_oversold']}
    
    Volatility Context:
    - Current vs Average Width: {((current_width / statistics['average_width']) - 1) * 100:.1f}%
    """
    
    prompt = f"""
    Analyze this Bollinger Bands data and provide actionable trading insights:
    
    {context}
    
    Focus on:
    1. Current volatility regime (high/low/normal)
    2. Mean reversion vs breakout probability
    3. Optimal trading strategies for current conditions
    4. Support and resistance levels
    5. Risk management based on band position
    6. Expected volatility changes and timing
    
    Provide specific price targets and stop-loss levels. Consider both scalping and swing trading perspectives.
    """
    
    try:
        response = ollama_client.generate(model=OLLAMA_MODEL, prompt=prompt)
        return response['response']
    except Exception as e:
        return f"Error generating Bollinger Bands analysis: {str(e)}"

Integrating Multi-Indicator Analysis

Combining RSI, MACD, and Bollinger Bands creates powerful trading signals. This integration reduces false signals and improves trading accuracy.

Multi-Indicator Signal Coordination

class MultiIndicatorAnalyzer:
    def __init__(self):
        self.rsi_analyzer = RSIAnalyzer()
        self.macd_analyzer = MACDAnalyzer()
        self.bb_analyzer = BollingerBandsAnalyzer()
        
    def comprehensive_analysis(self, symbol, period="6mo"):
        """Perform comprehensive technical analysis using all indicators"""
        # Fetch market data
        data_provider = MarketDataProvider()
        price_data = data_provider.fetch_price_data(symbol, period)
        
        if price_data is None or price_data.empty:
            return None
        
        # Calculate all indicators
        price_data = self.rsi_analyzer.calculate_rsi(price_data)
        price_data = self.macd_analyzer.calculate_macd(price_data)
        price_data = self.bb_analyzer.calculate_bollinger_bands(price_data)
        
        # Generate individual signals
        rsi_signals = self.rsi_analyzer.detect_rsi_signals(price_data)
        rsi_divergences = self.rsi_analyzer.find_rsi_divergences(price_data)
        
        macd_crossovers = self.macd_analyzer.detect_macd_crossovers(price_data)
        macd_momentum = self.macd_analyzer.analyze_macd_momentum(price_data)
        macd_divergences = self.macd_analyzer.detect_macd_divergences(price_data)
        
        bb_signals = self.bb_analyzer.detect_bb_signals(price_data)
        bb_breakouts = self.bb_analyzer.analyze_bb_breakouts(price_data)
        bb_statistics = self.bb_analyzer.calculate_bb_statistics(price_data)
        
        # Combine signals for comprehensive analysis
        combined_signals = self.combine_signals(
            rsi_signals, macd_crossovers, bb_signals, price_data
        )
        
        return {
            'symbol': symbol,
            'price_data': price_data,
            'rsi_analysis': {
                'signals': rsi_signals,
                'divergences': rsi_divergences
            },
            'macd_analysis': {
                'crossovers': macd_crossovers,
                'momentum': macd_momentum,
                'divergences': macd_divergences
            },
            'bb_analysis': {
                'signals': bb_signals,
                'breakouts': bb_breakouts,
                'statistics': bb_statistics
            },
            'combined_signals': combined_signals
        }
    
    def combine_signals(self, rsi_signals, macd_crossovers, bb_signals, price_data):
        """Combine signals from multiple indicators for stronger confirmations"""
        combined = []
        
        # Get recent signals (last 10 trading days)
        recent_date = price_data.index[-10]
        
        recent_rsi = [s for s in rsi_signals if s['date'] >= recent_date]
        recent_macd = [s for s in macd_crossovers if s['date'] >= recent_date]
        recent_bb = [s for s in bb_signals if s['date'] >= recent_date]
        
        # Look for signal confirmations
        for rsi_signal in recent_rsi:
            confirmations = []
            signal_date = rsi_signal['date']
            
            # Check for MACD confirmation within 3 days
            for macd_signal in recent_macd:
                days_diff = abs((macd_signal['date'] - signal_date).days)
                if days_diff <= 3:
                    if ((rsi_signal['type'] == 'buy' and macd_signal['type'] == 'bullish_crossover') or
                        (rsi_signal['type'] == 'sell' and macd_signal['type'] == 'bearish_crossover')):
                        confirmations.append('macd')
            
            # Check for Bollinger Band confirmation
            for bb_signal in recent_bb:
                days_diff = abs((bb_signal['date'] - signal_date).days)
                if days_diff <= 2:
                    if ((rsi_signal['type'] == 'buy' and bb_signal['type'] == 'lower_band_touch') or
                        (rsi_signal['type'] == 'sell' and bb_signal['type'] == 'upper_band_touch')):
                        confirmations.append('bollinger_bands')
            
            if confirmations:
                combined.append({
                    'date': signal_date,
                    'primary_signal': rsi_signal,
                    'confirmations': confirmations,
                    'strength': len(confirmations) + 1,
                    'type': rsi_signal['type']
                })
        
        return sorted(combined, key=lambda x: x['date'], reverse=True)
    
    def calculate_signal_strength(self, analysis_data):
        """Calculate overall signal strength based on indicator alignment"""
        current_rsi = analysis_data['price_data']['rsi'].iloc[-1]
        current_macd_hist = analysis_data['price_data']['macd_histogram'].iloc[-1]
        current_bb_pos = analysis_data['bb_analysis']['statistics']['current_position']
        
        # Signal strength factors
        strength_factors = []
        
        # RSI contribution
        if current_rsi > 70:
            strength_factors.append(-2)  # Bearish
        elif current_rsi < 30:
            strength_factors.append(2)   # Bullish
        elif 40 <= current_rsi <= 60:
            strength_factors.append(0)   # Neutral
        else:
            strength_factors.append(1 if current_rsi < 50 else -1)
        
        # MACD contribution
        if not np.isnan(current_macd_hist):
            if current_macd_hist > 0:
                strength_factors.append(2)   # Bullish
            else:
                strength_factors.append(-2)  # Bearish
        
        # Bollinger Bands contribution
        if current_bb_pos > 80:
            strength_factors.append(-1)  # Overbought
        elif current_bb_pos < 20:
            strength_factors.append(1)   # Oversold
        else:
            strength_factors.append(0)   # Neutral
        
        # Calculate weighted strength
        total_strength = sum(strength_factors)
        max_possible = 6  # Maximum bullish score
        min_possible = -6  # Maximum bearish score
        
        # Normalize to 0-100 scale
        normalized_strength = ((total_strength - min_possible) / (max_possible - min_possible)) * 100
        
        return {
            'raw_strength': total_strength,
            'normalized_strength': normalized_strength,
            'interpretation': self._interpret_strength(normalized_strength)
        }
    
    def _interpret_strength(self, strength):
        """Interpret normalized strength score"""
        if strength >= 80:
            return "Strong Bullish"
        elif strength >= 65:
            return "Moderate Bullish"
        elif strength >= 55:
            return "Weak Bullish"
        elif strength >= 45:
            return "Neutral"
        elif strength >= 35:
            return "Weak Bearish"
        elif strength >= 20:
            return "Moderate Bearish"
        else:
            return "Strong Bearish"

Comprehensive Multi-Indicator Analysis with Ollama

def generate_comprehensive_analysis(analysis_data, ollama_client):
    """Generate comprehensive trading analysis using all indicators and Ollama"""
    
    symbol = analysis_data['symbol']
    price_data = analysis_data['price_data']
    current_price = price_data['close'].iloc[-1]
    
    # Calculate signal strength
    multi_analyzer = MultiIndicatorAnalyzer()
    signal_strength = multi_analyzer.calculate_signal_strength(analysis_data)
    
    # Prepare comprehensive context
    context = f"""
    Comprehensive Technical Analysis for {symbol}
    Current Price: ${current_price:.2f}
    
    SIGNAL STRENGTH: {signal_strength['interpretation']} ({signal_strength['normalized_strength']:.0f}/100)
    
    RSI Analysis:
    - Current RSI: {price_data['rsi'].iloc[-1]:.2f}
    - Recent Signals: {len(analysis_data['rsi_analysis']['signals'])} in dataset
    - Divergences: {len(analysis_data['rsi_analysis']['divergences'])} detected
    
    MACD Analysis:
    - Current MACD: {price_data['macd'].iloc[-1]:.4f}
    - Current Signal: {price_data['macd_signal'].iloc[-1]:.4f}
    - Current Histogram: {price_data['macd_histogram'].iloc[-1]:.4f}
    - Trend Direction: {analysis_data['macd_analysis']['momentum']['trend_direction']}
    - Recent Crossovers: {len(analysis_data['macd_analysis']['crossovers'])}
    
    Bollinger Bands Analysis:
    - BB Position: {analysis_data['bb_analysis']['statistics']['current_position']:.1f}%
    - BB Width Percentile: {analysis_data['bb_analysis']['statistics']['width_percentile']:.0f}%
    - Recent Signals: {len(analysis_data['bb_analysis']['signals'])} detected
    - Recent Breakouts: {len(analysis_data['bb_analysis']['breakouts'])} detected
    
    Combined Signals:
    {'; '.join([f"{cs['type']} signal with {len(cs['confirmations'])} confirmations on {cs['date'].strftime('%Y-%m-%d')}" for cs in analysis_data['combined_signals'][:3]])}
    
    Market Context:
    - 5-day price change: {((current_price / price_data['close'].iloc[-6]) - 1) * 100:.2f}%
    - 20-day price change: {((current_price / price_data['close'].iloc[-21]) - 1) * 100:.2f}%
    - Volatility (20-day): {(price_data['close'].pct_change().tail(20).std() * np.sqrt(252) * 100):.1f}%
    """
    
    prompt = f"""
    As a professional trading analyst, provide a comprehensive investment recommendation based on this multi-indicator technical analysis:
    
    {context}
    
    Deliver a complete trading strategy including:
    
    1. EXECUTIVE SUMMARY
       - Overall market outlook (bullish/bearish/neutral)
       - Confidence level (1-10) and reasoning
       - Primary catalyst driving the recommendation
    
    2. TRADE SETUP
       - Recommended position (long/short/wait)
       - Optimal entry price range
       - Position sizing recommendation
       - Expected holding period
    
    3. RISK MANAGEMENT
       - Stop-loss levels with technical justification
       - Take-profit targets (multiple levels)
       - Maximum risk per trade
       - Position management rules
    
    4. SCENARIO ANALYSIS
       - Bull case: probability and price targets
       - Bear case: probability and downside risks
       - Base case: most likely outcome
    
    5. MONITORING PLAN
       - Key levels to watch
       - Indicators that would change the thesis
       - Exit signals for both profit and loss
    
    Be specific with exact price levels, percentages, and timeframes. Avoid generic advice and focus on actionable insights based on the technical evidence provided.
    """
    
    try:
        response = ollama_client.generate(model=OLLAMA_MODEL, prompt=prompt)
        return response['response']
    except Exception as e:
        return f"Error generating comprehensive analysis: {str(e)}"

Automation Framework and Deployment

Build a robust automation framework that monitors multiple symbols and generates real-time alerts.

Real-Time Monitoring System

import schedule
import time
import json
from datetime import datetime
import logging

class TechnicalAnalysisBot:
    def __init__(self, symbols_list, ollama_client):
        self.symbols = symbols_list
        self.ollama_client = ollama_client
        self.analyzer = MultiIndicatorAnalyzer()
        self.setup_logging()
        
    def setup_logging(self):
        """Configure logging for the trading bot"""
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(levelname)s - %(message)s',
            handlers=[
                logging.FileHandler('trading_bot.log'),
                logging.StreamHandler()
            ]
        )
        self.logger = logging.getLogger(__name__)
    
    def analyze_symbol(self, symbol):
        """Analyze a single symbol and return results"""
        try:
            self.logger.info(f"Analyzing {symbol}...")
            
            # Perform comprehensive analysis
            analysis_data = self.analyzer.comprehensive_analysis(symbol)
            
            if analysis_data is None:
                self.logger.warning(f"No data available for {symbol}")
                return None
            
            # Generate Ollama analysis
            comprehensive_analysis = generate_comprehensive_analysis(
                analysis_data, self.ollama_client
            )
            
            # Calculate signal strength
            signal_strength = self.analyzer.calculate_signal_strength(analysis_data)
            
            result = {
                'symbol': symbol,
                'timestamp': datetime.now().isoformat(),
                'current_price': float(analysis_data['price_data']['close'].iloc[-1]),
                'signal_strength': signal_strength,
                'analysis': comprehensive_analysis,
                'raw_data': {
                    'rsi': float(analysis_data['price_data']['rsi'].iloc[-1]),
                    'macd': float(analysis_data['price_data']['macd'].iloc[-1]),
                    'bb_position': analysis_data['bb_analysis']['statistics']['current_position']
                }
            }
            
            self.logger.info(f"Analysis complete for {symbol}: {signal_strength['interpretation']}")
            return result
            
        except Exception as e:
            self.logger.error(f"Error analyzing {symbol}: {str(e)}")
            return None
    
    def scan_all_symbols(self):
        """Scan all symbols and return prioritized results"""
        results = []
        
        for symbol in self.symbols:
            result = self.analyze_symbol(symbol)
            if result:
                results.append(result)
                
        # Sort by signal strength (strongest signals first)
        results.sort(key=lambda x: abs(x['signal_strength']['normalized_strength'] - 50), reverse=True)
        
        return results
    
    def generate_alerts(self, results, threshold=70):
        """Generate trading alerts for strong signals"""
        alerts = []
        
        for result in results:
            strength = result['signal_strength']['normalized_strength']
            
            # Strong bullish signals
            if strength >= threshold:
                alerts.append({
                    'type': 'STRONG_BUY',
                    'symbol': result['symbol'],
                    'price': result['current_price'],
                    'strength': strength,
                    'message': f"Strong bullish signal for {result['symbol']} at ${result['current_price']:.2f}"
                })
            
            # Strong bearish signals
            elif strength <= (100 - threshold):
                alerts.append({
                    'type': 'STRONG_SELL',
                    'symbol': result['symbol'],
                    'price': result['current_price'],
                    'strength': strength,
                    'message': f"Strong bearish signal for {result['symbol']} at ${result['current_price']:.2f}"
                })
        
        return alerts
    
    def save_results(self, results, filename_prefix="analysis"):
        """Save analysis results to JSON file"""
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        filename = f"{filename_prefix}_{timestamp}.json"
        
        try:
            with open(filename, 'w') as f:
                json.dump(results, f, indent=2, default=str)
            self.logger.info(f"Results saved to {filename}")
        except Exception as e:
            self.logger.error(f"Error saving results: {str(e)}")
    
    def run_analysis_cycle(self):
        """Run complete analysis cycle"""
        self.logger.info("Starting analysis cycle...")
        
        # Scan all symbols
        results = self.scan_all_symbols()
        
        # Generate alerts
        alerts = self.generate_alerts(results)
        
        # Save results
        self.save_results(results)
        
        # Log alerts
        for alert in alerts:
            self.logger.warning(f"ALERT: {alert['message']}")
        
        self.logger.info(f"Analysis cycle complete. Processed {len(results)} symbols, generated {len(alerts)} alerts.")
        
        return results, alerts

# Usage example
def setup_automated_trading_bot():
    """Setup and run the automated trading bot"""
    
    # Initialize Ollama client
    ollama_client = initialize_ollama()
    
    if not ollama_client:
        print("Failed to initialize Ollama. Please check your setup.")
        return
    
    # Define symbols to monitor
    symbols_to_monitor = [
        'AAPL', 'GOOGL', 'MSFT', 'AMZN', 'TSLA',
        'NVDA', 'META', 'NFLX', 'AMD', 'INTC',
        'BTC-USD', 'ETH-USD', 'SPY', 'QQQ', 'IWM'
    ]
    
    # Create trading bot
    bot = TechnicalAnalysisBot(symbols_to_monitor, ollama_client)
    
    # Schedule regular analysis
    schedule.every(30).minutes.do(bot.run_analysis_cycle)
    schedule.every().day.at("09:30").do(bot.run_analysis_cycle)  # Market open
    schedule.every().day.at("16:00").do(bot.run_analysis_cycle)  # Market close
    
    print("Trading bot started. Press Ctrl+C to stop.")
    
    try:
        while True:
            schedule.run_pending()
            time.sleep(60)  # Check every minute
    except KeyboardInterrupt:
        print("Trading bot stopped.")

if __name__ == "__main__":
    setup_automated_trading_bot()

Visualization and Reporting

Create comprehensive visualizations and reports for your automated analysis.

Interactive Charts and Dashboards

import plotly.graph_objects as go
from plotly.subplots import make_subplots
import plotly.express as px

def create_comprehensive_chart(analysis_data):
    """Create comprehensive technical analysis chart"""
    
    price_data = analysis_data['price_data']
    symbol = analysis_data['symbol']
    
    # Create subplots
    fig = make_subplots(
        rows=4, cols=1,
        shared_xaxes=True,
        vertical_spacing=0.05,
        subplot_titles=(
            f'{symbol} Price with Bollinger Bands',
            'RSI',
            'MACD',
            'Volume'
        ),
        row_heights=[0.5, 0.2, 0.2, 0.1]
    )
    
    # Price chart with Bollinger Bands
    fig.add_trace(
        go.Candlestick(
            x=price_data.index,
            open=price_data['open'],
            high=price_data['high'],
            low=price_data['low'],
            close=price_data['close'],
            name='Price'
        ),
        row=1, col=1
    )
    
    # Bollinger Bands
    fig.add_trace(
        go.Scatter(
            x=price_data.index,
            y=price_data['bb_upper'],
            name='BB Upper',
            line=dict(color='rgba(255,0,0,0.3)'),
            fill=None
        ),
        row=1, col=1
    )
    
    fig.add_trace(
        go.Scatter(
            x=price_data.index,
            y=price_data['bb_lower'],
            name='BB Lower',
            line=dict(color='rgba(255,0,0,0.3)'),
            fill='tonexty',
            fillcolor='rgba(255,0,0,0.1)'
        ),
        row=1, col=1
    )
    
    fig.add_trace(
        go.Scatter(
            x=price_data.index,
            y=price_data['bb_middle'],
            name='BB Middle',
            line=dict(color='blue', dash='dash')
        ),
        row=1, col=1
    )
    
    # RSI
    fig.add_trace(
        go.Scatter(
            x=price_data.index,
            y=price_data['rsi'],
            name='RSI',
            line=dict(color='purple')
        ),
        row=2, col=1
    )
    
    # RSI levels
    fig.add_hline(y=70, line_dash="dash", line_color="red", row=2, col=1)
    fig.add_hline(y=30, line_dash="dash", line_color="green", row=2, col=1)
    fig.add_hline(y=50, line_dash="dot", line_color="gray", row=2, col=1)
    
    # MACD
    fig.add_trace(
        go.Scatter(
            x=price_data.index,
            y=price_data['macd'],
            name='MACD',
            line=dict(color='blue')
        ),
        row=3, col=1
    )
    
    fig.add_trace(
        go.Scatter(
            x=price_data.index,
            y=price_data['macd_signal'],
            name='Signal',
            line=dict(color='red')
        ),
        row=3, col=1
    )
    
    fig.add_trace(
        go.Bar(
            x=price_data.index,
            y=price_data['macd_histogram'],
            name='Histogram',
            marker_color='gray',
            opacity=0.6
        ),
        row=3, col=1
    )
    
    # Volume
    if 'volume' in price_data.columns:
        fig.add_trace(
            go.Bar(
                x=price_data.index,
                y=price_data['volume'],
                name='Volume',
                marker_color='lightblue'
            ),
            row=4, col=1
        )
    
    # Update layout
    fig.update_layout(
        title=f'{symbol} - Comprehensive Technical Analysis',
        xaxis_rangeslider_visible=False,
        height=800,
        showlegend=True
    )
    
    # Update y-axes
    fig.update_yaxes(title_text="Price ($)", row=1, col=1)
    fig.update_yaxes(title_text="RSI", row=2, col=1, range=[0, 100])
    fig.update_yaxes(title_text="MACD", row=3, col=1)
    fig.update_yaxes(title_text="Volume", row=4, col=1)
    
    return fig

def generate_analysis_report(results):
    """Generate comprehensive analysis report"""
    
    report = f"""
    # Technical Analysis Report
    Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
    
    ## Executive Summary
    
    Analyzed {len(results)} symbols using automated RSI, MACD, and Bollinger Bands analysis.
    
    ### Signal Distribution
    """
    
    # Calculate signal distribution
    signal_counts = {'Strong Bullish': 0, 'Moderate Bullish': 0, 'Weak Bullish': 0, 
                    'Neutral': 0, 'Weak Bearish': 0, 'Moderate Bearish': 0, 'Strong Bearish': 0}
    
    for result in results:
        interpretation = result['signal_strength']['interpretation']
        signal_counts[interpretation] += 1
    
    for signal_type, count in signal_counts.items():
        percentage = (count / len(results)) * 100 if results else 0
        report += f"- {signal_type}: {count} symbols ({percentage:.1f}%)\n"
    
    report += "\n## Top Opportunities\n\n"
    
    # Top bullish opportunities
    bullish_results = [r for r in results if r['signal_strength']['normalized_strength'] > 60]
    bullish_results.sort(key=lambda x: x['signal_strength']['normalized_strength'], reverse=True)
    
    report += "### Bullish Signals\n"
    for result in bullish_results[:5]:
        report += f"- **{result['symbol']}**: ${result['current_price']:.2f} - {result['signal_strength']['interpretation']} ({result['signal_strength']['normalized_strength']:.0f}/100)\n"
    
    # Top bearish opportunities
    bearish_results = [r for r in results if r['signal_strength']['normalized_strength'] < 40]
    bearish_results.sort(key=lambda x: x['signal_strength']['normalized_strength'])
    
    report += "\n### Bearish Signals\n"
    for result in bearish_results[:5]:
        report += f"- **{result['symbol']}**: ${result['current_price']:.2f} - {result['signal_strength']['interpretation']} ({result['signal_strength']['normalized_strength']:.0f}/100)\n"
    
    report += "\n## Detailed Analysis\n\n"
    
    # Detailed analysis for top signals
    top_signals = sorted(results, key=lambda x: abs(x['signal_strength']['normalized_strength'] - 50), reverse=True)[:3]
    
    for result in top_signals:
        report += f"### {result['symbol']} - {result['signal_strength']['interpretation']}\n\n"
        report += f"**Current Price**: ${result['current_price']:.2f}\n"
        report += f"**Signal Strength**: {result['signal_strength']['normalized_strength']:.0f}/100\n\n"
        report += f"**Technical Indicators**:\n"
        report += f"- RSI: {result['raw_data']['rsi']:.2f}\n"
        report += f"- MACD: {result['raw_data']['macd']:.4f}\n"
        report += f"- BB Position: {result['raw_data']['bb_position']:.1f}%\n\n"
        
        # Add Ollama analysis (truncated for report)
        analysis_preview = result['analysis'][:500] + "..." if len(result['analysis']) > 500 else result['analysis']
        report += f"**AI Analysis Preview**:\n{analysis_preview}\n\n"
        report += "---\n\n"
    
    return report

Conclusion: Transform Your Trading with Automated Technical Analysis

Automating technical analysis with Ollama revolutionizes how traders approach market analysis. Instead of spending hours manually interpreting charts, you now have an intelligent system that identifies opportunities, generates insights, and provides actionable recommendations 24/7.

The combination of RSI momentum analysis, MACD trend detection, and Bollinger Bands volatility assessment creates a comprehensive trading framework. Ollama's natural language processing transforms raw technical data into clear, strategic guidance that improves decision-making and reduces emotional trading mistakes.

This automated approach scales effortlessly across multiple symbols and timeframes, providing consistent analysis quality that manual methods cannot match. Whether you're a day trader seeking quick scalping opportunities or a swing trader looking for multi-day positions, this system adapts to your trading style and market conditions.

Start with a small watchlist of familiar symbols, test the automation framework with paper trading, and gradually expand as you gain confidence in the system. The combination of proven technical indicators and advanced AI analysis gives you a significant edge in today's fast-moving markets.

Your trading evolution begins now - let Ollama handle the analysis while you focus on strategy and execution.

Ready to automate your technical analysis? Download the complete code framework and start building your intelligent trading system today.