# Handle currencies
Source: https://fragment.dev/guides/handle-currencies
[Documentation index](https://fragment.dev/llms.txt)
Use multi-currency Ledgers to easily build products that track currencies, stocks and inventories.
[Visual: handle-currencies. View on the HTML page.](https://fragment.dev/guides/handle-currencies)
A Ledger can contain Ledger Accounts of different currencies, like USD and GBP bank accounts. Ledger Accounts can also be multi-currency, like one representing a stock portfolio, with a balance for each symbol.
You can post Ledger Entries with multiple currencies. It must follow the Accounting Equation per currency, so you'll need at least four Ledger Lines.
Fragment includes a [list](https://fragment.dev/markdown/api-reference/ledger-types.md#scalars-and-enums-currencycode) of common currencies. You can also add your own [custom currencies](https://fragment.dev/markdown/guides/handle-currencies.md#custom-currencies)
## Track exposure
Multi-currency ledgers often reflect transitory states: a company accepts payment in one currency intending to convert it to another currency. Between accepting and converting the money, the exchange rate could change. Tracking the potential gain or loss from this change is called exposure.
To track exposure, use a Change Ledger Account that has multiple balances, one for each currency. Here's an example that tracks exposure between USD and EUR:
[Visual: multi-currency-example. View on the HTML page.](https://fragment.dev/guides/handle-currencies)
In this example,
- User 1 starts out with 100 USD, balanced by 100 USD in the platform's bank.
- User 1 pays User 2, but they have different wallet currencies, so we record the exchange rate they were given in an Exposure account. The exposure account balance is 100 USD - 90 EUR.
- The platform now owes User 2 EUR, so it exchanges 100 USD for 95 EUR. In this case, due to exchange rate fluctuation, they got a different rate. The exposure account balance records the difference in exchange rates: 5 EUR.
## Ledger Accounts
To create a multi-currency Ledger, set `defaultCurrencyMode` to `multi` and unset `defaultCurrency`:
```json title="Multi-currency Ledger"
{
"key": "...",
"chartOfAccounts": {
"defaultCurrencyMode": "multi",
"accounts": [
{...},
{...}
]
},
"ledgerEntries": {...}
}
```
For Ledger Accounts in a single currency, such as [bank accounts](https://fragment.dev/markdown/guides/reconcile-payments.md#linked-ledger-accounts), set `currencyMode` and `currency` on the account directly:
```json title="Single-currency Ledger Account"
{
"key": "...",
"chartOfAccounts": {
"defaultCurrencyMode": "multi",
"accounts": [
{
"key": "bank-account",
"currencyMode": "single",
"currency": {
"code": "USD"
}
},
{...}
]
},
"ledgerEntries": {...}
}
```
Like other Ledger Account properties, `currencyMode` and `currency` are inherited by child Ledger Accounts unless they are overridden.
## Ledger Entries
You can define multi-currency Ledger Entries types in your Schema in the same way as single-currency Ledger Entries.
Multi-currency Ledger Accounts accept Ledger Lines in any currency, so Ledger Entries that post to them must specify the currency of each Ledger Line:
```json title="Multi-currency Ledger Entry"
{
"key": "...",
"chartOfAccounts": {
"defaultCurrencyMode": "multi",
"accounts": [
{
"type": "asset",
"key": "bank-account"
},
{...}
]
},
"ledgerEntries": {
"types": [
{
"type": "user_funds_account_usd",
"description": "Fund {{funding_amount}} USD",
"lines": [
{
"key": "funds_arrive_in_bank",
"account": {
"path": "bank-account"
},
"amount": "{{funding_amount}}",
"currency": {
"code": "USD"
}
},
{...other line}
]
}
]
}
}
```
You can parameterize `currency` to make your Ledger Entries more reusable:
```json title="Parameterized multi-currency Ledger Entry"
{
"key": "...",
"chartOfAccounts": {
"defaultCurrencyMode": "multi",
"accounts": [
{
"type": "asset",
"key": "bank-account"
},
{...}
]
},
"ledgerEntries": {
"types": [
{
"type": "user_funds_account",
"description": "Fund {{funding_amount}} {{currency}}",
"lines": [
{
"key": "funds_arrive_in_bank",
"account": {
"path": "bank-account"
},
"amount": "{{funding_amount}}",
"currency": {
"code": "{{currency}}"
}
},
{...other line}
]
}
]
}
}
```
You can also post multi-currency Ledger Entries to Ledger Account templates which parameterize `currency`. This is useful for creating [linked Ledger Accounts](https://fragment.dev/markdown/guides/reconcile-payments.md#linked-ledger-accounts) if you have multiple bank accounts in different currencies in a multi-currency Ledger.
```json title="Parameterized Ledger Account templates"
{
"key": "...",
"chartOfAccounts": {
"defaultCurrencyMode": "multi",
"accounts": [
{
"key": "bank-accounts",
"template": true,
"currencyMode": "single",
"currency": {
"code": "{{currency}}"
}
},
{...}
]
},
"ledgerEntries": {
"types": [
{
"type": "user_funds_account",
"description": "Fund {{funding_amount}} {{currency}}",
"lines": [
{
"key": "funds_arrive_in_bank",
"account": {
"path": "bank-accounts:{{currency}}"
},
"amount": "{{funding_amount}}",
"currency": {
"code": "{{currency}}"
}
},
{...other line}
]
}
]
}
}
```
[Ledger Entry Conditions](https://fragment.dev/markdown/guides/configure-consistency.md#entry-conditions) against multi-currency Ledger Accounts need to specify the currency the condition applies to:
```json title="Parameterized Ledger Account templates"
{
"key": "...",
"chartOfAccounts": {
"defaultCurrencyMode": "multi",
"accounts": [
{...},
{...}
]
},
"ledgerEntries": {
"types": [
{
"type": "p2p_transfer",
"description": "Move {{funding_amount}} {{currency}}",
"lines": [
{...},
{...}
],
"conditions": [
{
"account": {
"path": "liabilities/users:{{from_user_id}}"
},
"currency": {
"code": "{{currency}}"
},
"postcondition": {
"totalBalance": {
"gte": "0"
}
}
}
]
}
]
}
}
```
## Read balances
Balances on multi-currency Ledger Accounts are lists of currency and amount, as opposed to just a
single amount. They support all the same balance queries as single-currency accounts. You can either:
1. Use plural field names (e.g. `balances` instead of `balance`) to query all currencies at once
2. Use singular field names with a `currency` argument to query a specific currency
### Latest
To read a specific currency's balance, pass in the `currency` argument:
```graphql title="Get specific currency balance"
query GetUSDBalance(
$ledgerAccount: LedgerAccountMatchInput!
) {
ledgerAccount(ledgerAccount: $ledgerAccount) {
ownBalance(currency: { code: USD })
}
}
```
```json title="GetUSDBalance variables"
{
"ledgerAccount": {
"path": "assets/banks/user-cash",
"ledger": {
"ik": "multi-currency-ledger"
}
}
}
```
To read all the balances for a multi-currency Ledger Account, use `ownBalances`:
```graphql title="Get all currency balances"
query GetAllBalances(
$ledgerAccount: LedgerAccountMatchInput!
) {
ledgerAccount(ledgerAccount: $ledgerAccount) {
ownBalances {
nodes {
currency {
code
}
amount
}
}
}
}
```
### Aggregated
To read [aggregated balances](https://fragment.dev/markdown/guides/read-balances.md#aggregated) for multi-currency Ledger Accounts, pass in the `currency` argument, or query `childBalances` and `balances`:
```graphql title="Get aggregated balances read"
query GetBalances(
$ledgerAccount: LedgerAccountMatchInput!
) {
ledgerAccount(ledgerAccount: $ledgerAccount) {
# Query specific currency
childBalance(currency: { code: USD })
balance(currency: { code: USD })
# Query all currencies
childBalances {
nodes {
currency {
code
}
amount
}
}
balances {
nodes {
currency {
code
}
amount
}
}
}
}
```
If a Ledger Account has a descendant that is a multi-currency Ledger Account or if it has descendants of different currencies, it has `childBalances` and `balances`.
### Consistent
Balance reads are eventually consistent by default. To read [consistent balances](https://fragment.dev/markdown/guides/configure-consistency.md#ledger-configuration) for multi-currency Ledger Accounts, pass in the `consistencyMode` argument:
```graphql title="Get strongly-consistent balances read"
query GetBalances(
$ledgerAccount: LedgerAccountMatchInput!
) {
ledgerAccount(ledgerAccount: $ledgerAccount) {
# Query specific currency
balance(
consistencyMode: strong
currency: { code: USD }
)
# Query all currencies
balances(consistencyMode: strong) {
nodes {
currency {
code
}
amount
}
}
}
}
```
`consistencyMode` can be set to:
- `strong` to perform a strongly consistent balance read
- `eventual` to perform an eventually consistent balance read
- `use_account` to use the Schema value of the Ledger Account's `consistencyConfig.totalBalanceUpdates` setting
### Point in time
To query the balance of a Ledger Account at a particular point in time, use the `at` argument:
```graphql title="Historical balance queries"
query GetHistoricalBalances(
$ledgerAccount: LedgerAccountMatchInput!
) {
ledgerAccount(ledgerAccount: $ledgerAccount) {
# Query specific currency
end_of_year: balance(
at: "1969"
currency: { code: USD }
)
end_of_month: balance(
at: "1969-07"
currency: { code: USD }
)
# Query all currencies
end_of_year_all: balances(at: "1969") {
nodes {
currency {
code
}
amount
}
}
end_of_month_all: balances(at: "1969-07") {
nodes {
currency {
code
}
amount
}
}
}
}
```
### Net change
Multi-currency Ledger Accounts support reading the [net change](https://fragment.dev/markdown/guides/read-balances.md#net-change) over a period:
- `ownBalanceChanges`, how much `ownBalances` changed
- `childBalanceChanges`, how much `childBalances` changed
- `balanceChanges`, how much `balances` changed
```graphql title="Balance change queries"
query GetBalanceChanges(
$ledgerAccount: LedgerAccountMatchInput!
) {
ledgerAccount(ledgerAccount: $ledgerAccount) {
# Query specific currency
ownBalanceChange(
period: "1969"
currency: { code: USD }
)
# Query all currencies
ownBalanceChanges(period: "1969") {
nodes {
currency {
code
}
amount
}
}
}
}
```
Balance change queries require you to specify a [`period`](https://fragment.dev/markdown/api-reference/ledger-types.md#scalars-and-enums-period). This can be a year, quarter, month, day or hour.
### Over a period
Use `balancesDuring` and `balanceChangesDuring` to track balances and balance changes over time. For multi-currency accounts, you can query either a specific currency or all currencies:
```graphql title="Balance history queries"
query GetBalanceHistory(
$ledgerAccount: LedgerAccountMatchInput!
) {
ledgerAccount(ledgerAccount: $ledgerAccount) {
# Query specific currency
usdBalances: balancesDuring(
startTime: "2021"
duration: 12
granularity: monthly
currency: { code: USD }
) {
nodes {
at
amount {
amount
}
}
}
# Query all currencies
allBalances: balancesDuring(
startTime: "2021"
duration: 12
granularity: monthly
) {
nodes {
at
amount {
currency {
code
}
amount
}
}
}
# Query specific currency changes
usdChanges: balanceChangesDuring(
startTime: "2021"
duration: 100
granularity: daily
currency: { code: USD }
) {
nodes {
period
amount {
amount
}
}
}
# Query all currency changes
allChanges: balanceChangesDuring(
startTime: "2021"
duration: 100
granularity: daily
) {
nodes {
period
amount {
currency {
code
}
amount
}
}
}
}
}
```
## Custom currencies
You can define your own currencies to track any type of value, like rewards points, stocks or physical items.
To create a custom currency, call the `createCustomCurrency` mutation:
```graphql title="createCustomCurrency mutation"
mutation CreateCustomCurrency (
$customCurrency: CreateCustomCurrencyInput!,
) {
createCustomCurrency(
customCurrency: $customCurrency
) {
... on CreateCustomCurrencyResult {
customCurrency {
code
customCurrencyId
precision
name
customCode
}
}
... on Error {
code
message
}
}
}
```
```json title="createCustomCurrency variables"
{
"customCurrency": {
"customCurrencyId": "blue-gems",
"precision": 0,
"name": "Blue Gems",
"customCode": "BLUE"
}
}
```
To use a custom currency, set the `customCurrencyId` on the `currency` field of a Ledger Account and Ledger Line:
```json title="Custom currency Ledger Account"
{
"key": "...",
"chartOfAccounts": {
"defaultCurrencyMode": "multi",
"accounts": [
{
"key": "gems-issued",
"currencyMode": "single",
"currency": {
"code": "CUSTOM",
"customCurrencyId": "blue-gems"
}
},
{...}
]
},
"ledgerEntries": {
"types": [
{
"type": "issue-blue-gems",
"description": "Issue blue gems",
"lines": [
{
"key": "increase-pool",
"account": {
"path": "gems-issued"
},
"amount": "{{amount}}",
"currency": {
"code": "CUSTOM",
"customCurrencyId": "blue-gems"
}
},
{...other line}
]
}
]
}
}
```