LUSID is a transaction-based system. It does not maintain a static record of your holdings (positions) in a transaction portfolio but rather generates them on demand from the stored history of transactions, replayed in date order.
Every transaction in a portfolio 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).
LUSID has a number of built-in transaction types (such as FundsIn
, Buy
and Sell
) that enable you to get started modeling basic economic activity. These are useful for understanding how transaction types work, but we recommend creating your own universe of custom transaction types for a production system.
Understanding the role of transaction types
Consider the following CSV file containing three input transactions ready for upsert to a portfolio, each with a particular txn_type:
- The
FundsIn
transaction deposits £500 in the portfolio. We would expect our cash holding in GBP to increase by £500, but no other holdings to be affected. - The
Buy
transaction purchases 10 BP shares at £10 a share. We would expect our equity holding in BP to increase by 10 units and our cash holding in GBP to decrease by £100. - The
Sell
transaction sells 5 BP shares at £11 a share. We would expect our equity holding in BP to decrease by 5 units and our cash holding in GBP to increase by £55.
When all trades have settled, we would expect LUSID to generate a holdings report for this portfolio with 5 BP shares and £455.
Examining the built-in LUSID transaction types
You can retrieve the definition of the built-in Buy
transaction type from the default
source and default
scope by calling the GetTransactionType API:
{ "aliases": [ { "type": "Buy", "description": "Purchase", "transactionClass": "Basic", "transactionRoles": "LongLonger", "isDefault": false }, ], "movements": [ { "movementTypes": "StockMovement", "side": "Side1", "direction": 1, "properties": {}, "mappings": [], "movementOptions": [] }, { "movementTypes": "CashCommitment", "side": "Side2", "direction": -1, "properties": {}, "mappings": [], "movementOptions": [] } ], "properties": {}, ... }
The built-in Buy
transaction type:
- Is grouped in the
default
source (data provider) anddefault
scope (logical repository) with all the other built-in transaction types provided with LUSID. More about scopes and sources. - Has a single alias, which defines the name of the transaction type and other characteristics that allow you to group similar types together for reporting purposes.
- Has two movements, each of which has:
- A movement type that controls how and when a holding is updated. Allowed values are these built-in movement types. You cannot customise this set.
- A direction that controls whether the change is an increase or decrease. Allowed values are
1
and-1
respectively. - A side that determines which economic attributes of the transaction impact the holding. Allowed values are either the built-in sides or you can create your own custom side.
The following table flattens and explains the definition of the built-in Buy
transaction type:
Component | Field | Value | Explanation | |
Alias | type | Buy | Defines the name of the transaction type. Note names are case-sensitive, so Buy is different to BUY . | |
description | Purchase | Describes the transaction type. | ||
transactionClass | Basic | Groups the transaction type with others that have the same economic impact for reporting purposes, in conjunction with the role (below). More information. | ||
transactionRoles | LongLonger | Groups the transaction type with others that have the same economic impact for reporting purposes, in conjunction with the class (above). | ||
isDefault | false | Specifies that this transaction type is not the default in the source, which means that unresolved transactions are not mapped to it. More information. | ||
Movement #1 | movementTypes | StockMovement | On the trade date, updates the number of units in an instrument (non-cash) holding. On the settlement date, updates the number of settled units. | |
direction | 1 | Increases the number of units in the holding. | ||
side | Side1 | Defines the name of the side. Note this built-in side is designed to generate instrument (non-cash) holdings, as per its definition below. | ||
| security | Txn:LusidInstrumentId | Updates the holding identified by the LUID of the instrument to which the transaction resolved upon upsert, for example LUID_ABCDEFGH . | |
currency | Txn:TradeCurrency | Determines the currency of the holding from the value of the transactionCurrency field on the transaction if populated, otherwise its totalConsideration.currency field (the settlement currency). | ||
rate | Txn:TradeToPortfolioRate | If the transaction is in a foreign currency, maintains the cost basis of the portfolio by updating the costPortfolioCcy.amount field on the holding using the exchange rate specified by the TradeToPortfolioRate system property attached to the transaction. | ||
units | Txn:Units | Updates the number of units on the holding by the value of the units field on the transaction. | ||
amount | Txn:TradeAmount | Updates the cost of the holding by the value of the totalConsideration.amount field on the transaction. | ||
Movement #2 | movementTypes | CashCommitment | On the trade date, creates a temporary separate cash holding (of holding type C ) to reflect a commitment to settling the trade. On the settlement date, updates the main cash holding (holding type B ) and removes the temporary holding. | |
direction | -1 | Decreases the number of units in the holding. | ||
side | Side2 | Defines the name of the side. Note this built-in side is designed to generate cash holdings, as per its definition below. | ||
security | Txn:SettleCcy | Updates the holding identified by the LUID of the currency specified in the totalConsideration.currency field on the transaction, prefixed by CCY_ (for example CCY_GBP ). | ||
currency | Txn:SettlementCurrency | Determines the currency of the holding from the value of the totalConsideration.currency field on the transaction. | ||
rate | SettledToPortfolioRate | If the transaction settles in a different currency to the transaction currency, and is again different to the portfolio base currency, maintains the cost basis of the portfolio by dividing the exchange rate specified by the Note the:
| ||
units | Txn:TotalConsideration | Updates the number of units on the holding by the value of the totalConsideration.amount field on the transaction (since for a currency the number of units is equal to the amount). | ||
amount | Txn:TotalConsideration | Updates the cost of the holding by the value of the totalConsideration.amount field on the transaction. |
In summary, we can see that the built-in Buy
transaction type:
- Impacts one instrument (non-cash) holding and one cash holding.
- Increases the instrument holding.
- Decreases the cash holding.
The built-in Sell
transaction type is virtually identical to Buy
. The only difference is that the movement directions are reversed:
- The
StockMovement
has a direction of-1
to decrease the instrument holding by the number of units. - The
CashCommitment
has a direction of1
to increase the cash holding by the total consideration.
By contrast, the built-in FundsIn
transaction type has a single movement, with a type of CashAccrual
, a direction of 1
, and uses the built-in Side1
. This transaction type:
- Impacts a single cash holding.
- On the trade date, creates a temporary separate cash holding (of holding type
A
) to reflect expected accrual of income. - On the settlement date, increases the main cash holding (holding type
B
) by the number of units and removes the temporary holding.
Interpreting the impact of transaction types on holdings
If we load our three transactions into a portfolio and call the GetHoldings API for end-of-day on the trade date (6 June 2022), we can see from the picture below that LUSID generates:
- One holding (with a Holding Type of
Position
) for the BP equity instrument. Note the number of Settled Units is 0. - Three holdings for the GBP cash instrument. The first (with a Holding Type of
Cash Accrual
) records the (unsettled)FundsIn
transaction for £500. The last two (both with a Holding Type ofCash Commitment
) record the commitment to pay (from theBuy
transaction) or receive (from theSell
transaction) GBP on the settlement date:
If we call the GetHoldings API again on or after the settlement date (8 June 2022), we can see from the picture below that LUSID normalises the GBP instrument to a single cash holding (with a Holding Type of Cash Balance
), and that all units are now Settled Units:
There are two main reasons to configure transaction types:
- You want to configure aliases in order to group similar or related transaction types together.
- You want to configure movements in order to change the economic impact of transactions on holdings.
For more information, start with the tutorials for the following use cases:
Use case 1 | “As a data controller, I load transactions from two providers that use a different transaction code to signal the same economic activity. I want to ensure that LUSID applies a uniform economic impact when generating holdings.” |
Use case 2 | “As a data controller, I load transactions from two providers that use the same transaction code to signal different economic activity. I want to ensure, for each transaction, that LUSID applies the correct economic impact when generating holdings.” |
Use case 3 | “As a portfolio manager, I want to generate a holdings report that reduces my cash balance by the money paid out in broker commissions, as well as by the cost of equities purchased.” |
Use case 4 | “As a fund accountant, I want to create a bespoke holdings report that breaks out the money paid in broker commissions on equity purchases into a separate cash holding.” |