Working with tests!
This commit is contained in:
parent
21d07b383c
commit
9c8b6cccf7
20
deps.edn
20
deps.edn
|
@ -9,7 +9,25 @@
|
|||
|
||||
org.fudo/fudo-clojure {
|
||||
:git/url "https://git.fudo.org/fudo-public/fudo-clojure.git"
|
||||
:sha "047f5d531c8d1493880313d13bbff6da88b0a4b8"
|
||||
:sha "6f80b1de51cdd92bb04857edc562a076f6820a2a"
|
||||
}
|
||||
}
|
||||
:aliases {
|
||||
:test {
|
||||
:extra-paths ["test"]
|
||||
:extra-deps {
|
||||
io.github.cognitect-labs/test-runner {
|
||||
:git/url "https://github.com/cognitect-labs/test-runner.git"
|
||||
:sha "dfb30dd6605cb6c0efc275e1df1736f6e90d4d73"
|
||||
}
|
||||
}
|
||||
:main-opts ["-m" "cognitect.test-runner"]
|
||||
:exec-fn cognitect.test-runner.api/test
|
||||
}
|
||||
|
||||
:lint {
|
||||
:replace-deps { clj-kondo/clj-kondo {:mvn/version "2022.04.25"} }
|
||||
:main-opts ["-m" "clj-kondo.main" "--lint" "src"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
[coinbase-pro.order :as order-req]
|
||||
|
||||
[fudo-clojure.common :refer [base64-decode base64-encode-string ensure-conform instant-to-epoch-timestamp to-uuid parse-timestamp]]
|
||||
[fudo-clojure.common :refer [base64-decode base64-encode-string ensure-conform instant-to-epoch-timestamp to-uuid parse-timestamp pthru]]
|
||||
[fudo-clojure.http.client :as http]
|
||||
[fudo-clojure.http.request :as req]
|
||||
[fudo-clojure.logging :as log]
|
||||
|
@ -19,15 +19,15 @@
|
|||
|
||||
(def signature-algo "HmacSHA256")
|
||||
|
||||
(defn- build-path [& path-elems]
|
||||
(str "/" (str/join "/" (map to-path-elem path-elems))))
|
||||
|
||||
(defn- to-path-elem [el]
|
||||
(cond (keyword? el) (name el)
|
||||
(uuid? el) (.toString el)
|
||||
(string? el) el
|
||||
:else (throw (ex-info (str "Bad path element: " el) {}))))
|
||||
|
||||
(defn- build-path [& path-elems]
|
||||
(str "/" (str/join "/" (map to-path-elem path-elems))))
|
||||
|
||||
(defn- make-signature-generator [key]
|
||||
(let [hmac (doto (javax.crypto.Mac/getInstance signature-algo)
|
||||
(.init (javax.crypto.spec.SecretKeySpec. key signature-algo)))]
|
||||
|
@ -35,10 +35,10 @@
|
|||
(-> (.doFinal hmac (.getBytes msg))
|
||||
(base64-encode-string)))))
|
||||
|
||||
(s/def ::secret string?)
|
||||
|
||||
(s/def ::secret string?)
|
||||
(s/def ::passphrase string?)
|
||||
(s/def ::key string?)
|
||||
(s/def ::hostname string?)
|
||||
|
||||
(s/def ::profile-id uuid?)
|
||||
(s/def ::trade-id uuid?)
|
||||
|
@ -137,7 +137,7 @@
|
|||
|
||||
(defn- ensure-keys [ks m]
|
||||
(let [diff (set/difference ks (set (keys m)))]
|
||||
(when (not (empty? diff))
|
||||
(when (seq diff)
|
||||
(throw (ex-info (str "missing keys: "
|
||||
(str/join "," diff))
|
||||
{:missing-keys diff
|
||||
|
@ -152,28 +152,28 @@
|
|||
:size
|
||||
:settled
|
||||
:created_at}]
|
||||
(do (ensure-keys required-keys order)
|
||||
(reify order/Order
|
||||
(id [_] (-> order :id to-uuid))
|
||||
(currency [_] (-> order :product_id product-currency))
|
||||
(limit? [_] (-> order :type keyword (= :limit)))
|
||||
(market? [_] (-> order :type keyword (= :market)))
|
||||
(stop? [_] (-> order :stop nil? not))
|
||||
(sell? [_] (-> order :side keyword (= :sell)))
|
||||
(buy? [_] (-> order :side keyword (= :buy)))
|
||||
(stop-loss? [_] (-> order :stop keyword (= :loss)))
|
||||
(stop-gain? [_] (-> order :stop keyword (= :entry)))
|
||||
(filled? [_] (-> order :done_reason keyword (= :filled)))
|
||||
(price [_] (-> order :price bigdec))
|
||||
(stop-price [_] (some-> order :stop_price bigdec))
|
||||
(size [_] (-> order :size bigdec))
|
||||
(settled? [_] (-> order :settled))
|
||||
(done? [_] (-> order :status keyword (= :done)))
|
||||
(cancelled? [_] (-> order :done_reason keyword (= :canceled)))
|
||||
(fees [_] (some-> order :fill_fees bigdec))
|
||||
(created [_] (-> order :created_at parse-timestamp))
|
||||
(completed [_] (some-> order :done_at parse-timestamp))
|
||||
(get-raw [_] order)))))
|
||||
(ensure-keys required-keys order)
|
||||
(reify order/Order
|
||||
(id [_] (-> order :id to-uuid))
|
||||
(currency [_] (-> order :product_id product-currency))
|
||||
(limit? [_] (-> order :type keyword (= :limit)))
|
||||
(market? [_] (-> order :type keyword (= :market)))
|
||||
(stop? [_] (-> order :stop nil? not))
|
||||
(sell? [_] (-> order :side keyword (= :sell)))
|
||||
(buy? [_] (-> order :side keyword (= :buy)))
|
||||
(stop-loss? [_] (-> order :stop keyword (= :loss)))
|
||||
(stop-gain? [_] (-> order :stop keyword (= :entry)))
|
||||
(filled? [_] (-> order :done_reason keyword (= :filled)))
|
||||
(price [_] (-> order :price bigdec))
|
||||
(stop-price [_] (some-> order :stop_price bigdec))
|
||||
(size [_] (-> order :size bigdec))
|
||||
(settled? [_] (-> order :settled))
|
||||
(done? [_] (-> order :status keyword (= :done)))
|
||||
(cancelled? [_] (-> order :done_reason keyword (= :canceled)))
|
||||
(fees [_] (some-> order :fill_fees bigdec))
|
||||
(created [_] (-> order :created_at parse-timestamp))
|
||||
(completed [_] (some-> order :done_at parse-timestamp))
|
||||
(get-raw [_] order))))
|
||||
|
||||
(defn- reify-ticker [currency ticker]
|
||||
(reify ticker/Ticker
|
||||
|
@ -185,7 +185,7 @@
|
|||
(volume [_] (-> ticker :volume bigdec))))
|
||||
|
||||
(defn- reify-exchange-client [{client ::http/client
|
||||
hostname ::common/hostname
|
||||
hostname ::hostname
|
||||
logger ::log/logger}]
|
||||
(let [request! (fn [req] (http/execute-request! client (req/with-host req hostname)))]
|
||||
(reify client/ExchangeClient
|
||||
|
@ -196,8 +196,11 @@
|
|||
(map-success (client/get-ticker! self currency)
|
||||
ticker/price)))))
|
||||
|
||||
(defn- reify-exchange-account-client [{:keys [http/client common/hostname] :as opts}]
|
||||
(let [public-client (reify-exchange-client opts)
|
||||
(defn- reify-exchange-account-client [opts]
|
||||
(let [{client ::http/client
|
||||
hostname ::hostname
|
||||
logger ::log/logger} opts
|
||||
public-client (reify-exchange-client opts)
|
||||
request! (fn [req] (http/execute-request! client (req/with-host req hostname)))
|
||||
before (fn [a b] (.isBefore a b))
|
||||
reify-orders (comp (partial sort-by order/created before)
|
||||
|
@ -285,9 +288,14 @@
|
|||
(order-req/with-size (bigdec size)))))
|
||||
(comp to-uuid :id))))))
|
||||
|
||||
(defn connect
|
||||
([client] ()))
|
||||
|
||||
(defn connect [& args]
|
||||
(reify-client (apply build-connection args)))
|
||||
(s/fdef connect :ret ::client/client)
|
||||
(defn connect [& {:keys [hostname logger credentials]
|
||||
:or { logger (log/print-logger) }}]
|
||||
(if credentials
|
||||
(let [authenticator (make-request-authenticator credentials)]
|
||||
(reify-exchange-account-client {::http/client (http/json-client :authenticator authenticator
|
||||
:logger logger)
|
||||
::log/logger logger
|
||||
::hostname hostname}))
|
||||
(reify-exchange-client {::http/client (http/json-client :logger logger)
|
||||
::log/logger logger
|
||||
::hostname hostname})))
|
|
@ -0,0 +1,337 @@
|
|||
(ns coinbase-pro.client-test
|
||||
(:require [clojure.test :as t :refer [deftest is testing]]
|
||||
[clojure.data.json :as json]
|
||||
[clojure.string :as str]
|
||||
|
||||
[fudo-clojure.http.client :as http]
|
||||
[fudo-clojure.http.request :as req]
|
||||
|
||||
[fudo-clojure.common :as common]
|
||||
[fudo-clojure.result :refer [unwrap map-success]]
|
||||
|
||||
[exchange.account :as acct]
|
||||
[exchange.client :as client]
|
||||
[exchange.order :as order]
|
||||
|
||||
[coinbase-pro.client :as cb]
|
||||
[fudo-clojure.logging :as log])
|
||||
(:use coinbase-pro.client))
|
||||
|
||||
(def cb-passphrase "6%CRf$zL!b@toz")
|
||||
(def cb-secret "J/KtQJgu/TRsWigrqnc2KFBxdOTC5L8woaubzmj7G9hfxv9N5tcgsZ+yd9DvqmroJt1nzcy5Hsrk3IipcdFucA==")
|
||||
(def cb-key "57c45fb2a300dd00f7a02af8353af81b")
|
||||
|
||||
(def test-time-str "2022-02-06T10:34:47.123456Z")
|
||||
(def test-time (common/parse-timestamp test-time-str))
|
||||
|
||||
(defmacro is-true [& body]
|
||||
`(clojure.test/is (= true ~@body)))
|
||||
|
||||
(defmacro is-false [& body]
|
||||
`(clojure.test/is (= false ~@body)))
|
||||
|
||||
(defmacro is-success? [& body]
|
||||
`(is-true (fudo-clojure.result/success? ~@body)))
|
||||
|
||||
(defmacro is-failure? [& body]
|
||||
`(is-true (fudo-clojure.result/failure? ~@body)))
|
||||
|
||||
(defn gen-uuid [] (java.util.UUID/randomUUID))
|
||||
|
||||
(defn base-request []
|
||||
(-> (req/base-request)
|
||||
(req/with-timestamp test-time)
|
||||
(req/as-get)
|
||||
(req/with-host "some.dummy.host")))
|
||||
|
||||
(defn- http-client-returning [response]
|
||||
(-> (reify http/HTTPClient
|
||||
(get! [_ _] response)
|
||||
(post! [_ _] response)
|
||||
(delete! [_ _] response))
|
||||
(http/client:wrap-results)
|
||||
(http/client:jsonify)))
|
||||
|
||||
(defn- http-client-throwing [e]
|
||||
(-> (reify http/HTTPClient
|
||||
(get! [_ _] (throw e))
|
||||
(post! [_ _] (throw e))
|
||||
(delete! [_ _] (throw e)))
|
||||
(http/client:wrap-results)
|
||||
(http/client:jsonify)))
|
||||
|
||||
(defn sample [coll]
|
||||
(nth coll (rand-int (count coll))))
|
||||
|
||||
(defn date-to-instant [date]
|
||||
(-> (java.time.LocalDateTime/parse (str date "T00:00"))
|
||||
(.atZone (java.time.ZoneId/systemDefault))
|
||||
(.toInstant)))
|
||||
|
||||
(defn instant-to-epoch [i]
|
||||
(.getEpochSecond i))
|
||||
|
||||
(defn random-instant [& {:keys [start end]
|
||||
:or {start (date-to-instant "2010-01-01")
|
||||
end (date-to-instant "2022-05-01")}}]
|
||||
(let [s (instant-to-epoch start)
|
||||
e (instant-to-epoch end)]
|
||||
(java.time.Instant/ofEpochSecond (+ s (rand-int (- e s))))))
|
||||
|
||||
(defn gen-order
|
||||
"A response from Coinbase"
|
||||
([] (gen-order {}))
|
||||
([overrides]
|
||||
(let [created-at (random-instant)]
|
||||
(merge {
|
||||
:id (gen-uuid)
|
||||
:product_id (sample ["BTC-USD" "ADA-USD" "ETH-USD"])
|
||||
:type (sample [:limit :market])
|
||||
:side (sample [:buy :sell])
|
||||
:done_reason (sample [nil :done :cancelled])
|
||||
:price (bigdec (rand 100000))
|
||||
:stop-price (sample [nil (rand 100000)])
|
||||
:size (bigdec (rand 1000))
|
||||
:settled (sample [true false])
|
||||
:fill_fees (bigdec (rand 100))
|
||||
:completed (sample [true false])
|
||||
:created_at (str created-at)
|
||||
:done_at (str (sample [nil (random-instant :end created-at)]))
|
||||
}
|
||||
overrides))))
|
||||
|
||||
(defn make-public-client [http-client]
|
||||
(#'coinbase-pro.client/reify-exchange-client {::http/client http-client}))
|
||||
|
||||
(defn public-client-returning [resp]
|
||||
(make-public-client (http-client-returning resp)))
|
||||
(defn public-client-throwing [e]
|
||||
(make-public-client (http-client-throwing e)))
|
||||
|
||||
(defn resp [& {:keys [code body]}]
|
||||
{:status (.toString (or code 200))
|
||||
:body (json/write-str (or body {}))})
|
||||
|
||||
(deftest test-auth-headers
|
||||
(let [creds {::cb/key cb-key
|
||||
::cb/secret cb-secret
|
||||
::cb/passphrase cb-passphrase}
|
||||
make-request-authenticator #'coinbase-pro.client/make-request-authenticator
|
||||
authenticator (make-request-authenticator creds)]
|
||||
(testing "timestamp-header"
|
||||
(is (= (str (common/instant-to-epoch-timestamp test-time))
|
||||
(-> (authenticator (-> (base-request)
|
||||
(req/as-get)
|
||||
(req/with-path "/one/two")
|
||||
(req/with-body "hello")))
|
||||
::req/headers
|
||||
::cb/cb-access-timestamp))))
|
||||
(testing "access-key-header"
|
||||
(is (= cb-key
|
||||
(-> (authenticator (-> (base-request)
|
||||
(req/as-get)
|
||||
(req/with-path "/one/two")
|
||||
(req/with-body "hello")))
|
||||
::req/headers
|
||||
::cb/cb-access-key))))
|
||||
(testing "access-passphrase"
|
||||
(is (= cb-passphrase
|
||||
(-> (authenticator (-> (base-request)
|
||||
(req/as-get)
|
||||
(req/with-path "/one/two")
|
||||
(req/with-body "hello")))
|
||||
::req/headers
|
||||
::cb/cb-access-passphrase))))
|
||||
(testing "signature"
|
||||
(let [signer (#'coinbase-pro.client/make-signature-generator (common/base64-decode cb-secret))
|
||||
ts (common/instant-to-epoch-timestamp test-time)]
|
||||
|
||||
(let [cat-str (str ts "GET/one/two?")
|
||||
sig (signer cat-str)]
|
||||
(is (= sig
|
||||
(-> (authenticator (-> (base-request)
|
||||
(req/as-get)
|
||||
(req/with-path "/one/two")))
|
||||
::req/headers
|
||||
::cb/cb-access-sign))))
|
||||
(let [cat-str (str ts "GET/one/two?hello")
|
||||
sig (signer cat-str)]
|
||||
(is (= sig
|
||||
(-> (authenticator (-> (base-request)
|
||||
(req/as-get)
|
||||
(req/with-path "/one/two")
|
||||
(req/with-body "hello")))
|
||||
::req/headers
|
||||
::cb/cb-access-sign))))
|
||||
(let [cat-str (str ts "GET/one/two?")
|
||||
sig (signer cat-str)]
|
||||
(is (not (= sig
|
||||
(-> (authenticator (-> (base-request)
|
||||
(req/as-get)
|
||||
(req/with-path "/one/two")
|
||||
(req/with-body "oops")))
|
||||
::req/headers
|
||||
::cb/cb-access-sign))))
|
||||
(is (not (= sig
|
||||
(-> (authenticator (-> (base-request)
|
||||
(req/as-get)
|
||||
(req/with-path "/one/two")
|
||||
(req/with-timestamp (java.time.Instant/now))))
|
||||
::req/headers
|
||||
::cb/cb-access-sign)))))))))
|
||||
|
||||
(defn make-account-client [http-client]
|
||||
(#'coinbase-pro.client/reify-exchange-account-client {::http/client http-client}))
|
||||
|
||||
(defn account-client-returning [resp]
|
||||
(make-account-client (http-client-returning resp)))
|
||||
(defn account-client-throwing [e]
|
||||
(make-account-client (http-client-throwing e)))
|
||||
|
||||
(deftest test-accounts
|
||||
(let [balance (bigdec (rand 10000000))
|
||||
available (bigdec (rand balance))
|
||||
hold (bigdec (- balance available))
|
||||
id (gen-uuid)
|
||||
profile-id (gen-uuid)
|
||||
currency "BTC"
|
||||
result [{:id (str id)
|
||||
:profile_id (str profile-id)
|
||||
:currency currency
|
||||
:balance (str balance)
|
||||
:available (str available)
|
||||
:hold (str hold)
|
||||
:trading_enabled true}
|
||||
{:id (str (gen-uuid))
|
||||
:profile_id (str (gen-uuid))
|
||||
:currency "ETH"
|
||||
:balance (str balance)
|
||||
:available (str available)
|
||||
:hold (str hold)
|
||||
:trading_enabled true}]
|
||||
response (resp :body result)]
|
||||
|
||||
(testing "get-accounts!"
|
||||
|
||||
(is-success? (client/get-accounts! (account-client-returning response)))
|
||||
|
||||
(is-failure? (client/get-accounts! (account-client-throwing (RuntimeException. "oops!"))))
|
||||
|
||||
(is-true (every? acct/account?
|
||||
(vals (unwrap (client/get-accounts! (account-client-returning response))))))
|
||||
|
||||
(is (= balance
|
||||
(-> (account-client-returning response)
|
||||
(client/get-accounts!)
|
||||
(unwrap)
|
||||
:btc
|
||||
(acct/balance))))
|
||||
(is (= available
|
||||
(-> (account-client-returning response)
|
||||
(client/get-accounts!)
|
||||
(unwrap)
|
||||
:btc
|
||||
(acct/available))))
|
||||
(is (= hold
|
||||
(-> (account-client-returning response)
|
||||
(client/get-accounts!)
|
||||
(unwrap)
|
||||
:btc
|
||||
(acct/hold))))
|
||||
(is (= (-> currency str/lower-case keyword)
|
||||
(-> (account-client-returning response)
|
||||
(client/get-accounts!)
|
||||
(unwrap)
|
||||
:btc
|
||||
(acct/currency)))))
|
||||
|
||||
(testing "get-account!"
|
||||
(is-success? (client/get-account! (account-client-returning response) :btc))
|
||||
(is-success? (client/get-account! (account-client-returning response) :eth))
|
||||
(is-failure? (client/get-account! (account-client-returning response) :ada))
|
||||
(is-true (-> (client/get-account! (account-client-returning response) :btc)
|
||||
(unwrap)
|
||||
(acct/account?))))))
|
||||
|
||||
(deftest test-cancel-order
|
||||
(let [order-id (gen-uuid)]
|
||||
(is-success? (client/cancel-order! (account-client-returning (resp :body order-id )) order-id))
|
||||
(is-failure? (client/cancel-order! (account-client-throwing (RuntimeException. "oops!")) order-id))
|
||||
(is-failure? (client/cancel-order! (account-client-throwing (ex-info "not found" { :status 404 }))
|
||||
order-id))
|
||||
(is (= 404
|
||||
(http/status
|
||||
(client/cancel-order! (account-client-throwing (ex-info "Not found!" { :status 404 }))
|
||||
order-id))))))
|
||||
|
||||
(defmacro test-order-property [order-fn val params]
|
||||
`(is (= ~val (-> (client/get-order! (account-client-returning (resp :body (gen-order ~params))) (gen-uuid))
|
||||
(unwrap)
|
||||
~order-fn))))
|
||||
|
||||
(deftest test-get-orders
|
||||
(testing "get-order!"
|
||||
(let [order-id (gen-uuid)
|
||||
respond-with-order (fn [m] (account-client-returning
|
||||
(resp :body (gen-order (merge { :id order-id } m)))))]
|
||||
(is-success? (client/get-order! (respond-with-order {}) order-id))
|
||||
(is-failure? (client/get-order! (account-client-throwing (RuntimeException. "oops!")) order-id))
|
||||
(is-failure? (client/get-order! (account-client-throwing (ex-info "not found" { :status 404 })) order-id))
|
||||
(is (= 404
|
||||
(http/status
|
||||
(client/get-order! (account-client-throwing (ex-info "not found" { :status 404 })) order-id))))
|
||||
|
||||
(test-order-property order/id order-id { :id order-id })
|
||||
|
||||
(let [price (bigdec (rand 100000))
|
||||
size (bigdec (rand 100000))
|
||||
fees (bigdec (rand 1000))
|
||||
stop-price (bigdec (rand 100000))]
|
||||
(test-order-property order/price price { :price price })
|
||||
(test-order-property order/size size { :size size })
|
||||
(test-order-property order/fees fees { :fill_fees fees })
|
||||
(test-order-property order/stop-price stop-price { :stop_price stop-price })
|
||||
(test-order-property order/stop-price nil { :stop_price nil }))
|
||||
|
||||
(test-order-property order/currency :btc { :product_id "BTC-USD" })
|
||||
(test-order-property order/currency :eth { :product_id "ETH-USD" })
|
||||
(is-failure? (-> (client/get-order! (respond-with-order { :product_id "BTC-USD?" }) order-id)
|
||||
(map-success order/currency)))
|
||||
|
||||
(test-order-property order/limit? true { :type :limit })
|
||||
(test-order-property order/market? false { :type :limit })
|
||||
|
||||
(test-order-property order/limit? false { :type :market })
|
||||
(test-order-property order/market? true { :type :market })
|
||||
|
||||
(test-order-property order/stop? true { :stop :stop })
|
||||
(test-order-property order/stop? true { :stop :entry })
|
||||
(test-order-property order/stop? false { :stop nil })
|
||||
|
||||
(test-order-property order/stop-loss? true { :stop :loss })
|
||||
(test-order-property order/stop-gain? false { :stop :loss })
|
||||
(test-order-property order/stop-loss? false { :stop :entry })
|
||||
(test-order-property order/stop-gain? true { :stop :entry })
|
||||
(test-order-property order/stop-loss? false { :stop nil })
|
||||
(test-order-property order/stop-gain? false { :stop nil })
|
||||
|
||||
(test-order-property order/sell? true { :side :sell })
|
||||
(test-order-property order/buy? false { :side :sell })
|
||||
(test-order-property order/sell? false { :side :buy })
|
||||
(test-order-property order/buy? true { :side :buy })
|
||||
|
||||
(test-order-property order/filled? true { :done_reason :filled })
|
||||
(test-order-property order/filled? false { :done_reason nil })
|
||||
|
||||
(test-order-property order/created test-time { :created_at test-time-str})
|
||||
(test-order-property order/completed test-time { :done_at test-time-str})
|
||||
(test-order-property order/completed nil { :done_at nil })))
|
||||
|
||||
(testing "get-orders!"
|
||||
(let [orders (map (fn [_] (gen-order)) (range 10))]
|
||||
(is-success? (client/get-orders! (account-client-returning (resp :body orders)) :btc))
|
||||
(is (= (count orders)
|
||||
(count (unwrap (client/get-orders! (account-client-returning (resp :body orders)) :btc)))))
|
||||
(is-true (->> (client/get-orders! (account-client-returning (resp :body orders)) :btc)
|
||||
(unwrap)
|
||||
(every? order/order?))))))
|
|
@ -0,0 +1,77 @@
|
|||
(ns coinbase-pro.order-test
|
||||
(:require [coinbase-pro.order :as cb-order]
|
||||
[coinbase-pro.test-helpers :refer [gen-order
|
||||
gen-limit-order
|
||||
gen-limit-buy
|
||||
gen-limit-sell
|
||||
gen-stop-gain
|
||||
gen-stop-loss]]
|
||||
[fudo-clojure.common :refer [is-valid? is-invalid? sample]]
|
||||
|
||||
[clojure.test :as t :refer [deftest testing is]]))
|
||||
|
||||
(defn gen-amount [& {:keys [min max]
|
||||
:or {min 0 max (+ min 10000000)}}]
|
||||
(bigdec (+ (rand (- max min)) min)))
|
||||
|
||||
(deftest test-order
|
||||
(testing "order"
|
||||
(is-valid? ::cb-order/base-order (gen-order))
|
||||
|
||||
(is-invalid? ::cb-order/base-order (gen-order { ::cb-order/product-id :btc-usd }))
|
||||
(is-invalid? ::cb-order/base-order (gen-order { ::cb-order/product-id "USD-BTC" }))
|
||||
(is-invalid? ::cb-order/base-order (gen-order { ::cb-order/product-id "BTCBTCBTC-USD" }))
|
||||
(is-invalid? ::cb-order/base-order (gen-order { ::cb-order/product-id "BTC-USDC" }))
|
||||
(is-invalid? ::cb-order/base-order (gen-order { ::cb-order/product-id nil }))
|
||||
|
||||
(is-invalid? ::cb-order/base-order (gen-order { ::cb-order/side :oops }))
|
||||
(is-invalid? ::cb-order/base-order (gen-order { ::cb-order/side nil }))
|
||||
|
||||
(is-invalid? ::cb-order/base-order (gen-order { ::cb-order/price "12345" }))
|
||||
(is-invalid? ::cb-order/base-order (gen-order { ::cb-order/price 5 }))
|
||||
(is-invalid? ::cb-order/base-order (gen-order { ::cb-order/price nil }))
|
||||
|
||||
(is-invalid? ::cb-order/base-order (gen-order { ::cb-order/size "12345" }))
|
||||
(is-invalid? ::cb-order/base-order (gen-order { ::cb-order/size 5 }))
|
||||
(is-invalid? ::cb-order/base-order (gen-order { ::cb-order/size nil }))))
|
||||
|
||||
(testing "buy-order"
|
||||
(is-valid? ::cb-order/buy-order (gen-order { ::cb-order/side :buy }))
|
||||
|
||||
(is-invalid? ::cb-order/buy-order (gen-order { ::cb-order/side :sell })))
|
||||
|
||||
(testing "sell-order"
|
||||
(is-valid? ::cb-order/sell-order (gen-order { ::cb-order/side :sell }))
|
||||
(is-invalid? ::cb-order/sell-order (gen-order { ::cb-order/side :buy })))
|
||||
|
||||
(testing "limit-buy-order"
|
||||
(is-valid? ::cb-order/limit-buy-order (gen-limit-buy))
|
||||
|
||||
(is-invalid? ::cb-order/limit-buy-order (gen-limit-buy { ::cb-order/side :sell }))
|
||||
(is-invalid? ::cb-order/limit-buy-order (gen-limit-buy { ::cb-order/type :market })))
|
||||
|
||||
(testing "limit-sell-order"
|
||||
(is-valid? ::cb-order/limit-sell-order (gen-limit-sell))
|
||||
|
||||
(is-invalid? ::cb-order/limit-sell-order (gen-limit-sell { ::cb-order/side :buy }))
|
||||
(is-invalid? ::cb-order/limit-sell-order (gen-limit-sell { ::cb-order/type :market })))
|
||||
|
||||
(testing "stop-gain-order"
|
||||
(is-valid? ::cb-order/stop-gain-order (gen-stop-gain))
|
||||
|
||||
(is-invalid? ::cb-order/stop-gain-order (gen-stop-gain {::cb-order/stop :loss}))
|
||||
(is-invalid? ::cb-order/stop-gain-order (gen-stop-gain {::cb-order/stop-price nil}))
|
||||
(is-invalid? ::cb-order/stop-gain-order (gen-stop-gain {::cb-order/stop-price "12345"}))
|
||||
(is-invalid? ::cb-order/stop-gain-order (gen-stop-gain {::cb-order/stop-price 5}))
|
||||
(is-invalid? ::cb-order/stop-gain-order (gen-stop-gain {::cb-order/stop-price (bigdec 8)
|
||||
::cb-order/price (bigdec 7)})))
|
||||
|
||||
(testing "stop-loss-order"
|
||||
(is-valid? ::cb-order/stop-loss-order (gen-stop-loss))
|
||||
|
||||
(is-invalid? ::cb-order/stop-loss-order (gen-stop-loss {::cb-order/stop :entry}))
|
||||
(is-invalid? ::cb-order/stop-loss-order (gen-stop-loss {::cb-order/stop-price nil}))
|
||||
(is-invalid? ::cb-order/stop-loss-order (gen-stop-loss {::cb-order/stop-price "12345"}))
|
||||
(is-invalid? ::cb-order/stop-loss-order (gen-stop-loss {::cb-order/stop-price 5}))
|
||||
(is-invalid? ::cb-order/stop-loss-order (gen-stop-loss {::cb-order/stop-price (bigdec 7)
|
||||
::cb-order/price (bigdec 8)})))
|
|
@ -0,0 +1,49 @@
|
|||
(ns coinbase-pro.test-helpers
|
||||
(:require [clojure.test :as t]
|
||||
[clojure.spec.alpha :as s]
|
||||
|
||||
[fudo-clojure.common :refer [sample]]
|
||||
[coinbase-pro.order :as cb-order]))
|
||||
|
||||
(defn gen-amount [& {:keys [min max]
|
||||
:or {min 0 max (+ min 10000000)}}]
|
||||
(bigdec (+ (rand (- max min)) min)))
|
||||
|
||||
(defn gen-order
|
||||
([] (gen-order {}))
|
||||
([ks] (merge {::cb-order/product-id (sample ["BTC-USD" "ETH-USD" "ADA-USD"])
|
||||
::cb-order/type (sample [:limit :market])
|
||||
::cb-order/side (sample [:buy :sell])
|
||||
::cb-order/price (gen-amount)
|
||||
::cb-order/size (gen-amount)}
|
||||
ks)))
|
||||
|
||||
(defn gen-limit-order
|
||||
([ks] (gen-order (merge { ::cb-order/type :limit } ks)))
|
||||
([] (gen-limit-order {})))
|
||||
|
||||
(defn gen-limit-buy
|
||||
([ks] (gen-limit-order (merge { ::cb-order/side :buy } ks)))
|
||||
([] (gen-limit-buy {})))
|
||||
(defn gen-limit-sell
|
||||
([ks] (gen-limit-order (merge { ::cb-order/side :sell } ks)))
|
||||
([] (gen-limit-sell {})))
|
||||
|
||||
(defn gen-stop-gain
|
||||
([] (gen-stop-gain {}))
|
||||
([ks]
|
||||
(let [stop-price (gen-amount)
|
||||
price (gen-amount :min stop-price)]
|
||||
(gen-limit-buy (merge {::cb-order/stop :entry
|
||||
::cb-order/stop-price stop-price
|
||||
::cb-order/price price}
|
||||
ks)))))
|
||||
(defn gen-stop-loss
|
||||
([] (gen-stop-loss {}))
|
||||
([ks]
|
||||
(let [stop-price (gen-amount)
|
||||
price (gen-amount :max stop-price)]
|
||||
(gen-limit-sell (merge {::cb-order/stop :loss
|
||||
::cb-order/stop-price stop-price
|
||||
::cb-order/price price}
|
||||
ks)))))
|
Loading…
Reference in New Issue