Currency handling for Go.

Related tags

Financial currency
Overview

currency Build Status Coverage Status Go Report Card PkgGoDev

Handles currency amounts, provides currency information and formatting.

Powered by CLDR v38, in just ~30kb of data.

Backstory: https://bojanz.github.io/price-currency-handling-go/

Features

  1. All currency codes, their numeric codes and fraction digits.
  2. Currency symbols and formats for all locales.
  3. Amount struct, with value semantics (Fowler's Money pattern)
  4. Formatter, for formatting amounts and parsing formatted amounts.
    amount, _ := currency.NewAmount("275.98", "EUR")
    total, _ := amount.Mul("4")

    locale := currency.NewLocale("fr")
    formatter := currency.NewFormatter(locale)
    fmt.Println(formatter.Format(total)) // 1 103,92 €

    // Convert the amount to Iranian rial and show it in Farsi.
    total, _ = total.Convert("IRR", "45.538")
    total = total.Round()
    locale = currency.NewLocale("fa")
    formatter = currency.NewFormatter(locale)
    fmt.Println(formatter.Format(total)) // ‎ریال ۵۰٬۲۷۰

Design goals

Real decimal implementation under the hood.

Currency amounts can't be floats. Storing integer minor units (2.99 => 299) becomes problematic once there are multiple currencies (difficult to sort in the DB), or there is a need for sub-minor-unit precision (due to merchant or tax requirements, etc). A real arbitrary-precision decimal type is required. Since Go doesn't have one natively, a userspace implementation is used, provided by the cockroachdb/apd package. The Amount struct provides an easy to use abstraction on top of it, allowing the underlying implementation to be replaced in the future without a backwards compatibility break.

Smart filtering of CLDR data.

The "modern" subset of CLDR locales is used, reducing the list from ~560 to ~370 locales.

Once gathered, locales are filtered to remove all data not used by this package, and then deduplicated by parent (e.g. don't keep fr-CH if fr has the same data).

Currency symbols are grouped together to avoid repetition. For example:

"ARS": {
    {"ARS", []string{"en", "fr-CA"}},
    {"$", []string{"es-AR"}},
    {"$AR", []string{"fr"}},
}

Currency names are not included because they are rarely shown, but need significant space. Instead, they can be fetched on the frontend via Intl.DisplayNames.

Comments
  • Document some more features

    Document some more features

    I think the README should document at least two more features in the code:

    • the fact that the package has builtin support for github.com/google/go-cmp, making comparisons reliable and reasonably fast without having to resort to Amount.Cmp when just testing for equality
    • the implementation of the encoding.BinaryMarshaler interface actually produces valid strings in its []byte results, allowing them to be stored in database as strings in SQL and compared for equality, but not for ordering.

    Alternatively, documenting a serialization that would allow SQL ordering would be nice too. I didn't find one in the current implementation, though.

    opened by fgm 9
  • Fix Amount.RoundTo concurrency issue

    Fix Amount.RoundTo concurrency issue

    Bug description

    decimalContext function returns globals which shouldn't be modified concurrently. Amount.RoundTo modifies the returned context's Rounding property which causes data race condition when Amount.RoundTo is called concurrently anywhere.

    My proposition

    Copy the context just in Amount.RoundTo method to make it safe for concurrent writes. In all other methods where the decimalContext is used it can be left as-is since no write operations are performed there.

    I considered also an option with not using globals at all and returning a copy every time from the decimalContext func, but I'll leave it up to you to decide if this optimization is needed.

    opened by czeslavo 8
  • amount:  create and return integer values

    amount: create and return integer values

    Enable handling of 64-bit integer and big integer amount values in minor units. These types are seen in financial and banking APIs to avoid the possibility of invalid amounts when dealing with multiple currencies that have varying decimal handling.

    Related to #5 (implementation can be done separately with these changes in place)

    opened by Kunde21 6
  • Switch to ericlagergren/decimal

    Switch to ericlagergren/decimal

    Benchmarks show ericlagergren/decimal to be noticeably faster than cockroachdb/apd, along with doing less allocations. We should try switching.

    I'd like to get an answer to https://github.com/ericlagergren/decimal/issues/153 before I merge this, since the current checks feel a bit repetitive.

    EDIT: Answer received, waiting on maintainer fix.

    opened by bojanz 6
  • add minor amount handling

    add minor amount handling

    Financial systems and APIs use minor amounts in order to make it impossible to encounter invalid amounts, like '0.5 JPY' or '0.001 EUR' that aren't allowed per ISO-4177 currency standard.

    Embedded the Amount type, to keep all calculation handling logic in the same place. For conversion to string (Number method and encoding) the minor unit string is used instead of the locale-encoded string.

    Fixes #5

    opened by Kunde21 5
  • Skip currencies basing on missing required properties instead of hardcoding

    Skip currencies basing on missing required properties instead of hardcoding

    First of all, thank you for the great library. It helped my team with printing currencies a lot! 🎖️

    Regarding the PR: When fetching ISO data, let's not skip currencies basing on arbitrary picked terms, but instead skip only when they're missing required properties.

    We've noticed that issue when one of our clients started using CHE currency and we weren't able to format it with your library. Turned out it was skipped due to IsFund flag, while it had all the required properties to get included.

    These are all skipped currencies now:

    2022/03/25 14:15:21 Fetching ISO data...
    Skipping 'No universal currency' (country: ANTARCTICA) - it has no code defined.
    Skipping 'XDR' - it has no digits defined.
    Skipping 'XUA' - it has no digits defined.
    Skipping 'No universal currency' (country: PALESTINE, STATE OF) - it has no code defined.
    Skipping 'XSU' - it has no digits defined.
    Skipping 'No universal currency' (country: SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS) - it has no code defined.
    Skipping 'XBA' - it has no digits defined.
    Skipping 'XBB' - it has no digits defined.
    Skipping 'XBC' - it has no digits defined.
    Skipping 'XBD' - it has no digits defined.
    Skipping 'XTS' - it has no digits defined.
    Skipping 'XXX' - it has no digits defined.
    Skipping 'XAU' - it has no digits defined.
    Skipping 'XPD' - it has no digits defined.
    Skipping 'XPT' - it has no digits defined.
    Skipping 'XAG' - it has no digits defined.
    
    opened by czeslavo 4
  • allow more uses of the zero value

    allow more uses of the zero value

    Hi! Thanks a bunch for this very useful package.

    Consider summing a slice of amounts. The natural code to write is something like:

    func Sum(amounts []currency.Amount) (currency.Amount, error) {
    	var sum currency.Amount
    	for _, x := range amounts {
    		var err error
    		sum, err = sum.Add(x)
    		if err != nil {
    			return currency.Amount{}, err
    		}
    	}
    	return sum, nil
    }
    

    However, this code doesn't work, because sum gets initialized to the zero value, which has an empty currency code. Playground: https://go.dev/play/p/SzMyDcxcos7

    I propose that Add and Sub the zero value (0 number, empty currency code) as special, and ignore currency mismatches in this case, adopting the non-empty currency code.

    I also hit something similar in currency.Amount.Scan. I want to be able to store the zero value in the database, as a "not yet initialized, unknown currency" value. But loading such a value from the database using currency.Amount.Scan fails, because the empty currency code is not valid. I similarly propose that Scan have a special case for number = 0 and currency code consisting of all spaces ("" or " "), and allow it to be parsed without error.

    Thanks again.

    opened by josharian 3
  • NoSql Support

    NoSql Support

    Are there any plans to support storing Amount in no-sql databases? (for example mongo)

    If not do you have any tips on how to store that info? (mostly the number part concerns me)

    opened by erotokritosVall 3
  • Formatter uses wrong symbol after locale fallback

    Formatter uses wrong symbol after locale fallback

    if using CAD currency for example, and i want to display the symbol for "en-CA", GetSymbol prioritizes "en" over "en-CA" so it will return "CAD$" and not "$"

    opened by Stormsabre 3
  • The calculation precision (16) is sometimes not enough

    The calculation precision (16) is sometimes not enough

    Problem discovered in https://github.com/bojanz/currency/pull/7, the RoundTo() tests are taken from there.

    ~~Still needs additional test coverage (in tests other than the one for RoundTo).~~

    Went with a simpler/more performant way of selecting the precision, what do you think @Kunde21?

    opened by bojanz 3
  • Handle minor amount values

    Handle minor amount values

    Financial APIs use minor values in order to avoid any invalid amounts or ambiguous encoding.

    As this library stands, it is unable to handle parsing amounts from or writing amounts to a string in minor units. ToMinorUnits risks overflow attacks on int64 datatype.

    opened by Kunde21 3
  • unsupported type []unit8 with pq driver

    unsupported type []unit8 with pq driver

    I am using PQ driver panics at this line as the type of src is []unit8

    https://github.com/bojanz/currency/blob/28b0298f7e60bd74027682fe75cbea971893442a/amount.go#L347

    maybe should check for types. something like the following

    https://github.com/rengas/currency/blob/253a204a22fbe171270075154ea9076769c8b87c/amount.go#L349

    opened by rengas 0
Releases(v1.0.6)
  • v1.0.6(Oct 20, 2022)

  • v1.0.5(Oct 3, 2022)

  • v1.0.4(Jun 29, 2022)

  • v1.0.3(Apr 8, 2022)

  • v1.0.2(Mar 3, 2022)

    Bumps cockroachdb/apd to v3.1.0, to benefit from division optimizations.

    Before:

    BenchmarkAmount_Div-12             	 1348638	       897.7 ns/op	      32 B/op	       1 allocs/op
    BenchmarkAmount_DivDec-12          	 1038622	      1131 ns/op	      48 B/op	       3 allocs/op
    

    After:

    BenchmarkAmount_Div-12             	 2553472	       471.0 ns/op	      32 B/op	       1 allocs/op
    BenchmarkAmount_DivDec-12          	 1862860	       636.5 ns/op	      48 B/op	       3 allocs/op
    
    Source code(tar.gz)
    Source code(zip)
  • v1.0.1(Jan 30, 2022)

    Reduced allocations, improved performance.

    Big thanks to @nvanbenschoten for his fixes.

    Before (v1.0.0):

    BenchmarkNewAmount-12              	 2291136	       501.8 ns/op	      80 B/op	       4 allocs/op
    BenchmarkNewAmountFromBigInt-12    	 8162307	       136.6 ns/op	      32 B/op	       1 allocs/op
    BenchmarkNewAmountFromInt64-12     	 9674274	       118.6 ns/op	      32 B/op	       1 allocs/op
    BenchmarkAmount_Add-12             	 4852062	       233.1 ns/op	      64 B/op	       2 allocs/op
    BenchmarkAmount_Sub-12             	 4484218	       241.5 ns/op	      64 B/op	       2 allocs/op
    BenchmarkAmount_Mul-12             	 2590292	       467.2 ns/op	      96 B/op	       3 allocs/op
    BenchmarkAmount_MulDec-12          	 1948760	       626.3 ns/op	     112 B/op	       5 allocs/op
    BenchmarkAmount_Div-12             	 1148326	      1089 ns/op	      96 B/op	       3 allocs/op
    BenchmarkAmount_DivDec-12          	  904795	      1284 ns/op	     112 B/op	       5 allocs/op
    BenchmarkAmount_Round-12           	 1982382	       579.5 ns/op	      64 B/op	       2 allocs/op
    BenchmarkAmount_Cmp-12             	98593598	        12.12 ns/op	       0 B/op	       0 allocs/op
    PASS
    ok  	github.com/bojanz/currency	18.277s
    

    After (v1.0.1):

    BenchmarkNewAmount-12              	 2807982	       433.2 ns/op	      48 B/op	       3 allocs/op
    BenchmarkNewAmountFromBigInt-12    	 8502402	       134.6 ns/op	      32 B/op	       1 allocs/op
    BenchmarkNewAmountFromInt64-12     	31978585	        33.04 ns/op	       0 B/op	       0 allocs/op
    BenchmarkAmount_Add-12             	23726313	        46.81 ns/op	       0 B/op	       0 allocs/op
    BenchmarkAmount_Sub-12             	20663590	        51.57 ns/op	       0 B/op	       0 allocs/op
    BenchmarkAmount_Mul-12             	 4536388	       291.8 ns/op	      32 B/op	       1 allocs/op
    BenchmarkAmount_MulDec-12          	 2657835	       437.1 ns/op	      48 B/op	       3 allocs/op
    BenchmarkAmount_Div-12             	 1315654	       916.9 ns/op	      32 B/op	       1 allocs/op
    BenchmarkAmount_DivDec-12          	 1101399	      1116 ns/op	      48 B/op	       3 allocs/op
    BenchmarkAmount_Round-12           	 3132978	       374.9 ns/op	       0 B/op	       0 allocs/op
    BenchmarkAmount_Cmp-12             	87646204	        13.40 ns/op	       0 B/op	       0 allocs/op
    PASS
    ok  	github.com/bojanz/currency	17.332s
    
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0(Jan 23, 2022)

A currency computations package.

Currency v1.0.0 Currency package helps you do currency computations accurately, by avoiding peddling. The Currency struct holds all the data required

Kamaleshwar 52 Nov 2, 2022
money and currency formatting for golang

accounting - money and currency formatting for golang accounting is a library for money and currency formatting. (inspired by accounting.js) Quick Sta

Kyoung-chan Lee 782 Dec 21, 2022
A currency computations package.

Currency v1.0.0 Currency package helps you do currency computations accurately, by avoiding peddling. The Currency struct holds all the data required

Kamaleshwar 52 Nov 2, 2022
A command line Crypto-currency ticker made using golang and WazirX Api

░█████╗░██████╗░██╗░░░██╗██████╗░████████╗███████╗██╗░░██╗ ██╔══██╗██╔══██╗╚██╗░██╔╝██╔══██╗╚══██╔══╝██╔════╝╚██╗██╔╝ ██║░░╚═╝██████╔╝░╚████╔╝░██████╔

Aditya Das 4 Feb 6, 2022
Go client for the Foreign exchange rates and currency conversion API 💰

fixer Go client for Fixer.io (Foreign exchange rates and currency conversion API) You need to register for a free access key if using the default Fixe

Peter Hellberg 19 Nov 14, 2022
Raymond 1.2k Dec 29, 2022
A reward-based pseudo-currency system built exclusively for IIT Kanpur campus junta.

IITK Coin IITK Coin is a reward-based pseudo-currency system for the IIT Kanpur campus junta. Detailed vision and regulation rules of this currency ar

Bhuvan Singla 27 Nov 29, 2022
Simple user currency module that uses JSON.

UserCurrency Simple user currency module that uses JSON. Install go get github.com/NeutronX-dev/UserCurrency Functions func UserCurrency.Read(path_to

Neutron 1 Nov 23, 2021
Go client for the Foreign exchange rates and currency conversion API moneybag

fixer Go client for Fixer.io (Foreign exchange rates and currency conversion API) You need to register for a free access key if using the default Fixe

Shingirai Chanakira 0 Nov 25, 2021
Prosper - General app launcher with support for translation, calc, currency and unit conversion.

Prosper Description General app launcher with support for translation, calc, currency and unit conversion. Global shortcut is Option (Alt) + Space Fea

Ventsislav Georgiev 21 Dec 6, 2022
Coinbase - You can use this to retrieve the current price for an crypto currency

whats-this You can use this to retrieve the current price for an crypto currency

Manuel 3 Sep 29, 2022
Composable, observable and performant config handling for Go for the distributed processing era

Konfig Composable, observable and performant config handling for Go. Written for larger distributed systems where you may have plenty of configuration

Lalamove 634 Dec 11, 2022
Simple error handling primitives

errors Package errors provides simple error handling primitives. go get github.com/pkg/errors The traditional error handling idiom in Go is roughly ak

null 8k Dec 26, 2022
A comprehensive error handling library for Go

Highlights The errorx library provides error implementation and error-related utilities. Library features include (but are not limited to): Stack trac

Joom 922 Jan 6, 2023
a tool for handling file uploads simple

baraka a tool for handling file uploads for http servers makes it easier to make operations with files from the http request. Contents Install Simple

Enes Furkan Olcay 46 Nov 30, 2022
A sync.WaitGroup with error handling and concurrency control

go-waitgroup How to use An package that allows you to use the constructs of a sync.WaitGroup to create a pool of goroutines and control the concurrenc

Pieter Claerhout 36 Dec 31, 2022
:speedboat: a limited consumer goroutine or unlimited goroutine pool for easier goroutine handling and cancellation

Package pool Package pool implements a limited consumer goroutine or unlimited goroutine pool for easier goroutine handling and cancellation. Features

Go Playgound 707 Jan 1, 2023
A Go package for handling common HTTP JSON responses.

go-respond A Go package for handling common HTTP JSON responses. Installation go get github.com/nicklaw5/go-respond Usage The goal of go-respond is to

Nick Law 50 Sep 26, 2022
Address handling for Go.

address Handles address representation, validation and formatting. Inspired by Google's libaddressinput. Backstory: https://bojanz.github.io/address-h

Bojan Zivanovic 58 Dec 28, 2022
handling 1M websockets connections in Go

Going Infinite, handling 1M websockets connections in Go This repository holds the complete implementation of the examples seen in Gophercon Israel ta

Eran Yanay 5.3k Jan 8, 2023