money

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 26, 2026 License: MIT Imports: 15 Imported by: 0

README

money

CI CodeQL Coverage Mutation Documentation Go Reference Release Go License

money is an immutable exact monetary value package. It delegates decimal, integer, and rational arithmetic to math and delegates ISO 4217 identity and metadata to international/currency.

The package never accepts or emits float32 or float64. Fixed Money operations preserve currency and resolved context identity. Multiplication and division return RationalMoney; callers choose the context and rounding mode at the boundary where a fixed amount is required.

Install

go get github.com/faustbrian/go-money

The monorepo checkout uses local replacements for the sibling math and international modules.

Quick start

euro, _ := currency.Parse("EUR")
ctx, _ := money.DefaultContext(euro)
left, _ := money.Parse("12.30", euro, ctx)
right, _ := money.Parse("0.45", euro, ctx)
total, _ := left.Add(right) // 12.75 EUR

Exact multiplication remains rational until rounding is explicit:

rate, _ := money.ParseRate("1/3")
exact, _ := total.Mul(context.Background(), rate)
fixed, result, _ := exact.Round(ctx, gomath.RoundHalfEven)
_ = result.Inexact()

Guarantees

  • Cross-currency or cross-context arithmetic returns an error.
  • Default scales come from authoritative ISO metadata; historic currencies are accepted only through the explicit historic parse policy.
  • Amount, scale, rate, ratio, allocation, output, and diagnostic work is bounded.
  • Equal and weighted allocations distribute minor-unit remainders deterministically and conserve the source total.
  • Tax and discount results derive one component by subtraction, so their documented totals are conserved.
  • Conversion uses only a caller-supplied directed exact rate with timestamp and source metadata. There is no live FX client.
  • Versioned JSON and SQL representations encode amounts as strings.

Packages

  • money: values, contexts, arithmetic, allocation, tax, discount, and FX.
  • money/format: exact locale display using international locale tags.
  • money/encoding: versioned JSON, text, SQL, and PostgreSQL numeric adapters.
  • money/moneytest: official edge fixtures and conservation assertions.

Verification

make check runs formatting, analysis, tests, meaningful 100% production coverage, race checks, docs, compatibility, dependency, float-contamination, and vulnerability gates. make release-check adds fuzzing, mutation testing, and correctness-gated comparative benchmarks.

See docs/api.md, docs/contexts-and-rounding.md, and docs/cookbook.md.

Ecosystem

Use the Golib documentation portal to choose companion packages, supported stacks, recipes, and operations guidance.

Documentation

Overview

Package money provides immutable, bounded, exact monetary values backed by math and identified by international currency codes.

Fixed Money never rounds implicitly. Multiplication and division produce RationalMoney, which requires an explicit context and rounding mode before it can return fixed Money. Currency and resolved context mismatches are errors.

Index

Examples

Constants

View Source
const MaxAllocationParts = 10_000

MaxAllocationParts bounds output and remainder-distribution work.

View Source
const MaxAmountDigits = 256

MaxAmountDigits bounds coefficient input and diagnostic output.

View Source
const MaxCashStep uint64 = 1_000_000_000_000_000_000

MaxCashStep bounds cash increments expressed at their context scale.

View Source
const MaxMoneyBagEntries = 1_000

MaxMoneyBagEntries bounds heterogeneous output and lookup work.

View Source
const MaxRateMagnitude int64 = 1_000_000

MaxRateMagnitude bounds generic multiplication and conversion rates.

View Source
const MaxRateSourceBytes = 128

MaxRateSourceBytes bounds persisted and diagnostic source metadata.

View Source
const MaxRatioDigits = 64

MaxRatioDigits bounds each positive integer allocation weight.

View Source
const MaxScale uint8 = 18

MaxScale bounds decimal work and serialized representation sizes.

View Source
const MaxTaxRate int64 = 10

MaxTaxRate bounds a tax multiplier to 1000 percent.

Variables

