【Quant(13)】RSI Indicator

Use common technical indicators to backtesting


  • Difficulty:★★☆☆☆
  • Backtesting by RSI indicator and relative performance of this strategy
  • Advice: This article adopts TEJ database to calculate RSI indicators, create signals and form the return. Before reading this, 【Quant(2)】Technical Analysis is recommended to explore to better understand the basic procedure of backtesting.


Relative strength index (RSI) is the momentum technical indicator. It is usually used as an oscillator interval to evaluate overbought or oversold condition by measuring recent trend of price movements. Following is the way to calculate this indicator:

  1. Based on daily spread of price, separately calculate average gain or loss in past N days. (Note: Convert the loss to positive value.)
  2. Calculate Relative Strength(RS): Average Gain in past N days / Average Loss in past N days.(Note: In the calculation of average gain, loss is counted as 0, vice versa.)
  3. Calculate Relative Strength Index: RS / (1 + RS) × 100

Criterion of RSI:

  1. RSI < 30 is oversold, RSI > 70 is overbought. (Note: Another conservative interval pair is (20, 80))
  2. RSI Divergence: Trend of RSI is opposite to trend of price, which implies market would become reversal.
  3. Golden Cross: Short-term RSI exceeds long-term RSI upwards, which implies the long position.
  4. Death Cross: Short-term RSI exceeds long-term RSI downwards, which implies the short position.

RSI’s Deactivation: Gain and loss is separately calculated in RSI. Therefore, if short-term trend of price is almost gain, the reference data of loss would be too few to be considered, which further causes that RSI loses the reference value, vice versa. This situation is specifically frequent during the high or low-level area. We would try to synthesize RSI interval, golden cross and death cross to lower the impact of deactivation.

The Editing Environment and Modules Required

MacOS and Jupyter Notebook

#Basic function
import pandas as pd
import numpy as np
import copy
import plotly.graph_objects as go
import plotly.express as px
import tejapi
tejapi.ApiConfig.api_key = 'Your Key'
tejapi.ApiConfig.ignoretz = True

Database Used

Data Processing

Step 1. Obtain the stock price

stock_data = tejapi.get('TWN/APRCD',
coid= '2324', # Compal Electronics, Inc.
mdate={'gte': '2020-01-01', 'lte':'2021-12-14'},
opts={'columns': ['coid', 'mdate', 'open_d', 'close_d']},

We choose Compal Electronics (2324) as the target. Time interval is ranged from 2020 to today (2021–12–14). With above circumstances, we would know the fluctuation of stock price of largest notebook OEM under the pandemic situation. On top of that, determine the buy and sell signal with RSI.

Step 2. Calculate 14-day RSI & 7-day RSI

# Calculate daily spread and categorize gain & loss
stock_data['隔日差價'] = stock_data['收盤價(元)'].diff()
stock_data['上漲'] = stock_data['隔日差價'].clip(lower = 0)
stock_data['下跌'] = (-1) * stock_data['隔日差價'].clip( upper = 0)
# 14-day RSI
stock_data['14日上漲均值'] = stock_data['上漲'].ewm(com = 14, adjust = False).mean()
stock_data['14日下跌均值'] = stock_data['下跌'].ewm(com = 14, adjust = False).mean()
stock_data['14日相對強弱值'] = stock_data['14日上漲均值'] / stock_data['14日下跌均值']
stock_data['14日相對強弱指標'] = stock_data['14日相對強弱值'].apply(lambda rs : rs/(1+rs)*100)
# 7-day RSI
stock_data['7日上漲均值'] = stock_data['上漲'].ewm(com = 7, adjust = False).mean()
stock_data['7日下跌均值'] = stock_data['下跌'].ewm(com = 7, adjust = False).mean()
stock_data['7日相對強弱值'] = stock_data['7日上漲均值'] / stock_data['7日下跌均值']
stock_data['7日相對強弱指標'] = stock_data['7日相對強弱值'].apply(lambda rs : rs/(1+rs)*100)

Step 3. RSI Trending Chart & Interval (30,70)(See the details in source code)

Comparing RSI trending and overbought, oversold interval, we find the Golden Cross and Death Cross. What we should notice is that deactivation remains serious at the high-level area. However, we should sell at the first death cross, since it is impossible to predict market accurately.

Step 4. Find Buy and Sell Signal & Visualize Trading Action(See the details in source code)

Over-sold Condition: RSI < 30. To boot, consider that 7-day RSI exceeds 14-day RSI upwards so as to lower impact of low-level deactivation.

Over-bought Condition: RSI > 70. To boot, consider that 7-day RSI exceeds 14-day RSI downwards so as to lower impact of high-level deactivation.

signal = []trade = 0for i in range(len(stock_data)):
if stock_data.loc[i, '14日相對強弱指標'] <= 30 and stock_data.loc[i-1, '14日相對強弱指標'] > stock_data.loc[i-1, '7日相對強弱指標'] and stock_data.loc[i, '14日相對強弱指標'] <= stock_data.loc[i, '7日相對強弱指標'] and trade == 0:
trade = trade + 1
elif stock_data.loc[i, '14日相對強弱指標'] >= 70 and stock_data.loc[i-1, '14日相對強弱指標'] < stock_data.loc[i-1, '7日相對強弱指標'] and stock_data.loc[i, '14日相對強弱指標'] >= stock_data.loc[i, '7日相對強弱指標'] and trade == 1: signal.append(-1000)
trade = trade - 1
stock_data['買賣股數'] = signal

Calculate the return

In 【Quant(8)】Backtesting by MACD Indicator, we have discussed the details in the calculation of frictions cost and the method to calculate return with initial principal. In this article, we use function to achieve all of it.

def target_return(data, principal):

data['手續費'] = data['開盤價(元)']* abs(data['買賣股數'])*0.001425
data['手續費'] = np.where((data['手續費']>0)&(data['手續費'] <20), 20, data['手續費'])
data['證交稅'] = np.where(data['買賣股數']<0, data['開盤價(元)']* abs(data['買賣股數'])*0.003, 0)
data['摩擦成本'] = (data['手續費'] + data['證交稅']).apply(np.floor)

data['股票價值'] = data['買賣股數'].cumsum() * data['收盤價(元)']
data['現金價值'] = principal - data['摩擦成本'] + (data['開盤價(元)']* -data['買賣股數']).cumsum()
data['資產價值'] = data['股票價值'] + data['現金價值']

data['當日價值變動(%)'] = (data['資產價值']/data['資產價值'].shift(1) - 1)*100
data['累計報酬(%)'] = (data['資產價值']/principal - 1)*100

return data

We only consider the trading of common stock. There is no margin buy or margin sell situation, so we do not have to calculate premium. With the data containing trading shares and initial principal, we can quickly get the transaction costs and return. In this article, we set 20,000 as our principal

RSI_return = target_return(data = stock_data, principal = 20000)

Besides, to demonstrate whether RSI strategy performs relatively well, we add buy-and-hold strategy and market performance as our benchmarks.

  • The return of buy-and-hold
bh_data = copy.deepcopy(stock_data)
bh_data['買賣股數'] = 0
bh_data.loc[0, '買賣股數'] = 1000
bh_data.loc[len(bh_data)-1, '買賣股數'] = -1000
# Drop the columns useless for buy-and-hold calculation.
bh_data = bh_data.drop(['隔日差價', '上漲', '下跌', '14日上漲均值', '14日下跌均值', '14日相對強弱值','14日相對強弱指標','7日上漲均值', '7日下跌均值', '7日相對強弱值', '7日相對強弱指標'], axis = 1)
bh_return = target_return(data=bh_data, principal = 20000)

Here we also use deepcopy() to get stock price, and choose to buy at the beginning and sell at the end during the same period with initial principal of 20000 as well.

  • Market Performance
market = tejapi.get('TWN/APRCD',
coid = 'Y9997',
mdate = {'gte':'2020-01-01', 'lte':'2021-12-14'},
opts = {'columns':['coid','mdate', 'close_d','roi']},
chinese_column_name = True)
market['累計報酬(%)'] = (market['報酬率%'].apply(lambda x:0.01*x +1).cumprod() - 1)*100

Obtain return of market return index (Y9997) to stands for market performance and calculate the cumulative return

Performance comparison

Step 1. Comparison of cumulative return (See the details in source code)

With the chart of cumulative return, We tell that RSI strategy made us long after the pandemic. As for the short position, RSI made us sell right at the point that electronic-related stocks were shorted, which was caused by the rise of Commodity’s price and market, therefore, focused on traditional stocks. Besides, fortunately, we avoid the impact of abruptly Covid’s intensification in Taiwan. However, due to the standard of signal trigger is strict, we cannot accurately handle every fluctuation period, like the drop during Aug 2020.

Step 2. Performance table (See the details in source code)

Above chart shows that RSI strategy outperforms buy-and-hold on both return and volatility, which means that RSI helps us find adequate long and short point and improve the efficiency of investment. As for the comparison with market, return of RSI does not outperform market return significantly, but the volatility of RSI is relatively stable. Resulting from avoiding pandemic crash in 2020 and local crash in Taiwan in 2021, sharpe ratio of RSI is better than market performance.


With the above content, we believe readers understand the influence of deactivation, especially during high or low-level area. Hence, we add another condition, cross of long-term and short-term RSI, making RSI strategy more steady. Of course, on the other hand, this sacrifices some trading opportunities. The balance between risk and trading opportunities is what investors should evaluate and strike. After all, the application of technical indicator varies from person to person. As a result, if readers are interested in diverse trading backtesting, welcome to purchase the plan offered in TEJ E-Shop. Construct trading strategies fitting you with high quality database.

Source Code

Extended Reading

Related Link

You could give us encouragement by …
We will share financial database applications every week.
If you think today’s article is good, you can click on the
applause icononce.
If you think it is awesome, you can hold the
applause icon until 50 times.
If you have any feedback, please feel free to leave a comment below.



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
TEJ 台灣經濟新報

TEJ 為台灣本土第一大財經資訊公司,成立於 1990 年,提供金融市場基本分析所需資訊,以及信用風險、法遵科技、資產評價、量化分析及 ESG 等解決方案及顧問服務。鑒於財務金融領域日趨多元與複雜,TEJ 結合實務與學術界的精英人才,致力於開發機器學習、人工智慧 AI 及自然語言處理 NLP 等新技術,持續提供創新服務