Warm tip: This article is reproduced from stackoverflow.com, please click
f#

how can I rewrite, and optimize, this EMA calculation in F#

发布于 2020-04-03 23:19:36

this is an EMA calculation:

let EMA (period : int) (data : double[]) : double[] =
    let multiplier = 2.0 / (double)(period + 1)
    let output = Array.create data.Length System.Double.NaN 

    output.[period - 1] <- data.[0..period - 1] |> Seq.average
    for i in seq {period .. (data.Length - 1)} do
        output.[i] <- (data.[i] * multiplier) + (output.[i - 1] * (1. - multiplier))

    output

you can test it with:

EMA 3 [|1.;3.;4.;7.;5.;6.;9.;8.|]

and it gives:

[|NaN;Nan;2.66.;4.83;4.91;5.45;7.22;7.61|]

my first question is regarding the loop at the end:

for i in seq {period .. (data.Length - 1)} do
    output.[i] <- (data.[i] * multiplier) + (output.[i - 1] * (1. - multiplier))

can this be rewritten using a sequence operator?

my second question is: is there a fast F# implementation floating around? I couldn't find any.

Questioner
Thomas
Viewed
90
torbonde 2020-01-31 18:08

Well, I don't know about floating around, but I did just write an EMA function yesterday. It looks like this:

let ema alpha vs =
    (None, vs)
    ||> Array.scan (fun prev x ->
        match prev with
        | None -> Some(x)
        | Some(s) -> Some(alpha*x + (1. - alpha)*s))
    |> Array.choose id

My version looks all the way back, rather than only a number of periods, as your version. If you want that behaviour instead, you could replace the state of type float option in my version with a float[], or even implement it using the Array.windowed function.