Reputation: 783
Trying to get the moving average on a per X months basis using SQLite. Problem is i cannot seem to figure or find anything remotely useful on how to aggregate with a X month(s) lookback period and thus create a moving average.
TableCREATE TABLE "nav" (
`id` TEXT,
`nav` NUMERIC,
`date` TEXT
);
Sample data
id nav date
1380 15.3 2005-01-09
1380 15.4 2005-01-16
1380 15.5 2005-01-23
1380 15.55 2005-01-30
1380 15.66 2005-02-06
1380 15.45 2005-02-13
1380 15.26 2005-02-20
1380 15.14 2005-02-27
1380 14.95 2005-03-06
1380 15.24 2005-03-13
1380 15.6 2005-03-20
1380 15.7 2005-03-27
1380 15.86 2005-04-03
1380 15.78 2005-04-10
1380 15.58 2005-04-17
1380 15.84 2005-04-24
1380 15.88 2005-05-01
1380 16.2 2005-05-08
1380 16.29 2005-05-15
1380 16.32 2005-05-22
1380 16.59 2005-05-29
1380 16.96 2005-06-05
1380 16.76 2005-06-12
1380 17.32 2005-06-19
1380 17.63 2005-06-26
1380 17.6 2005-07-03
1380 17.36 2005-07-10
1380 17.36 2005-07-17
1380 17.27 2005-07-24
1380 16.86 2005-07-31
1380 16.68 2005-08-07
1380 17.21 2005-08-14
1380 16.87 2005-08-21
1380 16.52 2005-08-28
1380 16.77 2005-09-04
1380 17.1 2005-09-11
1380 17.37 2005-09-18
1380 17.29 2005-09-25
1380 17.24 2005-10-02
1380 17.52 2005-10-09
1380 17.72 2005-10-16
1380 17.65 2005-10-23
1380 18.18 2005-10-30
1380 18.31 2005-11-06
1380 18.42 2005-11-13
1380 18.13 2005-11-20
1380 18.12 2005-11-27
1380 17.92 2005-12-04
1380 17.74 2005-12-11
1380 17.93 2005-12-18
1380 17.94 2005-12-25
1380 17.39 2006-01-01
1380 17.4 2006-01-08
1380 17.37 2006-01-15
1380 17.22 2006-01-22
1380 17.47 2006-01-29
1380 17.53 2006-02-05
1380 17.77 2006-02-12
1380 17.95 2006-02-19
1380 17.85 2006-02-26
1380 17.88 2006-03-05
1380 17.42 2006-03-12
1380 17.71 2006-03-19
1380 17.71 2006-03-26
1380 17.53 2006-04-02
1380 17.54 2006-04-09
1380 17.21 2006-04-16
1380 16.84 2006-04-23
1380 16.68 2006-04-30
1380 16.59 2006-05-07
1380 16.82 2006-05-14
1380 16.72 2006-05-21
1380 16.34 2006-05-28
1380 16.72 2006-06-04
1380 16.83 2006-06-11
1380 16.84 2006-06-18
1380 16.54 2006-06-25
1380 16.41 2006-07-02
1380 16.76 2006-07-09
1380 16.77 2006-07-16
1380 16.69 2006-07-23
1380 16.48 2006-07-30
1380 16.65 2006-08-06
1380 16.61 2006-08-13
1380 16.78 2006-08-20
1380 16.88 2006-08-27
1380 17.03 2006-09-03
1380 16.9 2006-09-10
1380 16.85 2006-09-17
1380 17.04 2006-09-24
1380 17.1 2006-10-01
1380 17.28 2006-10-08
1380 17.02 2006-10-15
1380 16.91 2006-10-22
1380 16.85 2006-10-29
1380 16.53 2006-11-05
1380 16.53 2006-11-12
1380 16.17 2006-11-19
1380 15.87 2006-11-26
1380 15.94 2006-12-03
1380 16.22 2006-12-10
1380 16.07 2006-12-17
1380 16.09 2006-12-24
1380 16.42 2006-12-31
1380 16.54 2007-01-07
1380 16.57 2007-01-14
1380 16.61 2007-01-21
1380 16.5 2007-01-28
1380 16.53 2007-02-04
1380 16.67 2007-02-11
1380 16.73 2007-02-18
1380 16.71 2007-02-25
1380 16.81 2007-03-04
1380 16.54 2007-03-11
1380 16.59 2007-03-18
1380 16.67 2007-03-25
1380 16.42 2007-04-01
1380 16.35 2007-04-08
1380 16.11 2007-04-15
1380 15.96 2007-04-22
1380 16.1 2007-04-29
1380 16.3 2007-05-06
1380 16.33 2007-05-13
1380 16.35 2007-05-20
1380 16.61 2007-05-27
1380 16.62 2007-06-03
1380 16.92 2007-06-10
1380 16.57 2007-06-17
1380 16.43 2007-06-24
1380 16.17 2007-07-01
1380 15.95 2007-07-08
1380 15.97 2007-07-15
1380 16.23 2007-07-22
1380 16.17 2007-07-29
1380 16.36 2007-08-05
1380 16.68 2007-08-12
1380 16.61 2007-08-19
1380 16.57 2007-08-26
1380 16.4 2007-09-02
1380 16.16 2007-09-09
1380 15.83 2007-09-16
1380 15.71 2007-09-23
1380 15.73 2007-09-30
1380 15.6 2007-10-07
1380 15.61 2007-10-14
1380 15.55 2007-10-21
1380 15.57 2007-10-28
1380 15.44 2007-11-04
1380 15.47 2007-11-11
1380 15.4 2007-11-18
1380 15.52 2007-11-25
1380 15.7 2007-12-02
1380 15.96 2007-12-09
1380 16.1 2007-12-16
1380 15.78 2007-12-23
1380 15.55 2007-12-30
1380 15.6 2008-01-06
1380 15.82 2008-01-13
1380 15.83 2008-01-20
1380 15.68 2008-01-27
1380 16.01 2008-02-03
1380 15.61 2008-02-10
1380 15.46 2008-02-17
1380 15.2 2008-02-24
1380 15.1 2008-03-02
1380 14.89 2008-03-09
1380 15.02 2008-03-16
1380 14.69 2008-03-23
1380 14.69 2008-03-30
1380 14.67 2008-04-06
1380 14.75 2008-04-13
1380 14.75 2008-04-20
1380 15 2008-04-27
1380 14.88 2008-05-04
1380 14.87 2008-05-11
1380 14.6 2008-05-18
1380 14.88 2008-05-25
1380 15 2008-06-01
1380 15.07 2008-06-08
1380 15 2008-06-15
1380 14.81 2008-06-22
1380 14.84 2008-06-29
1380 14.74 2008-07-06
1380 14.82 2008-07-13
1380 14.98 2008-07-20
1380 15.06 2008-07-27
1380 15.52 2008-08-03
1380 15.64 2008-08-10
1380 15.69 2008-08-17
1380 15.95 2008-08-24
1380 16.44 2008-08-31
1380 16.77 2008-09-07
1380 16.48 2008-09-14
1380 16.44 2008-09-21
1380 17.49 2008-09-28
1380 17.66 2008-10-05
1380 18.44 2008-10-12
1380 19.42 2008-10-19
1380 19.34 2008-10-26
1380 19.62 2008-11-02
1380 19.57 2008-11-09
1380 20.77 2008-11-16
1380 20.18 2008-11-23
1380 20.97 2008-11-30
1380 19.96 2008-12-07
1380 19.4 2008-12-14
1380 19.6 2008-12-21
1380 19.46 2008-12-28
1380 19.75 2009-01-04
1380 20.31 2009-01-11
1380 20.78 2009-01-18
1380 20.56 2009-01-25
Basically i would want... (fake data)
id nav date average_6m
1380 15.6 2008-01 14.1
1380 15.61 2008-02 14.12
1380 14.69 2008-03 14.2
1380 15 2008-04 14.5
1380 14.88 2008-05 14.4
1380 14.84 2008-06 14.3
1380 15.06 2008-07 14.6
1380 16.44 2008-08 15.2
1380 17.49 2008-09 15.9
1380 19.34 2008-10 16.4
1380 20.97 2008-11 18.4
1380 19.46 2008-12 18.2
1380 20.56 2009-01 18.4
date
isn't on a per-day structure, some intervalls even doesn't have all the work days in each month so have to use MAX() day on each month in order to get last value etcnull
Upvotes: 0
Views: 791
Reputation: 783
The "correct" answer to this seems to be "don't".
Since i'm using sqlite + php i figured it was simpler and more perfomant to do this with PHP instead, so i created a simple helper function for it:
/**
* Moving average calculations
* @param $arr: array with princing data
* @param $ma: moving average; no. of rows
* @param $nav: key for pricing data
*/
function sma( $arr, $ma=6, $nav='nav' ){
foreach( $arr as $key => $val ){
$avg = 0;
$average = 0;
// check if look-back period, else set 0
if( $key-$ma > -2 ) {
// create lookback period
$range = range(0,$ma-1);
foreach($range as $r){
$tmp = $key - $r;
$avg = $avg + $arr[$tmp][$nav];
}
// round with 2 decimal point
$average = round( ($avg/$ma) * 100 ) / 100;
}
// add to current key
$newkey = 'sma'.$ma;
$arr[$key][$newkey] = $average;
}
return $arr;
} // sma()
Since we're using arrays in PHP the performance hit is negliable and probably less then it would be to calculate it using SQLite.
Screen of result:
Upvotes: 1
Reputation: 180210
First, filter out any rows that are not the last in the month.
Then use a scalar subquery to compute the moving average; the condition with the m3
subquery ensures that the AVG()
subquery does not find any rows if there are not exactly six.
WITH months(id, nav, date) AS (
SELECT id, nav, MAX(date)
FROM nav
GROUP BY strftime('%Y-%m', date)
)
SELECT id,
nav,
strftime('%Y-%m', date),
(SELECT AVG(nav)
FROM months AS m2
WHERE m2.date BETWEEN date(months.date, 'start of month', '-5 months')
AND months.date
AND (SELECT COUNT(*)
FROM months AS m3
WHERE m3.date BETWEEN date(months.date, 'start of month', '-5 months')
AND months.date
) = 6
) AS average_6m
FROM months;
Upvotes: 0