Fixes for linting...and add real checks.

This commit is contained in:
niten 2022-06-21 16:48:42 -07:00
parent 3b4e2b6790
commit 72d3613970

View File

@ -1,16 +1,15 @@
(ns pricebot.core (ns pricebot.core
(:require [clojure.core.async :as async :refer [go go-loop >! >!! <! <!! chan timeout alt!]] (:require [clojure.core.async :as async :refer [go go-loop >! >!! <! <!! chan timeout alt!]]
[bebot.client :as bebot-client]
[bebot.api.channel :as bebot]
[bebot.logger :as bebot-log] [bebot.logger :as bebot-log]
[coinbase-pro.client :as coinbase-pro] [coinbase-pro.client :as coinbase-pro]
[exchange.client :as client] [exchange.client :as client]
[fudo-clojure.common :refer [*-> date<]] [fudo-clojure.common :refer [date<]]
[fudo-clojure.result :as result :refer [map-success send-success dispatch-result success? unwrap]] [fudo-clojure.result :as result :refer [map-success send-success]]
[fudo-clojure.logging :as log] [fudo-clojure.logging :as log]
[clojure.string :as str] [clojure.string :as str]
[clojure.math.numeric-tower :refer [floor]] [clojure.math.numeric-tower :refer [floor]]
[clojure.tools.cli :refer [parse-opts]] [clojure.tools.cli :refer [parse-opts]]
[clojure.pprint :refer [pprint]]
[camel-snake-kebab.core :refer [->SCREAMING_SNAKE_CASE]]) [camel-snake-kebab.core :refer [->SCREAMING_SNAKE_CASE]])
(:import java.math.RoundingMode (:import java.math.RoundingMode
java.time.format.DateTimeFormatter java.time.format.DateTimeFormatter
@ -33,7 +32,7 @@
:or {delay 60 :or {delay 60
buffer-size 10}}] buffer-size 10}}]
(let [stop-chan (chan) (let [stop-chan (chan)
price-chan (chan 10) price-chan (chan buffer-size)
client (coinbase-pro/connect :hostname hostname) client (coinbase-pro/connect :hostname hostname)
get-price #(client/get-market-price! client currency)] get-price #(client/get-market-price! client currency)]
(reify (reify
@ -52,15 +51,6 @@
IPriceChannel IPriceChannel
(get-price-channel [_] price-chan)))) (get-price-channel [_] price-chan))))
;; Bots can have a list of price checkers that take current price & price
;; history, and potentially return a message to send to the channel
(defn- lpthru [label o]
(println (str "****** " label))
(clojure.pprint/pprint o)
(println "******")
o)
(let [formatter (.withZone DateTimeFormatter/ISO_LOCAL_TIME (ZoneId/systemDefault))] (let [formatter (.withZone DateTimeFormatter/ISO_LOCAL_TIME (ZoneId/systemDefault))]
(defn- format-instant [inst] (defn- format-instant [inst]
(.format formatter inst))) (.format formatter inst)))
@ -84,7 +74,7 @@
(swap! mute-until (fn [_] (.plus (:instant curr) mute-duration)))))) (swap! mute-until (fn [_] (.plus (:instant curr) mute-duration))))))
(swap! prev (fn [_] curr))))) (swap! prev (fn [_] curr)))))
(defn- minimize-time-gap [prices threshold target] (defn- minimize-time-gap [prices target]
(let [smaller-duration? (fn [a b] (> 0 (.compareTo a b))) (let [smaller-duration? (fn [a b] (> 0 (.compareTo a b)))
get-gap (fn [m] (Duration/between target (:instant m)))] get-gap (fn [m] (Duration/between target (:instant m)))]
(when-let [initial-measure (first prices)] (when-let [initial-measure (first prices)]
@ -106,8 +96,8 @@
close-enough? (fn [target t] (date< (.minus target threshold) t (.plus target threshold)))] close-enough? (fn [target t] (date< (.minus target threshold) t (.plus target threshold)))]
(fn [curr] (fn [curr]
(let [target-time (.minus (:instant curr) duration) (let [target-time (.minus (:instant curr) duration)
prev (minimize-time-gap @prices threshold target-time)] prev (minimize-time-gap @prices target-time)]
(if prev (when prev
(let [diff-pct (.divide (- (:price curr) (:price prev)) (:price curr) RoundingMode/HALF_EVEN)] (let [diff-pct (.divide (- (:price curr) (:price prev)) (:price curr) RoundingMode/HALF_EVEN)]
(if (close-enough? target-time (:instant prev)) (if (close-enough? target-time (:instant prev))
(when (> (Math/abs diff-pct) percentage) (when (> (Math/abs diff-pct) percentage)
@ -141,7 +131,7 @@
(let [chan (get-price-channel @price-chan)] (let [chan (get-price-channel @price-chan)]
(go-loop [measure (<! chan)] (go-loop [measure (<! chan)]
(when (not (= measure :stopped)) (when (not (= measure :stopped))
(doseq [[id check] @checks] (doseq [[_ check] @checks]
(send-success measure check)) (send-success measure check))
(recur (<! chan))))) (recur (<! chan)))))
(start! @price-chan) (start! @price-chan)
@ -156,10 +146,6 @@
(remove-check! [_ id] (remove-check! [_ id]
(swap! checks dissoc id)))))) (swap! checks dissoc id))))))
(defn- make-bot [& {:keys [hostname currency logger]}]
(let [checks (create-checks logger)]
(reify-bot hostname hostname currency checks)))
(def cli-opts (def cli-opts
[["-x" "--exchange-host HOST" "Hostname of the Coinbase Pro target host."] [["-x" "--exchange-host HOST" "Hostname of the Coinbase Pro target host."]
["-b" "--bebot-url URL" "URL of the target Mattermost server."] ["-b" "--bebot-url URL" "URL of the target Mattermost server."]
@ -180,17 +166,17 @@
(System/exit status)) (System/exit status))
(defn pprint-to-string [o] (defn pprint-to-string [o]
(with-out-str (clojure.pprint/pprint o))) (with-out-str (pprint o)))
(defn get-args [keys args] (defn get-args [keys args]
(let [{:keys [options arguments errors summary]} (parse-opts args cli-opts) (let [{:keys [options errors summary]} (parse-opts args cli-opts)
resolved (into {} (map (partial get-key options) keys)) resolved (into {} (map (partial get-key options) keys))
missing (filter (fn [k] (not (get resolved k))) keys)] missing (filter (fn [k] (not (get resolved k))) keys)]
(cond (:help options) (exit! 0 summary) (cond (:help options) (exit! 0 summary)
errors (exit! 1 (str/join \newline errors)) errors (exit! 1 (str/join \newline errors))
(not (empty? missing)) (exit! 2 (str "missing arguments: " (str/join " " (map name missing)) (seq missing) (exit! 2 (str "missing arguments: " (str/join " " (map name missing))
\newline \newline
"available: " (pprint-to-string resolved))) "available: " (pprint-to-string resolved)))
:else resolved))) :else resolved)))
(defn -main [& args] (defn -main [& args]
@ -205,20 +191,22 @@
bebot-url bebot-url
bebot-auth-token-file bebot-auth-token-file
bebot-channel-id bebot-channel-id
notify-user]} (get-args required-keys args)] notify-user]} (get-args required-keys args)
(let [logger (bebot-log/make-logger bebot-url logger (bebot-log/make-logger bebot-url
(-> bebot-auth-token-file slurp str/trim-newline) (-> bebot-auth-token-file slurp str/trim-newline)
bebot-channel-id bebot-channel-id
notify-user) notify-user)
checks (create-checks logger) checks (create-checks logger)
bot (reify-bot exchange-host (-> currency str/lower-case keyword)) bot (reify-bot exchange-host
shutdown (chan)] (-> currency str/lower-case keyword)
(.addShutdownHook (Runtime/getRuntime) checks)
(Thread. (fn [] shutdown (chan)]
(println "Stopping pricebot...") (.addShutdownHook (Runtime/getRuntime)
(stop! bot) (Thread. (fn []
(>!! shutdown true)))) (println "Stopping pricebot...")
(log/notify! logger (str "Hi! I'm going to start watching the price of " (name currency) " for you.")) (stop! bot)
(start! bot) (>!! shutdown true))))
(<!! shutdown) (log/notify! logger (str "Hi! I'm going to start watching the price of " (name currency) " for you."))
(System/exit 0)))) (start! bot)
(<!! shutdown)
(System/exit 0)))