Calculate moving average - SQL
A moving average is a calculation using a subset of a larger set that is index centered and bounded by a N adjacent values.
For example say you have the following values
date_time | stock_price |
---|---|
2023-08-08 | 13 |
2023-08-09 | 18 |
2023-08-10 | 10 |
2023-08-11 | 10 |
2023-08-12 | 8 |
2023-08-13 | 2 |
2023-08-14 | 6 |
2023-08-15 | 16 |
2023-08-16 | 15 |
2023-08-17 | 12 |
Now you would like to calculate the moving average for a every day on the past week (7 days) to get this. Volatility is the measure of the last element in the partition minus the first element of the partition as if the price is 13 today but 7 days ago it was 1 that means a positive volatility of 12
date_time | stock_price | moving_average | volatility |
---|---|---|---|
2023-08-08 | 13 | 6 | 12 |
2023-08-09 | 18 | 7.5 | 1 |
2023-08-10 | 10 | 5.6666666666666675.666 | 3 |
2023-08-11 | 10 | 5.5 | 13 |
2023-08-12 | 8 | 7.4 | 4 |
2023-08-13 | 2 | 7.16666666666667 | 19 |
2023-08-14 | 6 | 8.85714285714286 | 8 |
2023-08-15 | 16 | 8.57142857142857 | -3 |
2023-08-16 | 15 | 8.85714285714286 | -2 |
2023-08-17 | 12 | 11.2857142857143 | -9 |
How to mimic this with SQL only
Here I am using sqlite to generate 365 days fake data on a daily stock price that will range between 0 and 20 randomly.
SELECT date_time,
stock_price,
AVG(stock_price) OVER (ORDER BY date_time ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS moving_average,
last_value(stock_price) OVER (ORDER BY date_time ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) -
first_value(stock_price) OVER (ORDER BY date_time ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) volatility
FROM (WITH RECURSIVE cnt(x) AS
(SELECT 1
UNION ALL
SELECT x + 1
FROM cnt
LIMIT 365)
SELECT date(strftime('%Y-%m-%d', 'now'), '-' || x || ' days') as date_time, (abs(random() % 20)) as stock_price
FROM cnt) as stocks;
Breaking it apart I use a recursive Common Table Expression to generate 365 random days of stock price from today backwards
WITH RECURSIVE cnt(x) AS
(
SELECT 1
UNION ALL
SELECT x + 1
FROM cnt
LIMIT 365
)
SELECT date(strftime('%Y-%m-%d', 'now'), '-' || x || ' days') as date_time, (abs(random() % 20)) as stock_price
FROM cnt
Here use the sqlite function of strftime with a parameter of now
and formatted to an ISO Date. Then use the random() function mode by 20 (it will +/- 20) and take the absolute value of that to make sure all the stock prices are positive.
Compute the windowed average by using the OVER function and specifying the rows between using PRECEDING and CURRENT ROW and send that data into the AVG function
AVG(stock_price) OVER (ORDER BY date_time ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS moving_average
Then for the volatility use the LAST_VALUE and FIRST_VALUE functions to grab the data points of the window
last_value(stock_price) OVER (ORDER BY date_time ROWS BETWEEN 6 PRECEDING AND CURRENT ROW)
first_value(stock_price) OVER (ORDER BY date_time ROWS BETWEEN 6 PRECEDING AND CURRENT ROW)
👍