RSI Indicator Calculation Tutorial

Learn what the RSI is and how to calculate it using Upbit market data from the API response with Python.

Tutorial Example Code Guide
This tutorial includes step-by-step partial code and API call/response examples to aid user understanding.
The full code examples can be found in the Recipes menu.
Click the button above to navigate to the full code Recipe page of this tutorial.

Get Started

RSI (Relative Strength Index) is a widely used technical analysis indicator in traditional financial markets such as stocks and futures, and it also provides meaningful insights in digital asset trading. In this guide, we will explore the concept and calculation method of RSI, and step-by-step learn how to calculate the RSI of digital assets using the Upbit API.

Check the proper endpoint based on your region.
The examples in this page is written using Singapore fiat code(SGD). Set the quote currency to match your region. The base_url differs by country/region. Make sure to specify the correct region value for your environment.

- Singapore (sg): https://sg-api.upbit.com
- Indonesia (id): https://id-api.upbit.com
- Thailand (th): https://th-api.upbit.com

What is RSI (Relative Strength Index)?

RSI, which stands for Relative Strength Index, is an indicator used to determine the overbought or oversold condition of an asset. It takes values between 0 and 100, where a value above 70 is interpreted as overbought, and below 30 as oversold. The formula to calculate RSI is as follows:

RSI = 100 - (100 / (1 + RS))
RSI is not an absolute investment indicator.
It is just one of many technical analysis tools based on market data, and it should not be used as the sole basis for trading decisions.

RS (Relative Strength) Calculation Formula

To calculate RSI, you first need to compute the RS (Relative Strength), which is derived from the Average Upward Movement (AU) and the Average Downward Movement (AD).

The formula for RS is:

RS = AU(Average Upward Movement) / AD(Average Downward Movement)

The formulas for calculating the Average Upward Movement (AU) and Average Downward Movement (AD) are:

AU = (Previous AU × (Period − 1) + Current Upward Change) ÷ Period  
AD = (Previous AD × (Period − 1) + Current Downward Change) ÷ Period  

Now, let’s compute the AU and AD for an actual digital asset and use them to calculate the RSI.


Calculating RSI

The previous day’s closing price of a digital asset can be obtained through the List Day Candles API. In this guide, we will use Bitcoin’s daily candle data to calculate the 14-day Average Upward Movement (AU) and Average Downward Movement (AD), then compute the Relative Strength (RS) and the RSI values.

def list_days_candles(trading_pair: str, count: int)-> Sequence:
    params = {
        "market": trading_pair,
        "count": count,
    }
    url = "https://sg-api.upbit.com/v1/candles/days"
    headers = {
        "Content-Type": "application/json"
    }
    response = requests.get(url, headers=headers, params=params).json()
    reversed_candle_data = response[::-1] 
    return reversed_candle_data

This API takes three parameters: market, to, and count. Their roles are:

  • market: Specifies which digital asset market to query. In this guide, we use Bitcoin in the SGD market.
  • to: Sets the end time for the candle data. Together with count, it defines the time range and number of candles to retrieve. The format is ISO 8601 (UTC). If omitted, the most recent candle time is used. In this guide, we leave it blank to get the latest data.
  • count: Sets how many candles to return. RSI is usually calculated with 14 daily candles.

Because the API returns candles in descending order (latest first), you’ll need to reorder them in ascending order to calculate price changes from oldest to newest.

Candles are created only when trades occur during the given time interval.
If no trades take place between the candle’s start and end times, the candle will not be generated and will not appear in the response. For example, a daily candle with candle_date_time_utc set to 2024-08-31T00:00:00 is generated only if trades occur between 2024-08-31T00:00:00 (inclusive) and 2024-09-01T00:00:00 (exclusive).
[
  {
    market: "SGD-BTC",
    candle_date_time_utc: "2025-06-23T00:00:00",
    opening_price: 140908000,
    high_price: 147101000,
    low_price: 139698000,
    trade_price: 146182000,
    timestamp: 1750723199727,
    candle_acc_trade_price: 346783036638.01794,
    candle_acc_trade_volume: 2422.95238077,
    prev_closing_price: 140908000,
    change_price: 5274000,
    change_rate: 0.0374286769
  },
...
  {
    market: "SGD-BTC",
    candle_date_time_utc: "2025-07-07T00:00:00",
    opening_price: 148500000,
    high_price: 149100000,
    low_price: 147196000,
    trade_price: 148100000,
    timestamp: 1751932799103,
    candle_acc_trade_price: 123245812310.68657,
    candle_acc_trade_volume: 832.29461691,
    prev_closing_price: 148500000,
    change_price: -400000,
    change_rate: -0.0026936027
  }
]

Calculating AU and AD

The change_price field in the daily candle response represents the difference from the previous day’s closing price. You can use this value to calculate the Average Upward Movement (AU) and Average Downward Movement (AD).

Implement a function that:

  • Treats positive change_price values as upward movements (for AU).
  • Treats negative change_price values as downward movements (for AD).
  • Uses these values to compute the averages over the given period (e.g., 14 days).