View Source
var (
	// ErrUnknownCurrency reports an absent or unrecognized currency identity.
	ErrUnknownCurrency = errors.New("money: unknown currency")
	// ErrMinorUnitsUnavailable reports that ISO metadata defines no minor-unit
	// exponent for a currency, so a default context cannot be inferred.
	ErrMinorUnitsUnavailable = errors.New("money: currency has no minor-unit metadata")
	// ErrInvalidContext reports an impossible or unsupported monetary context.
	ErrInvalidContext = errors.New("money: invalid context")
	// ErrPrecisionLoss reports input that cannot fit its context without
	// discarding represented decimal places.
	ErrPrecisionLoss = errors.New("money: precision loss")
	// ErrCurrencyMismatch reports arithmetic across different currencies.
	ErrCurrencyMismatch = errors.New("money: currency mismatch")
	// ErrContextMismatch reports arithmetic across different precision policies.
	ErrContextMismatch = errors.New("money: context mismatch")
	// ErrInvalidMoney reports use of an absent Money zero value.
	ErrInvalidMoney = errors.New("money: invalid value")
	// ErrAmountLimit reports an amount outside configured digit or scale bounds.
	ErrAmountLimit = errors.New("money: amount limit exceeded")
	// ErrInvalidRate reports a malformed, negative, or excessive exact rate.
	ErrInvalidRate = errors.New("money: invalid rate")
	// ErrInvalidAllocation reports an empty, excessive, or nonpositive split.
	ErrInvalidAllocation = errors.New("money: invalid allocation")
	// ErrMoneyBagLimit reports excessive distinct currency/context entries.
	ErrMoneyBagLimit = errors.New("money: bag limit exceeded")
)

Functions

This section is empty.

Types

type AllocationResult

type AllocationResult struct {
	// contains filtered or unexported fields
}

AllocationResult is an immutable ordered allocation. Earlier parts receive deterministic one-unit remainders.

func (AllocationResult) Parts

func (result AllocationResult) Parts() []Money

Parts returns an independent copy of the ordered allocation.

func (AllocationResult) Sum

func (result AllocationResult) Sum() (Money, error)

Sum returns the conserved total represented by all parts.

type Amount

type Amount struct {
	// contains filtered or unexported fields
}

Amount is an immutable bounded monetary decimal. Exact decimal arithmetic is delegated to math; Amount adds only monetary resource policy.

func AmountFromDecimal

func AmountFromDecimal(value decimal.Decimal) (Amount, error)

AmountFromDecimal applies monetary bounds to an exact math Decimal.

func ParseAmount

func ParseAmount(input string) (Amount, error)

ParseAmount parses strict decimal text without a float conversion.

func (Amount) Decimal

func (amount Amount) Decimal() decimal.Decimal

Decimal returns the immutable exact math value.

func (Amount) Equal

func (amount Amount) Equal(other Amount) bool

Equal reports numeric equality.

func (Amount) IsZero

func (amount Amount) IsZero() bool

IsZero reports numeric zero.

func (Amount) Scale

func (amount Amount) Scale() int32

Scale returns the retained number of fractional places.

func (Amount) Sign

func (amount Amount) Sign() int

Sign returns -1, 0, or +1.

func (Amount) String

func (amount Amount) String() string

String returns exact non-exponent decimal text.

type Context

type Context struct {
	// contains filtered or unexported fields
}

Context is an immutable, comparable monetary precision policy. It stores only arithmetic policy; currency identity and metadata remain owned by international.

func AutomaticContext

func AutomaticContext() Context

AutomaticContext constructs a policy that captures exact input scale. The resolved Money context carries that scale, making representation differences explicit during later arithmetic.

func CashContext

func CashContext(scale uint8, step uint64) (Context, error)

CashContext constructs a fixed scale with a positive cash increment. Step is expressed as an integer count of units at scale: scale 2 and step 5 means 0.05.

func CustomContext

func CustomContext(scale uint8) (Context, error)

CustomContext constructs an application-selected fixed decimal scale.

func DefaultContext

func DefaultContext(code currency.Code) (Context, error)

DefaultContext constructs a fixed-scale context from authoritative ISO 4217 metadata. Currencies without an applicable minor-unit exponent require an explicit custom context.

func (Context) CashStep

func (context Context) CashStep() uint64

CashStep returns the integer number of scale-sized units in one cash increment. It is zero for non-cash contexts.

func (Context) IsZero

func (context Context) IsZero() bool

IsZero reports whether context is absent or invalid.

func (Context) Kind

func (context Context) Kind() ContextKind

Kind returns the context's precision policy.

func (Context) Scale

func (context Context) Scale() uint8

Scale returns the number of decimal fractional digits for fixed contexts.

type ContextKind

type ContextKind uint8

ContextKind identifies the monetary precision policy without carrying currency metadata into the arithmetic layer.

const (

	// ContextDefault uses the authoritative ISO minor-unit exponent captured
	// when the context is constructed.
	ContextDefault ContextKind
	// ContextCustom uses an application-selected decimal scale.
	ContextCustom
	// ContextCash applies a positive cash increment at a selected scale.
	ContextCash
	// ContextAutomatic preserves exact input scale until an operation requires
	// an explicit rounding boundary.
	ContextAutomatic
)

type ConversionResult

