Skip to main content

Overview

Technical indicators are mathematical calculations based on price, volume, and open interest data. CryptoView Pro uses these indicators both for visual analysis and as features for machine learning models. Location: utils/indicators.py
All indicators are calculated automatically when data is loaded and are available for visualization and model training.

Indicator Catalog

RSI (Relative Strength Index)

Description

RSI is a momentum oscillator that measures the speed and magnitude of price changes. It ranges from 0 to 100 and identifies overbought/oversold conditions.

Calculation

utils/indicators.py
def calculate_rsi(data: pd.Series, periods: int = 14) -> pd.Series:
    """Relative Strength Index calculation"""
    
    # 1. Calculate price changes
    delta = data.diff()
    
    # 2. Separate gains and losses
    gain = delta.where(delta > 0, 0)  # Positive changes only
    loss = -delta.where(delta < 0, 0)  # Absolute value of negative changes
    
    # 3. Calculate rolling averages
    avg_gain = gain.rolling(window=periods).mean()
    avg_loss = loss.rolling(window=periods).mean()
    
    # 4. Relative strength
    rs = avg_gain / avg_loss
    
    # 5. RSI formula
    rsi = 100 - (100 / (1 + rs))
    
    return rsi

Interpretation

Oversold

RSI < 30Price may have fallen too far, potential buy signal

Neutral

30 ≤ RSI ≤ 70Price in normal range, no clear signal

Overbought

RSI > 70Price may have risen too far, potential sell signal

Example Values

# Bitcoin at $45,000
RSI = 75  # Overbought - consider taking profits

# After 20% correction to $36,000
RSI = 25  # Oversold - potential buying opportunity

# Gradual recovery to $40,000
RSI = 50  # Neutral - no clear signal

Visual Representation

RSI is displayed as a line chart below the main price chart:
  • Overbought line at 70 (red dashed)
  • Oversold line at 30 (green dashed)
  • RSI line in cyan

Usage in Models

XGBoost uses three RSI-derived features:
df['rsi_normalized'] = df['rsi'] / 100         # Scale to 0-1
df['rsi_oversold'] = (df['rsi'] < 30).astype(int)   # Binary flag
df['rsi_overbought'] = (df['rsi'] > 70).astype(int) # Binary flag

MACD (Moving Average Convergence Divergence)

Description

MACD is a trend-following momentum indicator that shows the relationship between two exponential moving averages. It consists of three components:
  1. MACD Line: Difference between 12-day and 26-day EMAs
  2. Signal Line: 9-day EMA of MACD line
  3. Histogram: MACD line minus Signal line

Calculation

utils/indicators.py
def calculate_macd(data: pd.Series, 
                   fast: int = 12, 
                   slow: int = 26, 
                   signal: int = 9) -> Tuple[pd.Series, pd.Series, pd.Series]:
    """MACD calculation"""
    
    # 1. Fast EMA (12 periods)
    exp1 = data.ewm(span=fast, adjust=False).mean()
    
    # 2. Slow EMA (26 periods)
    exp2 = data.ewm(span=slow, adjust=False).mean()
    
    # 3. MACD line = Fast EMA - Slow EMA
    macd = exp1 - exp2
    
    # 4. Signal line = 9-period EMA of MACD
    signal_line = macd.ewm(span=signal, adjust=False).mean()
    
    # 5. Histogram = MACD - Signal
    histogram = macd - signal_line
    
    return macd, signal_line, histogram

Interpretation

Above 0: Bullish momentum (short-term MA > long-term MA)Below 0: Bearish momentum (short-term MA < long-term MA)Crossing 0: Potential trend change

Visual Representation

MACD is displayed in a separate chart with three elements:
  • MACD line in teal
  • Signal line in red/orange
  • Histogram as bars (green when positive, red when negative)

Usage in Models

XGBoost creates two MACD features:
df['macd_diff'] = df['macd'] - df['macd_signal']  # Distance between lines
df['macd_positive'] = (df['macd_diff'] > 0).astype(int)  # Bullish flag

Bollinger Bands

Description

Bollinger Bands consist of a middle moving average band with upper and lower bands that represent standard deviations. They expand and contract based on market volatility.

Calculation

utils/indicators.py
def calculate_bollinger_bands(data: pd.Series, 
                              period: int = 20, 
                              std_dev: float = 2) -> Tuple[pd.Series, pd.Series, pd.Series]:
    """Bollinger Bands calculation"""
    
    # 1. Middle band = 20-period SMA
    middle = data.rolling(window=period).mean()
    
    # 2. Standard deviation
    std = data.rolling(window=period).std()
    
    # 3. Upper band = Middle + (2 * std)
    upper = middle + (std * std_dev)
    
    # 4. Lower band = Middle - (2 * std)
    lower = middle - (std * std_dev)
    
    return upper, middle, lower

Interpretation

1

Band Width

