Transactions and exchange rates

When you create a transaction, the mandatory fields in the request assume that the transaction, settlement and portfolio currencies are all the same.

For example, to buy 10 units of BP @ £20, paying and settling in GBP in a GBP-denominated portfolio, you only need set the totalConsideration.currency field to GBP and the totalConsideration.amount field to £200.

LUSID automatically sets the transactionCurrency field to GBP, the exchangeRate field to 1, the TradeToPortfolioRate system property to 1, and the SettledToPortfolioRate calculated field to 1 (more on these in the next section).

{
  "transactionRequest-1": {
    "transactionId": "Txn-0000001",
    "type": "Buy",
    "instrumentIdentifiers": {"Instrument/default/Figi": "BBG000C6K6G9"},
    "transactionDate": "2024-06-15T00:00:00.0000000+00:00",
    "settlementDate": "2024-06-18T00:00:00.0000000+00:00",
    "units": 10,
    "transactionPrice": {"price": 20, "type": "Price"},
    "totalConsideration": {"amount": 200, "currency": "GBP"},
  }
}

Handling different transaction, settlement and portfolio currencies

Hard-coding an exchange rate between transaction and settlement currencies

If a transaction has different transaction and settlement currencies, you can specify the exchangeRate field to hard-code an exchange rate when you upsert that transaction. Note if you subsequently want to amend this rate you must re-upsert the transaction.

Consider the following example, to buy 100 units of MSFT @ $30, paying in USD but settling in EUR:

  • The transactionCurrency field is the transaction currency, USD.

  • The totalConsideration.currency field is the settlement currency, EUR.

  • The totalConsideration.amount field is the trade amount in the settlement currency, €2,700.

  • The exchangeRate field is the USD/EUR spot rate on the transaction date, 0.9. This enables LUSID to calculate the trade amount in the transaction currency: €2,700 / 0.9 = $3,000.

{
  "transactionRequest-2": {
    "transactionId": "Txn-0000002",
    "type": "Buy",
    "instrumentIdentifiers": {"Instrument/default/Figi": "BBG000BPH459"},
    "transactionDate": "2024-06-15T00:00:00.0000000+00:00",
    "settlementDate": "2024-06-18T00:00:00.0000000+00:00",
    "units": 100,
    "transactionPrice": {"price": 30, "type": "Price"},
    "transactionCurrency": "USD",
    "totalConsideration": {"amount": 2700, "currency": "EUR"},
    "exchangeRate": 0.9
  }
}

Hard-coding an exchange rate between transaction and portfolio currencies

If a transaction has different transaction and portfolio currencies, you can specify the TradeToPortfolioRate system property to hard-code an exchange rate when you upsert that transaction. This is optional but enables LUSID to maintain the cost basis of the portfolio. Note if you subsequently want to amend this rate you must re-upsert the transaction.

Consider the following example, to buy 100 units of MSFT @ $30, paying and settling in USD in a GBP-denominated portfolio:

  • The transactionCurrency field is the transaction currency, USD.

  • The totalConsideration.currency field is the settlement currency, also USD.

  • The totalConsideration.amount field is the trade amount in the settlement currency, $3,000.

  • The TradeToPortfolioRate system property is the USD/GBP spot rate on the transaction date, 0.8. This enables LUSID to calculate the trade amount in the portfolio currency: $3,000 * 0.8 = £2,400.

{
  "transactionRequest-3": {
    "transactionId": "Txn-0000003",
    "type": "Buy",
    "instrumentIdentifiers": {"Instrument/default/Figi": "BBG000BPH459"},
    "transactionDate": "2024-06-15T00:00:00.0000000+00:00",
    "settlementDate": "2024-06-18T00:00:00.0000000+00:00",
    "units": 100,
    "transactionPrice": {"price": 30, "type": "Price"},
    "transactionCurrency": "USD",
    "totalConsideration": {"amount": 3000, "currency": "USD"},
    "properties": {
      "Transaction/default/TradeToPortfolioRate": {
        "key": "Transaction/default/TradeToPortfolioRate",
        "value": {
          "metricValue": {"value": 0.8, "unit": ""}
        }
      }
    }
  }
}

Note: LUSID automatically sets the SettledToPortfolioRate calculated field to TradeToPortfolioRate / exchangeRate. In this example, this is 0.8 / 1 = 0.8, but will be different if the transaction and settlement currencies are different, and means you don't need a triangulating spot rate between the settlement and portfolio currencies. This field can be used in the rate field of a side.

Looking up exchange rates dynamically using a recipe

You can use a combination of a transaction type and a recipe to look up exchange rates dynamically:

  • From transaction currency to settlement currency, and/or

  • From transaction currency to portfolio currency.

