Currency¶
Money, FX conversion, and locale-aware formatting.
Money¶
import simweave as sw
price = sw.Money(199, "GBP")
tax = sw.Money(40, "GBP")
total = price + tax # Money(239, "GBP")
Money enforces currency consistency. Adding Money(1, "GBP") and
Money(1, "USD") raises CurrencyMismatchError.
FX conversion¶
fx = sw.StaticFXConverter({("GBP", "USD"): 1.27})
usd = fx.convert(sw.Money(100, "GBP"), to="USD") # Money(127, "USD")
For dynamic rates, use CallableFXConverter(fn) where fn(base, quote)
returns the rate.
Custom currencies¶
sw.register_custom("ZED", decimals=2)
sw.is_valid_currency("ZED") # True
sw.unregister_custom("ZED")
Locale-aware formatting (with [intl])¶
API¶
simweave.currency: Decimal-backed Money, ISO 4217 codes, FX conversion.
Public surface
- :class:
Money— frozen, currency-taggedDecimalvalue with strict same-currency arithmetic and explicit FX conversion. - :class:
CurrencyMismatchError— raised on cross-currency ops. - :class:
FXConverter— Protocol for rate lookup. - :class:
StaticFXConverter— in-memory rate table with automatic inverse lookup. Use for deterministic tests and fixed-rate models. - :class:
CallableFXConverter— adapts an arbitrary callable to the :class:FXConverterprotocol. - :func:
format_money— ASCII default formatter, optional locale-aware path viasimweave[intl](babel). - :func:
register_custom, :func:unregister_custom— escape hatch for crypto, in-game, or test-fixture currency codes. - :func:
is_valid_currency, :func:get_decimals, :func:list_codes— registry introspection helpers.
Design contract in short
- Decimal everywhere; float inputs routed via
str()to avoid binary-float drift. - Banker's rounding (
ROUND_HALF_EVEN) at display / quantisation time; full precision preserved during computation. - Cross-currency arithmetic refuses — use
money.to(target, fx). - Scalar * Money allowed; Money * Money refused.
- Money / Money (same currency) returns
float; Money / scalar returns Money. - Negatives are legal (debts, refunds, signed cashflows).
CallableFXConverter
¶
Wrap an arbitrary callable as an :class:FXConverter.
The wrapped callable must accept (source, target, at) and return
something coercible to Decimal (via Decimal(str(...))).
Useful when you already have a rate-lookup function.
Example
def my_lookup(src, tgt, at=None): ... return 1.27 if (src, tgt) == ("GBP", "USD") else 1.0 fx = CallableFXConverter(my_lookup) fx.rate("GBP", "USD") Decimal('1.27')
Source code in src/simweave/currency/fx.py
FXConverter
¶
Bases: Protocol
Protocol for anything that can quote an FX rate.
rate(source, target, at=None) returns the number of target
units per one unit of source. A rate of 1.27 for
("GBP", "USD") means one pound buys 1.27 dollars.
StaticFXConverter
¶
Fixed-rate converter backed by an in-memory table.
Automatically handles inverses: if ("GBP", "USD") -> 1.27 is
registered, rate("USD", "GBP") returns 1 / 1.27 without
needing an explicit entry.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rates
|
Mapping[tuple[str, str], Decimal | float | int | str]
|
Mapping of |
required |
Source code in src/simweave/currency/fx.py
CurrencyMismatchError
¶
Bases: TypeError
Raised when an operation requires two Money values to share a currency
but they don't (e.g. GBP + USD).
Subclasses :class:TypeError so that except TypeError catches it in
legacy code, while new code can except CurrencyMismatchError for
precise handling.
Money
dataclass
¶
A currency-tagged monetary amount.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
amount
|
Decimal
|
The numeric amount. Accepts |
required |
currency
|
str
|
ISO 4217 three-letter code (case-insensitive; normalised to
uppercase). Must be either a known ISO 4217 code or a code
previously registered via
:func: |
required |
Examples:
>>> from simweave.currency import Money
>>> Money(100, "GBP") + Money("50.25", "GBP")
Money(Decimal('150.25'), 'GBP')
>>> Money(100, "GBP") * 3
Money(Decimal('300'), 'GBP')
round_to_currency
¶
Return a new Money rounded to the currency's minor-unit
precision. Defaults to banker's rounding (ROUND_HALF_EVEN).
Source code in src/simweave/currency/money.py
to
¶
Convert to target currency using converter.rate(...).
Short-circuits to self (no-op) when the target currency
already matches. Preserves full Decimal precision; call
:meth:round_to_currency afterwards if you want to quantise
to the target's minor-unit precision.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str
|
ISO 4217 code to convert to. |
required |
converter
|
'FXConverter'
|
Any object satisfying :class: |
required |
at
|
'datetime | None'
|
Optional datetime passed through to the converter (for historical-rate lookups). Converters may ignore it. |
None
|
Source code in src/simweave/currency/money.py
get_decimals
¶
Return the canonical decimal places for code.
Raises:
| Type | Description |
|---|---|
KeyError
|
If the code is not a known ISO 4217 or custom-registered code. |
Source code in src/simweave/currency/codes.py
is_valid_currency
¶
Return True if code is a known ISO 4217 or custom-registered code.
list_codes
¶
Return a sorted tuple of all currently known codes.
register_custom
¶
Register a non-ISO currency code (crypto, in-game currency, test fixture).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
Uppercase identifier. Overwrites a prior custom registration but refuses to shadow a real ISO 4217 code. |
required |
decimals
|
int
|
Non-negative number of minor-unit decimal places. |
required |
Examples:
>>> from simweave.currency import register_custom, Money
>>> register_custom("BTC", decimals=8)
>>> Money("0.12345678", "BTC").amount
Decimal('0.12345678')
Source code in src/simweave/currency/codes.py
unregister_custom
¶
format_money
¶
Format a :class:Money for display.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
money
|
'Money'
|
The value to render. |
required |
spec
|
str
|
Format specifier. See module docstring. |
''
|
locale
|
str | None
|
Optional locale identifier (e.g. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The formatted string. |