DEV Community

Cover image for How to Calculate RSI, MACD, and Moving Averages Using a Market Data API
San Si wu
San Si wu

Posted on

How to Calculate RSI, MACD, and Moving Averages Using a Market Data API

In quantitative trading and technical analysis, RSI (Relative Strength Index), MACD (Moving Average Convergence Divergence), and Moving Averages (MA) are three of the most classic and widely used technical indicators. They help traders identify market trends, momentum, and potential reversal points. This article will explain in detail how to obtain high-quality financial data through common market data APIs and manually compute these core indicators in Python, laying a solid foundation for building your own quantitative strategies.

1. Data Acquisition and Indicator Calculation

1. Environment Setup and Data Retrieval

Install the required Python libraries and fetch candlestick (K-line) data.

# Install required libraries
# pip install requests pandas numpy matplotlib

import requests
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# Set API request headers (common to all iTick endpoints)
headers = {
    "accept": "application/json",
    "token": "your_API_Token"  # Replace with your actual token
}

def fetch_kline_from_itick(symbol='700.HK', region='HK', k_type=8, limit=100):
    """
    Fetch historical K-line data from the iTick API

    Parameters:
    symbol: Ticker symbol, e.g., '700.HK' for Tencent Holdings
    region: Market region, e.g., 'HK' (Hong Kong), 'US' (US), 'CN' (Mainland China)
    k_type: K-line period, 1=1-minute, 2=5-minute, ..., 8=daily
    limit: Number of K-lines to retrieve
    """
    url = f"https://api.itick.org/stock/kline?region={region}&code={symbol}&kType={k_type}&limit={limit}"
    try:
        response = requests.get(url, headers=headers)
        data = response.json()
        if data["code"] == 0:  # Request successful
            kline_list = data["data"]
            df = pd.DataFrame(kline_list)
            # Rename and format columns
            df.rename(columns={'t': 'timestamp', 'o': 'open', 'h': 'high',
                               'l': 'low', 'c': 'close', 'v': 'volume'}, inplace=True)
            df['datetime'] = pd.to_datetime(df['timestamp'], unit='s')
            df.set_index('datetime', inplace=True)
            df.sort_index(inplace=True)  # Ensure chronological order
            print(f"Successfully fetched {len(df)} K-lines for {symbol}")
            return df[['open', 'high', 'low', 'close', 'volume']]
        else:
            print(f"Request failed: {data['msg']}")
            return None
    except Exception as e:
        print(f"Error fetching data: {e}")
        return None

# Example: Fetch daily data for Tencent Holdings (0700.HK)
df = fetch_kline_from_itick(symbol='700.HK', region='HK', k_type=8, limit=200)
print(df.head())
Enter fullscreen mode Exit fullscreen mode

2. Calculating Moving Averages (MA)

Moving averages are the most basic trend indicators, used to smooth price data.

def calculate_ma(df, windows=[5, 10, 20]):
    """
    Calculate Simple Moving Averages (SMA)
    """
    for window in windows:
        df[f'MA_{window}'] = df['close'].rolling(window=window).mean()
    return df

# Calculate 5-day, 10-day, and 20-day moving averages
df = calculate_ma(df, [5, 10, 20])
print(df[['close', 'MA_5', 'MA_10', 'MA_20']].tail())
Enter fullscreen mode Exit fullscreen mode

3. Calculating Exponential Moving Average (EMA) and MACD

MACD is a trend-following momentum indicator consisting of the fast line (DIF), slow line (DEA), and histogram (MACD Histogram).

def calculate_ema(series, period):
    """Calculate Exponential Moving Average (EMA)"""
    return series.ewm(span=period, adjust=False).mean()

def calculate_macd(df, fast=12, slow=26, signal=9):
    """
    Calculate MACD indicator
    Standard parameters: fast period 12, slow period 26, signal period 9
    """
    # Calculate fast and slow EMAs
    df['EMA_Fast'] = calculate_ema(df['close'], fast)
    df['EMA_Slow'] = calculate_ema(df['close'], slow)
    # Calculate DIF (difference)
    df['DIF'] = df['EMA_Fast'] - df['EMA_Slow']
    # Calculate DEA (signal line: EMA of DIF)
    df['DEA'] = calculate_ema(df['DIF'], signal)
    # Calculate MACD histogram (typically 2 × (DIF - DEA))
    df['MACD_Hist'] = 2 * (df['DIF'] - df['DEA'])
    return df

# Calculate MACD
df = calculate_macd(df)
print(df[['close', 'DIF', 'DEA', 'MACD_Hist']].tail())
Enter fullscreen mode Exit fullscreen mode

4. Calculating Relative Strength Index (RSI)

RSI is a momentum oscillator that measures the speed and magnitude of price movements to identify overbought or oversold conditions.

def calculate_rsi(df, window=14):
    """
    Calculate Relative Strength Index (RSI)
    """
    # Calculate price changes
    delta = df['close'].diff()
    # Separate gains and losses
    gain = (delta.where(delta > 0, 0)).rolling(window=window).mean()
    loss = (-delta.where(delta < 0, 0)).rolling(window=window).mean()
    # Calculate Relative Strength (RS) and RSI
    rs = gain / loss
    df[f'RSI_{window}'] = 100 - (100 / (1 + rs))
    return df

