started to refactor code.

This commit is contained in:
didier amyot committed 2020-09-10 13:25:10 -04:00
1 parent c5d4e83d44
commit ed7f4ef668
5 files changed
+300 -111

No files matched your search

+9 -87
View File
@@ -7,6 +7,7 @@
[compojure.route :refer [not-found]]
[skirmish.exchange-gateway.exchange-gateway :refer [create-routes-for-exchange]]
[skirmish.rest.util :refer [build-response get-body cors-middleware]]
[skirmish.ledger.data-layer :as data-layer]
[skirmish.ledger.accounts]
[datomic.api :as d]
[taoensso.timbre :as timbre]
@@ -17,86 +18,6 @@
;; the ledger is the central
(defn compute-simple-face-value [amount daily-interest-rate deposit-date value-date]
(let [min-in-milliseconds (infix 60 * 1000)
minute-interest-rate (infix daily-interest-rate / (24 * 60. * 10000.))
ite (quot (- (. value-date getTime) (. deposit-date getTime)) min-in-milliseconds)
base (infix 1 + minute-interest-rate)]
(infix amount * base ** ite)))
(defn- in-interval? [[_ start end] x]
(let [x-time (. x getTime)]
(and (<= (. start getTime) x-time)
(< x-time (. end getTime)))))
(defn- larger-then-interval? [[ _ start end] x]
(and (<= (. x getTime) (. end getTime))
(<= (. x getTime) (. start getTime))))
(defn- split-interest-rate-intervals [interest-rates-intervals deposit-date value-date]
(let [xf (comp (drop-while #((comp not in-interval?) % deposit-date))
(take-while #((comp not larger-then-interval?) % value-date)))
ret (into [] xf interest-rates-intervals)
ret (assoc-in ret [0 1] deposit-date)
ret (assoc-in ret [(-> ret count dec) 2] value-date)]
ret))
(defn- find-interest-rate-for [interest-rates-intervals d]
(if (in-interval? (first interest-rates-intervals) d)
(ffirst interest-rates-intervals)
(recur (rest interest-rates-intervals) d)))
(defn split-interest-rates [interest-rates deposit-date value-date]
(let [interest-rates (into [[0 (new java.util.Date 0)]] interest-rates)
interest-rates (conj interest-rates [0 (new java.util.Date Long/MAX_VALUE)])
interest-rates-intervals (partition 2 1 interest-rates)
interest-rates-intervals (map (juxt ffirst (comp second first) (comp second second)) interest-rates-intervals)]
(if (= deposit-date value-date)
[[(find-interest-rate-for interest-rates-intervals deposit-date) deposit-date value-date]]
(split-interest-rate-intervals interest-rates-intervals deposit-date value-date))))
(defn compute-face-value [amount interest-rates deposit-date value-date]
(loop [interest-rates (split-interest-rates interest-rates deposit-date value-date)
amount amount]
(let [[interest-rate start end] (first interest-rates)
amount (compute-simple-face-value amount interest-rate start end)]
(if (empty? (rest interest-rates))
(long amount)
(recur (rest interest-rates) amount)))))
(defn get-interest-rate-history [db sec-id]
(let [history (d/history db)
interest-rates (d/q '[:find ?interest-rate ?v
:in $ ?eid
:where [?e :skirmish.security/interest-rate ?interest-rate ?tx]
[?e :skirmish.security/sec-id ?sec-id]
[?tx :db/txInstant ?v]]
history sec-id)]
(sort-by second interest-rates)))
(defn compute-face-value-from-db [db amount sec-id date]
(compute-face-value amount
(get-interest-rate-history db sec-id)
date
(new java.util.Date)))
(defn get-amount-in-account [db account-id]
(let [[amount sec-id pk date] (d/q '[:find [?amount ?sec-id ?pk ?v]
:in $ ?account-id
:where [?e :skirmish.ledger/owner-public-key ?account-id]
[?e :skirmish.ledger/amount ?amount ?tx]
[?e :skirmish.ledger/sec ?sec]
[?sec :skirmish.security/sec-id ?sec-id]
[?e :skirmish.ledger/owner-public-key ?pk]
[?tx :db/txInstant ?v]] db account-id)]
{:amount (when (some? sec-id) (compute-face-value-from-db db amount sec-id date))
:sec-id sec-id
:account-public-key pk}))
;; an :account-id is the public key of a
;; an :account-secret is an :account-id private key
@@ -114,7 +35,7 @@
"Returns the value of an account."
(fn [{{account-id :account-id} :route-params :as request}]
(let [db (d/db conn)
account-info (get-amount-in-account db account-id)]
account-info (data-layer/get-amount-in-account db account-id)]
(get-account-body request account-info))))
(s/def ::transaction (s/cat :type #{:transfer-immediately}
@@ -152,7 +73,7 @@
name :skirmish.security/name
number-of-decimal :skirmish.security/number-of-decimal
type_ :skirmish.security/type} e
interest-rate-history (get-interest-rate-history db id)
interest-rate-history (data-layer/get-interest-rate-history db id)
volumes (d/q '[:find ?amount ?date
:in $ ?id
:where [?e :skirmish.ledger/sec ?id]
@@ -160,7 +81,7 @@
[?tx :db/txInstant ?date]]
db id)
now (new java.util.Date)
volume (reduce + 0 (map #(compute-face-value (first %) interest-rate-history (second %) now) volumes))]
volume (reduce + 0 (map #(data-layer/compute-face-value (first %) interest-rate-history (second %) now) volumes))]
{:sec-id sec-id
:interest-rate interest-rate
:volume (or volume 0)
@@ -184,7 +105,7 @@
(fn [{body :body :as request}]
(try+
(let [[sec-id interest-rate] (-> body get-body get-new-security)
new-sec @(d/transact conn [[:skirmish.ledger/create-security sec-id interest-rate]])]
new-sec @(data-layer/create-security conn sec-id interest-rate)]
(build-response request {:body {:success true}
:headers {"location" (str "/api/1" "/ledger/securities/" (name sec-id))}}))
(catch [:error-type :4XX] e (build-response request e))
@@ -219,7 +140,8 @@
(defn get-available-securities [conn]
(fn [request]
(let [securities (d/q '[:find ?s :where [?e :skirmish.security/sec-id ?s]] (d/db conn))]
(let [db (d/db conn)
securities (data-layer/get-all-securities-id db)]
(build-response request (get-available-securities-body securities)))))
@@ -259,8 +181,8 @@
(defn get-available-accounts [conn]
(fn [request]
(let [securities (d/q '[:find ?pk :where [?e :skirmish.ledger/owner-public-key ?pk]] (d/db conn))]
(build-response request (get-available-accounts-body securities)))))
(let [db (d/db conn)]
(build-response request (get-available-accounts-body (data-layer/get-all-account-public-key db))))))
(defn get-api-description [request]
+88
View File
@@ -0,0 +1,88 @@
(ns skirmish.ledger.data-layer
(:require [datomic.api :as d]
[infix.macros :refer [infix]]))
(defn get-all-account-public-key [db]
(d/q '[:find ?pk :where [?e :skirmish.ledger/owner-public-key ?pk]] db))
(defn compute-simple-face-value [amount daily-interest-rate deposit-date value-date]
(let [min-in-milliseconds (infix 60 * 1000)
minute-interest-rate (infix daily-interest-rate / (24 * 60. * 10000.))
ite (quot (- (. value-date getTime) (. deposit-date getTime)) min-in-milliseconds)
base (infix 1 + minute-interest-rate)]
(infix amount * base ** ite)))
(defn- in-interval? [[_ start end] x]
(let [x-time (. x getTime)]
(and (<= (. start getTime) x-time)
(< x-time (. end getTime)))))
(defn- larger-then-interval? [[ _ start end] x]
(and (<= (. x getTime) (. end getTime))
(<= (. x getTime) (. start getTime))))
(defn- split-interest-rate-intervals [interest-rates-intervals deposit-date value-date]
(let [xf (comp (drop-while #((comp not in-interval?) % deposit-date))
(take-while #((comp not larger-then-interval?) % value-date)))
ret (into [] xf interest-rates-intervals)
ret (assoc-in ret [0 1] deposit-date)
ret (assoc-in ret [(-> ret count dec) 2] value-date)]
ret))
(defn- find-interest-rate-for [interest-rates-intervals d]
(if (in-interval? (first interest-rates-intervals) d)
(ffirst interest-rates-intervals)
(recur (rest interest-rates-intervals) d)))
(defn split-interest-rates [interest-rates deposit-date value-date]
(let [interest-rates (into [[0 (new java.util.Date 0)]] interest-rates)
interest-rates (conj interest-rates [0 (new java.util.Date Long/MAX_VALUE)])
interest-rates-intervals (partition 2 1 interest-rates)
interest-rates-intervals (map (juxt ffirst (comp second first) (comp second second)) interest-rates-intervals)]
(if (= deposit-date value-date)
[[(find-interest-rate-for interest-rates-intervals deposit-date) deposit-date value-date]]
(split-interest-rate-intervals interest-rates-intervals deposit-date value-date))))
(defn compute-face-value [amount interest-rates deposit-date value-date]
(loop [interest-rates (split-interest-rates interest-rates deposit-date value-date)
amount amount]
(let [[interest-rate start end] (first interest-rates)
amount (compute-simple-face-value amount interest-rate start end)]
(if (empty? (rest interest-rates))
(long amount)
(recur (rest interest-rates) amount)))))
(defn get-interest-rate-history [db sec-id]
(let [history (d/history db)
interest-rates (d/q '[:find ?interest-rate ?v
:in $ ?eid
:where [?e :skirmish.security/interest-rate ?interest-rate ?tx]
[?e :skirmish.security/sec-id ?sec-id]
[?tx :db/txInstant ?v]]
history sec-id)]
(sort-by second interest-rates)))
(defn compute-face-value-from-db [db amount sec-id date]
(compute-face-value amount
(get-interest-rate-history db sec-id)
date
(new java.util.Date)))
(defn get-amount-in-account [db account-id]
(let [[amount sec-id pk date] (d/q '[:find [?amount ?sec-id ?pk ?v]
:in $ ?account-id
:where [?e :skirmish.ledger/owner-public-key ?account-id]
[?e :skirmish.ledger/amount ?amount ?tx]
[?e :skirmish.ledger/sec ?sec]
[?sec :skirmish.security/sec-id ?sec-id]
[?e :skirmish.ledger/owner-public-key ?pk]
[?tx :db/txInstant ?v]] db account-id)]
{:amount (when (some? sec-id) (compute-face-value-from-db db amount sec-id date))
:sec-id sec-id
:account-public-key pk}))
(defn get-all-securities-id [db]
(d/q '[:find ?s :where [?e :skirmish.security/sec-id ?s]] db))
(defn create-security [conn sec-id interest-rate]
(d/transact conn [[:skirmish.ledger/create-security sec-id interest-rate]]))
+183
View File
@@ -0,0 +1,183 @@
(ns skirmish.ledger.routes
(:require [skirmish.rest.util :refer [build-response get-body cors-middleware]]
[skirmish.ledger.data-layer :as data-layer]
[skirmish.routes.core :as routes]
[compojure.core :as compojure]
[compojure.route :refer [not-found]]
[clojure.spec.alpha :as s]
[datomic.api :as d]
[slingshot.slingshot :refer [try+ throw+]]))
(defn get-api-description [request]
(let [body [{:url "ledger/accounts" :name "accounts"}
{:url "ledger/securities" :name "securities"}]]
(build-response request body)))
(defn get-available-accounts-body [accounts]
(let [sec-id->url #(routes/get-path-for :ledger/account {:account-id (name %)})
to-security-description #(hash-map :account-public-key % :url (sec-id->url %))
xf (comp cat (map to-security-description))
securities-info (sequence xf accounts)]
{:accounts securities-info}))
(defn get-available-accounts [conn]
(fn [request]
(let [db (d/db conn)
resp (get-available-accounts-body (data-layer/get-all-account-public-key db))]
(build-response request resp))))
(defn get-account-body [request {sec-id :sec-id amount :amount account-public-key :account-public-key}]
(if (nil? sec-id)
(build-response request {:status 404})
(let [sec-url (str "/api/1/ledger/securities/" (name sec-id))
beautify-account { :amount amount
:sec-id sec-id
:account-public-key account-public-key}
ret {:account beautify-account :sec-url sec-url}]
(build-response request ret))))
(defn get-account [conn]
"Returns the value of an account."
(fn [{{account-id :account-id} :route-params :as request}]
(let [db (d/db conn)
account-info (data-layer/get-amount-in-account db account-id)]
(get-account-body request account-info))))
(defn get-account [conn]
"Returns the value of an account."
(fn [{{account-id :account-id} :route-params :as request}]
(let [db (d/db conn)
account-info (data-layer/get-amount-in-account db account-id)]
(get-account-body request account-info))))
(defn get-transactions [conn]
(fn [request]
(let [all-pending-transactions []]
(build-response request all-pending-transactions))))
(defn create-transactions [conn]
(fn [{body :body :as request}]
(try+
(build-response request {:message "merci beaucoup"})
(catch [:error-type :4XX] e (build-response request e))
(catch [:error-type :5XX] e (build-response request e)))))
(defn get-available-securities-body [securities]
(let [sec-id->url #(routes/get-path-for :ledger/security {:sec-id (name %)})
to-security-description #(hash-map :sec-id % :url (sec-id->url %))
xf (comp cat (map to-security-description))
securities-info (sequence xf securities)];;
{:securities securities-info}))
(defn get-available-securities [conn]
(fn [request]
(let [db (d/db conn)
securities (data-layer/get-all-securities-id db)]
(build-response request (get-available-securities-body securities)))))
(defn get-new-security [raw-securities]
(if (s/valid? :skirmish.ledger.core/security raw-securities)
raw-securities
(throw+ {:error-type :4XX :status 400 :body {:error "Invalid security format."}})))
(defn create-security [conn]
"Create a fresh new security."
(fn [{body :body :as request}]
(try+
(let [[sec-id interest-rate] (-> body get-body get-new-security)
new-sec @(data-layer/create-security conn sec-id interest-rate)]
(build-response request {:body {:success true}
:headers {"location" (str "/api/1" "/ledger/securities/" (name sec-id))}}))
(catch [:error-type :4XX] e (build-response request e))
(catch [:error-type :5XX] e (build-response request e)))))
(defn prettify-security [e db]
(let [{interest-rate :skirmish.security/interest-rate
description :skirmish.security/description
id :db/id
sec-id :skirmish.security/sec-id
symbol :skirmish.security/symbol
name :skirmish.security/name
number-of-decimal :skirmish.security/number-of-decimal
type_ :skirmish.security/type} e
interest-rate-history (data-layer/get-interest-rate-history db id)
volumes (d/q '[:find ?amount ?date
:in $ ?id
:where [?e :skirmish.ledger/sec ?id]
[?e :skirmish.ledger/amount ?amount ?tx]
[?tx :db/txInstant ?date]]
db id)
now (new java.util.Date)
volume (reduce + 0 (map #(data-layer/compute-face-value (first %) interest-rate-history (second %) now) volumes))]
{:sec-id sec-id
:interest-rate interest-rate
:volume (or volume 0)
:name name
:interest-rate-history interest-rate-history
:symbol symbol
:number-of-decimal number-of-decimal
:type type_
:description description}))
(defn get-security-from-db [conn sec-id]
(let [db (d/db conn)
e (d/pull db '[*] [:skirmish.security/sec-id sec-id])]
(if (-> e :db/id nil?)
(throw+ {:error-type :4XX :status 404 :body {:error "Security not found."}})
(prettify-security e db))))
(defn get-security [conn]
"Returns the security."
(fn [{{sec-id-str :sec-id} :route-params :as request}]
(try+
(let [sec-id (keyword sec-id-str)]
(build-response request (get-security-from-db conn sec-id)))
(catch [:error-type :4XX] e (build-response request e))
(catch [:error-type :5XX] e (build-response request e)))))
(defn update-security [conn]
"Update a security with new features."
(fn [request]
(try+
(catch [:error-type :4XX] e (build-response request e))
(catch [:error-type :5XX] e (build-response request e)))))
(defn get-emit-securities-request [[amount account] sec-id]
(if (s/valid? :skirmish.ledger.core/emit-securities [sec-id amount account])
[:skirmish.ledger/emit-securities sec-id amount account]
(throw+ {:error-type :4XX :status 400 :body {:error "Invalid emit request."}})))
(defn emit-securities [conn]
"Emit securities in the specified wallet. This is the only way to
create securities.
payload
[:amount :account]"
(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)))]
@(d/transact conn [emit-request])
(build-response request {}))
(catch [:error-type :4XX] e (build-response request e))
(catch [:error-type :5XX] e (build-response request e)))))
(defn create-routes-for-ledger [conn]
(compojure/routes
(routes/context :ledger []
(routes/GET :ledger/home [] get-api-description)
(routes/GET :ledger/accounts [] (get-available-accounts conn))
(routes/GET :ledger/account [] (get-account conn))
(routes/GET :ledger/transactions [] (get-transactions conn))
(routes/POST :ledger/transactions [] (create-transactions conn))
(routes/GET :ledger/securities [] (get-available-securities conn))
(routes/POST :ledger/securities [] (create-security conn))
(routes/GET :ledger/security [] (get-security conn))
(routes/PUT :ledger/security [] (update-security conn))) ;; not done
;(routes/POST :ledger/security [] (emit-securities conn))) ;; done
(not-found "not found")))
-6
View File
@@ -1,6 +0,0 @@
(ns skirmish.ledger.core-test
(:require [clojure.test :refer :all])
(:require [skirmish.ledger.core :refer [get-amount-in-account]]))
(deftest get-amount-in-account-test)
+20 -18
View File
@@ -1,13 +1,15 @@
(ns skirmish.ledger.test-core
(:require [midje.sweet :refer :all]
[datomic.api :as d]
[clojure.edn]
[skirmish.ledger.data-layer :as data-layer]
[skirmish.ledger.accounts :refer [create-connection schema]]
[skirmish.rest.util :refer [build-response]]
[skirmish.ledger.core :refer :all]
[skirmish.rest.util :refer [edn->stream]]
[datomic.api :as d]
[clojure.edn]
[clojure.spec.alpha :as s]
[infix.macros :refer [infix]]
[skirmish.ledger.accounts :refer [create-connection schema]]))
[infix.macros :refer [infix]]))
(defn spec-check [spec]
#(s/valid? spec %))
@@ -208,11 +210,11 @@
start #inst"2020-09-05T00:00:00.000-00:00"
start+1d #inst"2020-09-06T00:00:00.000-00:00"
start+2d #inst"2020-09-07T00:00:00.000-00:00"]
(compute-simple-face-value amount interest-rate start start) => (roughly amount)
(compute-simple-face-value amount interest-rate start start+1d) => (roughly 1105.1670808)
(compute-simple-face-value amount interest-rate start start+2d) => (roughly 1221.3942766)
(compute-simple-face-value amount negative-interest-rate start start+2d) => (roughly 818.7250672)
(compute-simple-face-value amount 0 start start+2d) => (roughly amount)))
(data-layer/compute-simple-face-value amount interest-rate start start) => (roughly amount)
(data-layer/compute-simple-face-value amount interest-rate start start+1d) => (roughly 1105.1670808)
(data-layer/compute-simple-face-value amount interest-rate start start+2d) => (roughly 1221.3942766)
(data-layer/compute-simple-face-value amount negative-interest-rate start start+2d) => (roughly 818.7250672)
(data-layer/compute-simple-face-value amount 0 start start+2d) => (roughly amount)))
(fact "split-interest-rates"
(let [d-1d #inst"2020-09-04T00:00:00.000-00:00"
d-½d #inst"2020-09-04T12:00:00.000-00:00"
@@ -225,11 +227,11 @@
d+3d #inst"2020-09-08T00:00:00.000-00:00"
d+3½d #inst"2020-09-08T12:00:00.000-00:00";;
interest-rates [[10 d] [15 d+1d] [20 d+2d]]]
(split-interest-rates interest-rates d+½d d+3d) => [[10 d+½d d+1d] [15 d+1d d+2d] [20 d+2d d+3d]]
(split-interest-rates interest-rates d+3d d+3½d) => [[20 d+3d d+3½d]]
(split-interest-rates interest-rates d-1d d-½d) => [[0 d-1d d-½d]]
(split-interest-rates interest-rates d d+½d) => [[10 d d+½d]]
(split-interest-rates interest-rates d+2½d d+2½d) => [[20 d+2½d d+2½d]]))
(data-layer/split-interest-rates interest-rates d+½d d+3d) => [[10 d+½d d+1d] [15 d+1d d+2d] [20 d+2d d+3d]]
(data-layer/split-interest-rates interest-rates d+3d d+3½d) => [[20 d+3d d+3½d]]
(data-layer/split-interest-rates interest-rates d-1d d-½d) => [[0 d-1d d-½d]]
(data-layer/split-interest-rates interest-rates d d+½d) => [[10 d d+½d]]
(data-layer/split-interest-rates interest-rates d+2½d d+2½d) => [[20 d+2½d d+2½d]]))
(fact "compute-face-value"
(let [d-1d #inst"2020-09-04T00:00:00.000-00:00"
d-½d #inst"2020-09-04T12:00:00.000-00:00"
@@ -243,15 +245,15 @@
d+3½d #inst"2020-09-08T12:00:00.000-00:00"
amount 10000000
interest-rates [[10 d] [15 d+1d] [20 d+2d]]]
(compute-face-value amount interest-rates d+½d d+½d) => amount
(compute-face-value amount interest-rates d-1d d-½d) => amount
(compute-face-value amount interest-rates d-1d d+3d) => 10045101))
(data-layer/compute-face-value amount interest-rates d+½d d+½d) => amount
(data-layer/compute-face-value amount interest-rates d-1d d-½d) => amount
(data-layer/compute-face-value amount interest-rates d-1d d+3d) => 10045101))
(fact "we had bugs... we need to fix them"
(let [deposit-date #inst"2020-09-09T18:49:47.205-00:00"
value-date #inst"2020-09-09T18:52:56.760-00:00"
amount 105402100
interest-rates (list [1000 #inst "2020-09-09T18:47:34.152-00:00"])]
(compute-face-value amount interest-rates deposit-date value-date) => 105424060)))
(data-layer/compute-face-value amount interest-rates deposit-date value-date) => 105424060)))
;; 4. transfer sec from one account to the other.