def calculate(candle_data: Sequence, period: int = 14) -> Mapping:
    if len(candle_data) < period:
        raise ValueError("At least {period} candle data are required.".format(period=period))
    
    gains = []
    losses = []
    
    for item in candle_data:
        change = item.get('change_price')
        gains.append(change if change > 0 else 0)
        losses.append(abs(change) if change < 0 else 0)
    
    initial_au = sum(gains[:period]) / period
    initial_ad = sum(losses[:period]) / period
    
    au = initial_au
    ad = initial_ad
    
    for i in range(period, len(gains)):
        au = (au * (period - 1) + gains[i]) / period
        ad = (ad * (period - 1) + losses[i]) / period

Before calculating RSI, first make sure there are enough daily candle entries. Then, check the change_price value of each candle and classify it into two arrays: gains and losses.

  • gains: If change_price is positive, add the value; if it is negative or zero, add 0.
  • losses: If change_price is negative, add its absolute value; if it is positive or zero, add 0.

After this process, both arrays will have the same length as the number of candle entries.

if len(candle_data) < period:
    raise ValueError("At least {period} candle data are required.".format(period=period))
    
    gains = []
    losses = []
    
    for item in candle_data:
        change = item.get('change_price')
        gains.append(change if change > 0 else 0)
        losses.append(abs(change) if change < 0 else 0)

First, calculate the initial averages by summing the values in the gains and losses arrays from the first element up to the specified period, then divide each sum by the period. This gives you the initial Average Upward Movement (AU) and Average Downward Movement (AD).

Next, use the full gains and losses arrays to update and compute the cumulative AU and AD for the entire candle dataset.

initial_au = sum(gains[:period]) / period
initial_ad = sum(losses[:period]) / period

au = initial_au
ad = initial_ad

for i in range(period, len(gains)):
    au = (au * (period - 1) + gains[i]) / period
    ad = (ad * (period - 1) + losses[i]) / period

Calculating RS and RSI

Using the Average Upward Movement (AU) and Average Downward Movement (AD), you can calculate the Relative Strength (RS) and the Relative Strength Index (RSI). Add the RS and RSI formulas to your calculate function.

  • If AD is 0, it means there were no declines during the period. In this case, set RSI to 100.
  • Otherwise, calculate RS as AU ÷ AD, then apply the formula:
    • RSI = 100 − (100 ÷ (1 + RS))
def calculate(candle_data: Sequence[Mapping[str, Any]], period: int = 14) -> Mapping[str, Any]:

    ...
    
    rs = float('inf') if ad == 0 else au / ad
    
    rsi = (100 - 100 / (1 + rs))
    
    return {
        "AU": str(Decimal(au).quantize(Decimal("1e-4"), rounding=ROUND_DOWN)),
        "AD": str(Decimal(ad).quantize(Decimal("1e-4"), rounding=ROUND_DOWN)),
        "RS": str(Decimal(rs).quantize(Decimal("1e-4"), rounding=ROUND_DOWN)),
        "RSI": str(Decimal(rsi).quantize(Decimal("1e-4"), rounding=ROUND_DOWN))
    }

Code Execution Result

Run the code to see the RSI value for the SGD-BTC pair.

if __name__ == "__main__":
    candle_data = list_days_candles("SGD-BTC", 200)
    rsi_data = calculate(candle_data, 14)
    print(rsi_data)
{
  "AU": "521785.71428571426"
  "AD": "384785.71428571426"
  "RS": "1.3560423241136068"
  "RSI": "57.55594075007878"
}

Full Code Example

Below is the full code for calculating RSI. It uses 200 daily candles to compute the cumulative average gains and losses, then applies those values to calculate the 14-day RSI.

from typing import Any
from collections.abc import Mapping, Sequence
import requests
from decimal import Decimal, ROUND_DOWN

def list_days_candles(trading_pair: str, count: int) -> Sequence:
    params = {
        "market": trading_pair,
        "count": count,
    }
    url = "https://sg-api.upbit.com/v1/candles/days"
    headers = {
        "Content-Type": "application/json"
    }
    response = requests.get(url, headers=headers, params=params).json()
    reversed_candle_data = response[::-1] 
    return reversed_candle_data

def calculate(candle_data: Sequence, period: int = 14) -> Mapping:
    if len(candle_data) < period:
        raise ValueError("At least {period} candle are required.".format(period=period))
    
    gains = []
    losses = []
    
    for item in candle_data:
        change = item.get('change_price')
        gains.append(change if change > 0 else 0)
        losses.append(abs(change) if change < 0 else 0)
    
    initial_au = sum(gains[:period]) / period
    initial_ad = sum(losses[:period]) / period
    
    au = initial_au
    ad = initial_ad
    
    for i in range(period, len(gains)):
        au = (au * (period - 1) + gains[i]) / period
        ad = (ad * (period - 1) + losses[i]) / period
    
    rs = float('inf') if ad == 0 else au / ad
    
    rsi = (100 - 100 / (1 + rs))
    
    return {
        "AU": str(Decimal(au).quantize(Decimal("1e-4"), rounding=ROUND_DOWN)),
        "AD": str(Decimal(ad).quantize(Decimal("1e-4"), rounding=ROUND_DOWN)),
        "RS": str(Decimal(rs).quantize(Decimal("1e-4"), rounding=ROUND_DOWN)),
        "RSI": str(Decimal(rsi).quantize(Decimal("1e-4"), rounding=ROUND_DOWN))
    }
if __name__ == "__main__":
    candle_data = list_days_candles("SGD-BTC", 200)
    rsi_data = calculate(candle_data, 14)
    print(rsi_data)