Liking cljdoc? Tell your friends :D

Sneak peeks

Note on code literals (#money, #currency): tagged literals in Clojure code are handled at read time via data_readers.clj. Clojure does not auto-require the handler namespaces, so make sure Bankster namespaces are loaded before code containing these literals is read (otherwise you may see Attempting to call unbound fn .../code-literal). For scripts, prefer using read-string after require (or clojure.edn/read-string with money/readers).

It shows information about a currency

;; front API helpers

(require '[io.randomseed.bankster.api.currency :as currency])

;; global registry lookup with a keyword

(currency/of :PLN)
#currency{:id :PLN, :domain :ISO-4217, :kind :iso/fiat, :numeric 985, :scale 2}

;; global registry lookup using namespaced symbol

(currency/of crypto/ETH)
#currency{:id :crypto/ETH, :domain :CRYPTO, :kind :virtual/native, :scale 18, :weight 5}

;; global registry lookup with a string (incl. namespace a.k.a domain)

(currency/of "crypto/BTC")
#currency{:id :crypto/BTC, :domain :CRYPTO, :kind :virtual/native, :scale 8, :weight 5}

;; global registry lookup with a currency code
;; (weight solves potential conflicts when two currencies have the same currency code)

(currency/of ETH)
#currency{:id :crypto/ETH, :domain :CRYPTO, :kind :virtual/native, :scale 18, :weight 5}

;; global registry lookup using ISO currency number

(currency/of 840)
#currency{:id :USD, :domain :ISO-4217, :kind :iso/fiat, :numeric 840, :scale 2}

;; global registry lookup using tagged literal with a currency code

#currency XLM
#currency{:id :crypto/XLM, :domain :CRYPTO, :kind :virtual/native, :scale 8}

;; tagged literal accepts single-element vector (unwrapped)

#currency [EUR]
#currency{:id :EUR, :domain :ISO-4217, :kind :iso/fiat, :numeric 978, :scale 2}

;; global registry lookup using tagged literal with a namespaced identifier

#currency crypto/XLM
#currency{:id :crypto/XLM, :domain :CRYPTO, :kind :virtual/native, :scale 8}

;; global registry lookup using tagged literal with an ISO currency number

#currency 978
#currency{:id :EUR, :domain :ISO-4217, :kind :iso/fiat, :numeric 978, :scale 2}

;; Full currency information (including registry metadata).
(currency/info :PLN)
{:id :PLN,
 :numeric 985,
 :scale 2,
 :domain :ISO-4217,
 :kind :iso/fiat,
 :weight 0,
 :countries #{:PL},
 :localized {:pl {:name "złoty polski", :symbol "zł"}}}

(currency/info :crypto/USDC)
{:id :crypto/USDC,
 :numeric -1,
 :scale 8,
 :domain :CRYPTO,
 :kind :virtual.stable.peg/fiat,
 :weight 4,
 :localized {:* {:name "USD Coin", :symbol "USDC"}},
 :traits #{:peg/fiat :stable/coin :token/erc20}}

It allows to create a currency and register it

;; front API helpers

(require '[io.randomseed.bankster.api.currency :as currency])

;; ad hoc currency creation using constructor function

(currency/new :petro/USD 999 2 :COMBANK)
#currency{:id :petro/USD, :domain :PETRO, :kind :COMBANK, :numeric 999, :scale 2}

;; ad-hoc currency creation using tagged literal

#currency{:id :crypto/ETH :scale 18}
#currency{:id :crypto/ETH, :domain :CRYPTO, :scale 18}

;; putting new currency into a global, shared registry

(currency/register! (currency/new :petro/USD 9999 2 :COMBANK) :USA)
#Registry[{:currencies 221, :countries 250, :version "2021022121170359"} 0x11efe93f]

;; getting currency from a global registry

(currency/of :petro/USD)
#currency{:id :petro/USD, :domain :PETRO, :kind :COMBANK, :numeric 9999, :scale 2}

;; registering new currency expressed as a tagged literal

(currency/register! #currency{:id :crypto/AAA :scale 8})
#Registry[{:currencies 221, :countries 249, :version "2021022121170359"} 0x7eaf7a70]

;; creating an ISO currency (must have: a simple 3-letter code w/o ns and a numeric ID)

(currency/new :XOX 999 2 :COMBANK)
#currency{:id :XOX, :domain :ISO-4217, :kind :COMBANK, :numeric 999, :scale 2}

;; creating a strange ISO currency (forced by a namespace but w/o a numerical ID)

(currency/new :ISO-4217/XOX nil 2 :COMBANK)
#currency{:id :XOX, :domain :ISO-4217, :kind :COMBANK, :scale 2}

It allows to create monetary amounts

;; front API helpers

(require '[io.randomseed.bankster.api.money    :as    money]
         '[io.randomseed.bankster.api.currency :as currency])

;; using money/of macro with keyword ID and an amount

(money/of :EUR 25)
#money[25.00 EUR]

;; using money/of macro with keyword ID and an amount as a first argument

(money/of 25 :EUR)
#money[25.00 EUR]

;; using money/of macro with joint keyword ID and an amount as a first argument

(money/of :25_EUR)
#money[25.00 EUR]

(money/of :25EUR)
#money[25.00 EUR]

;; using money/of macro with unquoted symbolic ID and an amount

(money/of EUR 25)
#money[25.00 EUR]

;; using money/of macro with joint unquoted symbolic ID and an amount

(money/of EUR_25)
#money[25.00 EUR]

(money/of EUR25)
#money[25.00 EUR]

;; using money/of macro with namespaced keyword ID and an amount

(money/of crypto/BTC 10.1)
#money/crypto[10.10000000 BTC]

;; using money/of macro with currency code and an amount

(money/of BTC 10.1)
#money/crypto[10.10000000 BTC]

;; using tagged literals

#money EUR
#money[0.00 EUR]

#money/crypto ETH
#money/crypto[0.000000000000000000 ETH]

#money[PLN 2.50]
#money[2.50 PLN]

;; using tagged literal with a namespace

#money/crypto[1.31337 ETH]
#money/crypto[1.313370000000000000 ETH]

;; using tagged literal with a currency code

#money[1.31337 ETH]
#money/crypto[1.313370000000000000 ETH]

;; using tagged literal with a namespace but the amount goes first

#money/crypto[BTC 1.31337]
#money/crypto[1.31337000 BTC]

;; using default currency in a lexical context

(currency/with EUR (money/of 1000))
#money[1000.00 EUR]

;; using default currency in a lexical context (alias for the above)

(money/with-currency EUR (money/of 1000))
#money[1000.00 EUR]

;; using composed amounts and currencies

#money EUR100
#money[100 EUR]

#money :100_EUR
#money[100 EUR]

#money :100EUR
#money[100 EUR]

#money "100 EUR"
#money[100 EUR]

(money/of "100EUR")
#money[100 EUR]

It allows to perform logical operations on monetary amounts

;; front API helper

(require '[io.randomseed.bankster.api.money :as money])

(money/eq? #money[5 GBP] #money[GBP 5])
true

(money/ne? #money[5 GBP] #money[GBP 5])
false

(money/eq? #money[5 GBP] #money/crypto[5 ETH])
false

(money/gt? #money[1 JPY] #money[0 JPY])
true

(money/ge? #money[1 JPY] #money[1 JPY])
true

(money/lt? #money[1 JPY] #money[0 JPY])
false

(money/le? #money[1 JPY] #money[0 JPY])
false

(money/zero? #money[0 USD])
true

(money/neg? #money[-2 XXX])
true

(money/pos? #money[-2 XXX])
false

It allows to perform math operations on monetary amounts

;; front API helpers

(require '[io.randomseed.bankster.api       :as   api]
         '[io.randomseed.bankster.api.money :as money])

;; adding money expressed with tagged literals and with a macro call

(money/add #money[EUR 7] #money[0.54 EUR] (money/of 4.40 EUR))
#money[11.94 EUR]

;; dividing money by a number

(money/div #money/crypto[5 USDT] 2)
#money/crypto[2.50000000 USDT]

;; dividing money by numbers that separately would require rounding

(money/div #money[1 GBP] 8 0.5)
#money[0.25 GBP]

;; dividing money by numbers with rounding after each consecutive calculation

(api/with-rescaling :HALF_UP
  (money/div #money[1 GBP] 8 0.5))
#money[0.26 GBP]

;; dividing money by money (of the same currency)

(money/div #money/crypto[5 BTC] #money/crypto[2 BTC])
2.5M

;; dividing causing scale to exceed in one of the steps
;; but no rounding is necessary due to later operation

(money/div #money[1 PLN] 8 0.5)
#money[0.25 PLN]

;; dividing, scaled and rounded with each operation

(api/with-rounding :HALF_UP
  (money/div-scaled #money[1 PLN] 8 0.5))
#money[0.26 PLN]

;; same as the above but shorter

(api/with-rescaling :HALF_UP
  (money/div #money[1 PLN] 8 0.5))
#money[0.26 PLN]

;; handling non-terminating decimal expansion

(api/with-rounding :HALF_UP
  (money/div #money[1 PLN] 3))
#money[0.33 PLN]

;; rounding with unit reduction

(api/with-rounding :HALF_UP
  (money/div #money[1 PLN] #money[3 PLN]))
0.33M

;; rounding and unit reduction (regular numbers, dynamic scale)

(api/with-rounding :HALF_UP
  (money/div 1 3))
0.33333M

;; multiplying money by numbers

(money/mul #money/crypto[5 ETH] 1 2 3 4 5)
#money/crypto[600.000000000000000000 ETH]

;; adding to major part

(money/add-major #money[1.23 PLN] 100)
#money[101.23 PLN]

;; adding to minor part

(money/add-minor #money[1.23 PLN] 77)
#money[2.00 PLN]

;; converting

(money/convert #money/crypto[1.5 ETH] :crypto/USDT 1646.75)
#money/crypto[2470.12500000 USDT]

;; comparing

(sort money/compare [(money/of 10    PLN)
                     (money/of  0    PLN)
                     (money/of 30    PLN)
                     (money/of  1.23 PLN)])
(#money[0.00 PLN]
 #money[1.23 PLN]
 #money[10.00 PLN]
 #money[30.00 PLN])

;; rounding to the given interval

(money/round-to #money[31.33 USD] 0.5)
#money[31.50 USD]

;; allocation

(money/allocate #money[10.00 PLN] [1 1 1])
[#money[3.34 PLN]
 #money[3.33 PLN]
 #money[3.33 PLN]]

(money/allocate #money[1.00 PLN] [1 2 3])
[#money[0.17 PLN]
 #money[0.33 PLN]
 #money[0.50 PLN]]

;; distribution

(money/distribute (money/of 1 PLN) 3)
[#money[0.34 PLN]
 #money[0.33 PLN]
 #money[0.33 PLN]]

(money/distribute (money/of 3 PLN) 3)
[#money[1.00 PLN]
 #money[1.00 PLN]
 #money[1.00 PLN]]

;;
;; using API ops (money-aware operators)
;;

(require '[io.randomseed.bankster.api.ops :refer :all])

;; NOTE: `api.ops` is intentionally polymorphic (Money + numeric fallbacks).
;; If you need the lower-level namespace, use `io.randomseed.bankster.money.inter-ops`.

(+ 1 2 3)
6

(+ #money[USD 8] #money[USD 7.12])
#money[15.12 USD]

(* 1 2 3 4 5 #money/crypto[0.7 ETH])
#money/crypto[84.000000000000000000 ETH]

;;
;; using api (front API)
;;

(require '[io.randomseed.bankster.api          :as      api]
         '[io.randomseed.bankster.api.money    :as    money]
         '[io.randomseed.bankster.api.currency :as currency])

;; NOTE: `api/amount` is an alias for `scale/amount`.
;; NOTE: `api/scale` returns scale (Money amount scale / Currency nominal scale).

;; For auto-scaled currencies, Money scale reflects the current amount's scale
;; (it adapts to the value), not a nominal currency scale.

(api/with-rescaling :HALF_UP
  (api/auto-scaled? :XAU))
;; => true

(api/amount 12.30M)
;; => 12.30M  ; BigDecimal

(api/scale #money[12.30 EUR])
;; => 2

(api/scale :XAU)
;; => -1  ; auto-scaled currency

(money/add #money[10 EUR] #money[5 EUR])
;; => #money[15 EUR]

(money/gt? #money[10 EUR] #money[5 EUR])
;; => true

(currency/resolve-all :EUR)
;; => #{#currency{:id :EUR, ...}}

(currency/id-str :crypto/eth)
;; => "crypto/ETH"

(currency/code-str :crypto/eth)
;; => "ETH"

(currency/symbol :USD :en_US)
;; => "$"

(currency/name :EUR :en_US)
;; => "Euro"

(money/of-registry (api/default-registry) #money[10 EUR])
;; => #money[10.00 EUR]

(money/cast #money[10 EUR] :USD :HALF_UP)
;; => #money[... USD]

(money/cast-try #money[10 EUR] :NOPE)
;; => nil

(money/format #money[1234.50 PLN] :pl_PL)
;; => "1 234,50 zł"

It allows to perform generic, polymorphic operations

;; front API helpers

(require '[io.randomseed.bankster.api          :as      api]
         '[io.randomseed.bankster.api.money    :as    money]
         '[io.randomseed.bankster.api.currency :as currency])

(api/scale #currency PLN)
2

(api/scale #currency crypto/ETH)
18

(api/scale 123.45)
2

(api/scale #money[100 EUR])
2

(api/scale :GBP)
2

;; scale of the currency (low-level)

(api/scale :XXX)
-1

;; scale of the amount

(api/scale #money[12.34567 XXX])
5 ; current scale

;; nominal scale of the currency

(currency/scale #money[12.34567 XXX])
-1 ; auto-scaled

(currency/auto-scaled? :XXX)
true

(currency/auto-scaled? #money[12.34567 XXX])
true

(api/auto-scaled? :XXX)
true

(api/auto-scaled? #money[12.34567 XXX])
false ; scale of the amount, not the currency

(api/scale-apply #money[10 USD] 8) ;; use with caution
#money[10.00000000 USD]

(api/scale-apply #currency USD 8)  ;; use with caution
#currency{:id :USD, :domain :ISO-4217, :kind :iso/fiat, :numeric 840, :scale 8}

(money/rescale #money[10 USD] 8)
#money[10.00000000 USD]

;; unary variant of money/rescale
;; rescales back to nominal scale

(money/rescale
 (money/rescale #money[10 USD] 8))
#money[10.00 USD]

(api/amount #money[108.11 CHF])
108.11M

(scale/integer #money[108.11 CHF])
108M

(scale/fractional #money[108.11 CHF])
11M

(currency/iso? #money[1 GBP])
true

(currency/code #money[1 GBP])
"GBP"

It allows to serialize and deserialize monetary amounts

(require '[io.randomseed.bankster.serializers.json :as sj]
         '[io.randomseed.bankster.serializers.edn  :as se])

;; JSON serialization (minimal - only currency ID and amount)

(sj/money->json-map #money[12.30 PLN])
{:currency "PLN", :amount "12.30"}

;; JSON full serialization (currency as nested map)

(sj/money->json-full-map #money[12.30 PLN])
{:currency {:id "PLN", :numeric 985, :scale 2, :kind "iso/fiat", :domain "ISO-4217"},
 :amount "12.30"}

;; JSON with :full? option

(sj/to-json-map #money[12.30 PLN] {:full? true})
{:currency {:id "PLN", :numeric 985, :scale 2, :kind "iso/fiat", :domain "ISO-4217"},
 :amount "12.30"}

;; JSON with :keys filtering (nested options for currency)

(sj/money->json-full-map #money[12.30 PLN]
                        {:keys [:amount {:currency {:keys [:id :numeric]}}]})
{:amount "12.30", :currency {:id "PLN", :numeric 985}}

;; JSON string representation

(sj/money->json-string #money[12.30 PLN])
"12.30 PLN"

;; EDN serialization (BigDecimals, keyword IDs)

(se/money->edn-map #money[12.30 PLN])
{:currency :PLN, :amount 12.30M}

;; EDN tagged literal string

(se/money->edn-string #money[12.30 PLN])
"#money[12.30M PLN]"

(se/money->edn-string #money/crypto[1.5 ETH])

"#money/crypto[1.500000000000000000M ETH]"

;; Deserialization

(sj/json-map->money {:currency "PLN" :amount "12.30"})
#money[12.30 PLN]

;; Note: for JSON inputs prefer string amounts (or configure your
;; JSON parser to produce BigDecimal) to avoid double-precision loss.

(sj/json-text->money "{\"currency\":\"PLN\",\"amount\":12.30}")
#money[12.30 PLN]

(sj/json-string->money "12.30 PLN")
#money[12.30 PLN]

(se/edn-string->money "#money[12.30M PLN]")
#money[12.30 PLN]

;; With custom registry and rounding (also accepts keywords/strings)

(sj/json-map->money {:currency "PLN" :amount "1.005"}
                    {:rounding-mode :HALF_UP})
#money[1.01 PLN]

;; Rescaling - preserve precision beyond currency's nominal scale

(sj/json-map->money {:currency "PLN" :amount "12.3456"}
                    {:rescale 4})
#money[12.3456 PLN]  ; Currency has scale 4, not the registry's 2

;; Rescaling during serialization

(sj/money->json-map #money[12.30 PLN] {:rescale 4})
{:currency "PLN", :amount "12.3000"}

;; Currency serialization (minimal by default)

(sj/currency->json-map #currency PLN)
{:id "PLN"}

(sj/currency->json-full-map #currency PLN)
{:id "PLN", :numeric 985, :scale 2, :kind "iso/fiat", :domain "ISO-4217"}

;; :code-only? omits namespace

(sj/money->json-map #money/crypto[1.5 ETH] {:code-only? true})
{:currency "ETH", :amount "1.500000000000000000"}

For more complete, runnable examples see the examples/ directory in the source repository.

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close