# Handle currencies

Source: https://fragment.dev/guides/handle-currencies

[Documentation index](https://fragment.dev/llms.txt)

<a id="handle-currencies"></a>

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)

<a id="track-exposure"></a>

## 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.

<a id="ledger-accounts"></a>

## 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.

<a id="ledger-entries"></a>

## 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"
              }
            }
          }
        ]
      }
    ]
  }
}
```

<a id="read-balances"></a>

## 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

<a id="read-balances-latest"></a>

### 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
      }
    }
  }
}
```

<a id="read-balances-aggregated"></a>

### 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`.

<a id="read-balances-consistent"></a>

### 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

<a id="read-balances-point-in-time"></a>

### 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
      }
    }
  }
}
```

<a id="read-balances-net-change"></a>

### 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.

<a id="read-balances-over-a-period"></a>

### 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
        }
      }
    }
  }
}
```

<a id="custom-currencies"></a>

## 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}
        ]
      }
    ]
  }
}
```