Wide bands: High volatility, large price swings expectedNarrow bands: Low volatility, potential breakout coming (“Squeeze”)
2

Price Position

Above upper band: Overbought, potential reversalBelow lower band: Oversold, potential reversalNear middle band: Neutral zone
3

Band Bounces

Bounce off lower band: Bullish reversalBounce off upper band: Bearish reversalBreak through band: Strong trend continuation
4

Squeeze Pattern

Bands narrow → volatility compression → likely followed by large moveDirection determined by breakout (up or down)

Statistical Basis

Assuming normal distribution, approximately 95% of price action occurs within the bands (±2 standard deviations).

Visual Representation

Bollinger Bands overlay the main price chart:
  • Upper band in light blue (transparent)
  • Lower band in light blue with fill between bands
  • Middle band typically hidden (coincides with 20-day SMA)

Usage in Models

XGBoost uses Bollinger Band position:
df['bb_middle'] = middle_band
df['bb_upper'] = upper_band
df['bb_lower'] = lower_band

# Normalized position within bands (0 = lower, 1 = upper)
df['bb_position'] = (df['close'] - df['bb_lower']) / (df['bb_upper'] - df['bb_lower'])

Exponential Moving Averages (EMAs)

Description

EMAs are weighted moving averages that give more importance to recent prices, making them more responsive to price changes than simple moving averages (SMAs).

Calculation

utils/indicators.py
def calculate_ema(data: pd.Series, period: int) -> pd.Series:
    """Exponential Moving Average"""
    return data.ewm(span=period, adjust=False).mean()
EMA Formula: EMA(t) = Price(t) × α + EMA(t-1) × (1 - α) where α = 2 / (period + 1)

Standard Periods Used

CryptoView Pro calculates four EMA periods:

EMA 9

Very short-termTracks immediate price action, used for scalping

EMA 21

Short-termIntraday and swing trading, quick trend changes

EMA 50

Medium-termKey support/resistance, shown on main chart (orange)

EMA 200

Long-termMajor trend indicator, shown on main chart (purple)

Interpretation

Price above EMA: UptrendPrice below EMA: DowntrendLonger EMAs = stronger signal
EMAs often act as dynamic support/resistance:
  • In uptrend: EMA acts as support (buy zone)
  • In downtrend: EMA acts as resistance (sell zone)
  • Bounce or break determines continuation/reversal
Golden Cross: EMA 50 crosses above EMA 200 → Strong bullish signalDeath Cross: EMA 50 crosses below EMA 200 → Strong bearish signalThese are major trend-change signals for position traders.
When EMAs stack in order (9 > 21 > 50 > 200):
  • Bullish alignment: Strong uptrend
  • Bearish alignment: Strong downtrend (reverse order)
  • Mixed: Consolidation or transition phase

Visual Representation

EMA 50 and EMA 200 overlay the main price chart:
  • EMA 50 in orange (dashed line)
  • EMA 200 in purple (dashed line)
  • Crossovers visually apparent

Usage in Models

XGBoost uses EMAs and price-to-EMA ratios:
for span in [12, 26, 50]:
    df[f'ema_{span}'] = calculate_ema(df['close'], span)

# Relative position to moving average
for window in [7, 14, 30, 50]:
    df[f'ma_{window}'] = df['close'].rolling(window=window).mean()
    df[f'price_to_ma_{window}'] = df['close'] / df[f'ma_{window}']

Additional Indicators

Volatility

Calculation: Rolling standard deviation of returns
for window in [7, 14, 30]:
    df[f'volatility_{window}'] = df['return_1'].rolling(window=window).std()
Interpretation:
  • High volatility: Large price swings, increased risk and opportunity
  • Low volatility: Price stability, potential breakout ahead
  • Used for position sizing and risk management

Momentum

Calculation: Absolute price change over period
df['momentum_7'] = df['close'] - df['close'].shift(7)   # 7-period momentum
df['momentum_14'] = df['close'] - df['close'].shift(14) # 14-period momentum
Interpretation:
  • Positive momentum: Upward price pressure
  • Negative momentum: Downward price pressure
  • Magnitude indicates strength of move

Volume Features

Calculations:
df['volume_ma_7'] = df['volume'].rolling(window=7).mean()
df['volume_ratio'] = df['volume'] / df['volume_ma_7']  # Relative volume
df['volume_change'] = df['volume'].pct_change(1)       # Volume momentum
Interpretation:
  • volume_ratio > 2.0: High volume, strong conviction
  • volume_ratio < 0.5: Low volume, weak conviction
  • Rising volume + rising price = healthy uptrend
  • Rising volume + falling price = potential reversal

OHLC Ratios

Calculations:
df['high_low_ratio'] = df['high'] / df['low']      # Intrabar volatility
df['close_open_ratio'] = df['close'] / df['open']  # Directional strength
Interpretation:
  • high_low_ratio > 1.05: High intrabar volatility
  • close_open_ratio > 1.0: Bullish candle
  • close_open_ratio < 1.0: Bearish candle

