(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)
      )
    )
  )
)