type ConversionResult struct {
	// contains filtered or unexported fields
}

ConversionResult retains both values, exact rate metadata, and rounding.

func Convert

func Convert(ctx context.Context, source Money, rate ExchangeRate, target Context, mode gomath.RoundingMode) (ConversionResult, error)

Convert applies only the supplied directed exact rate and rounds explicitly into target. It never contacts a live FX service.

Example
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/faustbrian/go-international/currency"
	gomath "github.com/faustbrian/go-math"
	"github.com/faustbrian/go-money"
)

func main() {
	euro, _ := currency.Parse("EUR")
	dollar, _ := currency.Parse("USD")
	euroContext, _ := money.DefaultContext(euro)
	dollarContext, _ := money.DefaultContext(dollar)
	value, _ := money.Parse("10.00", euro, euroContext)
	exact, _ := money.ParseRate("1.1")
	rate, _ := money.NewExchangeRate(
		euro,
		dollar,
		exact,
		time.Date(2026, 7, 19, 6, 0, 0, 0, time.UTC),
		"central-bank-daily",
	)
	result, _ := money.Convert(
		context.Background(),
		value,
		rate,
		dollarContext,
		gomath.RoundHalfEven,
	)

	fmt.Println(result.Converted())
}
Output:
11.00 USD

func (ConversionResult) Converted

func (result ConversionResult) Converted() Money

Converted returns the rounded quote-currency value.

func (ConversionResult) Rate

func (result ConversionResult) Rate() ExchangeRate

Rate returns exact conversion and attribution metadata.

func (ConversionResult) Rounding

func (result ConversionResult) Rounding() RoundingResult

Rounding returns conditions raised at the explicit quote boundary.

func (ConversionResult) Source

func (result ConversionResult) Source() Money

Source returns the original monetary value.

type DiscountRate

type DiscountRate struct {
	// contains filtered or unexported fields
}

DiscountRate is an exact validated fraction in the inclusive range [0, 1].

func ParseDiscountRate

func ParseDiscountRate(input string) (DiscountRate, error)

ParseDiscountRate parses an exact discount fraction without floats.

func (DiscountRate) Rate

func (rate DiscountRate) Rate() Rate

Rate returns the generic exact multiplier.

type DiscountResult

type DiscountResult struct {
	// contains filtered or unexported fields
}

DiscountResult contains conserved original, discount, and final amounts.

func ApplyDiscount

func ApplyDiscount(ctx context.Context, original Money, rate DiscountRate, mode gomath.RoundingMode) (DiscountResult, error)

ApplyDiscount rounds the discount once and derives final by subtraction so final + discount always equals original.

func (DiscountResult) Discount

func (result DiscountResult) Discount() Money

Discount returns the rounded discount component.

func (DiscountResult) Final

func (result DiscountResult) Final() Money

Final returns original minus discount.

func (DiscountResult) Original

func (result DiscountResult) Original() Money

Original returns the amount before discount.

func (DiscountResult) Rounding

func (result DiscountResult) Rounding() RoundingResult

Rounding returns conditions raised at the monetary boundary.

type ExchangeRate

type ExchangeRate struct {
	// contains filtered or unexported fields
}

ExchangeRate is an injected exact directed rate with required attribution. It contains no fetching or ambient live-FX behavior.

func NewExchangeRate

func NewExchangeRate(base, quote currency.Code, rate Rate, observedAt time.Time, source string) (ExchangeRate, error)

NewExchangeRate validates a directed rate and its observation metadata.

func (ExchangeRate) Base

func (rate ExchangeRate) Base() currency.Code

Base returns the source currency.

func (ExchangeRate) Exact

func (rate ExchangeRate) Exact() Rate

Exact returns the exact directed multiplier.

func (ExchangeRate) ObservedAt

func (rate ExchangeRate) ObservedAt() time.Time

ObservedAt returns the rate observation timestamp.

func (ExchangeRate) Quote

func (rate ExchangeRate) Quote() currency.Code

Quote returns the destination currency.

func (ExchangeRate) Source

func (rate ExchangeRate) Source() string

Source returns bounded attribution metadata.

type Money

type Money struct {
	// contains filtered or unexported fields
}

Money is an immutable exact decimal amount bound to one currency and one precision context. Its zero value is absent and cannot participate in arithmetic.

func FromMinorUnits

func FromMinorUnits(units integer.Integer, code currency.Code, context Context) (Money, error)

FromMinorUnits constructs Money from an arbitrary-precision integer count of units at the supplied fixed context scale.

func Parse

func Parse(input string, code currency.Code, context Context) (Money, error)

