Simple 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
- Main strength: Followed heavily by traders across the world thus benefitting from the fact that reactions are more likely to occur than on other moving averages.
- Main weakness: Lagging and slow to react to quick moves.
Exponential Moving Averages
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. To code a function in Python that outputs this type of average, you can use the below snippet:
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
- Main strength: Quick to react to sudden moves and followed by traders across the world even though a little less than the Simple Moving Averages.
- Main weakness: As it takes into account the latest data more than the previous ones, it may miss the overall trend
Smoothed Moving Averages
This moving average takes into account the general picture and is less impacted by recent movements. It’s my favourite trend-following indicator. Mathematically speaking, it can be found by simply multiplying the Days variable in the EMA function by 2 and subtract 1. This means that to transform an exponential moving average into a smoothed one, we follow this equation in python language, that transforms the exponential moving average into a smoothed one:
smoothed = (exponential * 2) - 1 # From exponential to smoothed
- Main strength: Works well for dynamic support and resistance levels. Also, a component of the well known Relative Strength Index.
- Main weakness: Less followed by traders and slow to react to sudden changes.
Linear-Weighted Moving Averages
Also referred to as a linear-weighted moving average, the weighted moving average 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 npdef 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 needmy_ohlc_data = lwma(my_ohlc_data, 20)
- Main strength: Quick to react as it takes into account the latest values.
- Main weakness: Not very followed and not very efficient in detecting support and resistance levels.
Triangular Moving Averages
What is called a Triangular Moving Average is simply the moving average of the moving average itself and this is to provide an extra layer of smoothing that can act as a dynamic support or resistance level. Surely it is not as reactive as the first moving average but in stable times, it can provide excellent reactionary levels.
Notice how in the above plot of NZDUSD, the Triangular Moving Average in purple is provide better trading levels. The market seems to prefer this moving average more than the first one (100-period).
The disadvantage of the Triangular Moving Average is that sometimes it tends to be far away from the market price when there is a strong trend. This situation makes it less useful to determine a close support or resistance level. The seen in the plot below on the USDCAD with the first bearish trend. Notice how the simple 100-period moving average is close to the market price and can provide dynamic reactionary levels but the Triangular Moving Average is quite far.
To code the Triangular Moving Average, we simple write the ma function again but apply it on the moving average column.
# Consider my_data is the OHLC data array with two extra free columns my_data = ma(my_data, lookback, closing_price, where_to_put_ma) my_data = ma(my_data, lookback, where_to_put_ma, where_to_put_tma)# the variable where_to_put_tma refers to the index of the column where you want to put the Triangular Moving Average
- Main strength: Acts relatively well as dynamic support and resistance levels.
- Main weakness: Super slow to react as it an average over an average.
Fibonacci Moving Averages
The sequence follows this distinct pattern:

The numbers are found by adding the previous two numbers behind them. In the case of 13, it is calculated as 8 + 5, hence the formula is:

This gives us the intuition to code it using the following code:
def Fibonacci(n):
if n == 1:
return 1
elif n == 0:
return 0
else:
return Fibonacci(n - 1) + Fibonacci(n - 2)
The below function gives us the Fibonacci number according to its index. The index is simply the order of the numbers seen in the table below.
Now, if we use the function and look at its results, we will understand better.
fib(14) # Output: 377 fib(5) # Output: 5
Now, from the exponential moving average seen above and the Fibonacci sequence, we will create the new indicator, the Fibonacci Moving Average.
The Fibonacci Moving Average is an equally weighted exponential moving average using the lookbacks of selected Fibonacci numbers. Here is what I mean step by step:
- We calculate exponential moving averages using the following lookbacks {2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597}.
- We divide the sum of the exponential moving averages by their number. In our case, we will divide by 15.
- Plot the Fibonacci Moving Average alongside the market price.
It is clear that the FMA shows better reactionary levels especially when prices dip. The way to trade the FMA is simply by initiating a contrarian trade when markets approach it. For example, if the market is dropping and heading towards the FMA, we can think about a bullish reaction that could happen around that zone.
def fibonnaci_moving_average(Data, fib_range, what, where): for i in range(3, fib_range): Data = adder(Data, 1) lookback = fib(i) Data = ema(Data, 2, lookback, what, -1) Data = adder(Data, 1) for i in range(len(Data)): Data[i, -1] = np.sum(Data[i, where:where + 15]) Data[i, -1] = Data[i, - 1] / 15 return Data
- Main strength: Acts extremely well as a dynamic support and resistance level.
- Main weakness: Terrible in choppy markets and a little slow to react.
Hull Moving Average
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.
def hull_moving_average(Data, what, lookback, where): Data = lwma(Data, lookback, what) second_lookback = round((lookback / 2), 1) second_lookback = int(second_lookback) Data = lwma(Data, second_lookback, what) Data = adder(Data, 1) Data[:, where + 2] = ((2 * Data[:, where + 1]) - Data[:, where]) third_lookback = round(np.sqrt(lookback), 1) third_lookback = int(third_lookback) Data = lwma(Data, third_lookback, where + 2) return Data
Why do we say that the Hull Moving Average reduces lag? The answer comes mainly from the building blocks which are the weighted moving averages. They place more weights on more recent values. Furthermore, the lag is also reduced by offsetting one weighted moving average with the one that has half the lookback period. Finally, we take the square root of the lookback period and apply it on the moving average itself using the weighting method. This gives us a moving average close to the market price and serves as an early trend reversal indicator.
There is a special trick I like to use to add a visual component and helps to know whether the moving average is tilting to the upside or to the downside. Notice that in the chart above, when the moving average is pointing upwards, it is painted in green and when it is pointing downwards, it is painted in red. The way to do this is to simply put the following conditions:
- When the current moving average is greater than the one preceding it, then the next column should equal to the current moving average. Similarly, when the current moving average is less than the one preceding it, then the next column should equal to the current moving average divided by zero so that it gets an “inf” value in the array. This is so that when it is plotted on the chart, the “inf” values do not appear thus leaving the place for other values to be filled.
- When the current moving average is less than the one preceding it, then the next column should equal to the current moving average. Similarly, when the current moving average is greater than the one preceding it, then the next column should equal to the current moving average divided by zero so that it gets an “inf” value in the array.
The plotting function will be used on both new columns and we will choose our colors accordingly, giving us one moving average plot stemming from two columns, here is how to do this in Python:
my_data[:, new_column1] = my_data[:, hull_average] my_data[:, new_column2] = my_data[:, hull_average]for i in range(len(my_data)): if my_data[i, hull_average] > my_data[i - 1, hull_average]: my_data[i, new_column1] = my_data[i, hull_average] my_data[i, new_column2] = my_data[i, hull_average] / 0 if my_data[i, hull_average] < my_data[i - 1, hull_average]: my_data[i, new_column2] = my_data[i, hull_average] my_data[i, new_column1] = my_data[i, hull_average] / 0 plt.plot(my_data[-1000:, new_column1], color = 'green') plt.plot(my_data[-1000:, new_column2], color = 'red')
You can use this trick for pretty much any type of data.
- Main strength: Reacts well to moves due to its dynamic nature. Crossover strategies work well using it.
- Main weakness: Does not provide good dynamic support and resistance levels.
Kaufman’s Adaptive Moving Average
The KAMA has been created to reduce the noise and whipsaw effects. It works the same as other moving averages do and follows the same intuition. The generation of false trading signals is one of the problems with moving averages and this is due to short-term sudden fluctuations that bias the calculation. The KAMA’s primary objective is to reduce as much noise as possible.
The first concept we should measure is the Efficiency Ratio which is the absolute change of the current close relative to the change in the past 10 periods divided by a type of volatility calculated in a special manner. We can say that the Efficiency Ratio is the change divided by volatility.

Then we calculate a smoothing constant based on the following formula:

Finally, to calculate the KAMA we use the below formula:

The calculation may seem complicated but it is easily automated and you do not have to think about it much. Let us see how to code it in Python and then proceed by seeing some examples.
def kama(Data, what, where, change):
# Change from previous period
for i in range(len(Data)):
Data[i, where] = abs(Data[i, what] - Data[i - 1, what])
Data[0, where] = 0
# Sum of changes
for i in range(len(Data)):
Data[i, where + 1] = (Data[i - change + 1:i + 1, where].sum())
# Volatility
for i in range(len(Data)):
Data[i, where + 2] = abs(Data[i, 3] - Data[i - 10, 3])
Data = Data[11:, ]
# Efficiency Ratio
Data[:, where + 3] = Data[:, where + 2] / Data[:, where + 1]
for i in range(len(Data)):
Data[i, where + 4] = np.square(Data[i, where + 3] * 0.6666666666666666667)
for i in range(len(Data)):
Data[i, where + 5] = Data[i - 1, where + 5] + (Data[i, where + 4] * (Data[i, 3] - Data[i - 1, where + 5]))
Data[11, where + 5] = 0
The below chart illustrates what the above function can give us. Notice the more stable tendency of the KAMA relative to other moving averages. It is an improvement with regards to whipsaws and false breaks.
- Main strength: Highly reactive to moves and adapted to fast markets.
- Main weakness: Less efficient in detecting support and resistance levels.
Conclusion
It can be seen that there are a lot of types of moving averages and our job is to choose the ones that fits us the most as there is not really one that beats the others on all different spectrums. My personal preference is for a long-term simple moving average and for the default Fibonacci moving average but I will leave it to you find your preferred one.