This might be more convenient than hard-coding exchange rates when you upsert transactions, and means you can amend exchange rates if required without having to re-upsert them.

Consider the following example, to buy 100 units of MSFT @ $30, paying in USD but settling in EUR in a GBP-denominated portfolio on 01 June 2024, settling three days later on 04 June 2024:

  • The transactionCurrency field is the transaction currency, USD.

  • The totalConsideration.currency field is the settlement currency, EUR.

  • The totalConsideration.amount field is the trade amount in the settlement currency, €2,700.

  • The exchangeRate field is omitted, so LUSID requires an exchange rate from transaction to settlement currency.

  • The TradeToPortfolioRate system property is omitted, so LUSID requires an exchange rate from transaction to portfolio currency.

{
  "transactionRequest-4": {
    "transactionId": "Txn-0000004",
    "type": "Buy",
    "instrumentIdentifiers": {"Instrument/default/Figi": "BBG000BPH459"},
    "transactionDate": "2024-06-01T00:00:00.0000000+00:00",
    "settlementDate": "2024-06-04T00:00:00.0000000+00:00",
    "units": 100,
    "transactionPrice": {"price": 30, "type": "Price"},
    "transactionCurrency": "USD",
    "totalConsideration": {"amount": 2700, "currency": "EUR"},
  }
}

If you were to call the GetHoldings API at this point, the cost.amount (in USD) and costPortfolioCcy.amount (in GBP) fields would both be set to the total consideration of the transaction (in EUR):


To look up exchange rates dynamically for this transaction:

  1. Call the SetTransactionType API to extend the Buy transaction type to which this transaction belongs to include both the Txn:TradeToPortfolioRate and Txn:ExchangeRate calculation types:

    {
      ...
      "calculations": [
        {
          "type": "Txn:TradeToPortfolioRate",
        }
        {
          "type": "Txn:ExchangeRate",
        }
      ]
    }
  2. Call the UpsertConfigurationRecipe API to either create a new recipe or configure an existing one to look up exchange rates in the LUSID Quote Store. In this example, two market data rules are required: the first to locate USD/EUR rates from transaction to settlement currency, and the second to locate USD/GBP rates from transaction to portfolio currency:

    {
      "configurationRecipe": {
        "scope": "MyRecipes",
        "code": "MyFxRateLookupRecipe",
        "market": {
          "marketRules": [
            {
              "key": "Fx.USD.EUR",
              "supplier": "Lusid",
              "dataScope": "MyFxRates",
              "quoteType": "Rate",
              "field": "mid",
              "quoteInterval": "1D.0D"
            },
            {
              "key": "Fx.USD.GBP",
              "supplier": "Lusid",
              "dataScope": "MyFxRates",
              "quoteType": "Rate",
              "field": "mid",
              "quoteInterval": "1D.0D"
            }
          ]
        }
      }
    }
  3. Call the PatchPortfolioDetails API to register the scope and code of this recipe with the portfolio containing the transaction, for example:

    [
      {
        "value": {
          "scope": "MyRecipes",
          "code": "MyFxRateLookupRecipe"
        },
        "path": "/instrumentEventConfiguration/recipeId",
        "op": "add"
      }
    ]
  4. Call the UpsertQuotes API to load appropriate exchange rates for the transaction date (not settlement date) into the Quote Store, ensuring the scope (in the URL) matches the dataScope field in the recipe, for example:

    curl -X POST 'https://<your-domain>.lusid.com/api/api/quotes/MyFxRates'
      -H 'Authorization: Bearer <your-API-access-token>'
      -H 'Content-Type: application/json-patch+json'
      -d '{
      "Quote-0001": {
        "quoteId": {
          "quoteSeriesId": {
            "provider": "Lusid",
            "instrumentIdType": "CurrencyPair",
            "instrumentId": "USD/EUR",
            "quoteType": "Rate",
            "field": "mid"
          },
          "effectiveAt": "2024-06-01T00:00:00Z"
        },
        "metricValue": {
          "value": 0.93, "unit": "USD/EUR"
        }
      },
      "Quote-0002": {
         "quoteId": {
          "quoteSeriesId": {
            "provider": "Lusid",
            "instrumentIdType": "CurrencyPair",
            "instrumentId": "USD/GBP",
            "quoteType": "Rate",
            "field": "mid"
          },
          "effectiveAt": "2024-06-01T00:00:00Z"
        },
        "metricValue": {
          "value": 0.75, "unit": "USD/GBP"
        }
      }
    }'

