How do I create or update a transaction?

Providing you have suitable access control permissions, you can create (that is, book) an input transaction in a transaction portfolio to register a change to the quantity and/or cost of one or more holdings.

A new transaction must resolve to:

  • An underlying instrument mastered in LUSID.

  • A known transaction type defining the precise economic impact; that is, the effect on your holding in the underlying instrument, and also potentially on holdings in other instruments in the portfolio too (for example, currency holdings).

By default, LUSID operates a ‘data load first’ policy, so transactions are upserted and resolution failures must be handled manually as a post-process step. You can increase the level of validation to reject transactions that do not resolve to instruments, to transaction types, or both. Find out more about this.

A transaction in a portfolio is keyed by its unique transaction ID. If you specify the ID of an existing transaction then the original is updated rather than a new one created. Note LUSID stores every update as a separate record so you can see a full change history for a transaction by calling the GetTransactions API with ShowCancelledTransactions=True, and examining the transactionStatus field:

  • A new (never updated) transaction has a single record with a status of Active.

  • An updated transaction has one record per update with a status of Amended, followed by an Active record with the latest information unless the transactionDate field changed (see below).

  • A transaction has a status of Cancelled if you:

    • Explicitly cancelled the transaction.

    • Updated the transactionDate field. This is because LUSID automatically rebooks the transaction on the new transaction date.

Using Luminesce

You can use the Lusid.Portfolio.Txn.Writer provider. More information.

Using the LUSID REST API

You can upsert (that is, create or update) up to 10,000 transactions per call to the BatchUpsertTransactions API. A transaction is created if its transactionId is not yet registered with the portfolio, and updated if it is. 

Note: This API is recommended over UpsertTransactions because it resolves transactions to instruments using individual transaction dates rather than the generic date of the upsert operation itself, reducing the likelihood of instrument resolution failures.

  1. Obtain an API access token.

  2. Call the BatchUpsertTransactions API, passing in your API token and specifying in the URL:

    • The scope and code of the parent portfolio.

    • A successMode of Atomic to reject all transactions in the request if one fails validation. The default mode of Partial means each transaction is validated on its own merits. Find out more about this.

  3. In the body of the API request, specify for each transaction in the batch:

    • An ephemeral ID that can be used to track warnings and failures in the response. This can be the same as the transactionID (below), or different. It is not stored in LUSID.

    • A transactionId that uniquely identifies the transaction in the portfolio. You can use the LUSID sequence APIs to auto-generate unique IDs.

    • A type that resolves to a known transaction type determining the precise economic impact, for example Buy or Sell.

    • A source in which to locate this transaction type if it is grouped with other types from a particular data provider. If omitted, LUSID searches the default source. Note if the parent portfolio has a transaction type scope set then this transaction type must also be domiciled in that scope. More about scopes and sources.

    • A set of instrumentIdentifiers that resolve to an instrument mastered in LUSID, each consisting of a 3-stage key (for example, Instrument/default/Figi or Instrument/default/LusidInstrumentId) and an appropriate value (for example, BBG000C6K6G9 or LUID_0000G7H5). You must specify at least one unique instrument identifier; you can specify as many more (unique and non-unique) as you like to increase the likelihood of successful instrument resolution. See how LUSID resolves transactions to instruments.

    • A transactionDate and a settlementDate. Note that if the settlement date is later, LUSID categorises any cash holdings impacted by the transaction differently between the two dates.

    • A number of units to transact. Depending on the underlying instrument, this might represent a number of shares, the principle in an interest rate swap, the face value of a bond, or a number of ETD contracts.

    • A transactionPrice. Note this field is only used if you ask LUSID to automatically calculate gross consideration for you.

    • A totalConsideration.currency that is the ISO 4217 code of the settlement currency, for example GBP.

    • A totalConsideration.amount that represents the trade amount in the settlement currency. Note this field is mandatory out-of-the-box, but you can configure LUSID to omit it and instead automatically calculate total consideration according to a formula.

      Note: The mandatory API fields assume that the transaction and settlement currencies are the same. If not, or if you are booking a FX transaction to exchange two currencies, then you must additionally set the transactionCurrency and exchangeRate fields. See how to do this

    • Optionally in the properties collection, any number of custom properties from the Transaction domain to extend the data model.

    • Optionally in the properties collection, any of the available system properties to record additional information that LUSID can subsequently use in business operations. For example, if the transaction currency is GBP but the portfolio base currency is EUR, adding the TradeToPortfolioRate system property to record the GBP/EUR spot rate enables LUSID to maintain the cost basis of the portfolio.

    • Optionally in the properties collection, one or more sub-holding keys (SHKs) registered with the portfolio to strategy-tag the transaction. If omitted, LUSID generates one holding per underlying instrument, with all transactions in that instrument contributing to the holding.

