module Data.Metrics.MovingAverage.ExponentiallyWeighted (
ExponentiallyWeightedMovingAverage,
new1MinuteMovingAverage,
new5MinuteMovingAverage,
new15MinuteMovingAverage,
movingAverage,
clear,
rate,
empty,
update,
tick
) where
import Control.Lens
import Control.Lens.TH
import Control.Monad.Primitive
import qualified Data.Metrics.MovingAverage as MA
import Data.Metrics.Types (Minutes)
data ExponentiallyWeightedMovingAverage = ExponentiallyWeightedMovingAverage
{ exponentiallyWeightedMovingAverageUncounted :: !Double
, exponentiallyWeightedMovingAverageCurrentRate :: !Double
, exponentiallyWeightedMovingAverageInitialized :: !Bool
, exponentiallyWeightedMovingAverageInterval :: !Double
, exponentiallyWeightedMovingAverageAlpha :: !Double
} deriving (Show)
makeFields ''ExponentiallyWeightedMovingAverage
makeAlpha :: Double -> Minutes -> Double
makeAlpha i m = 1 exp (negate i / 60 / fromIntegral m)
new1MinuteMovingAverage :: MA.MovingAverage
new1MinuteMovingAverage = movingAverage 5 1
new5MinuteMovingAverage :: MA.MovingAverage
new5MinuteMovingAverage = movingAverage 5 5
new15MinuteMovingAverage :: MA.MovingAverage
new15MinuteMovingAverage = movingAverage 5 15
movingAverage :: Double -> Minutes -> MA.MovingAverage
movingAverage i m = MA.MovingAverage
{ MA.movingAverageClear = clear
, MA.movingAverageUpdate = update
, MA.movingAverageTick = tick
, MA.movingAverageRate = rate
, MA.movingAverageState = empty i m
}
clear :: ExponentiallyWeightedMovingAverage -> ExponentiallyWeightedMovingAverage
clear = (initialized .~ False) . (currentRate .~ 0) . (uncounted .~ 0)
rate :: ExponentiallyWeightedMovingAverage -> Double
rate e = (e ^. currentRate) * (e ^. interval)
empty :: Double
-> Minutes
-> ExponentiallyWeightedMovingAverage
empty i m = ExponentiallyWeightedMovingAverage 0 0 False i $ makeAlpha i m
update :: Double -> ExponentiallyWeightedMovingAverage -> ExponentiallyWeightedMovingAverage
update = (uncounted +~)
tick :: ExponentiallyWeightedMovingAverage -> ExponentiallyWeightedMovingAverage
tick e = uncounted .~ 0 $ initialized .~ True $ updateRate e
where
instantRate = (e ^. uncounted) / (e ^. interval)
updateRate a = if a ^. initialized
then currentRate +~ ((a ^. alpha) * (instantRate a ^. currentRate)) $ a
else currentRate .~ instantRate $ a