Now if you call the GetHoldings API, LUSID looks up the exchange rates and re-calculates the cost.amount (€2,700 / 0.93 = $2903.23) and costPortfolioCcy.amount ($2903.23 * 0.75 = £2177.42) fields:

You can call the BuildTransactions API to audit the exchange rates used:

Booking FX transactions

Booking a FX spot transaction

Consider the following example, to sell GBP and buy USD at a spot rate of 1.2:

  • The instrumentIdentifiers field maps the transaction to the sell currency instrument, GBP. Note all major currency instruments are pre-mastered in LUSID.

  • The transactionCurrency field is the currency to sell, GBP.

  • The units field is the amount to sell, £100,000.

  • The totalConsideration.currency field is the currency to buy, USD.

  • The totalConsideration.amount field is the trade amount in the buy currency, $120,000.

  • The exchangeRate field is the GBP/USD spot rate, 1.2. This enables LUSID to calculate the trade amount in the sell currency: $120,000 / 1.2 = £100,000.

{
  "transactionRequest-3": {
    "transactionId": "Txn-0000003",
    "type": "FxSell",
    "instrumentIdentifiers": {"Instrument/default/Currency": "GBP"},
    "transactionDate": "2023-05-15T00:00:00.0000000+00:00",
    "settlementDate": "2023-05-18T00:00:00.0000000+00:00",
    "transactionCurrency": "GBP",
    "units": 100000,
    "transactionPrice": {"price": 1, "type": "Price"},
    "totalConsideration": {"amount": 120000, "currency": "USD"},
    "exchangeRate": 1.2
 }
}

Modelling a FxForward as a transaction

Note: The recommended way to model a foreign exchange or currency forward contract ("FxForward") is to master an instrument in the LUSID Security Master and then book a unitised transaction in a portfolio. This is a small amount of extra setup, but LUSID's full suite of analytical capability is available. See how to do this.

Alternatively, you can forgo mastering an instrument and book a forward-settled cash transaction directly in a portfolio with different buy and sell currencies, using the FwdFxBuy and FwdFxSell built-in transaction types provided with LUSID.

Consider the following example, of a transaction to sell GBP and buy USD in a EUR-denominated portfolio in 6 months' time at a strike rate of 1.2:

  • The transaction type field is set to FwdFxSell but this could equally be FwdFxBuy with the amounts and rates reversed.

  • The instrumentIdentifiers field maps the transaction to the sell currency instrument, GBP. Note all major currency instruments are pre-mastered in LUSID.

  • The transactionDate field marks the start of the contract.

  • The settlementDate field is the maturity date.

  • The transactionCurrency field is the currency to sell, GBP.

  • The units field is the amount to sell, £100,000.

  • The totalConsideration.currency field is the currency to buy, USD.

  • The totalConsideration.amount field is the trade amount in the buy currency: $120,000.

  • The exchangeRate is the GBP/USD strike rate: 1.2. This enables LUSID to calculate the trade amount in the sell currency: $120,000 / 1.2 = £100,000.

  • The TradeToPortfolioRate system property records the GBP/EUR spot rate, 1.1. This enables LUSID to calculate the trade amount in the portfolio currency, thereby maintaining the cost basis of the portfolio: £100,000 * 1.1 = €110,000.

curl -X POST 'https://<your-domain>.lusid.com/api/api/transactionportfolios/Funds/Managed/transactions/$batchUpsert?successMode=Partial' 
  -H 'Content-Type: application/json-patch+json'
  -H 'Authorization: Bearer <your-API-access-token>'
  -d '{
  "transactionRequest-4": {
    "transactionId": "Txn-0000004",
    "type": "FwdFxSell",
    "instrumentIdentifiers": {"Instrument/default/Currency": "GBP"},
    "transactionDate": "2023-06-07T00:00:00.0000000+00:00",
    "settlementDate": "2023-12-11T00:00:00.0000000+00:00",
    "transactionCurrency": "GBP",
    "units": 100000,
    "transactionPrice": {"price": 1, "type": "Price"},
    "totalConsideration": {"amount": 120000, "currency": "USD"},
    "exchangeRate": 1.2,
    "properties": {
      "Transaction/default/TradeToPortfolioRate": {
        "key": "Transaction/default/TradeToPortfolioRate",
        "value": {
          "metricValue": {
            "value": 1.1, "unit": ""
          }
        }
      }
    }
  }
}'

LUSID generates two temporary holdings for each FxForward booked as a transaction while the contract is live, one per currency. Note the holding type is Forward FX on the inception date, 7 June 2023, and that no units are settled:

When the contract matures, LUSID automatically updates the main cash holdings in the two currencies and removes the temporary holdings. Note the holding type is Cash Balance on the maturity date, 11 December 2023, and that all units are now settled: