Chapter 5. Traversing Data, Branching, and Conditional Dispatch

In this chapter, we'll be looking at more advanced branching and conditional logic. However, these are just the implementation details of the kind of control we're beginning to realize. Alan Kay, a notable computer programmer, has stated that, "Lisp isn't a language, it's a building material." The fact that Clojure is a Lisp fits this description. It also helps us understand that Clojure should just be the starting point when building functions and engineering our systems. It is the kind of building material that lets us be as precise as we need.

Our second refactor – the generate prices function

For example, our current oscillating stochastic price generator isn't the best fit for our needs. The generated prices either regularly trend to zero or trend much too high:

(defn random-in-range [lower upper]

  (let [r (+ (rand (- upper lower))
             lower)]

    (if (> r upper)
      upper r)))

(defn stochastic-k [last-price low-price high-price]
  (let[hlrange (- high-price low-price)
       hlmidpoint (/ hlrange 2)
       numerator (if (> last-price hlmidpoint)
                   (- last-price hlmidpoint)
                   (- hlmidpoint low-price))]
    (/ numerator hlrange)))

(defn break-local-minima-maxima [k]
  (as-> k k
    (if (<= (int (+ 0.95 k)) 0)
      (+ 0.15 k) k)
    (if (>= k 1)
      (- k 0.15) k)))

(defn generate-prices

  ([low high]
   (generate-prices (random-in-range low high) low high))

  ([last-price low high]
   (iterate (fn [{:keys [last lows highs]}]

              (let [low (-> lows first)
                    high (-> highs reverse first)
                    k (stochastic-k last low high)
                    plus-OR-minus (rand-nth [- +])

                    kPM (if (< k 0.5)
                          (if (= plus-OR-minus +)
                            (+ 1 (break-local-minima-maxima k))
                            (- 1 (break-local-minima-maxima k)))
                          (if (= plus-OR-minus +)
                            (+ 1 (- 1 (break-local-minima-maxima k)))
                            (- 1 (- 1 (break-local-minima-maxima k)))))

                    newprice (* kPM last)
                    newlow (if (< newprice low) newprice low)
                    newhigh (if (> newprice high) newprice high)]

                (println (str "[" last " | " low " | " high "] <=> k[" k "] / kPM[" kPM "] / newprice[" newprice "]"))
                {:last newprice
                 :lows (take 5 (conj lows newlow))
                 :highs (take 5 (conj highs newhigh))}))

            {:last last-price :lows [low] :highs [high]})))

The preceding code shows why we need the break-local-minima-maxima guard function: to keep our prices within a reasonable range. However, this is a less elegant workaround, akin to a set of guardrails. You can evaluate the function in your REPL using the following code:

(require '[edgar.core :as core

         '[edgar.analytics :as analytics]


(def price-only-list 
  (analytics/extract-price-only (core/generate-prices 5 15))


(take 50 price-only-list)

;; (6.223646585866444 6.985200975509927 8.371903754581446 11.194829124888214 6.935005351100434 5.593078104671486 5.261364888511671 6.188083226538929 5.45288743795115 4.3879999001375065 4.00841221377164 3.465993865670304 2.7745356087257838 3.5321301673360033 2.567672336389048 1.814142431466118 2.471619508589675 3.367377825071922 4.587774686750374 6.25046480370463 8.51574301919856 7.273943476527238 7.649623431542617 8.26265266318511 9.308951577246637 11.22640931794612 7.281474468812783 6.901246804698159 5.7046915345434 7.772171932425898 8.467256074658573 9.670849322422702 11.928275932423938 7.101779616374013 8.441108859931914 9.624246864446237 7.411713217948748 6.951478047181248 5.719732052754735 7.792663398578526 7.083636475728704 8.409797330774097 7.251018224192089 5.801476360694858 3.698919442080443 2.3583660758658964 1.5036527923589607 0.9193511112141406 0.5197016575549582 0.7609002592527179)

We may want to keep this function around as stock prices do sometimes flatline to zero or skyrocket as well. However, we also need a function that more directly keeps prices within a reasonable range for long periods. We should be able to choose or combine both functionalities now that we know we can get more finely grained control over our input data. The only barrier now is figuring out which function can give us a more consistent oscillation. Are there any math functions that give us the kind of wave formation that we usually see in stock graphs? For this, both polynomials and sine waves approximate the oscillating behavior that we're interested in. Also, having a powerful building material at our disposal, we can spend our time investigating their mathematical properties, confident that Clojure will be able to clearly and directly implement any solution we derive.

In both cases, these are several desirable properties that we'll want our function to have:

  • Can the function's graph repeat, to get continuous waves?
  • Can we make the waves slimmer or wider?
  • Can we make the waves taller or shorter?
  • Are we able to nest waves within waves?
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.187.223