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:

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:

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.