Skip to content

Instantly share code, notes, and snippets.

@lnostdal
Last active August 20, 2018 02:24
Show Gist options
  • Save lnostdal/9075a6eb2899bf3c97ca7af23e333f14 to your computer and use it in GitHub Desktop.
Save lnostdal/9075a6eb2899bf3c97ca7af23e333f14 to your computer and use it in GitHub Desktop.
Moving average in Clojure using JVM arrays vs. transducer variant
;; ...and yes; I know there are more efficient ways to do this.
(defn sma ^doubles [^doubles pseries ^long ma-len]
"Simple Moving Average: https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average
Returns a JVM double[]. Head is padded with ##NaN entries to align it with `pseries`."
(if (= ma-len 1)
pseries
(let [pseries-len (alength pseries), ret (double-array pseries-len ##NaN)]
;; Dealing with NaN isn't needed here as SMA doesn't carry previous results on forever like e.g. EMA does.
(loop [pseries-idx (- ma-len 1)]
(if (>= pseries-idx pseries-len)
ret
(do
(loop [prt-idx 0, prt-sum 0.0]
(if (= prt-idx ma-len)
(aset ret pseries-idx (/ prt-sum ma-len))
(recur (+ prt-idx 1) (+ prt-sum (aget pseries (- pseries-idx prt-idx))))))
(recur (inc pseries-idx))))))))
(defn t-sma [^long ma-len]
(fn [xf]
(let [q (EvictingQueue/create ma-len)]
(fn
([] (xf))
([result] (xf result))
([result ^double price]
(.add q price)
(if (zero? (do (.remainingCapacity q)))
(xf result (com.google.common.math.Stats/meanOf q))
(xf result ##NaN)))))))
;; quantataraxia.core> (println (seq (sma (double-array (range 1.0 20.0)) 10)))
;; (##NaN ##NaN ##NaN ##NaN ##NaN ##NaN ##NaN ##NaN ##NaN 5.5 6.5 7.5 8.5 9.5 10.5 11.5 12.5 13.5 14.5)
;; nil
;; quantataraxia.core> (println (sequence (t-sma 10) (range 1.0 20.0)))
;; (##NaN ##NaN ##NaN ##NaN ##NaN ##NaN ##NaN ##NaN ##NaN 5.5 6.5 7.5 8.5 9.5 10.5 11.5 12.5 13.5 14.5)
;; nil
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment