more stuff
This commit is contained in:
1 parent
d22f0515d5
commit
f93a09d2fe
6 files changed
+190
-11
No files matched your search
@@ -0,0 +1,116 @@
|
||||
(ns skirmish.cli.benchmark
|
||||
(:require [clojure.math.combinatorics :refer [cartesian-product]]
|
||||
[clojure.core.async :refer [go chan <! >! close! to-chan close!] :as async]
|
||||
[gniazdo.core :as ws]
|
||||
[clj-http.client :as client]
|
||||
[com.stuartsierra.frequencies :as freq]
|
||||
[clojure.edn :as edn]))
|
||||
|
||||
|
||||
(def ids (atom 0))
|
||||
|
||||
|
||||
(defn web-socket-to-channel [url output-ch]
|
||||
(let [on-receive #(go (>! output-ch %))
|
||||
headers {"accept" "application/edn"}]
|
||||
(ws/connect url :headers headers :on-receive on-receive)))
|
||||
|
||||
|
||||
(defn create-trades-channel [securities]
|
||||
(let [output (chan 1 (map edn/read-string))
|
||||
url #(format "ws://localhost:%s/api/1/securities/%s/trades" 8080 %)
|
||||
xf (comp (map :sec-id)
|
||||
(map name)
|
||||
(map url)
|
||||
(map #(web-socket-to-channel % output)))]
|
||||
(doall (sequence xf securities))
|
||||
(async/pub output :book.order/order-id)))
|
||||
|
||||
|
||||
(defn create-orders-channel [securities]
|
||||
(let [output (chan 1 (map edn/read-string))
|
||||
url #(format "ws://localhost:%s/api/1/securities/%s/orders" 8080 %)
|
||||
xf (comp (map :sec-id)
|
||||
(map name)
|
||||
(map url)
|
||||
(map #(web-socket-to-channel % output)))]
|
||||
(doall (sequence xf securities))
|
||||
(async/pub output :book.order/id)))
|
||||
|
||||
|
||||
|
||||
(defn create-requests-payload-for-security [i security id]
|
||||
(let [rd (new java.util.Random i)
|
||||
rand-bool (= (mod (. rd nextInt) 2) 0) ;; java random number generator REALLY sucks
|
||||
type (if rand-bool :sell :buy)
|
||||
price (+ (* 3 (. rd nextGaussian)) 30)
|
||||
int-price (int (* price 100))
|
||||
quantity (reduce + 1 (map #(. rd nextInt (inc %)) (range 100)))]
|
||||
|
||||
{:type type :quantity quantity :price int-price :security security :benchmark/id id}))
|
||||
|
||||
|
||||
(defn create-requests-for-security [[i security]]
|
||||
(let [req-id (swap! ids inc)
|
||||
payload (create-requests-payload-for-security i (str (:sec-id security)) req-id)
|
||||
sec-id-str (-> security :sec-id name)
|
||||
params {:throw-exceptions false :content-type :edn :headers {"accept" "application/edn"}}
|
||||
url (format "http://localhost:%s/api/1/securities/%s" 8080 sec-id-str)]
|
||||
(fn []
|
||||
(let [now (System/nanoTime)]
|
||||
(client/post url (assoc params :form-params (assoc payload :benchmark/request-time now)))))))
|
||||
|
||||
|
||||
(defn make-requests [n securities]
|
||||
(map create-requests-for-security (cartesian-product (range n) securities)))
|
||||
|
||||
|
||||
(defn no-more-then-n-simultaneously [f input-chan n]
|
||||
(let [buffer (chan)]
|
||||
(go (async/pipeline n buffer (map f) input-chan))
|
||||
buffer))
|
||||
|
||||
(defn decode-response [response]
|
||||
(let [now (System/nanoTime)
|
||||
ret {}
|
||||
ret (assoc ret :status (:status response))
|
||||
ret (assoc ret :order (edn/read-string (:body response)))
|
||||
ret (assoc ret :duration (/ (- now (get-in ret [:order :benchmark/request-time])) (Math/pow 10 9)))]
|
||||
ret))
|
||||
|
||||
|
||||
(defn benchmark [requests order trades]
|
||||
(let [decoded-responses (chan 1 (map decode-response))
|
||||
responses (no-more-then-n-simultaneously #(%) (to-chan requests) 10)]
|
||||
(go (async/pipe responses decoded-responses))
|
||||
decoded-responses))
|
||||
|
||||
|
||||
(defn compute-result [results new-result]
|
||||
(let [status-code (:status new-result)
|
||||
duration (:duration new-result)
|
||||
old-status-code-count (get-in results [:status status-code] 0)
|
||||
ret results
|
||||
ret (assoc-in ret [:status status-code] (inc old-status-code-count))
|
||||
ret (assoc ret :durations (conj (:durations ret) duration))]
|
||||
ret))
|
||||
|
||||
(defn pp-durations [durations]
|
||||
(let [freq-map (frequencies durations)]
|
||||
(print (freq/stats freq-map))))
|
||||
|
||||
(defn display-results [results]
|
||||
(println results)
|
||||
(println "number of request: " (:n results))
|
||||
(println "status map: " (:status results))
|
||||
(pp-durations (:durations results)))
|
||||
|
||||
(defn -main [config]
|
||||
(let [benchmark-config (:benchmark config)
|
||||
requests (-> benchmark-config :number-of-requests-per-securities (make-requests (:securities config)))
|
||||
trades (create-trades-channel (:securities config))
|
||||
orders (create-orders-channel (:securities config))
|
||||
results (benchmark requests orders trades)]
|
||||
(display-results (async/<!! (async/reduce compute-result {} results)))))
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
(ns skirmish.core
|
||||
(:require [skirmish.cli.core]
|
||||
[skirmish.rest.core]
|
||||
[skirmish.cli.benchmark]
|
||||
[aero.core :refer (read-config)])
|
||||
(:gen-class))
|
||||
|
||||
(def apps {:webserver skirmish.rest.core/-main})
|
||||
(def apps {:webserver skirmish.rest.core/-main
|
||||
:benchmark skirmish.cli.benchmark/-main})
|
||||
|
||||
(defn -main [& argv]
|
||||
(let [config (-> "config.edn" clojure.java.io/resource read-config)
|
||||
|
||||
@@ -1 +1,13 @@
|
||||
(ns skirmish.rest.marketdata)
|
||||
(ns skirmish.rest.marketdata
|
||||
(:require [compojure.core :refer :all]))
|
||||
|
||||
|
||||
(defn create-routes [exchange-apis]
|
||||
(routes
|
||||
(context "/api/1" []
|
||||
(GET "/securities" [] )
|
||||
(GET "/securities/:sec-id" [] (get-security exchange-apis))
|
||||
(POST "/securities/:sec-id" [] (create-order exchange-apis))
|
||||
(GET "/securities/:sec-id/trades" [] (listen-to-trades exchange-apis))
|
||||
(GET "/securities/:sec-id/orders" [] (listen-to-orders exchange-apis)))
|
||||
(not-found "not found")))
|
||||
Reference in new issue
Block a user