(do ;; =========================================================================== ;; 1. DEFINITIONEN ;; =========================================================================== ;; --------------------------------------------------------------------------- ;; Factory: create-wma ;; Berechnet WMA. Wartet, bis das Fenster voll ist. ;; Phase 1: Warten (< len) ;; Phase 2: Init (= len) -> Loop berechnung ;; Phase 3: Slide (> len) -> O(1) berechnung ;; --------------------------------------------------------------------------- (def create-wma (fn [len] (do (def prev-w-sum 0.0) (def prev-s-sum 0.0) (def weight-div (/ (* len (+ len 1)) 2)) (fn [src] (do (def cnt (count src)) ;; PHASE 1: Nicht genug Daten (if (< cnt len) NaN ;; Genug Daten vorhanden (if (= cnt len) ;; PHASE 2: Initialisierung (Das Fenster ist gerade voll geworden) ;; Wir müssen einmalig die Basis-Summen berechnen. (do (def init-w 0.0) (def init-s 0.0) ;; Loop über die Elemente 0 bis len-1 ((fn [i] (if (< i len) (do (def val (get src i)) ;; Gewichtung: Index 0 ist das neueste -> Gewicht 'len' (assign init-w (+ init-w (* (- len i) val))) (assign init-s (+ init-s val)) (recur (+ i 1))) )) 0) ;; State speichern (assign prev-w-sum init-w) (assign prev-s-sum init-s) (/ init-w weight-div)) ;; PHASE 3: Sliding Window (O(1)) ;; Wir haben bereits einen gültigen State aus dem vorherigen Schritt. (do (def val-new (get src 0)) ; Neuster Wert (def val-out (get src len)) ; Wert der rausfällt ;; Update Formeln (def w-sum (+ prev-w-sum (- (* len val-new) prev-s-sum))) (def s-sum (+ (- prev-s-sum val-out) val-new)) ;; State speichern (assign prev-w-sum w-sum) (assign prev-s-sum s-sum) (/ w-sum weight-div)) ) ) )) ))) ;; --- HMA Factory --- (def create-hma (fn [len] (do (def half-len (round (/ len 2))) (def sqrt-len (round (sqrt len))) (def wma-half (create-wma half-len)) (def wma-full (create-wma len)) (def wma-smooth (create-wma sqrt-len)) (def diff-series (new-series [[:val :Float]])) (fn [src] (do (def v1 (wma-half src)) (def v2 (wma-full src)) ;; WICHTIG: Wir dürfen erst weitermachen, wenn BEIDE WMAs gültig sind. ;; Da v2 das längere Fenster (len) braucht, diktiert es den Takt. (def diff (- (* 2 v1) v2)) (if (not (is-NaN diff)) (add-item diff-series {:val diff})) ;; Glättung auf dem Resultat (wma-smooth (.val diff-series)) )) ))) ;; --- Repeat Macro --- (defmacro repeat [n body] `((fn [cnt] (if (> cnt 0) (do ~body (recur (- cnt 1))))) ~n)) ;; =========================================================================== ;; 2. SIMULATION ;; =========================================================================== (def hma-length 14) (def simulation-steps 500) (def hma-calc (create-hma hma-length)) (def prices (new-series [[:close :Float]])) (def current-price 100.0) (print "Step | Price | HMA(14)") (print "-----|----------|----------") (repeat simulation-steps (do ;; Random Walk (assign current-price (+ current-price (- (random) 0.5))) (add-item prices {:close current-price}) ;; Berechnung ;; Nutzung von Member Access (.close), da es eine Record-Series ist (def hma-val (hma-calc (.close prices))) ;; Ausgabe (if (not (is-NaN hma-val)) (print (count prices) " |" current-price " | " hma-val) ) ) ) )