Predictive Hacks

How to Apply an RSI Trading Strategy to your Cryptos

rsi

One of the most famous indicators in algorithmic trading and technical analysis is the Relative Strength Index. Almost all trading platforms provide you with this momentum indicator. Below are two screenshots of the ETH/USD 14 Days RSI taken from eToro and Kraken respectively.

eToro
Kraken

The RSI

The RSI stands for the “Relative Strength Index” and was developed by J.Welles Wilder back in 1987. In essence, it measures the momentum of a trend. Some things that you have to keep in mind are:

  • It takes values from 0 to 100
  • An RSI>70 implies an overbought in the market, meaning that the asset is overvalued and the price may fall
  • An RSI<30 implies an oversold in the market, meaning that the asset is undervalued and the price may increase

The formula of the RSI is the following:

Between the current trading day versus the previous one, we calculate the ups and the downs. The ups are calculated as:

\(U = close_{now}-close_{previous}\)

\(D = 0\)

and the downs are calculated as:

\(U = 0\)

\(D = close_{previous}-close_{now}\)

Then we calculate average U and D using a 14-day period smoothed moving average (SMMA) which is an exponentially smoothed Moving Average with α = 1/period. The ratio of the above averages is the relative strength factor (RS).

\(RS = \frac{SMMA(U,n)}{SMMA(D,n)}\).

Finally, the RSI is calculated as:

\(RSI = 100 – \frac{100}{1+RS}\)

Apply the RSI using the Kraken API and Python

In previous posts, we have shown you how to get data from the Kraken API. For the calculation of the RSI we will work with the talib Python library. Let’s start an example by taking into consideration the Cardano cryptocurrency (ADA) over USD.

Load the required libraries

import pandas as pd
import requests
import time
import datetime
import talib
import numpy as np

import matplotlib.pyplot as plt
%matplotlib inline
 

Get the Historical Prices from 2021-4-15 up to 2022-4-15.

epoch = int(datetime.datetime(2021, 4, 15, 0, 0, 0).timestamp())
# pair = 'XETHZUSD'
pair = 'ADAUSD'
# pair = 'XXBTZUSD'
url = f'https://api.kraken.com/0/public/OHLC?pair={pair}&amp;since={epoch}&amp;interval=1440'.format(pair,epoch)
resp = requests.get(url)
df = pd.DataFrame(resp.json()['result'][pair])
df.columns = ['unixtimestap', 'Open', 'High', 'Low', 'Close', 'vwap', 'volume', 'count']
df['unixtimestap'] = pd.to_datetime(df['unixtimestap'], unit='s')
df.set_index('unixtimestap', inplace=True)
df = df[['Open', 'High', 'Low', 'Close']]
df['Open'] = df['Open'].astype('float')
df['High'] = df['High'].astype('float')
df['Low'] = df['Low'].astype('float')
df['Close'] = df['Close'].astype('float')
df

Add the RSI (14-Days)

df['RSI_14'] = talib.RSI(df['Close'], timeperiod=14)
df.tail(10)

Show the Trading Signals on the Chart

def rsi_trend(x):
    if x<30:
        return "Buy"
    if x>70:
        return "Sell"

df['RSI_Trend'] = df['RSI_14'].apply(lambda x:rsi_trend(x))


ax = df.Close.plot(figsize=(30,15), title= "RSI Trading Strategy" )
for i in range(df.shape[0]):
    label = df.RSI_Trend[i]
    if label=='Sell':
        ax.annotate(label,
             (df.index[i], df['Close'][i]),
             xytext=(0, 15), 
             textcoords='offset points',
             arrowprops=dict(arrowstyle='-|>'), color='r', ha='center')
    if label=='Buy':
        ax.annotate(label,
             (df.index[i], df['Close'][i]),
             xytext=(0, 15), 
             textcoords='offset points',
             arrowprops=dict(arrowstyle='-|>'), color='g', ha='center')
 

As we can see from the graph above, some trading signals make sense. For example, it predicted correctly the first peak where there was an overbought, it also predicted another deep later on and so on.

But let’s represent again the closing prices of ADA/USD but showing also the RSI with the upper (70) and lower bound (30).

# Create subplots
fig, (ax1, ax2) = plt.subplots(2)
fig.set_size_inches(18.5, 10.5)
# plot RSI with the price
ax1.set_ylabel('Price')
ax1.plot(df['Close'])
ax2.set_ylabel('RSI_14')
ax2.plot(df['RSI_14'], color='orangered')
ax2.axhline(y = 30, color = 'black', linestyle = '-')
ax2.axhline(y = 70, color = 'black', linestyle = '-')
ax1.set_title('Price and RSI')
plt.show()

Apply and Back Test the RSI Trading Strategy

We can apply the RSI trading strategy assuming that we open a position the first time the RSI is less than 30 and we close the position the first time that the RSI exceeds 70. Finally, we hold a position, we will close it at the end of the observation period in order to estimate the overall ROI.

trades = []
tmp = df.copy()
tmp['rn'] = np.arange(len(tmp))
last_i = tmp.rn.max()
status = 'neutral'
for i in tmp.rn:
    if (i == last_i) and (status == 'long'):
        trades.append((i,'sell',tmp.loc[tmp.rn==i]['Close'][0]))
    elif (tmp.loc[tmp.rn==i]['RSI_14'].values[0]<30) and (status == 'neutral'):
        trades.append((i, 'buy', tmp.loc[tmp.rn==i]['Close'][0]))
        status = 'long'
    elif (tmp.loc[tmp.rn==i]['RSI_14'].values[0]>70) and (status == 'long'):
        trades.append((i, 'sell', tmp.loc[tmp.rn==i]['Close'][0]))
        status = 'neutral'
        
    