# Calculate 14-period RSI
df = calculate_rsi(df, window=14)
print(df[['close', 'RSI_14']].tail())
Enter fullscreen mode Exit fullscreen mode

2. Data Visualization

Visualizing the price and indicators after calculation makes analysis more intuitive.

def plot_indicators(df, symbol='700.HK'):
    """Plot price, moving averages, MACD, and RSI charts"""
    fig, axes = plt.subplots(3, 1, figsize=(14, 12), gridspec_kw={'height_ratios': [3, 2, 2]})

    # Subplot 1: Price and Moving Averages
    axes[0].plot(df.index, df['close'], label='Close Price', linewidth=1.5, color='black')
    colors = ['blue', 'orange', 'green']
    for i, period in enumerate([5, 10, 20]):
        if f'MA_{period}' in df.columns:
            axes[0].plot(df.index, df[f'MA_{period}'], label=f'MA{period}', alpha=0.8, color=colors[i])
    axes[0].set_title(f'{symbol} - Price & Moving Averages')
    axes[0].set_ylabel('Price')
    axes[0].legend(loc='upper left')
    axes[0].grid(True, alpha=0.3)

    # Subplot 2: MACD
    axes[1].plot(df.index, df['DIF'], label='DIF', color='blue', linewidth=1.5)
    axes[1].plot(df.index, df['DEA'], label='DEA', color='red', linewidth=1.5)
    # Histogram: green for positive, red for negative
    colors_hist = ['green' if x >= 0 else 'red' for x in df['MACD_Hist']]
    axes[1].bar(df.index, df['MACD_Hist'], color=colors_hist, alpha=0.5, width=0.8, label='MACD Hist')
    axes[1].axhline(y=0, color='grey', linestyle='--', linewidth=0.8)
    axes[1].set_ylabel('MACD')
    axes[1].legend(loc='upper left')
    axes[1].grid(True, alpha=0.3)

    # Subplot 3: RSI
    axes[2].plot(df.index, df['RSI_14'], label='RSI 14', color='purple', linewidth=1.5)
    axes[2].axhline(y=70, color='red', linestyle='--', alpha=0.7, label='Overbought (70)')
    axes[2].axhline(y=30, color='green', linestyle='--', alpha=0.7, label='Oversold (30)')
    axes[2].axhline(y=50, color='grey', linestyle='--', alpha=0.5)
    axes[2].set_ylabel('RSI')
    axes[2].set_ylim(0, 100)
    axes[2].legend(loc='upper left')
    axes[2].grid(True, alpha=0.3)
    axes[2].set_xlabel('Date')

    plt.tight_layout()
    plt.show()

# Plot the charts
plot_indicators(df, '700.HK')
Enter fullscreen mode Exit fullscreen mode

3. Advanced: Generating Simple Trading Signals

Using the calculated indicators, we can create basic trading signals.

def generate_signals(df):
    """Generate simple trading signals based on MACD and RSI"""
    df['signal'] = 0  # Initialize signal column

    # Rule 1: RSI oversold (<30) + MACD golden cross (DIF crosses above DEA) → potential buy
    rsi_oversold = df['RSI_14'] < 30
    macd_golden_cross = (df['DIF'] > df['DEA']) & (df['DIF'].shift(1) <= df['DEA'].shift(1))
    df.loc[rsi_oversold & macd_golden_cross, 'signal'] = 1  # Buy signal

    # Rule 2: RSI overbought (>70) + MACD death cross (DIF crosses below DEA) → potential sell
    rsi_overbought = df['RSI_14'] > 70
    macd_death_cross = (df['DIF'] < df['DEA']) & (df['DIF'].shift(1) >= df['DEA'].shift(1))
    df.loc[rsi_overbought & macd_death_cross, 'signal'] = -1  # Sell signal

    return df

df_with_signals = generate_signals(df)
signal_points = df_with_signals[df_with_signals['signal'] != 0]
print("Trading signal points:")
print(signal_points[['close', 'RSI_14', 'DIF', 'DEA', 'signal']])
Enter fullscreen mode Exit fullscreen mode

4. Summary and Practical Advice

By using market data APIs, we can easily obtain high-quality financial data and leverage Python’s powerful computation capabilities to implement core indicators like RSI, MACD, and moving averages from scratch. This gives you full flexibility and transparency when developing, testing, and optimizing quantitative strategies.

Key Recommendations:

  1. Understand the Indicators — Never use indicators blindly. Grasp the market implications behind RSI overbought/oversold levels, MACD golden/death crosses, and moving average alignments.
  2. Combine Multiple Indicators — A single indicator can have flaws (e.g., RSI can become desensitized in strong trends). Combining trend indicators (like MA) with momentum indicators (like RSI and MACD) improves signal reliability.
  3. Parameter Optimization and Backtesting — Indicator periods (e.g., RSI window, MACD fast/slow lines) are not fixed. Perform rigorous backtesting on different markets and assets to find optimal parameters.
  4. Risk Management — No technical indicator guarantees 100% accuracy. Always incorporate strict position sizing and stop-loss rules in real trading.

Reference: https://blog.itick.org/rsi-strategy-hands-on-guide-with-itick-data-python

GitHub: https://github.com/itick-org/

Top comments (0)