Parabolic Trading System Backtest for Bitcoin

Parabolic trading system has been tested in Backtesting.py examples. I wondered how this system would work for Bitcon, so I tested it. In this article I share my results of the original and optimized system. I also provide the python code, which is almost identical to the example in the Backtesting.py tutorial. I remind the reader that this is not trading or investment advice.

Rules of Parabolic Trading

Buy a position when:

  • Daily RSI(30) > 70
  • Weekly RSI(30) > 70
  • Weekly RSI(30) > Daily RSI(30)
  • MA(10) > MA(20) > MA(50) > MA(100)
  • Close > MA(10)

Close the position when:

  • Close is > 2% below MA(10)
  • Or 8% fixed stop loss

MA(n) refers to daily moving average for n period. RSI refers to Relative Strength Index.

Backtesting Results

The time frame is about 10 years.

Start                     2011-12-31 00:00:00
End                       2021-05-07 00:00:00
Duration                   3415 days 00:00:00
Exposure Time [%]                    5.127454
Equity Final [$]               13928401.54364
Equity Peak [$]                18496624.22084
Return [%]                        1292.840154
Buy & Hold Return [%]           1224946.28821
Return (Ann.) [%]                   32.535866
Volatility (Ann.) [%]               55.133799
Sharpe Ratio                         0.590126
Sortino Ratio                        1.190161
Calmar Ratio                         0.442563
Max. Drawdown [%]                  -73.516997
Avg. Drawdown [%]                   -14.74722
Max. Drawdown Duration     1505 days 00:00:00
Avg. Drawdown Duration      212 days 00:00:00
# Trades                                   11
Win Rate [%]                        54.545455
Best Trade [%]                     184.068227
Worst Trade [%]                     -8.361804
Avg. Trade [%]                       27.05959
Max. Trade Duration          47 days 00:00:00
Avg. Trade Duration          15 days 00:00:00
Profit Factor                       11.882994
Expectancy [%]                      37.569798
SQN                                  1.745046

The return of this strategy is about 1300%, compare it to buy and hold return of 1.3 million percent. A thousand times less. Moreover in 10 years only 11 trades were executed with a win rate of 55%. Does it look promising? I doubt it. Let’s optimize the parameters for level, and RSI periods.

Optimization Results

The top performing parameters are:

d_rsi  w_rsi  level
10     10     70       2.700518
20     10     70       2.195220
10     15     70       2.154148
20     30     70       2.094387
       25     70       2.093891
       20     70       2.047324
10     20     70       2.036921
30     10     70       2.013026
20     15     70       2.009252
25     10     70       1.996257

So level = 70 was a good idea for RSI. But the RSI periods is another story. Let’s look at the heatmap:

Parabolic trading system for Bitcoin daily data for 10 years, heatmap of parameters daily and weekly RSI

So daily RSI(10), weekly RSI(10) and level = 70 are the optimal parameters. But will they hold if we test them for the last two years?

Backtesting Last Year

The optimized parameters for the 10 year period and the last year period were the same: RSI(10), weekly RSI(10) and level = 70. Here are the results:

Start                     2020-05-02 00:00:00                                                   
End                       2021-05-07 00:00:00
Duration                    370 days 00:00:00
Exposure Time [%]                   21.563342
Equity Final [$]                2016514.22728
Equity Peak [$]                 2560315.38848
Return [%]                         101.651423
Buy & Hold Return [%]              527.557111
Return (Ann.) [%]                   99.377031
Volatility (Ann.) [%]               86.028028
Sharpe Ratio                          1.15517
Sortino Ratio                        3.959251
Calmar Ratio                         4.671619
Max. Drawdown [%]                  -21.272501
Avg. Drawdown [%]                   -5.812147
Max. Drawdown Duration       75 days 00:00:00
Avg. Drawdown Duration       15 days 00:00:00
# Trades                                    4
Win Rate [%]                             75.0
Best Trade [%]                      42.142179
Worst Trade [%]                     -5.304165
Avg. Trade [%]                      19.414756
Max. Trade Duration          28 days 00:00:00
Avg. Trade Duration          19 days 00:00:00
Profit Factor                       16.608972
Expectancy [%]                      20.698138
SQN                                  1.909592

We managed to get one fifth of buy and hold but we were also in the market only one fifth of the time. Here’s how the graph looks like:

The equity plot for the parabolic trading system for 1 year.

Complete Python Code

import sqlite3 as sql
import pandas as pd
import numpy as np
import talib as talib
from datetime import datetime
from datetime import timedelta
from backtesting import Strategy, Backtest
from backtesting.lib import resample_apply
from matplotlib import pyplot as plt
import seaborn as sns

dbfile = 'bitcoin.db'
table = 'bitcoin_daily_raw'
conn = sql.connect(dbfile)

start_date = '2021-05-01'
how_many_months = "-12"
SQL = "SELECT DISTINCT Date,Open,Close,Low,High,Volume_USD FROM " + table + \
" WHERE Date < date('"+ start_date +"') and \
Date > date('"+ start_date+"','start of month','"+how_many_months+" month') ORDER BY Date"

data = pd.read_sql(SQL, conn)
data['Date'] = pd.to_datetime(data['Date'])
data.set_index("Date", inplace = True)
data.columns = ['Open','Close','Low','High','Volume'];

class System(Strategy):
    d_rsi = 30  # Daily RSI lookback periods
    w_rsi = 30  # Weekly
    level = 70
    
    def init(self):
        # Compute moving averages the strategy demands
        self.ma10 = self.I(talib.SMA, self.data.Close, 10)
        self.ma20 = self.I(talib.SMA, self.data.Close, 20)
        self.ma50 = self.I(talib.SMA, self.data.Close, 50)
        self.ma100 = self.I(talib.SMA, self.data.Close, 100)
        
        # Compute daily RSI(30)
        self.daily_rsi = self.I(talib.RSI, self.data.Close, self.d_rsi)
        
        # To construct weekly RSI, we can use `resample_apply()`
        # helper function from the library
        self.weekly_rsi = resample_apply(
            'W-FRI', talib.RSI, self.data.Close, self.w_rsi)
       
    def next(self):
        price = self.data.Close[-1]
        
        # If we don't already have a position, and
        # if all conditions are satisfied, enter long.
        if (not self.position and
            self.daily_rsi[-1] > self.level and
            self.weekly_rsi[-1] > self.level and
            self.weekly_rsi[-1] > self.daily_rsi[-1] and
            self.ma10[-1] > self.ma20[-1] > self.ma50[-1] > self.ma100[-1] and
            price > self.ma10[-1]):
            
            # Buy at market price on next open, but do
            # set 8% fixed stop loss.
            self.buy(sl=.92 * price)
        
        # If the price closes 2% or more below 10-day MA
        # close the position, if any.
        elif price < .98 * self.ma10[-1]:
            self.position.close()

bt = Backtest(data, System, commission=.002, exclusive_orders=True, cash=1000000)
output = bt.run()
print(output)
stats,heatmap = bt.optimize(d_rsi=range(10, 35, 5), w_rsi=range(10, 35, 5), \
level=range(30, 80, 10),return_heatmap=True)
print(stats)
hm = heatmap.groupby(['d_rsi', 'w_rsi', 'level']).mean().unstack()
print(heatmap.sort_values(ascending=False).iloc[:10])
plt.figure(figsize=(12, 10))
sns.heatmap(hm[::-1], cmap='viridis')
plt.savefig('foo.png')
bt.plot()

Again reminding, this is for educational purposes only not investment or trading advice.

References

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.