5

I know that it is easy to calculate simple moving average using SQL Server 2012 window functions and OVER() clause. But how can I calculate exponential moving average using this approach? Thanks!

sashkello
  • 17,306
  • 24
  • 81
  • 109
xiagao1982
  • 1,077
  • 1
  • 13
  • 25
  • Please try it yourself before asking. Here is a good discussion which can be of use: http://stackoverflow.com/questions/8871426/how-to-calculate-an-exponential-moving-average-on-postgres – sashkello Apr 14 '13 at 02:23
  • I have read the discussion you mentioned. It is applicable to PostgreSQL since it is allowed to create user-defined aggregate function using SQL in PostgreSQL, But not allowed in SQL Server. Using recursive CTE is a feasible way in SQL Server, but I notice that CTE way may incur more table scan than window functions. So I make this post to ask if it is possible to calculate exponential moving average using SQL Server 2012 window function just like calculating simple moving average. – xiagao1982 Apr 14 '13 at 02:53
  • @xiagao1982 Did you get this working? Would be interested to see an example. – D'Arcy Rittich May 01 '13 at 17:37

2 Answers2

10

The formula for EMA(x) is:

EMA(x1) = x1
EMA(xn) = α * xn + (1 - α) * EMA(xn-1)

With

β := 1 - α
that is equivalent to
EMA(xn) = βn-1 * x1 + α * βn-2 * x2 + α * βn-3 * x3 + ... + α * xn

In that form it is easy to implement with LAG. For a 4 row EMA it would look like this:

SELECT LAG(x,3)OVER(ORDER BY ?) * POWER(@beta,3) + 
       LAG(x,2)OVER(ORDER BY ?) * POWER(@beta,2) * @alpha + 
       LAG(x,1)OVER(ORDER BY ?) * POWER(@beta,1) * @alpha + 
       x * @alpha
FROM ...

OK, as you seem to be after the EWMA_Chart I created a SQL Fiddle showing how to get there. However, be aware that it is using a recursive CTE that requires one recursion per row returned. So on a big dataset you will most likely get disastrous performance. The recursion is necessary as each row depends on all rows that happened before. While you could get all preceding rows with LAG() you cannot also reference preceding calculations as LAG() cannot reference itself.

Also, the formular in the spreadsheet you attached below does not make sense. It seems to be trying to calculate the EWMA_Chart value but it is failing at that. In the above SQLFiddle I included a column [Wrong] that calculates the same value that the spreadsheet is calculating.

Either way, if you need to use this on a big dataset, you are probably better of writing a cursor.

This is the code that does the calculation in above SQLFiddle. it references th vSMA view that calculates the 10 row moving average.

WITH

smooth AS(
  SELECT CAST(0.1818 AS NUMERIC(20,5)) AS alpha
),

numbered AS(
  SELECT Date, Price, SMA, ROW_NUMBER()OVER(ORDER BY Date) Rn
  FROM vSMA
  WHERE SMA IS NOT NULL
),

EWMA AS(
  SELECT Date, Price, SMA, CAST(SMA AS NUMERIC(20,5)) AS EWMA, Rn
  , CAST(SMA AS NUMERIC(20,5)) AS Wrong
  FROM numbered
  WHERE Rn = 1
  UNION ALL
  SELECT numbered.Date, numbered.Price, numbered.SMA, 
    CAST(EWMA.EWMA * smooth.alpha + CAST(numbered.SMA AS NUMERIC(20,5)) * (1 - smooth.alpha) AS NUMERIC(20,5)), 
    numbered.Rn
    , CAST((numbered.Price - EWMA.EWMA) * smooth.alpha + EWMA.EWMA AS NUMERIC(20,5))
  FROM EWMA
  JOIN numbered
  ON EWMA.rn + 1 = numbered.rn
  CROSS JOIN smooth
)
SELECT Date, Price, SMA, EWMA
, Wrong
FROM EWMA

ORDER BY Date;
Sebastian Meine
  • 11,260
  • 29
  • 41
  • I have attempted to implement this query with [this SqlFiddle](http://sqlfiddle.com/#!6/d736e/1). The numbers seem way off compared to the Excel calculations on the same data here: http://stockcharts.com/school/data/media/chart_school/technical_indicators_and_overlays/moving_averages/cs-movavg.xls Can you see where I have gone wrong? – D'Arcy Rittich May 01 '13 at 03:29
  • 1
    First, you calculate the EMA(SMA(x)) instead of the EMA(x). Second, your "smoothing constant" is actually the beta value in my formula, not the alpha. With those two changes the SQLFiddle looks like this: http://sqlfiddle.com/#!6/19192/1 However, there is still a little difference between the actual result and the expected result. I would go back and see if their EMA definition matches the one I know. – Sebastian Meine May 07 '13 at 13:46
  • I just looked at the formular in the spreadsheet you attached and it is way off the standard EMA definition. My formula calculates the exponential moving average of the last ten rows. The spreadsheet first calculates the standard average over the last ten rows and then the unrestricted exponentially weighted moving average over all averages. This follows the formular here: http://en.wikipedia.org/wiki/EWMA_chart – Sebastian Meine May 07 '13 at 13:52
  • +1 Thanks for the reply - it completely escaped me that their formula could be non-standard. Your edits are above much appreciated! – D'Arcy Rittich May 07 '13 at 15:19
  • 1
    @RedFilter I looked at the fiddle, you used .8181 but the comment says .1818 so that might be why numbers were off. – Rich Bianco Nov 02 '19 at 21:58
  • i dont think this query works, cause as u said, u need recursive lag, basically a window over few rows doesnt quite cut it – Andrew Rebane Feb 19 '22 at 18:34
0

Just tried to do an SQL EMA 9 period function.

Alpha: 2 ( 9 + 1) = 0.2 Beta: 1 - Alpha = 0.8

The SQL Query I used was:

SELECT
LAG(ClosePrice,8)OVER(ORDER BY QuoteTime DESC) * POWER(0.8,8) + 
LAG(ClosePrice,7)OVER(ORDER BY QuoteTime DESC) * POWER(0.8,7) * 0.2 +
LAG(ClosePrice,6)OVER(ORDER BY QuoteTime DESC) * POWER(0.8,6) * 0.2 +
LAG(ClosePrice,5)OVER(ORDER BY QuoteTime DESC) * POWER(0.8,5) * 0.2 +
LAG(ClosePrice,4)OVER(ORDER BY QuoteTime DESC) * POWER(0.8,4) * 0.2 +
LAG(ClosePrice,3)OVER(ORDER BY QuoteTime DESC) * POWER(0.8,3) * 0.2 +
LAG(ClosePrice,2)OVER(ORDER BY QuoteTime DESC) * POWER(0.8,2) * 0.2 +
LAG(ClosePrice,1)OVER(ORDER BY QuoteTime DESC) * POWER(0.8,1) * 0.2 + ClosePrice * 0.2 from StreamingStockQuotes

It returns a value of 185.3352.

My dataset (recent to oldest): 181.68, 181.77, 181.98, 182.06, 181.72, 181.74, 181.63, 181.63, 181.59.

It seems that 185, is not possible since it is higher than any of the dataset.