Creating a Structured Trading Strategy
Creating a Combined Strategy Using the Parabolic SAR & the Hull Moving Average
Combining strategies and indicators is always the right path towards a robust technical or quantitative trading system. In this article, the quest continues towards combining different elements in the hope of finding a reliable system. We will code and discuss the parabolic SAR and the Hull moving average so that they provide meaningful and intuitive signals.
As of now, the trading results will no longer be published due to it being a function of costs, slippage, the nature of the algorithms, risk management, and a plethora of other variables. This can mislead the reader and therefore, I will only provide the functions for the indicators, their signals, and how to calculate returns as well as analyze them. The only spoon-feeding here will be presenting the indicator’s code and how to calculate the performance.
I have just released a new book after the success of the previous book. It features advanced trend following indicators and strategies with a GitHub page dedicated to the continuously updated code. Also, this book features the original colors after having optimized for printing costs. If you feel that this interests you, feel free to visit the below Amazon link, or if you prefer to buy the PDF version, you could contact me on LinkedIn.
Trend Following Strategies in Python: How to Use Indicators to Follow the Trend.
Amazon.com: Trend Following Strategies in Python: How to Use Indicators to Follow the Trend.: 9798756939620: Kaabar…www.amazon.com
The Parabolic Stop-And-Reverse Indicator
The Parabolic stop-and-reverse is an interesting indicator created by Wilder Willes who is also the creator of the famous RSI. This indicator is mostly used as a trailing stop that tracks the trend as it develops but there is no harm in testing it as a trading strategy. It is worth noting that it performs relatively well in steady trends but just as any other indicator, it has its weakness, in this case, ranging markets.
I will refer to a python library called talib from where the user could import the sar function that uses a data frame and calculates the values. Having modified that function, you can refer to the one below (I do not take credit for it as I merely just changed some lines as opposed to the other functions which have been coded by me):
def sar(s, af = 0.02, amax = 0.2):
high, low = s.high, s.low
# Starting values
sig0, xpt0, af0 = True, high[0], af
sar = [low[0] - (high - low).std()]
for i in range(1, len(s)):
sig1, xpt1, af1 = sig0, xpt0, af0
lmin = min(low[i - 1], low[i])
lmax = max(high[i - 1], high[i])
if sig1:
sig0 = low[i] > sar[-1]
xpt0 = max(lmax, xpt1)
else:
sig0 = high[i] >= sar[-1]
xpt0 = min(lmin, xpt1) if sig0 == sig1:
sari = sar[-1] + (xpt1 - sar[-1])*af1
af0 = min(amax, af1 + af)
if sig0:
af0 = af0 if xpt0 > xpt1 else af1
sari = min(sari, lmin)
else:
af0 = af0 if xpt0 < xpt1 else af1
sari = max(sari, lmax)
else:
af0 = af
sari = xpt0sar.append(sari)
return sar
The basic understanding is that when the Parabolic SAR (the dots around the market price) is under the current price, then the outlook is bullish and when it is above the current price, then the outlook is bearish.
To add the Parabolic SAR to your OHLC array (preferably numpy), use the following steps:
importing pandas as pd
# Converting to a pandas Data frame
my_data = pd.DataFrame(my_data)
# Renaming columns to fit the function
my_data.columns = ['open','high','low','close']
# Calculating the Parabolic SAR
Parabolic = sar(my_data, 0.02, 0.2)
# Converting the Parabolic values back to an array
Parabolic = np.array(Parabolic)
# Reshaping
Parabolic = np.reshape(Parabolic, (-1, 1))
# Concatenating with the OHLC Data
my_data = np.concatenate((my_data, Parabolic), axis = 1)
The Hull Moving Average
Moving averages help us confirm and ride the trend. They are the most known technical indicator and this is because of their simplicity and their proven track record of adding value to the analyses. We can use them to find support and resistance levels, stops and targets, and to understand the underlying trend. This versatility makes them an indispensable tool in our trading arsenal.
As the name suggests, this is your plain simple mean that is used everywhere in statistics and basically any other part in our lives. It is simply the total values of the observations divided by the number of observations. Mathematically speaking, it can be written down as:
What interests us to build the Hull Moving Average is the Linear-Weighted Moving Average, it is a simple moving average that places more weight on recent data. The most recent observation has the biggest weight and each one prior to it has a progressively decreasing weight. Intuitively, it has less lag than the other moving averages but it is also the least used, and hence, what it gains in lag reduction, it loses in popularity.
Mathematically speaking, it can be written down as:
Basically, if we have a dataset composed of two numbers [1, 2] and we want to calculate a linear weighted average, then we will do the following:
(2 x 2) + (1 x 1) = 5
5 / 3 = 1.66
This assumes a time series with the number 2 as being the most recent observation.
import numpy as np
def lwma(Data, lookback):
weighted = []
for i in range(len(Data)):
try:
total = np.arange(1, lookback + 1, 1)
matrix = Data[i - lookback + 1: i + 1, 3:4]
matrix = np.ndarray.flatten(matrix)
matrix = total * matrix
wma = (matrix.sum()) / (total.sum())
weighted = np.append(weighted, wma)
except ValueError:
pass
Data = Data[lookback - 1:, ]
weighted = np.reshape(weighted, (-1, 1))
Data = np.concatenate((Data, weighted), axis = 1)
return Data
# For this function to work, you need to have an OHLC array composed of the four usual columns, then you can use the below syntax to get a data array with the weighted moving average using the lookback you need
my_ohlc_data = lwma(my_ohlc_data, 20)
Now that we have understood what a Weighted Moving Average is, we can proceed by presenting the Hull Moving Average, a powerful trend-following early system.
The Hull Moving Average uses the Weighted Moving Average as building blocks and it is calculated following the below steps:
Choose a lookback period such as 20 or 100 and calculate the Weighted Moving Average of the closing price.
Divide the lookback period found in the first step and calculate the Weighted Moving Average of the closing price using this new lookback period. If the number cannot by divided by two, then take the closest number before the comma (e.g. a lookback of 15 can be 7 or 8 as the second lookback).
Multiply the second Weighted Moving Average by two and subtract from it the first Weighted Moving Average.
As a final step, take the square root of the first lookback (e.g. if you have chosen a lookback of 100, then the third lookback period is 10) and calculate the Weighted Moving Average on the latest result we have had in the third step. Be careful not to calculate it on market prices.
Therefore, if we choose a lookback period of 100, we will calculate on 100 lookback period, then on 50, and finally, on 10 applied to the latest result.
Creating the Signals
As with any proper research method, the aim is to test the strategy and to be able to see for ourselves whether it is worth having as an add-on to our pre-existing trading framework or not.
The first step is creating the trading rules. When will the system buy and when will it go short? In other words, when is the signal given that tells the system that the current market will go up or down?
The trading conditions we can choose are:
Long (Buy) the market is above the Parabolic SAR while also being above the current Hull Moving Average. As a last condition, the last closing price must be less than the last Parabolic SAR.
Short (Sell) the market is below the Parabolic SAR whille also being below the current Hull Moving Average. As a last condition, the last closing price must be above than the last Parabolic SAR.
The above chart shows the signals generated from the system. We have to keep in mind the frequency of the signals when we are developing a trading algorithm. The signal function used to generate the triggers based on the conditions mentioned above can be found in this snippet:
def signal(Data, close, psar, hull_ma, buy, sell):
Data = adder(Data, 10)
for i in range(len(Data)):
if Data[i, close] > Data[i, psar] and Data[i, close] > Data[i, hull_ma] and Data[i - 1, close] < Data[i - 1, hull_ma]:
Data[i, buy] = 1
elif Data[i, close] < Data[i, psar] and Data[i, close] < Data[i, hull_ma] and Data[i - 1, close] > Data[i - 1, hull_ma]:
Data[i, sell] = -1
return Data
The above shows the same technique applied onto hourly values of the USDCHF. Without a doubt, lagging indicators will have a tough time detecting the trend at its beginning, which is why we sometimes see some bullish signals around the tops and bearish signals around the bottoms.
Remember to always do your back-tests. You should always believe that other people are wrong. My indicators and style of trading may work for me but maybe not for you.
I am a firm believer of not spoon-feeding. I have learnt by doing and not by copying. You should get the idea, the function, the intuition, the conditions of the strategy, and then elaborate (an even better) one yourself so that you back-test and improve it before deciding to take it live or to eliminate it. My choice of not providing specific Back-testing results should lead the reader to explore more herself the strategy and work on it more.