going somewhere
This commit is contained in:
1 parent
b972e6e69f
commit
10b054db25
31 files changed
+621
-302
No files matched your search
@@ -1,3 +1,7 @@
|
||||
; This is the service that broadcast market data to the
|
||||
; world. The users need to expect websocket and REST api.
|
||||
|
||||
|
||||
(ns skirmish.bookkeeper.core
|
||||
(:require [org.httpkit.server :refer [run-server]]
|
||||
[compojure.handler :refer [api]]
|
||||
|
||||
@@ -4,15 +4,50 @@
|
||||
[datomic.api :as d]
|
||||
[slingshot.slingshot :refer [try+ throw+]]
|
||||
[skirmish.rest.util :refer [build-response not-found-resp get-body]]
|
||||
[taoensso.timbre :as timbre]))
|
||||
[taoensso.timbre :as timbre]
|
||||
[skirmish.routes.core :as routes]
|
||||
[clj-http.client :as http-client]))
|
||||
|
||||
|
||||
(defn create-user [conn]
|
||||
(defn generate-amount-for-new-user [seed]
|
||||
(let [random (new java.util.Random seed)
|
||||
random-fn #(. random nextInt 20000)]
|
||||
(* 100 (reduce + (take 100 (repeatedly random-fn))))))
|
||||
|
||||
|
||||
(defn create-account [account-id sec-id]
|
||||
#:skirmish.broker.account{:id account-id
|
||||
:sec-id sec-id
|
||||
:private-key (str account-id)})
|
||||
|
||||
|
||||
(defn create-user-account [seed default-sec-id]
|
||||
(let [url (routes/get-path-for :ledger/security {:sec-id (name default-sec-id)})
|
||||
account-id (java.util.UUID/randomUUID)
|
||||
amount (generate-amount-for-new-user seed)
|
||||
payload [amount (str account-id)]]
|
||||
(timbre/info "Creating new account " account-id " with ammount " amount " of " default-sec-id)
|
||||
(timbre/info url)
|
||||
(http-client/post url {:body (str payload)})
|
||||
(create-account account-id default-sec-id)))
|
||||
|
||||
(defn create-user-seed-from-email [email]
|
||||
(let [hasher (java.security.MessageDigest/getInstance "SHA-256")
|
||||
salt (. "this should be private and set in config" getBytes "utf-8")
|
||||
msg (. email getBytes "utf-8")
|
||||
_ (doto hasher (.update salt) (.update msg))
|
||||
hash (. hasher digest)]
|
||||
(int (mod (new BigInteger 1 hash) (biginteger Integer/MAX_VALUE)))))
|
||||
|
||||
|
||||
(defn create-user [conn default-sec-id]
|
||||
(fn [{body :body :as request}]
|
||||
(try+
|
||||
(let [email (-> body get-body :email)]
|
||||
(timbre/info "Creating new user" email)
|
||||
@(d/transact conn [[:skirmish.broker.user/create-user email]])
|
||||
(let [email (-> body get-body :email)
|
||||
_ (timbre/info "Creating new user" email)
|
||||
seed (create-user-seed-from-email email)
|
||||
account (create-user-account seed default-sec-id)]
|
||||
@(d/transact conn [[:skirmish.broker.user/create-user email [account]]])
|
||||
(build-response request {:status 201 :headers {"location" (str "/api/1/users/" email)}}))
|
||||
(catch [:error-type :4XX] e (build-response request e))
|
||||
(catch [:error-type :5XX] e (build-response request e)))))
|
||||
@@ -24,6 +59,7 @@
|
||||
ret (map #(assoc % :skirmish.broker.account/url (url %)) accounts)]
|
||||
(assoc user :skirmish.broker.user/accounts ret)))
|
||||
|
||||
|
||||
(defn get-user [conn]
|
||||
(fn [{{email :email} :route-params :as request}]
|
||||
(try+
|
||||
@@ -54,14 +90,15 @@
|
||||
(catch [:error-type :4XX] e (build-response request e))
|
||||
(catch [:error-type :5XX] e (build-response request e)))))
|
||||
|
||||
|
||||
(defn get-account [conn]
|
||||
(fn [email account]))
|
||||
|
||||
|
||||
(defn create-routes [conn]
|
||||
(defn create-routes [conn config]
|
||||
(routes
|
||||
(context "/api/1" []
|
||||
(POST "/users" [] (create-user conn))
|
||||
(POST "/users" [] (create-user conn (:default-sec-id config)))
|
||||
(GET "/users" [] (get-all-users conn))
|
||||
(GET "/users/:email" [email] (get-user conn))
|
||||
(GET "/users/:email/accounts/:account" [email account] (get-account conn)))
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
; The backend of the skirmish web user interface.
|
||||
|
||||
(ns skirmish.broker.core
|
||||
(:require [org.httpkit.server :refer [run-server]]
|
||||
[compojure.handler :refer [api]]
|
||||
@@ -11,7 +13,7 @@
|
||||
(defn -main [config securities-api exchange-apis]
|
||||
(let [webserver-config (:api-broker config)
|
||||
conn (create-connection (-> webserver-config :datomic :transactor-url))
|
||||
api-routes (create-routes conn)]
|
||||
api-routes (create-routes conn webserver-config)]
|
||||
(timbre/info "Running skirmish.broker.core server with config: " webserver-config)
|
||||
(run-server
|
||||
(-> api-routes api cors-middleware)
|
||||
|
||||
@@ -8,12 +8,11 @@
|
||||
:db/doc "Create a new user in the system."
|
||||
:db/fn (d/function {:lang "clojure"
|
||||
:requires ['[slingshot.slingshot]]
|
||||
:params '[db email]
|
||||
:params '[db email accounts]
|
||||
:code '(let [existing-user (d/pull db '[:db/id] [:skirmish.broker.user/email email])]
|
||||
;:skirmish.broker.user/accounts []])]
|
||||
(when (-> existing-user :db/id some?)
|
||||
(slingshot.slingshot/throw+ {:error-type :4XX :status 400 :body {:error "A user with this email already exists."}}))
|
||||
[#:skirmish.broker.user{:email email}])})}])
|
||||
[#:skirmish.broker.user{:email email :accounts accounts}])})}])
|
||||
|
||||
|
||||
(def accounts-schema [
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
(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-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 orders]
|
||||
(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 exchange-apis]
|
||||
(let [benchmark-config (:benchmark config)
|
||||
requests (-> benchmark-config :number-of-requests-per-securities (make-requests (:securities config)))
|
||||
orders (create-orders-channel (:securities config))
|
||||
results (benchmark requests orders)]
|
||||
(display-results (async/<!! (async/reduce compute-result {} results)))))
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
(ns skirmish.cli.core)
|
||||
@@ -1,56 +0,0 @@
|
||||
(ns skirmish.cli.exchange
|
||||
(:require [skirmish.exchange.match :refer [sell?]]
|
||||
[skirmish.util :refer :all]
|
||||
[skirmish.exchange.exchange :refer [empty-book create-exchange-for-security]]
|
||||
[skirmish.order] ;; for spec side effect
|
||||
[slingshot.slingshot :refer [try+ throw+]]
|
||||
[clojure.edn :as edn]
|
||||
[clojure.core.async :refer [chan go >!! <!!]]
|
||||
[clojure.spec.alpha :as s]))
|
||||
|
||||
|
||||
(def incoming-order-channel (chan))
|
||||
|
||||
;(def trades-channel (chan))
|
||||
;
|
||||
;(def new-book-order-channel (chan))
|
||||
|
||||
(defn print-chan [prefix c]
|
||||
(loop [shit (<!! c)]
|
||||
(println prefix shit)
|
||||
(recur (<!! c))))
|
||||
|
||||
;(def master-book {:sell (create-sell-book)
|
||||
; :buy (create-buy-book)})
|
||||
|
||||
;;; repl things.
|
||||
(defn read-order []
|
||||
(try
|
||||
(print "$ ")
|
||||
(let [order (edn/read-string (read-line))
|
||||
valid? (s/valid? :book.order/order order)]
|
||||
(if valid? order nil))
|
||||
(catch Exception e nil)))
|
||||
|
||||
(def order-ids (atom -1))
|
||||
|
||||
|
||||
(defn add-id-to-order [order]
|
||||
(assoc order :book.order/id (swap! order-ids inc)))
|
||||
|
||||
|
||||
(defn -main [& args]
|
||||
(let [initial-book empty-book
|
||||
incoming-order-channel (chan)
|
||||
[trades-channel new-book-order-channel] (create-exchange-for-security initial-book incoming-order-channel)]
|
||||
(go (print-chan "trade > " trades-channel))
|
||||
(go (print-chan "order > " new-book-order-channel))
|
||||
(loop [order (read-order)]
|
||||
(if (some? order)
|
||||
(do
|
||||
(let [order-with-id (add-id-to-order order)]
|
||||
(>!! incoming-order-channel order-with-id)
|
||||
(println "\n\norder accepted with id: " (:book.order/id order-with-id))))
|
||||
|
||||
(println "invalid order"))
|
||||
(recur (read-order)))))
|
||||
@@ -1,7 +1,5 @@
|
||||
(ns skirmish.core
|
||||
(:require [skirmish.cli.core]
|
||||
[skirmish.cli.benchmark]
|
||||
[skirmish.exchange.core]
|
||||
(:require [skirmish.exchange.core]
|
||||
[skirmish.wui.server]
|
||||
[skirmish.bookkeeper.core]
|
||||
[skirmish.exchange-gateway.core]
|
||||
|
||||
@@ -106,6 +106,3 @@
|
||||
(<! orders-copy)
|
||||
(<! trades-copy)
|
||||
(recur (<! new-order-publisher) new-book)))))
|
||||
|
||||
|
||||
|
||||
@@ -153,6 +153,11 @@
|
||||
|
||||
|
||||
(defn emit-securities [conn]
|
||||
"Emit securities in the specified wallet. This is the only way to
|
||||
create securities.
|
||||
|
||||
payload
|
||||
[:amount :acount]"
|
||||
(fn [{{sec-id-str :sec-id} :route-params body :body :as request}]
|
||||
(try+
|
||||
(let [emit-request (-> body get-body (get-emit-securities-request (keyword sec-id-str)))]
|
||||
@@ -184,7 +189,6 @@
|
||||
|
||||
(defn create-routes-for-ledger [conn]
|
||||
(compojure/routes
|
||||
;; (routes/get-context :ledger)
|
||||
(routes/context :ledger []
|
||||
(routes/GET :ledger/home [] get-api-description)
|
||||
(routes/GET :ledger/accounts [] (get-available-accounts conn))
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
(head)
|
||||
[:body.body-container
|
||||
mount-target
|
||||
(include-js "/js/app.js")]))
|
||||
(include-js "/js/app.js")
|
||||
(include-js "https://sidecar.gitter.im/dist/sidecar.v1.js")]))
|
||||
|
||||
(defn loading-page []
|
||||
(html5
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
(ns skirmish.wui.components.home-page
|
||||
(:require [cljs-http.client :as http]
|
||||
[reagent.core :refer [atom]]
|
||||
[skirmish.wui.util :refer [email?]]
|
||||
[cljs.core.async :refer [<!] :refer-macros [go]]
|
||||
[secretary.core :as secretary]))
|
||||
|
||||
(defn email? [s]
|
||||
(let [email-pattern #"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$"]
|
||||
(->> s str (re-matches email-pattern) some?)))
|
||||
|
||||
|
||||
(defn create-user [email]
|
||||
(go
|
||||
(let [url "http://localhost:1605/api/1/users"
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
(ns skirmish.wui.components.otc-page
|
||||
(:require [reagent.core :refer [atom]]
|
||||
[skirmish.wui.util :refer [email?]]))
|
||||
|
||||
|
||||
(defn set-amount [state event]
|
||||
(let [inserted-value (-> event .-target .-value)
|
||||
new-value (first (re-find #"^\d+(\.\d*)?" inserted-value))]
|
||||
(swap! state #(assoc % :value new-value))))
|
||||
|
||||
(defn set-email [state event]
|
||||
(let [new-email (-> event .-target .-value)]
|
||||
(swap! state #(assoc % :email new-email))))
|
||||
|
||||
(defn set-sec-id [state event]
|
||||
(let [sec-id (-> event .-target .-value)]
|
||||
(swap! state #(assoc % :sec-id sec-id))))
|
||||
|
||||
|
||||
|
||||
(defn ready-to-transfer? [state]
|
||||
(and (-> state :value js/parseInt pos?)
|
||||
(-> state :email email?)))
|
||||
|
||||
(defn direct-send-confirmation []
|
||||
[:div.model
|
||||
[:div.modal-background]
|
||||
[:div.modal-content
|
||||
[:div.modal-card
|
||||
[:header.modal-card-head
|
||||
[:p.modal-card-title "Modal title"]
|
||||
[:button.delete]]]]
|
||||
[:a.button.modal-close.is-large {:aria-label "close"} "dslkds"]])
|
||||
|
||||
(defn direct-send []
|
||||
(let [state (atom {})]
|
||||
(fn []
|
||||
[:div
|
||||
[:div.content
|
||||
[:p "Send securities to another person."]
|
||||
[:pre (str @state)]]
|
||||
[:form.form
|
||||
[:div.field.has-addons.has-addons-centered
|
||||
[:div.control
|
||||
[:span.select
|
||||
[:select {:value (:sec-id @state)
|
||||
:on-change (partial set-sec-id state)}
|
||||
[:option {:value :foo :selected true} "damp"]
|
||||
[:option {:value :bar } "blah blah blah"]]]]
|
||||
[:div.control
|
||||
[:input.input {:type "text"
|
||||
:value (:value @state)
|
||||
:on-change (partial set-amount state)
|
||||
:placeholder "110.90"}]]
|
||||
[:div.control
|
||||
[:input.input {:type "text"
|
||||
:value (:email @state)
|
||||
:on-change (partial set-email state)
|
||||
:placeholder "boris.johnson@redneck.co.uk"}]]
|
||||
[:a.button.is-primary {:style {:marginLeft "1em"}
|
||||
:disabled (-> @state ready-to-transfer? not)} "transfer"]]]])))
|
||||
|
||||
(defn page [state]
|
||||
[:div
|
||||
;[direct-send-confirmation]
|
||||
[:div.content
|
||||
[:h5 "Do transaction without passing by the exchange."]]
|
||||
|
||||
[direct-send]])
|
||||
@@ -23,8 +23,9 @@
|
||||
accounts (map :body (<! (async/into [] ar)))]
|
||||
(reset! a-accounts accounts))))
|
||||
|
||||
(defn shit [urls accounts]
|
||||
(defn page-wrapper [accounts]
|
||||
[:div.content
|
||||
[:h5 "View all the assets you own."]
|
||||
[make-portfolio-summary-table accounts]])
|
||||
|
||||
|
||||
@@ -32,4 +33,4 @@
|
||||
(let [urls (map :skirmish.broker.account/url accounts)
|
||||
a-accounts (atom [])]
|
||||
(get-accounts-information urls a-accounts)
|
||||
#(shit urls @a-accounts)))
|
||||
#(page-wrapper @a-accounts)))
|
||||
@@ -49,7 +49,9 @@
|
||||
(defn create-currencies-table [securities]
|
||||
[:div
|
||||
[:div.content
|
||||
[:h3 "Currencies"]]
|
||||
[:h3 "Currencies"]
|
||||
[:p "A " [:em "currency"] " is currency that pays interest rate in
|
||||
its own nomination."]]
|
||||
[:table.table.is-hoverable.is-fullwidth
|
||||
table-head
|
||||
[:tbody
|
||||
@@ -58,7 +60,11 @@
|
||||
(defn create-equities-table [securities]
|
||||
[:div
|
||||
[:div.content
|
||||
[:h3 "Equities"]]
|
||||
[:h3 "Equities"]
|
||||
[:p "An " [:em "equity"] " is a type of security that pays dividends. In
|
||||
real life, an equity (or stock) is a share in a company ownership.
|
||||
When the company make profits, it shares the its profits between its stock
|
||||
holder in a pro rata fashion."]]
|
||||
[:table.table.is-hoverable.is-fullwidth
|
||||
table-head
|
||||
[:tbody
|
||||
@@ -76,4 +82,7 @@
|
||||
#(create-securities-table @securities)))
|
||||
|
||||
(defn page [state]
|
||||
(security-table))
|
||||
[:div
|
||||
[:div.content
|
||||
[:h5 "View the available securities for trading."]]
|
||||
[security-table]])
|
||||
@@ -6,12 +6,29 @@
|
||||
|
||||
(def done {:style {:text-decoration "line-through"}})
|
||||
|
||||
|
||||
(defn filter-out-done [n]
|
||||
(let [state (volatile! n)]
|
||||
(fn [item]
|
||||
(when (= (second item) done)
|
||||
(vswap! state dec))
|
||||
(or (not= (second item) done) (neg? @state)))))
|
||||
|
||||
|
||||
(defn todo-list [& items]
|
||||
(let [filter-fn (filter-out-done (- (count items) 20))
|
||||
item-to-show (filter filter-fn items)]
|
||||
[:ol
|
||||
(for [item item-to-show]
|
||||
item)]))
|
||||
|
||||
(defn page []
|
||||
[:div.content
|
||||
[:h5 "This still to do in this project. This is a jira for the poor (or for the geek)."]
|
||||
[:ol
|
||||
[:li "Broker"
|
||||
[:p "the brooker should use the exchange-gateway and ledge as apis. Nothing else."]
|
||||
[:ol
|
||||
[todo-list
|
||||
[:li done "implement basic login in the wui"]
|
||||
[:li done "implement basic web server"]
|
||||
[:li done "implement stupid login on server side."]
|
||||
@@ -22,14 +39,18 @@
|
||||
[:li done "make the " [:a {:href "/portfolio"} "portfolio"] " page representing the use accounts"]
|
||||
[:li done "make a somewhat pretty " [:a {:href "/securities/dapm"} "security"] " page"]
|
||||
[:li done "create two stocks"]
|
||||
[:li on-it "add stocks to didier@gmail.com"]
|
||||
[:li on-it "make something for urls on the backend"]
|
||||
[:li done "add stocks to didier@gmail.com"]
|
||||
[:li done "make something for urls on the backend"]
|
||||
[:li on-it "add a wui for OTC"]
|
||||
[:li "fix the stupid chat. This is an important feature for OTC users."]
|
||||
[:li "make something that allows to send securities from one user to another"]
|
||||
[:li "move 'routes' in 'rest'"]
|
||||
[:li "add a little bit of professionalism on top of the broker api"]
|
||||
[:li "make the api support transit and edn"]
|
||||
[:li "implements interest rate in ledger."]
|
||||
[:li "Make sure the ledger can have accounts"]
|
||||
[:li "when a new user signs in, he should receive some sort of money"]
|
||||
[:li "\u22EE"]
|
||||
[:li done "when a new user signs in, he should receive some sort of money"]
|
||||
[:li "implement send securities to friends"]
|
||||
[:li "implement buy/sell securities."]
|
||||
[:li "Use ledger as source of through in each sub server."]
|
||||
[:li "Update web ui for integrating securities with ledger."]
|
||||
|
||||
@@ -37,8 +37,9 @@
|
||||
[:div.navbar-start
|
||||
[:a.navbar-item.is-tab {:href "/" :class (top-bar-class state :home)} "Home"]
|
||||
[:a.navbar-item.is-tab {:href "/securities" :class (top-bar-class state :securities)} "Securities"]
|
||||
[:a.navbar-item.is-tab {:href "/create-order" :class (top-bar-class state :create-order)} "Create order"]
|
||||
[:a.navbar-item.is-tab {:href "/portfolio" :class (top-bar-class state :view-portfolio)} "View Portfolio"]
|
||||
[:a.navbar-item.is-tab {:href "/create-order" :class (top-bar-class state :create-order)} "Create Order"]
|
||||
[:a.navbar-item.is-tab {:href "/otc" :class (top-bar-class state :otc)} "Over The Counter"]
|
||||
[:a.navbar-item.is-tab {:href "/portfolio" :class (top-bar-class state :view-portfolio)} "Portfolio"]
|
||||
[:a.navbar-item.is-tab {:href "/todo" :class (top-bar-class state :todo)} "Todo"]]
|
||||
[:div.navbar-end
|
||||
[:a.navbar-item {} "Skirmish Shitty Exchange"]
|
||||
|
||||
@@ -53,6 +53,12 @@
|
||||
(session/put! :current-page #'pages/portfolio-page)
|
||||
(swap! state #(into % {:page :portfolio})))
|
||||
|
||||
(secretary/defroute "/otc" []
|
||||
(session/put! :current-page #'pages/otc-page)
|
||||
(swap! state #(into % {:page :otc})))
|
||||
|
||||
|
||||
|
||||
(secretary/defroute "/create-order" []
|
||||
(session/put! :current-page #'pages/create-order-page)
|
||||
(swap! state #(into % {:page :create-order})))
|
||||
@@ -77,6 +83,7 @@
|
||||
[(session/get :current-page) @state]]]]])
|
||||
|
||||
|
||||
|
||||
(defn mount-root []
|
||||
(reagent/render [current-page] (.getElementById js/document "app")))
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
[skirmish.wui.components.portfolio-page]
|
||||
[skirmish.wui.components.create-order-page]
|
||||
[skirmish.wui.components.securities-page]
|
||||
[skirmish.wui.components.security-page]))
|
||||
[skirmish.wui.components.security-page]
|
||||
[skirmish.wui.components.otc-page]))
|
||||
|
||||
(def home-page skirmish.wui.components.home-page/page)
|
||||
|
||||
@@ -17,3 +18,5 @@
|
||||
(def portfolio-page skirmish.wui.components.portfolio-page/page)
|
||||
|
||||
(def security-page skirmish.wui.components.security-page/page)
|
||||
|
||||
(def otc-page skirmish.wui.components.otc-page/page)
|
||||
@@ -26,4 +26,9 @@
|
||||
(if (<= dec 0)
|
||||
(pp-int n)
|
||||
(let [i (int (Math/pow 10 dec))]
|
||||
(str (pp-int (quot n i)) "," (right-pad (str (mod n i)) "0" dec))))))
|
||||
(str (pp-int (quot n i)) "," (right-pad (str (mod n i)) "0" dec))))))
|
||||
|
||||
(defn email? [s]
|
||||
(let [email-pattern #"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$"]
|
||||
(->> s str (re-matches email-pattern) some?)))
|
||||
|
||||
@@ -25,6 +25,10 @@ $table-background-color: $grey-dark;
|
||||
$table-cell-heading-color: $grey-darker;
|
||||
$table-row-hover-background-color: $black-ter;
|
||||
|
||||
// box
|
||||
$box-background-color: $grey;
|
||||
$box-color: $white;
|
||||
$box-shadow: 0 2px 3px $grey-light, 0 0 0 1px $grey-light;// $red;
|
||||
|
||||
@import "../../node_modules/bulma/sass/base/_all";
|
||||
|
||||
|
||||
Reference in new issue
Block a user