Signal Generation

Location: TechnicalIndicators.get_signals(df) CryptoView Pro aggregates indicators into actionable signals:
utils/indicators.py
def get_signals(df: pd.DataFrame) -> dict:
    signals = {
        'rsi_signal': 'neutral',
        'macd_signal': 'neutral',
        'bb_signal': 'neutral',
        'overall': 'neutral'
    }
    
    last_row = df.iloc[-1]
    
    # RSI Signal
    if last_row['rsi'] > 70:
        signals['rsi_signal'] = 'overbought'  # Consider selling
    elif last_row['rsi'] < 30:
        signals['rsi_signal'] = 'oversold'     # Consider buying
    
    # MACD Signal
    if last_row['macd'] > last_row['macd_signal']:
        signals['macd_signal'] = 'bullish'     # Buy signal
    else:
        signals['macd_signal'] = 'bearish'     # Sell signal
    
    # Overall Signal (consensus)
    bullish_count = sum([
        signals['rsi_signal'] == 'oversold',
        signals['macd_signal'] == 'bullish'
    ])
    
    bearish_count = sum([
        signals['rsi_signal'] == 'overbought',
        signals['macd_signal'] == 'bearish'
    ])
    
    if bullish_count >= 2:
        signals['overall'] = 'buy'      # ✅ Strong buy signal
    elif bearish_count >= 2:
        signals['overall'] = 'sell'     # ⛔ Strong sell signal
    else:
        signals['overall'] = 'neutral'  # ⚠️ No clear signal
    
    return signals

Signal Display

The overall signal is shown in the metrics dashboard:
  • 🟢 COMPRA (Buy): At least 2 bullish indicators
  • 🔴 VENTA (Sell): At least 2 bearish indicators
  • 🟡 NEUTRAL: Conflicting or neutral indicators
Technical signals are not financial advice. Always perform your own analysis and risk assessment.

Indicator Integration

Automatic Calculation

All indicators are calculated when data is loaded:
app.py
@st.cache_data(ttl=60)
def load_crypto_data(symbol, timeframe, limit):
    collector = CryptoDataCollector('kraken')
    df = collector.fetch_ohlcv(symbol, timeframe, limit)
    
    if not df.empty:
        df = TechnicalIndicators.add_all_indicators(df)  # ← All indicators added
    
    return df

Caching

Indicator calculations are cached for 60 seconds to avoid redundant computation:
  • First load: Calculate all indicators
  • Subsequent loads (within 60s): Use cached results
  • After 60s: Refresh data and recalculate

Usage Across System

Visualization

Plotted on main chart and indicator subplots

ML Features

Fed into XGBoost as engineered features

Signals

Used for alert generation and notifications

Configuration

Indicator parameters are configurable in config/settings.py:
config/settings.py
INDICATORS_CONFIG = {
    'rsi_period': 14,        # RSI lookback period
    'macd_fast': 12,         # MACD fast EMA
    'macd_slow': 26,         # MACD slow EMA
    'macd_signal': 9,        # MACD signal line
    'bb_period': 20,         # Bollinger Bands period
    'bb_std': 2,             # Bollinger Bands standard deviations
    'ema_periods': [9, 21, 50, 200]  # EMA periods
}
Advanced users can modify these parameters to optimize for different trading styles:
  • Scalping: Shorter periods (RSI 9, EMA 5/10/20)
  • Day Trading: Default parameters
  • Swing Trading: Longer periods (RSI 21, EMA 50/100/200)

Best Practices

1

Use Multiple Indicators

Never rely on a single indicator. Confirm signals across RSI, MACD, and Bollinger Bands for higher confidence.
2

Confirm with Volume

Strong price moves should be accompanied by above-average volume. Low-volume moves are less reliable.
3

Consider Timeframes

Check indicators across multiple timeframes (1h, 4h, 1d) for trend confirmation.
4

Context Matters

RSI >70 during strong uptrend ≠ immediate reversal. Look for divergence and other confirming signals.
5

Backtesting

Use the backtesting feature to validate indicator strategies on historical data before live trading.

Common Patterns

Bullish Divergence

Setup: Price makes lower low, but RSI/MACD makes higher low Signal: Momentum weakening to downside, potential reversal up

Bearish Divergence

Setup: Price makes higher high, but RSI/MACD makes lower high Signal: Momentum weakening to upside, potential reversal down

Bollinger Squeeze

Setup: Bands narrow significantly (low volatility) Signal: Breakout imminent, direction determined by band break

MACD Histogram Reversal

Setup: Histogram peaks and starts shrinking toward zero Signal: Momentum fading, potential trend change

Further Reading

Architecture

See how indicators fit into the overall system

ML Models

Learn how indicators are used as model features
Technical analysis is one tool among many. Combine with fundamental analysis, market sentiment, and proper risk management for best results.

Build docs developers (and LLMs) love