Skip to content
All library documents

Computing a Linear Weighted Moving Average Efficiently in Python

Article Quant Q&A · Author: user89135

Summary

The document examines a linear weighted moving average in which more recent closing prices receive larger weights. The original function computes each window with a loop and divides the weighted sum by the sum of the weights. The answer confirms that this calculation is mathematically correct for the stated weighting scheme, then recommends applying a dot product to rolling windows with pandas and NumPy.

The suggested approach uses rolling-window application and can return the resulting values or attach them as a dataframe column. The example also makes the input column and window length configurable. The reported runtime comparison is specific to the answerer’s machine and sample, so it should not be treated as a general performance guarantee. The discussion covers calculation and implementation speed, but not how to use the indicator in a trading strategy or evaluate its predictive value.

Key ideas

  • A linear weighted moving average assigns progressively larger weights to later observations in each window.
  • The original loop computes the weighted average using weights normalized by their sum.
  • A rolling window and vector dot product provide a concise alternative implementation.
  • Performance comparisons depend on the machine, data, and implementation details.
  • The document does not evaluate the moving average as a trading signal.

Tags

Full text
# Calculating a Linear Weighted Moving Average in Python


# Calculating a Linear Weighted Moving Average in Python












Usually called WMA. The weighting is linear (as opposed to exponential) defined here: Moving Average, Weighted. I attempt to implement this in a python function as show below. The result is a list of values. My question is: are the result right? Also it is very slow...

I input a dataframe from pandas with a column called 'close'

```
def wma(df):
    n = 20
    k = (n * (n + 1)) / 2.0
    wmas = []
    for i in range(0, len(df) - n + 1):
        product = [df['close'][i + n_i] * (n_i + 1) for n_i in range(0, n)]
        wma = sum(product) / k
        wmas.append(wma)
    return wmas
```

Any help would be appreciated. Thanks.

## Answer by amdopt (score 3, accepted)

https://quant.stackexchange.com/a/57730

Though your code is already giving you the correct result, I almost feel bad for you that you have to wait 5 seconds for such a small amount of data. Your code is slow because you are kind of reinventing the wheel instead of using some built-in pandas and numpy functionality. For example, `product` and `wma` in your code can be combined and accomplished using numpy's dot product function (`np.dot`) that is applied to the whole column in a rolling fashion with an anonymous function by chaining pandas `.rolling()` and `.apply()` methods. It is always better to look for ready-made solutions becuase the functions are optimized behind the scenes. I ran your code on my machine, and the results take about 2 seconds for 5200 values. Try something like this (I added some basic functionality as an example to get you thinking):

```
import pandas as pd
import numpy as np

def wma(df, column='close', n=20, add_col=False):

    weights = np.arange(1, n + 1)
    wmas = df[column].rolling(n).apply(lambda x: np.dot(x, weights) /
                                       weights.sum(), raw=True).to_list()

    if add_col == True:
        df[f'{column}_WMA_{n}'] = wmas
        return df
    else:
        return wmas
```

The above function will take the same dataframe you were using and return the same list the same way you had it--just call `wma(df)`. In addition can change the column name, the period value, and you can opt to not return a list but add the values as a new column to the dataframe that you originally passed in. It also runs on my machine in about 20 milliseconds--almost 100x faster than your original code...

Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.