XTDB Portfolio
This shows how the db at a point in time can comfortably enable account or portfolio analysis.
Before we get started, we'll start with a deps.edn that includes xtdb
{:deps {org.clojure/clojure {:mvn/version "1.10.3"} org.clojure/tools.deps.alpha {:git/url "https://github.com/clojure/tools.deps.alpha.git" :sha "e4fb92eef724fa39e29b39cc2b1a850567d490dd"} compliment/compliment {:mvn/version "0.3.11"} com.xtdb/xtdb-core {:mvn/version "1.23.0"}}}(require [xtdb.api :as xt])This starts a database, re-executing this cell will create a new empty portfolio.
(def xtdb.api.PXtdb system (xt/start-node {}))This creates a utility to view portfolios at 4 asof valuation dates
;; portfolios on a series of valuation dates(defn hld-hist [] (dorun (for [asof [inst "2019-01-10" inst "2019-01-15" inst "2019-03-14" inst "2019-03-15"]] (println asof (xt/q (xt/db system asof) [:find ?t ?q :where [?port :name "My Trading Portfolio"] [?port :holdings ?hldg] [?hldg :quantity ?q] [?hldg :asset ?a] [?a :ticker ?t]])))))(hld-hist)Load up some data (asof 12/31/2018)
(->> [;; add 3 companies [::xt/put {:xt/id :company/t1, :name "IBM"}] [::xt/put {:xt/id :company/t2, :name "JP Morgan"}] [::xt/put {:xt/id :company/t3, :name "Ford"}] ;; add 3 assets based on those companies [::xt/put {:xt/id :security/t1, :issuer :company/t1 :ticker "IBM", :type "Equity"}] [::xt/put {:xt/id :security/t2, :issuer :company/t2 :ticker "JPM", :type "Equity"}] [::xt/put {:xt/id :security/t3, :issuer :company/t3 :ticker "F", :type "Equity"}] ;; add 2 positions (portfolio unspecified) [::xt/put {:xt/id :pm/p1, :asset :security/t1, :quantity 100}] [::xt/put {:xt/id :pm/p2, :asset :security/t2, :quantity 200}] ;; add portfolio (with those 2 positions) [::xt/put {:xt/id :p/p1, :holdings [:pm/p1 :pm/p2], :name "My Trading Portfolio"}]] ;; adding this asof date is optional (mapv (conj % inst "2018-12-31")) ;; submit the transaction (xt/submit-tx system) ;; get the tx time and wait before proceeding ::xt/tx-time ((fn wait [tx-time] (xt/sync system tx-time nil))))(hld-hist)(->> [[::xt/put {:xt/id :pm/p1, :asset :security/t1, :quantity 350} inst "2019-01-15"]] (xt/submit-tx system) :tx/tx-time ((xt/sync system % nil )))(hld-hist)This adds 1000 to position 1 (:pm/p1).
Although (hld-hist) may show the new shares but if you execute this in a separate repl it typically will not. This "submits" the transaction and then queries the db without waiting for the transaction to complete (e.g. kafka-log->crux-indexer) .
(let [asof inst "2019-02-14" position (xt/entity (xt/db system asof) :pm/p1)] (xt/submit-tx system [[::xt/put (update position :quantity + 1000) asof]]))(hld-hist)(let [asof inst "2019-03-15" port (xt/entity (xt/db system asof) :p/p1)] (->> [[::xt/put {:xt/id :pm/p3, :asset :security/t3, :quantity 440}] [::xt/put (update port :holdings conj :pm/p3)]] ;; add asof date to each entry (mapv (conj % asof)) ;; submit transaction (xt/submit-tx system)))(hld-hist);; portfolios on a series of valuation dates(hld-hist)entity history
(->> (xt/entity-history (xt/db system) :p/p1 :asc {:with-docs? true}) (map (get-in % [:xtdb.api/doc :holdings])))Note that the above shows two entries. The portfolio ':p/p1' only reflects the addition of a position, not the change to position :pm/p2 on "2019-01-15" where the "IBM" position was increased. e.g. no "nested history"
(xt/entity-history (xt/db system) :p/p1 :asc {:with-docs? true})(with-open [entity-hist (xt/open-entity-history (xt/db system) :p/p1 :asc {:with-docs? true})] (iterator-seq entity-hist))Logs
new version moved the log stuff .... need to re-find it
(doc xt/open-tx-log)(require [clojure.reflect :refer [reflect]])(with-open [tx-log (xt/open-tx-log system 0 true)] (iterator-seq tx-log))(defn hld-hist2 [] (for [asof [inst "2019-01-10" inst "2019-01-15" inst "2019-03-14" inst "2019-03-15"]] [asof (xt/q (xt/db system asof) [:find (pull ?port [{:holdings [{:asset [:ticker]}:quantity]}]) :where [?port :name "My Trading Portfolio"] ])]))(pprint (hld-hist2))(pprint (-> (hld-hist2) first second ffirst))