Consider the following example of a transaction to purchase 10 units of BP @ £20, paying and settling in GBP in a EUR-denominated portfolio:

  • The transaction and settlement currencies are the same, so totalConsideration.amount is £200. Note you need to set additional fields if the transaction and settlement currencies are different, or if you are booking a FX transaction to exchange two currencies.

  • The portfolio base currency (specified on the parent portfolio) is EUR, so the TradeToPortfolioRate system property records a GBP/EUR spot rate of 1.125, so LUSID can calculate the trade amount in the portfolio currency (£200 * 1.125 = €225) and thereby maintain the cost basis of the portfolio.

curl -X POST 'https://<your-domain>.lusid.com/api/api/transactionportfolios/Finbourne-Examples/Global-Equity/transactions/$batchUpsert?successMode=Partial' 
  -H 'Content-Type: application/json-patch+json'
  -H 'Authorization: Bearer <your-API-access-token>'
  -d '{
  "transactionRequest-1": {
    "transactionId": "Txn-0000001",
    "type": "Buy",
    "source": "Bloomberg",
    "instrumentIdentifiers": {"Instrument/default/Figi": "BBG000C6K6G9"},
    "transactionDate": "2023-05-15T00:00:00.0000000+00:00",
    "settlementDate": "2023-05-18T00:00:00.0000000+00:00",
    "units": 10,
    "transactionPrice": {"price": 20, "type": "Price"},
    "totalConsideration": {"amount": 200, "currency": "GBP"},
    "properties": {
      "Transaction/MyProperties/Broker": {
        "key": "Transaction/MyProperties/Broker",
        "value": {
          "labelValue": "JohnDoe"
        }
      },
      "Transaction/default/TradeToPortfolioRate": {
        "key": "Transaction/default/TradeToPortfolioRate",
        "value": {
          "metricValue": {
            "value": 1.125, "unit": ""
          }
        }
      },
      "Transaction/SHKs/Strategy": {
        "key": "Transaction/SHKs/Strategy",
        "value": {
          "labelValue": "Growth"
        }
      }
    }
  }
}'

Note the following in the response:

  • LUSID confirms the globally-unique instrumentUid (LUID) and the instrumentScope of the underlying instrument.

  • LUSID sets the transactionStatus to Active. If you subsequently edit the transaction, this becomes AmendedSee how to cancel a transaction.

  • LUSID sets the transactionCurrency to the same as the settlement currency, at an exchangeRate of 1.

  • We recommend checking the metadata section for transactions in a batch that were upserted but failed to resolve to instruments, to transaction types, or both. Find out more about this.

  • If you increased the level of validation, we recommend checking the failed section for transactions in a batch that were rejected for failing to resolve to instruments, to transaction types, or both.

{
  "values": {
    "transactionRequest-1": {
      "transactionId": "Txn-0000001",
      "type": "Buy",
      "instrumentIdentifiers": {
        "Instrument/default/Figi": "BBG000C6K6G9",
        "Instrument/default/Isin": "GB00BH4HKS39"
      },
      "instrumentScope": "default",
      "instrumentUid": "LUID_R7E12YK8",
      "transactionDate": "2023-05-15T00:00:00.0000000+00:00",
      "settlementDate": "2023-05-18T00:00:00.0000000+00:00",
      "units": 10,
      "transactionPrice": {
        "price": 20,
        "type": "Price"
      },
      "totalConsideration": {
        "amount": 200,
        "currency": "GBP"
      },
      "exchangeRate": 1,
      "transactionCurrency": "GBP",
      "properties": {
        "Transaction/MyProperties/BrokerName": {
          "key": "Transaction/MyProperties/BrokerName",
          "value": {
            "labelValue": "JohnDoe"
          }
        },
        "Transaction/default/TradeToPortfolioRate": {
          "key": "Transaction/default/TradeToPortfolioRate",
          "value": {
            "metricValue": {
              "value": 1.125,
              "unit": ""
            }
          }
        },
        "Transaction/SHKs/Strategy": {
          "key": "Transaction/SHKs/Strategy",
          "value": {
            "labelValue": "Growth"
          }
        }
      },
      "source": "Bloomberg",
      "entryDateTime": "0001-01-01T00:00:00.0000000+00:00",
      "transactionStatus": "Active"
    }
  },
  "failed": {},
  "metadata": {},
  ...
}

Using the LUSID web app

  1. Sign in to the LUSID web app.

  2. Navigate to Dashboard > Transactions.

  3. Click the Create transaction button and follow the instructions: