Last active
August 2, 2018 21:55
-
-
Save lnostdal/935e74fa3f7bbd313ad371630ca8a292 to your computer and use it in GitHub Desktop.
Ehlers' Super Smoother in Clojure
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ;; 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