RSI Indicator Calculation Tutorial
Learn what the RSI is and how to calculate it using Upbit market data from the API response with Python.
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.
- 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))
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 withcount
, 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.
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)
Updated 9 days ago