Skip to content

Instantly share code, notes, and snippets.

@lnostdal
Last active August 2, 2018 21:55
Show Gist options
  • Select an option

  • Save lnostdal/935e74fa3f7bbd313ad371630ca8a292 to your computer and use it in GitHub Desktop.

Select an option

Save lnostdal/935e74fa3f7bbd313ad371630ca8a292 to your computer and use it in GitHub Desktop.
Ehlers' Super Smoother in Clojure
;; Based on this talk https://www.youtube.com/watch?v=BR5pDiPYsnw
;;
;; NOTE: I've not yet tested these for correctness(!)
(defn double-finite-or-zero ^double [^double n]
(if (Double/isFinite n)
n
0.0))
;; TODO: This is using a 2-step lookback on `pseries`. Perhaps this can be generalized via a parameter or something?
(defn ehlers-super-smoother
"Ehlers' Super Smoother filter: http://www.stockspotter.com/files/PredictiveIndicators.pdf
`len`: A common value here is 10."
(^doubles [^doubles pseries]
(ehlers-super-smoother pseries 10))
(^doubles [^doubles pseries ^long len]
(let [pseries-len (alength pseries), ret (double-array pseries-len ##NaN)
start-idx (long (loop [idx 0] ;; Find first valid entry in `pseries`.
(if (or (= idx pseries-len)
(Double/isFinite (aget pseries idx)))
idx
(recur (inc idx)))))
sqrt2 (Math/sqrt 2.0) ;; 1.4142135623730951
a1 (Math/exp (/ (- (* sqrt2 Math/PI)) ;; expvalue(-1.414*3.14159 / 10);
len))
c2 (* 2.0 a1 (Math/cos (Math/toRadians (/ (* sqrt2 180.0) len)))) ;; 2*a1*cos(1.414*180 / len)
c3 (* (- a1) a1) ;; -a1*a1
c1 (- 1.0 c2 c3)] ;; 1 - c2 - c3
(loop [pseries-idx (+ start-idx 2)]
(if (>= pseries-idx pseries-len)
ret
(do ;; c1*(Close + Close[1]) / 2 + c2*Filt[1] + c3*Filt[2]
(aset ret pseries-idx
(+ (* c1
(+ (aget pseries pseries-idx)
(aget pseries (- pseries-idx 1)))
0.5)
(* c2 (double-finite-or-zero (aget ret (- pseries-idx 1))))
(* c3 (double-finite-or-zero (aget ret (- pseries-idx 2))))))
(recur (inc pseries-idx))))))))
;; TODO: This is using a 2-step lookback on `pseries`. Perhaps this can be generalized via a parameter or something?
(defn ehlers-roofing-filter
"Ehlers' Roofing filter: http://www.stockspotter.com/files/PredictiveIndicators.pdf
In his PDF the output of this is passed to EHLERS-SUPER-SMOOTHER and the output of this in turn is passed to e.g. a stocchastic indicator or similar.
`len`: A common value here is 48."
(^doubles [^doubles pseries]
(ehlers-roofing-filter pseries 48))
(^doubles [^doubles pseries ^long len]
(let [pseries-len (alength pseries), ret (double-array pseries-len ##NaN)
start-idx (long (loop [idx 0] ;; Find first valid entry in `pseries`.
(if (or (= idx pseries-len)
(Double/isFinite (aget pseries idx)))
idx
(recur (inc idx)))))
sqrt2 (Math/sqrt 2.0) ;; 1.4142135623730951
cutoff (/ 1.0 sqrt2) ;; 0.7071067811865476
;;;alpha1 = (cos(sqrt(2) * PI / (2 * upper)) + sin (sqrt(2) * PI / (2 * upper)) - 1) / cos(sqrt(2) * PI / (2 * upper))
a1 (/ (- (+ (Math/cos (/ (* sqrt2 Math/PI)
(* 2.0 len)))
(Math/sin (/ (* sqrt2 Math/PI)
(* 2.0 len))))
1.0)
(Math/cos (/ (* sqrt2 Math/PI )
(* 2.0 len))))]
(loop [pseries-idx (+ start-idx 2)]
(if (>= pseries-idx pseries-len)
ret
(do
(aset ret pseries-idx
(- (+ (* (- 1.0 (/ a1 2.0)) ;; (1 - alpha1 / 2)*(1 - alpha1 / 2)*(Close - 2*Close[1] + Close[2]) +
(- 1.0 (/ a1 2.0))
(+ (- (aget pseries pseries-idx)
(* 2.0 (aget pseries (- pseries-idx 1))))
(aget pseries (- pseries-idx 2))))
(* 2.0 (- 1.0 a1) (double-finite-or-zero (aget ret (- pseries-idx 1))))) ;; 2*(1 - alpha1)*HP[1] -
(* (- 1.0 a1) (- 1.0 a1) ;; (1 - alpha1)*(1 - alpha1)*HP[2]
(double-finite-or-zero (aget ret (- pseries-idx 2))))))
(recur (inc pseries-idx))))))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment