(ns watcher.core
(:require [postal.core :as postal]
[postal.sendmail :as local]
[postal.smtp :as smtp]
[clj-http.client :as client]
[clojure.data.json :as json]
[clj-time.local :as l]
(:require [watcher.log :as log])
(:gen-class))
;;;;;; constants ;;;;;;
(def zoho (read-string (slurp "zoho.edn"))) ; mail config
(def tr 0.001)
(def trminus (* tr -1))
(def sec 1000)
(def minute (* 60 sec))
(def waitp minute)
(def totalwait (* 3 60)) ; 3hours. totalwait * waitp
(def intervalstep 30) ; every 30 minutes notify
(def title "btcusd notify")
(def titleshutdown "btcusd watch")
(def titlestartup "startup btcusd watch")
(def recp "ben@gmail.com")
(def frommail "ben@metaexchange.io")
(def btcusd-price (atom -1))
(def btcusd-prev (atom -1))
(def delta (atom -1))
(def maxd (atom -1))
(def mind (atom 1))
(def nfirst (atom false))
(def ptimestamp (atom nil))
(def ptimestamp-prev (atom nil))
(defn sendmailinfo
"send signup via email"
[info subj]
(log/info "sending mail " info)
(postal/send-message
zoho {:from frommail :to [recp] :subject subj :body (str info)}))
;;;;;; functions ;;;;;;
(defn formatDouble [t]
(format "%.3f" (double (* t 100))))
(defn mailstr [t]
(str (formatDouble t) " % in the last " (/ waitp 1000)
" seconds. \n" "btcusd: " @btcusd-price "\nlast " @btcusd-prev)
"\n" (l/local-now))
(defn sendmailtresh []
(sendmailinfo (str (mailstr @delta)
"\nbids " @polobids "\nasks: " @poloasks) title))
(defn btce []
(json/read-str (get (client/get "https://btc-e.com/api/3/ticker/btc_usd" {:query-params {}}) :body)))
(defn roc [cur prev]
"rate of change"
(let [d (- cur prev)]
(/ d cur)))
(defn currentROC []
"current rate of change"
(roc @btcusd-price @btcusd-prev))
;;;;;; updates ;;;;;;
(defn parse-number
"Reads a number from a string. Returns nil if not a number."
[s]
(if (re-find #"^-?\d+\.?\d*$" s)
(read-string s)))
(defn logcurrent []
(def sttr (atom ""))
(reset! sttr (str @sttr "delta: " (formatDouble (currentROC)) "%" " btc: " @btcusd-price
" last: " @btcusd-prev " timestamp: " @ptimestamp "\n"))
(reset! sttr (str @sttr "bids\n" (prettyob @polobids) "\n"))
(reset! sttr (str @sttr "asks\n" (prettyob @poloasks) "\n"))
(log/info @sttr)
(if @nfirst
(sendmailinfo @sttr "watch")))
(defn updates [new-price]
(log/info "updates")
(reset! delta (currentROC))
(reset! btcusd-prev @btcusd-price)
(reset! btcusd-price new-price)
(if (> @delta @maxd)
(reset! maxd @delta))
(if (< @delta @mind)
(reset! mind @delta))
(updateob)
(logcurrent)
(if (not @nfirst)
(reset! nfirst true)))
(defn updateaction []
;lost more than tr
(if (and (< @delta trminus)) ; (> @delta -0.5) (< @delta 0.5))
(sendmailtresh)
(log/debug "no significant change" (formatDouble @delta))))
(defn update-timestamp [new-ts]
(reset! ptimestamp-prev @ptimestamp)
(reset! ptimestamp new-ts))
(defn pricewatch []
(let [v (btce)]
(updates (get-in v ["btc_usd" "last"]))
(update-timestamp (get-in v ["btc_usd" "updated"])))
(updateaction))
(defn send-interval [i]
(log/info "interval function")
(sendmailinfo (str "price: " @btcusd-price ". previous: " @btcusd-prev
". ROC: " (format "%.3f" (currentROC)) ". min ROC: " @mind) "btcusd interval"))
(defn doseq-interval
"a function to do things in a thread in steps. no threadpool"
[f coll interval]
(doseq [i coll]
(f)
(Thread/sleep interval)))
;;;;;; main thread ;;;;;;
(defn startup []
;(sendmailinfo "startup btcusd watcher" titlestartup)
(log/info "starting watcher app"))
(defn shutdown []
(log/info "shutdown watcher app")
(sendmailinfo "shutdown" titleshutdown))
(defn -main
"main"
[& args]
(log/info "startup")
(startup)
(doseq-interval pricewatch (range totalwait) waitp)
(shutdown))