Parse constructs Money from strict exact decimal text. It never converts through binary floating point. Fixed contexts reject represented fractional places beyond their scale, including trailing zeroes, rather than silently normalizing a context difference.

func (Money) Abs

func (money Money) Abs() (Money, error)

Abs returns the nonnegative magnitude without mutating money.

func (Money) Add

func (money Money) Add(other Money) (Money, error)

Add returns the exact sum. Currency and resolved context must match.

Example
package main

import (
	"fmt"

	"github.com/faustbrian/go-international/currency"
	"github.com/faustbrian/go-money"
)

func main() {
	euro, _ := currency.Parse("EUR")
	monetaryContext, _ := money.DefaultContext(euro)
	left, _ := money.Parse("12.30", euro, monetaryContext)
	right, _ := money.Parse("0.45", euro, monetaryContext)
	total, _ := left.Add(right)

	fmt.Println(total)
}
Output:
12.75 EUR

func (Money) Allocate

func (money Money) Allocate(ctx context.Context, ratios []integer.Integer) (AllocationResult, error)

Allocate apportions money by positive integer ratios using stable largest remainders. Equal remainders are resolved by original ratio order.

Example
package main

import (
	"context"
	"fmt"

	"github.com/faustbrian/go-international/currency"
	"github.com/faustbrian/go-math/integer"
	"github.com/faustbrian/go-money"
)

func main() {
	euro, _ := currency.Parse("EUR")
	monetaryContext, _ := money.DefaultContext(euro)
	total, _ := money.Parse("10.00", euro, monetaryContext)
	result, _ := total.Allocate(context.Background(), []integer.Integer{
		integer.New(1), integer.New(2), integer.New(3),
	})

	for _, part := range result.Parts() {
		fmt.Println(part)
	}
}
Output:
1.67 EUR
3.33 EUR
5.00 EUR

func (Money) Amount

func (money Money) Amount() Amount

Amount returns the immutable exact decimal amount.

func (Money) Compare

func (money Money) Compare(other Money) (int, error)

Compare returns -1, 0, or +1 after verifying currency and context identity.

func (Money) Context

func (money Money) Context() Context

Context returns the resolved precision context.

func (Money) Currency

func (money Money) Currency() currency.Code

Currency returns the currency identity owned by international.

func (Money) Equal

func (money Money) Equal(other Money) (bool, error)

Equal reports exact monetary equality after verifying currency and context identity. Mismatched values return an error instead of comparing false.

func (Money) EqualSplit

func (money Money) EqualSplit(ctx context.Context, count int) (AllocationResult, error)

EqualSplit divides money into count fixed-context parts and distributes any positive or negative minor-unit remainder from the first part onward.

func (Money) IsZero

func (money Money) IsZero() bool

IsZero reports whether a valid Money has numeric value zero.

func (Money) MinorUnits

func (money Money) MinorUnits() (integer.Integer, error)

MinorUnits returns the exact arbitrary-precision integer coefficient at the Money context's resolved scale.

func (Money) Mul

func (money Money) Mul(ctx context.Context, rate Rate) (RationalMoney, error)

Mul returns an exact rational result without applying the Money context's fixed scale.

func (Money) Neg

func (money Money) Neg() (Money, error)

Neg returns the additive inverse without mutating money.

func (Money) Quo

func (money Money) Quo(ctx context.Context, rate Rate) (RationalMoney, error)

Quo divides by an exact nonzero rate without rounding.

func (Money) Ratio

func (money Money) Ratio(ctx context.Context, other Money) (Ratio, error)

Ratio returns money/other exactly after verifying currency and context.

func (Money) Sign

func (money Money) Sign() int

Sign returns the amount sign. Invalid zero-value Money reports zero; callers can distinguish it with Valid.

func (Money) String

func (money Money) String() string

String returns a deterministic diagnostic representation. Locale display belongs to the optional format package.

func (Money) Sub

func (money Money) Sub(other Money) (Money, error)

Sub returns the exact difference. Currency and resolved context must match.

func (Money) Valid

func (money Money) Valid() bool

Valid reports whether money has a currency and context.

type MoneyBag

type MoneyBag struct {
	// contains filtered or unexported fields
}

MoneyBag is an immutable deterministic collection keyed by exact currency and context identity. Its zero value is an empty bag.

func NewMoneyBag

func NewMoneyBag(values ...Money) (MoneyBag, error)

NewMoneyBag validates and combines values with identical identities.

func (MoneyBag) Add

func (bag MoneyBag) Add(value Money) (MoneyBag, error)

Add returns a new bag, combining only an identical currency/context entry.

func (MoneyBag) Get

func (bag MoneyBag) Get(code currency.Code, context Context) (Money, bool)

