From 90aefc0f29fc7f22c485b4210b9da5375d1b1648 Mon Sep 17 00:00:00 2001 From: Peter Selby Date: Thu, 13 Sep 2018 10:19:04 -0700 Subject: [PATCH] Added taxes.org, and a table printer --- resources/taxes.org | 31 ++++++++++++++++++++++++++++++ src/taxer/core.clj | 43 ++++++++++++++++++++++++------------------ src/taxer/executor.clj | 41 +++++++++++++++++++++++++++++++++++++--- src/taxer/importer.clj | 39 +++++++++++++++++++++++--------------- 4 files changed, 118 insertions(+), 36 deletions(-) create mode 100644 resources/taxes.org diff --git a/resources/taxes.org b/resources/taxes.org new file mode 100644 index 0000000..82e00a0 --- /dev/null +++ b/resources/taxes.org @@ -0,0 +1,31 @@ + + +* Taxes 2017 + +Basic idea: + + - Make a list of all purchases and all sales in history. + - Take the sales from 2017. Calculate capital gains. That's the bulk of the + taxes. + - Trades count as sale of crypto #1, purchase of crypto #2. + - Already paid taxes for 2013...that should be accounted for in step 1. + - BCH and BTG count as income: all deposits should be multiplied by the day-1 + value and counted as income--everything else counts as capital gains. + - BCH traded on the first day at $277, according to bitcoin.tax. + - Uhh, some Coinbase "sends" are really sales. Look for "Sent to Coinbase". + - Coinbase sent to Vault of Satoshi, where they were sold. Should be + considered sold...but I don't know how to calculate the tax. Anyway, I + paid it in 2014: + - 1AF6ZPez9NFc7nUfJtwBgod6aWcYaDTi3F + - 1LTrqFApTvfSn415Rjs1ukCVN149zADxxJ + - 1LTrqFApTvfSn415Rjs1ukCVN149zADxxJ + - 18as544Wxg3ZScCZo7fjWSV4JGUhsLv6AR + - 18as544Wxg3ZScCZo7fjWSV4JGUhsLv6AR + - 1GxxcLKjqHD1wZWoZ3KNgiqjQonGDpZS4 + - 1G3GM7izNsegWnxTB3RbuXAkC9YZxfeYP1 + - Definitely gifts: + - 1L4kschshGtKJPM3T5RXhBJYtEHhFa3xq (Omar) + - 18Co5639x3Dp1EExfbgEzBKXYn329cwYKt + - Vircurex: + - 1FripmTRgNFx6M2C7udeDWKYW8wfR5vuUU + - 1FripmTRgNFx6M2C7udeDWKYW8wfR5vuUU diff --git a/src/taxer/core.clj b/src/taxer/core.clj index 18d13e5..11ca7c1 100644 --- a/src/taxer/core.clj +++ b/src/taxer/core.clj @@ -22,32 +22,36 @@ (s/def ::recipient [:self :buyer :merchant]) -(def txn-common [::id - ::amount - ::currency - ::txn-type - ::account - ::currency]) +(s/def ::notes string?) + +(def txn-common-req [::id + ::amount + ::currency + ::txn-type + ::account + ::currency]) (defmulti txn-type ::txn-type) (defmethod txn-type :buy [_] - (s/keys :req (concat txn-common + (s/keys :req (concat txn-common-req [::from-currency]) - :opt [::amount-consumed])) + :opt [::notes])) (defmethod txn-type :sell [_] - (s/keys :req (concat txn-common + (s/keys :req (concat txn-common-req []) - :opt [::consuming-txns])) + :opt [::notes])) (defmethod txn-type :trade [_] - (s/keys :req (concat txn-common - [::to-currency]))) + (s/keys :req (concat txn-common-req + [::to-currency]) + :opt [::notes])) (defmethod txn-type :send [_] - (s/keys :req (concat txn-common []) - :opt [::recipient])) + (s/keys :req (concat txn-common-req []) + :opt [::notes])) (defmethod txn-type :receive [_] - (s/keys :req (concat txn-common []) - :opt [::sender])) + (s/keys :req (concat txn-common-req []) + :opt [::notes])) (defmethod txn-type :fee [_] - (s/keys :req (concat txn-common []))) + (s/keys :req (concat txn-common-req []) + :opt [::notes])) (s/def ::txn (s/multi-spec txn-type ::txn-type)) (s/def ::txns (s/coll-of ::txn)) @@ -59,4 +63,7 @@ ::open ::high ::low ::close] :opt-un [::volume_from ::volume_to]))) -(s/def ::pricemaps (s/map-of keyword? ::pricemap)) +(s/def ::rate-fetcher (s/fspec :args (s/cat :date ::datetime) + :ret decimal?)) + +(s/def ::rate-fetchers (s/map-of keyword? ::rate-fetcher)) diff --git a/src/taxer/executor.clj b/src/taxer/executor.clj index 86d15e5..132453c 100644 --- a/src/taxer/executor.clj +++ b/src/taxer/executor.clj @@ -13,7 +13,8 @@ (import/load-coinbase-csv (io/resource "Coinbase-50f5ebcff0c2719719000033-TaxTransactionsReport-2018-05-18-21_36_35.csv")))) (defn rates [] - (let [btcusd (import/load-bittrex-rates (io/resource "Bittrex_BTCUSD_1h.csv"))] + (let [btcusd (import/load-bittrex-rates (io/resource "Bittrex_BTCUSD_1h.csv")) + ada->usd (partial import/bittrex-rate:ada->usd btcusd)] (-> {} (assoc :btc->usd (-> btcusd (import/bittrex-rate:btc->usd))) (assoc :bcc->usd (-> (io/resource "Poloniex_BCHUSD_1h.csv") @@ -21,7 +22,7 @@ (import/bittrex-rate:bcc->usd))) (assoc :ada->usd (-> (io/resource "Bittrex_ADABTC_1h.csv") (import/load-bittrex-rates) - (partial import/bittrex-rate:ada->usd btcusd))) + ada->usd)) (assoc :eth->usd (-> (io/resource "Bittrex_ETHUSD_1h.csv") (import/load-bittrex-rates) (import/bittrex-rate:eth->usd))) @@ -30,8 +31,42 @@ (import/coinmarketcap-rate:btg->usd))) (assoc :usdt->usd (fn [date] 1))))) (s/fdef rates - :ret ::tax/pricemaps) + :ret ::tax/rate-fetchers) (defn bittrex [] (import/merge-bittrex-transactions (rates) (import/load-bittrex-csv "resources/bittrex-fullOrders.csv"))) + +(defn all-txns [] + (concat (gdax) (coinbase) (bittrex))) + +(defn project [ks m] + (into {} (map (fn [k] [k (get m k)]) ks))) + +(defn round-str [dec] + (format "%.2f" dec)) + +(let [format (java.text.SimpleDateFormat. "yyyy-MM-dd")] + (defn format-date [date] + (.format format date))) + +(defn update-field [field f txns] + (map (fn [txn] (update txn field f)) txns)) + +(defn print-as-table [txns] + (let [restrict-fields (partial project [::tax/timestamp + ::tax/txn-id + ::tax/txn-type + ::tax/amount + ::tax/currency + ::tax/usd-amount + ::tax/id + ;;::tax/notes + ])] + (clojure.pprint/print-table + (->> txns + (sort-by ::tax/timestamp) + (update-field ::tax/amount round-str) + (update-field ::tax/usd-amount round-str) + (update-field ::tax/timestamp format-date) + (map restrict-fields))))) diff --git a/src/taxer/importer.clj b/src/taxer/importer.clj index 3416c4c..29a161e 100644 --- a/src/taxer/importer.clj +++ b/src/taxer/importer.clj @@ -8,7 +8,7 @@ [clojure.spec.alpha :as s] [orchestra.spec.test :as st])) -(st/instrument) +;;(st/instrument) (defn file? [obj] (instance? java.io.File obj)) @@ -114,7 +114,8 @@ ::tax/usd-amount (:usd_amount_transacted txn) ::tax/account :coinbase ::tax/amount (:quantity_transacted txn) - ::tax/currency (:asset txn)) + ::tax/currency (:asset txn) + ::tax/notes (str/replace (:notes txn) #"\n" " :: ")) (common->local))) (defmulti coinbase->local :transaction_type) @@ -130,7 +131,7 @@ (defmethod coinbase->local :receive [txn] (-> txn - (assoc ::tax/txn-type :recieve) + (assoc ::tax/txn-type :receive) (common-coinbase->local))) (defmethod coinbase->local :send [txn] @@ -144,8 +145,7 @@ (common-coinbase->local))) (defn merge-coinbase-transactions [txns] - (map coinbase->local txns -)) + (map coinbase->local txns)) (defn common-gdax->local [txn] (-> txn @@ -296,15 +296,18 @@ :args (s/cat :bccusd ::tax/pricemap) :ret (s/fspec :args (s/cat :date ::tax/timestamp) :ret decimal?)) +(defn pp [obj] + (clojure.pprint/pprint obj) + obj) + (defn bittrex-rate:ada->usd [btcusd adabtc] (let [->usd (bittrex-rate:btc->usd btcusd) timestamps (sort (keys adabtc))] (fn [date] (let [ada-in-btc (first (map get-avg-rate (get adabtc (bsearch-timestamps timestamps - (.getTime date))))) - btc-in-usd (->usd date)] - (* ada-in-btc btc-in-usd))))) + (.getTime date)))))] + (* ada-in-btc (->usd date)))))) (s/fdef bittrex-rate:ada->usd :args (s/cat :btcusd ::tax/pricemap :adabtc ::tax/pricemap) :ret (s/fspec :args (s/cat :date ::tax/timestamp) :ret decimal?)) @@ -329,14 +332,14 @@ (s/fdef bittrex-rate:usdt->usd :ret (s/fspec :args (s/cat :date ::tax/timestamp) :ret decimal?)) -(defn build-bittrex-rates [btcusd-csv bccusd-csv adabtc-csv ethusd-csv] +#_(defn build-bittrex-rates [btcusd-csv bccusd-csv adabtc-csv ethusd-csv] (let [btcusd (load-bittrex-rates btcusd-csv)] {:btc->usd (-> btcusd bittrex-rate:btc->usd) :bcc->usd (-> bccusd-csv load-bittrex-rates bittrex-rate:bcc->usd) :ada->usd (-> adabtc-csv load-bittrex-rates (partial bittrex-rate:ada->usd btcusd)) :eth->usd (-> ethusd-csv load-bittrex-rates bittrex-rate:eth->usd) :usdt->usd (bittrex-rate:usdt->usd)})) -(s/fdef build-bittrex-rates +#_(s/fdef build-bittrex-rates :args (s/cat :btcusd-csv file? :bccusd-csv file? :adabtc-csv file? @@ -364,30 +367,36 @@ (assoc ::tax/txn-type :sell ::tax/amount (:quantity txn) ::tax/currency (:from-currency txn) - ::tax/usd-amount usd-amount) + ::tax/usd-amount usd-amount + ::tax/notes "split from sell") (common-bittrex->local)) (-> txn (assoc ::tax/txn-type :buy ::tax/amount (with-precision 10 (/ (:quantity txn) (:price txn))) ::tax/currency (:to-currency txn) - ::tax/usd-amount usd-amount))])) + ::tax/usd-amount usd-amount + ::tax/notes "split from sell") + (common-bittrex->local))])) (defn bittrex-buy->local-txns [rates txn] (let [usd-amount (* (:quantity txn) - (get-bittrex-rate rates (:to-currency txn) :usd (:closed txn)))] + (get-bittrex-rate rates (:from-currency txn) :usd (:closed txn)))] [(-> txn (assoc ::tax/txn-type :buy ::tax/amount (:quantity txn) ::tax/currency (:to-currency txn) - ::tax/usd-amount usd-amount) + ::tax/usd-amount usd-amount + ::tax/notes "split from buy") (common-bittrex->local)) (-> txn (assoc ::tax/txn-type :sell ::tax/amount (with-precision 10 (/ (:quantity txn) (:price txn))) ::tax/currency (:from-currency txn) - ::tax/usd-amount usd-amount))])) + ::tax/usd-amount usd-amount + ::tax/notes "split from buy") + (common-bittrex->local))])) (defn bittrex-txn->local-txns [rates txn] (match (:type txn)