This post was originally published by Sofien Kaabar at Medium [AI]
Creating an Exponential Smoothing Indicator to Trade the Market in Python.
Smoothing or averaging is a form of noise reduction and traders use to get a clearer picture on the trend or on extreme moves. The Stochastic Oscillator is known to have a signal line called the %D where a simple moving average is applied to the Stochastic formula so that it smoothes it out. In this article, we will create a Stochastic Oscillator entirely made out of exponential moving averages.
If you are interested by market sentiment and how to model the sentiment of institutional traders, feel free to have a look at the below article:
The Concept of Moving Averages
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:
We can see that the moving average is providing decent dynamic support and resistance levels from where we can place our orders in case the market goes down there.
def ma(Data, lookback, what, where):
for i in range(len(Data)):
try:
Data[i, where] = (Data[i – lookback + 1:i + 1, what].mean())
except IndexError:
pass
return Data
Another even more dynamic moving average is the exponential one. Its idea is to give more weight to the more recent values so that it reduces the lag between the price and the average.
Notice how the exponential moving average is closer to prices than the simple one when the trend is strong. This is because it gives more weight to the latest values so that the average does not stay very far.
def ema(Data, alpha, lookback, what, where):
# alpha is the smoothing factor
# window is the lookback period
# what is the column that needs to have its average calculated
# where is where to put the exponential moving average
alpha = alpha / (lookback + 1.0)
beta = 1 – alpha
# First value is a simple SMA
Data = ma(Data, lookback, what, where)
# Calculating first EMA
Data[lookback + 1, where] = (Data[lookback + 1, what] * alpha) + (Data[lookback, where] * beta)
# Calculating the rest of EMA
for i in range(lookback + 2, len(Data)):
try:
Data[i, where] = (Data[i, what] * alpha) + (Data[i - 1, where] * beta)
except IndexError:
pass
return Data
The Stochastic Smoothing Oscillator
First, let us start by the usual Stochastic Oscillator before proceeding with the Stochastic Smoothing Oscillator. The Stochastic Oscillator seeks to find oversold and overbought zones by incorporating the highs and lows using a normalization formula as shown below:
An overbought level is an area where the market is perceived to be extremely bullish and is bound to consolidate. An oversold level is an area where market is perceived to be extremely bearish and is bound to bounce. Hence, the Stochastic Oscillator is a contrarian indicator that seeks to signal reactions of extreme movements.
We will create the below function that calculates the Stochastic on an OHLC data:
def stochastic(Data, lookback, what, high, low, where):
for i in range(len(Data)):
try:
Data[i, where] = (Data[i, what] – min(Data[i – lookback + 1:i + 1, low])) / (max(Data[i – lookback + 1:i + 1, high]) – min(Data[i – lookback + 1:i + 1, low]))
except ValueError:
pass
Data[:, where] = Data[:, where] * 100 return Data
# The Data variable refers to the OHLC array
# The lookback variable refers to the period (5, 14, 21, etc.)
# The what variable refers to the closing price
# The high variable refers to the high price
# The low variable refers to the low price
# The where variable refers to where to put the Oscillator
The above plot shows the EURUSD values plotted with a 14-period Stochastic Oscillator. Notice that the indicator will always be bounded between 0 and 100 due to the nature of its normalization function that traps values between the minimum and the maximum.
Now, to get the Stochastic Smoothing Oscillator, we can follow the below steps:
- Calculate a 14-period exponential moving average on the highs.
- Calculate a 14-period exponential moving average on the lows.
- Calculate a 14-period exponential moving average on the close.
- Apply the Stochastic formula on the above results.
def stochastic_smoothing_oscillator(Data, high, low, close, lookback, where):
Data = ema(Data, 2, lookback, high, where)
Data = ema(Data, 2, lookback, low, where + 1)
Data = ema(Data, 2, lookback, close, where + 2)
for i in range(len(Data)):
try:
Data[i, where + 3] = (Data[i, where + 2] – min(Data[i – lookback + 1:i + 1, where + 1])) / (max(Data[i – lookback + 1:i + 1, where]) – min(Data[i – lookback + 1:i + 1, where + 1]))
except ValueError:
pass
Data[:, where + 3] = Data[:, where + 3] * 100
Data = deleter(Data, where, 3)
Data = jump(Data, lookback)
return Data
For the above function to work, we need an OHLC array containing a few spare columns and the deleter/jump functions. Here is how to do that:
# To add a number of columns
def adder(Data, times):
for i in range(1, times + 1):
z = np.zeros((len(Data), 1), dtype = float)
Data = np.append(Data, z, axis = 1) return Data
# To delete a number of columns starting from an index
def deleter(Data, index, times):
for i in range(1, times + 1):
Data = np.delete(Data, index, axis = 1) return Data
# To delete a number of rows
def jump(Data, jump):
Data = Data[jump:, ]
return Data
Strategy Back-test
Now, it is time to back-test the usual barriers strategy. The following conditions apply:
- Go long (Buy) whenever the Stochastic Smoothing Oscillator reaches 25 with the two previous values above 25. Hold this position until getting a new signal or getting stopped out by the risk management system.
- Go short (Sell) whenever the Stochastic Smoothing Oscillator reaches 75 with the two previous values below 75. Hold this position until getting a new signal or getting stopped out by the risk management system.
I will switch the lookback to 5 so that we have more signals.
def signal(Data, what, buy, sell):
for i in range(len(Data)):
if Data[i, what] < lower_barrier and Data[i – 1, what] > lower_barrier and Data[i – 2, what] > lower_barrier :
Data[i, buy] = 1
if Data[i, what] > upper_barrier and Data[i – 1, what] < upper_barrier and Data[i – 2, what] < upper_barrier :
Data[i, sell] = -1
The results are based on M15 price bars with a spread of 0.2 per round trade using a 0.25 theoretical risk reward ratio from the Average True Range Indicator discussed below.
A Word on Risk Management
When I say I use ATR-based risk management system (Average True Range), it means that the algorithm will do the following steps with regards to the position it takes.
A long (Buy) position:
- The algorithm initiates a buy order after a signal has been generated following a certain strategy.
- Then, the algorithm will monitor the ticks and whenever the high equals a certain constant multiplied by ATR value at the time of the trade inception, an exit (at profit) order is initiated. Simultaneously, if a low equals a certain constant multiplied by ATR value at the time of the trade inception is seen, an exit (at loss) is initiated. The exit encountered first is naturally the taken event.
A short (Sell) position:
- The algorithm initiates a short sell order after a signal has been generated following a certain strategy.
- Then, the algorithm will monitor the ticks and whenever the low equals a certain constant multiplied by ATR value at the time of the trade inception, an exit (at profit) order is initiated. Simultaneously, if a high equals a certain constant multiplied by ATR value at the time of the trade inception is seen, an exit (at loss) is initiated. The exit encountered first is naturally the taken event.
The plot above shows the Average True Range I generally use. It is based on an exponential moving average as opposed to the original smoothed moving average.
Take a look at the latest value on the ATR. It is around 0.0014 (14 pips). If we initiate a buy order following a simple 2.00 risk-reward ratio (risking half of what we expect to gain), we can place an order this way:
- Buy at current market price.
- Take profit at current market price + (2 x 14 pips).
- Stop the position at current market price — (1 x 14 pips).
Conclusion
Why was this article written? It is certainly not a spoon-feeding method or the way to a profitable strategy. If you follow my articles, you will notice that I place more emphasize on how to do it instead of here it is and that I also provide functions not full replicable code. In the financial industry, you should combine the pieces yourself from other exogenous information and data, only then, will you master the art of research and trading.
I always advise you to do the proper back-tests and understand any risks relating to trading. For example, the above results are not very indicative as the spread we have used is very competitive and may be considered hard to constantly obtain in the retail trading world (but not impossible). However, with institutional bid/ask spreads, it may be possible to lower the costs such as that a systematic medium-frequency strategy starts being very profitable.
This post was originally published by Sofien Kaabar at Medium [AI]