Get returns the value with exact currency and context identity.

func (MoneyBag) Values

func (bag MoneyBag) Values() []Money

Values returns an independent deterministic slice.

type Rate

type Rate struct {
	// contains filtered or unexported fields
}

Rate is an immutable, exact, nonnegative rational multiplier.

func ParseRate

func ParseRate(input string) (Rate, error)

ParseRate parses either exact decimal text or a numerator/denominator pair.

func ParseRateWithMaximum

func ParseRateWithMaximum(input, maximum string) (Rate, error)

ParseRateWithMaximum parses a rate while applying an explicit inclusive caller-defined maximum. The maximum accepts the same exact decimal or numerator/denominator syntax as input and must be positive.

func (Rate) IsZero

func (rate Rate) IsZero() bool

IsZero reports whether the multiplier is zero.

func (Rate) Rational

func (rate Rate) Rational() rational.Rational

Rational returns the immutable exact math value.

func (Rate) String

func (rate Rate) String() string

String returns the normalized exact fraction.

func (Rate) Valid

func (rate Rate) Valid() bool

Valid reports whether rate was constructed through a validating API.

type Ratio

type Ratio struct {
	// contains filtered or unexported fields
}

Ratio is an immutable exact signed relationship between compatible monetary values.

func (Ratio) Rational

func (ratio Ratio) Rational() rational.Rational

Rational returns the exact math fraction.

func (Ratio) String

func (ratio Ratio) String() string

String returns the normalized fraction or empty text for an absent ratio.

type RationalMoney

type RationalMoney struct {
	// contains filtered or unexported fields
}

RationalMoney is an immutable exact fractional monetary result. It must be rounded explicitly before persistence as fixed-context Money.

func (RationalMoney) Currency

func (money RationalMoney) Currency() currency.Code

Currency returns the monetary identity.

func (RationalMoney) Rational

func (money RationalMoney) Rational() rational.Rational

Rational returns the immutable exact math value.

func (RationalMoney) Round

func (money RationalMoney) Round(target Context, mode gomath.RoundingMode) (Money, RoundingResult, error)

Round creates fixed-context Money at an explicit rounding boundary.

func (RationalMoney) String

func (money RationalMoney) String() string

String returns the normalized exact fraction and currency code.

type RoundingResult

type RoundingResult struct {
	// contains filtered or unexported fields
}

RoundingResult describes whether an explicit boundary discarded value.

func (RoundingResult) Conditions

func (result RoundingResult) Conditions() gomath.Condition

Conditions returns the math arithmetic conditions.

func (RoundingResult) Inexact

func (result RoundingResult) Inexact() bool

Inexact reports whether nonzero value was discarded.

type TaxRate

type TaxRate struct {
	// contains filtered or unexported fields
}

TaxRate is an exact validated nonnegative tax fraction: 0.24 means 24%.

func ParseTaxRate

func ParseTaxRate(input string) (TaxRate, error)

ParseTaxRate parses an exact tax fraction without binary floating point.

func (TaxRate) Rate

func (rate TaxRate) Rate() Rate

Rate returns the generic exact multiplier.

type TaxResult

type TaxResult struct {
	// contains filtered or unexported fields
}

TaxResult contains conserved net, tax, and gross components.

func AddTax

func AddTax(ctx context.Context, net Money, rate TaxRate, mode gomath.RoundingMode) (TaxResult, error)

AddTax calculates tax from a net amount, rounds the tax once, then derives gross by addition so net + tax always equals gross.

func ExtractTax

func ExtractTax(ctx context.Context, gross Money, rate TaxRate, mode gomath.RoundingMode) (TaxResult, error)

ExtractTax derives exact net from gross/(1+rate), rounds net once, then subtracts it from gross so the returned components conserve gross.

func (TaxResult) Gross

func (result TaxResult) Gross() Money

Gross returns the amount including tax.

func (TaxResult) Net

func (result TaxResult) Net() Money

Net returns the amount excluding tax.

func (TaxResult) Rounding

func (result TaxResult) Rounding() RoundingResult

Rounding returns conditions raised at the explicit monetary boundary.

func (TaxResult) Tax

func (result TaxResult) Tax() Money

Tax returns the rounded tax component.

Directories

Path Synopsis
Package encoding provides bounded, versioned money wire and persistence adapters.
Package encoding provides bounded, versioned money wire and persistence adapters.
Package format renders exact Money values for display.
Package format renders exact Money values for display.
Package moneytest provides currency fixtures and conservation assertions for consumers of money.
Package moneytest provides currency fixtures and conservation assertions for consumers of money.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL