Using DecayedValue as a moving average

103 views
Skip to first unread message

Ian Hummel

unread,
May 2, 2014, 5:02:04 PM5/2/14
to alge...@googlegroups.com
Hey everyone,

I'm trying to understand how the DecayedValue can be used as moving average.  According to the wiki, and some comments in the source, this should be possible.  I must not be using correctly though, because I am getting numbers that I can't make sense of.  What I thought you would do is
  • Define a half-life equal to the moving average window, say, 5 days
  • For each quantity at time t, created a DecayedValue(q, t, 5)
  • Sum the DecayedValue at each t from t = 0 until t = end
  • Calling `average(half-life)` of the final DecayedValue gives me the average over the last 5 days
    • Since I'm interested in the intermediate output, I can do this at each time t, starting from t = 4
I seem to be missing something though, because the numbers I am getting for the initail intermediate resulsts are really, really low.  Why would the numbers be so low at the beginning of the series, for so long? I'm getting numbers in the 50s and 60s initially...

See below for example program + output.

Advice appreciated!  Hope to funnel this back into the docs.  Thanks!


/ Generate the list of cumulative sums using the implicit monoid
def cumulativeSum[T : Monoid](list: List[T]) = {
  list.tail.scanLeft(list.head)(_ + _)
}

// S&P 500 closing prices for April 2014
val raw = List(
  99.74, 100.25, 99.81, 96.61, 95.09, 95.96, 98.42, 96.28,
  96.2, 96.49, 97.36, 99.74, 99.94, 99.39, 99.8, 102.31,
  101.1, 99.17, 97.81, 98.85, 101.84
)

val days = 5 // window for moving average/decayed value
val warmup = Seq.fill(days - 1)(Double.NaN) // no moving avgs until n days

val simpleAverages = raw.map(AveragedValue(_))

// a simple, cumulative average using the AveragedValue monoid
val simpleAvgs = cumulativeSum(simpleAverages).map(_.value)

// Moving average requires keeping O(N) memory
val moving5DayAvgs = warmup ++ simpleAverages.sliding(days, 1).map{ (window) =>
  window.monoidSum.value
}

// DecayedValue can represent a moving average with O(1) memory
implicit val monoid = (DecayedValue.monoidWithEpsilon(0.99))
val decayedValues = raw.zipWithIndex.map { case (value, atTime) =>
  DecayedValue.build(value, atTime, days)
}

val decayedValueAvgs = warmup ++ cumulativeSum(decayedValues).drop(days - 1).map(_.average(days))


Here's the output (notice how Decayed is well below the other averages for most of the series)

Close     Cumulative Moving     Decayed   
     99.74     99.74       NaN       NaN
    100.25    100.00       NaN       NaN
     99.81     99.93       NaN       NaN
     96.61     99.10       NaN       NaN
     95.09     98.30     98.30     52.44
     95.96     97.91     97.54     58.95
     98.42     97.98     97.18     64.97
     96.28     97.77     96.47     69.90
     96.20     97.60     96.39     74.19
     96.49     97.49     96.67     77.96
     97.36     97.47     96.95     81.37
     99.74     97.66     97.21     84.66
     99.94     97.84     97.95     87.56
     99.39     97.95     98.58     90.00
     99.80     98.07     99.25     92.19
    102.31     98.34    100.24     94.44
    101.10     98.50    100.51     96.23
     99.17     98.54    100.35     97.52
     97.81     98.50    100.04     98.45
     98.85     98.52     99.85     99.41
    101.84     98.67     99.75    100.66

Avi Bryant

unread,
May 2, 2014, 5:07:30 PM5/2/14
to Ian Hummel, alge...@googlegroups.com
One naive but effective strategy I've used for this is to generate a paired stream of DecayedValue objects with the same half-life and timestamp but with value 1. Then I can divide the sum of my data stream by the sum of that stream to get an average.

(I think I have this right, but I may be misremembering).

Avi


--
You received this message because you are subscribed to the Google Groups "algebird" group.
To unsubscribe from this group and stop receiving emails from it, send an email to algebird+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Oscar Boykin

unread,
May 2, 2014, 5:07:47 PM5/2/14
to Ian Hummel, algebird


On Fri, May 2, 2014 at 2:02 PM, Ian Hummel <themod...@gmail.com> wrote:

--
You received this message because you are subscribed to the Google Groups "algebird" group.
To unsubscribe from this group and stop receiving emails from it, send an email to algebird+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Oscar Boykin :: @posco :: http://twitter.com/posco
Reply all
Reply to author
Forward
0 new messages