trades = pd.DataFrame(trades)
trades.columns = ['time', 'position', 'price']
trades['pct_change'] = trades['price'].pct_change()

trades['returns'] = trades['pct_change']+1
trades
 

As we can see, we opened and closed 2 positions, i.e. totally 4 trades. The first time that we opened and closed the position, the ROI was 39.66% and the second time was -27.77%. The overall ROI of the strategy was 0.94% only:

# we care only when we close the position is when the position is "sell"

overall_return = trades.loc[trades['position']=='sell'].returns.cumprod().values[-1]-1
overall_return
 

Apply the Trading Strategy to more Cryptos

Let’s apply the same trading strategy to more cryptocurrencies.

my_dict={}

dollar_pairs = ['XXBTZUSD', 'XXRPZUSD', 'ADAUSD', 'ALGOUSD', 'BCHUSD', 'TRXUSD', 
                 'DOTUSD', 'XXLMZUSD', 'ATOMUSD', 'XLTCZUSD', 'XZECZUSD']


for pair in dollar_pairs:
    url = f'https://api.kraken.com/0/public/OHLC?pair={pair}&amp;since={epoch}&amp;interval=1440'.format(pair,epoch)
    resp = requests.get(url)
    df = pd.DataFrame(resp.json()['result'][pair])
    df.columns = ['unixtimestap', 'Open', 'High', 'Low', 'Close', 'vwap', 'volume', 'count']
    df['unixtimestap'] = pd.to_datetime(df['unixtimestap'], unit='s')
    df.set_index('unixtimestap', inplace=True)
    df = df[['Open', 'High', 'Low', 'Close']]
    df['Open'] = df['Open'].astype('float')
    df['High'] = df['High'].astype('float')
    df['Low'] = df['Low'].astype('float')
    df['Close'] = df['Close'].astype('float')
    df['RSI_14'] = talib.RSI(df['Close'], timeperiod=14)
    
    try:
        trades = []
        tmp = df.copy()
        tmp['rn'] = np.arange(len(tmp))
        last_i = tmp.rn.max()
        status = 'neutral'
        for i in tmp.rn:
            if (i == last_i) and (status == 'long'):
                trades.append((i,'sell',tmp.loc[tmp.rn==i]['Close'][0]))
            elif (tmp.loc[tmp.rn==i]['RSI_14'].values[0]<30) and (status == 'neutral'):
                trades.append((i, 'buy', tmp.loc[tmp.rn==i]['Close'][0]))
                status = 'long'
            elif (tmp.loc[tmp.rn==i]['RSI_14'].values[0]>70) and (status == 'long'):
                trades.append((i, 'sell', tmp.loc[tmp.rn==i]['Close'][0]))
                status = 'neutral'


        trades = pd.DataFrame(trades)
        trades.columns = ['time', 'position', 'price']
        trades['pct_change'] = trades['price'].pct_change()
        trades['returns'] = trades['pct_change']+1
        overall_return = trades.loc[trades['position']=='sell'].returns.cumprod().values[-1]-1
        my_dict[pair] = {'return':overall_return, 'trades':trades.shape[0]}
    except:
        pass


my_dict
 
{'XXBTZUSD': {'return': -0.24973255380572845, 'trades': 4},
 'XXRPZUSD': {'return': 0.7164490011890363, 'trades': 4},
 'ADAUSD': {'return': 0.009457418301859644, 'trades': 4},
 'ALGOUSD': {'return': -0.22554860907145557, 'trades': 2},
 'BCHUSD': {'return': 0.3638647750057158, 'trades': 4},
 'TRXUSD': {'return': 0.16013150289220057, 'trades': 4},
 'DOTUSD': {'return': 0.15684373584261624, 'trades': 4},
 'XXLMZUSD': {'return': 0.6384810213489527, 'trades': 4},
 'XLTCZUSD': {'return': 0.0287640760521084, 'trades': 4},
 'XZECZUSD': {'return': 0.6432236897450998, 'trades': 2}}

The dictionary above shows the overall return of each asset as well as the number of trades. In 8 out of 10 cryptos we had a profit from 0.9% up to 71%.

  • BTC: -24.97%
  • XRP: 71.64%
  • ADA: 0.9%
  • ALGO: -22.5%
  • BCH: 36.38%
  • TRX: 16.01%
  • DOT: 15.68%
  • XLM: 63.38%
  • LTC: 2.87%
  • ZEC: 64.32%

Final Thoughts

There are many variations in the trading strategies. In our case, we could have been less greedy, meaning that we could have opened a position when the RSI was below 30 but we could have taken a profit when the price was above a threshold or to stop loss when the direction was not the expected. Some other investors set the upper and lower bounds of the RSI to 80 and 20 respectively. Some others add more criteria. If you are interested in more trading strategies, stay tuned!

Want some Free Cryptos?

You can get $25 in Bitcoin by investing $100 in Nexo

Share This Post

Share on facebook
Share on linkedin
Share on twitter
Share on email

Leave a Comment

Subscribe To Our Newsletter

Get updates and learn from the best

More To Explore

Python

Image Captioning with HuggingFace

Image captioning with AI is a fascinating application of artificial intelligence (AI) that involves generating textual descriptions for images automatically.

Python

Intro to Chatbots with HuggingFace

In this tutorial, we will show you how to use the Transformers library from HuggingFace to build chatbot pipelines. Let’s