Views:

Related resources:

Explanation

Reference

How-to guides

In this tutorial we'll learn how LUSID generates holdings on demand from a history of all the economic activity in a portfolio, and how you can configure this process using transaction types.

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

You can examine and run the complete code sample from the Jupyter Notebook linked to each use case, providing you have suitable access control permissions. This can most easily be achieved by assigning your LUSID user the built-in lusid-administrator role, which should already be the case if you are the LUSID domain owner. If you are told you do not have a license to perform a particular operation, please contact support.
 

Understanding how LUSID generates holdings

LUSID does not maintain a static record of your instrument holdings (positions) in a portfolio but rather generates them on demand from a history of all the transactions affecting those instruments in that portfolio. LUSID also automatically takes into account other economic activity, such as corporate actions and manual holding adjustments.

Understanding the role of transaction types

Every transaction upserted to a portfolio should:

  • Resolve to an underlying instrument mastered in LUSID.
  • Have a transaction type that defines its economic impact; that is, the effect on your holding in the underlying instrument, and also potentially on holdings in other instruments in the portfolio.

Consider the following CSV file containing three transactions ready for upsert to a portfolio, each with a particular txn_type:

  1. 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.
  2. 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.
  3. 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 with 5 BP shares and £455.

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. For a complete list, call the ListConfigurationTransactionTypes API.

You can create as many custom transaction types as you need to model more complex behavior.

Examining the built-in transaction types

You can retrieve the definition of the built-in Buy transaction type by calling the GetTransactionType API:

{
  "aliases": [
    {
      "type": "Buy",
      "description": "Purchase",
      "source": "default",
      "transactionClass": "Basic",
      "transactionRoles": "LongLonger",
      "isDefault": false
    },
  ],
  "movements": [
    {
      "movementTypes": "StockMovement",
      "side": "Side1",
      "direction": 1,
      "properties": {},
      "mappings": []
    },
    {
      "movementTypes": "CashCommitment",
      "side": "Side2",
      "direction": -1,
      "properties": {},
      "mappings": []
    }
  ],
  "properties": {},
  ...
}

We can see that Buy consists of the following components:

  • A single alias, which defines the name of the transaction type and other characteristics that allow you to group similar types together, so that for example Buy, By and Acheter types can all have the same economic impact.
  • 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 (currently Side1, Side2, BondInt and Side2WithoutBondInterest), or you can create your own custom sides.

      Note a side has five mandatory fields (security, currency, rate, units, amount); the allowed values for these fields are documented here. You can retrieve the definition of the built-in sides by calling the ListConfigurationTransactionTypes API, which returns all the transaction types and the sides at the bottom; there should soon be a dedicated API for retrieving sides.

The following table flattens and explains the definition of the built-in Buy transaction type:

Transaction type componentValueExplanation
AliasTypeBuyDefines the name of the transaction type. Note names are case-sensitive, so Buy is different to BUY.
DescriptionPurchaseDescribes the transaction type.
SourcedefaultGroups the transaction type with other types from the same data provider (in this case, the built-in types from LUSID). Note that a transaction expects its transaction type to be in the default source unless you explicitly set the source field on the transaction. Note also this field used to be called transactionGroup.
ClassBasicGroups the transaction type with others that have the same economic impact for reporting purposes, in conjunction with the role (below). More information.
RoleLongLongerGroups the transaction type with others that have the same economic impact for reporting purposes, in conjunction with the class (above).
Movement #1TypeStockMovementOn the trade date, updates the number of units in an instrument (non-cash) holding. On the settlement date, updates the number of settled units.
Direction1Increases the number of units in the holding.
SideSide1Defines the name of the side. Note this built-in side is designed to generate instrument (non-cash) holdings, as per its definition below.

 

 

 

 

 

SecurityTxn:LusidInstrumentIdUpdates the holding identified by the LUID of the instrument to which the transaction resolved upon upsert, for example LUID_ABCDEFGH.
CurrencyTxn:TradeCurrencyDetermines the currency of the holding from the value of the transactionCurrency field on the transaction if populated, otherwise its totalConsideration.currency field.
RateTxn:TradeToPortfolioRateIf the transaction is in a foreign currency, maintains the cost basis of the portfolio by calculating the value of the costPortfolioCcy.amount field on the holding using the exchange rate specified by the TradeToPortfolioRate system property attached to the transaction.
UnitsTxn:UnitsUpdates the number of units on the holding by the value of the units field on the transaction.
AmountTxn:TradeAmountUpdates the cost of the holding by the value of the totalConsideration.amount field on the transaction.
Movement #2TypeCashCommitmentOn 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-1Decreases the number of units in the holding.
SideSide2Defines the name of the side. Note this built-in side is designed to generate cash holdings, as per its definition below.
 SecurityTxn:SettleCcyUpdates 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).
CurrencyTxn:SettlementCurrencyDetermines the currency of the holding from the value of the totalConsideration.currency field on the transaction.
RateSettledToPortfolioRate

If the transaction settles in a different currency to the trade 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 TradeToPortfolioRate system property attached to the transaction by the value of the exchangeRate field.
 

Note the:

  • Trade currency is defined by the transactionCurrency field on a transaction.
  • Settlement currency is defined by its totalConsideration.currency field.
  • Portfolio base currency is nominated when a portfolio is created.
UnitsTxn:TotalConsiderationUpdates 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).
AmountTxn:TotalConsiderationUpdates 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 of 1 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 of Cash Commitment) record the commitment to pay (from the Buy transaction) or receive (from the Sell 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:

As stated earlier, the built-in transaction types model basic economic behavior. You can use them as-is, or change their default behavior. Alternatively, you can discard the built-in types and create your own universe of custom transaction types and custom sides.

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.
     

Use case 1: Ensuring different transaction codes have the same economic impact

Imagine we source transactions from two providers: Societe Generale and Deutsche Bank.

SG labels an equity purchase with a code of Acheter; DB labels an equity purchase with a code of Kaufen.

We can modify the built-in Buy transaction type to model this behavior by appending an alias for each of Acheter and Kaufen.

When transactions are upserted into a portfolio with any of the Buy, Acheter or Kaufen transaction types, the economic impact is the same: LUSID increases an equity holding by the number of units, and decreases a cash holding by the total consideration. However, we haven't altered the format or lineage of the imported data in any way, so it remains meaningful to the originating system.

Examining the source file

Let's assume we have amalgamated a stream of transactions from SG and DB into a single CSV file, each with a particular txn_code:

We can see that:

  • The first transaction has a transaction code of FundsIn, which we can map directly to the built-in FundsIn transaction type to deposit £500 in the portfolio.
  • The second transaction is from SG and has a transaction code of Acheter to signal a purchase of 50 BP shares at a cost of £250.
  • The third transaction is from DB and has a transaction code of Kaufen to signal a purchase of 25 BP shares at a cost of £125.

When all trades have settled, we would expect LUSID to generate a holdings report with 75 BP shares and £125.

Modelling the transaction type

We want to change the aliases component of the built-in Buy transaction type to have the following definition (the movements of Buy are unchanged, and so omitted below):

Transaction type componentAlias #1Alias #2Alias #3
AliasTypeBuyAcheterKaufen
DescriptionPurchasePurchase from Societe GeneralePurchase from Deutsche Bank
Sourcedefaultdefaultdefault
ClassBasicBasicBasic
RoleLongLongerLongLongerLongLonger

Modifying the built-in Buy transaction type

We can call the SetTransactionType API to change the built-in Buy transaction type.

Note: The PUT SetTransactionType API operates differently to the standard PUSH Upsert* model used elsewhere in LUSID. The transaction type is replaced rather than updated, so it's important to specify the entire definition each time.

We should first retrieve the existing definition of the Buy transaction type to make sure we recreate it correctly:

  1. Call the GetTransactionType API to retrieve the definition of Buy (which is in the default source).
  2. Append the aliases for Kaufen and Acheter to the alias for Buy, retaining all the other settings.
  3. Call the SetTransactionType API to replace the existing definition in LUSID.

Step 4 of the accompanying Jupyter Notebook demonstrates how to do this using the LUSID Python SDK. You can execute the Notebook in Jupyterhub for your LUSID domain and then sign in to your web app to examine the new definition for Buy (with Acheter and Kaufen aliases) on the System Settings > Transaction Types dashboard:

Loading transactions into LUSID

We can now call the UpsertTransactions API to load our transaction source file into a portfolio.

We map the txn_code column in the source file to the type field on each transaction, safe in the knowledge that LUSID will apply the same economic impact to Acheter and Kaufen as to Buy.

Step 5 of the accompanying Jupyter Notebook demonstrates how to do this using the LUSID Python SDK. You can execute the Notebook in Jupyterhub for your LUSID domain and then sign in to the web app to examine the Type of each transaction on the Transactions dashboard:

Generating a holdings report

We can now call the GetHoldings API for any date after the settlement date to generate a holdings report.

Step 6 of the accompanying Jupyter Notebook demonstrates how to do this using the LUSID Python SDK. You can execute the Notebook in Jupyterhub for your LUSID domain and then sign in to the web app to examine the Holdings dashboard.

As expected, after all trades have settled, LUSID generates a holdings report with 75 BP shares and £125:


 

Use case 2: Ensuring the same transaction code has different economic impacts

Imagine we source transactions from two data providers: UBS and Goldman Sachs.

Both label equity transactions with a code of 10. For UBS this signals an equity purchase; for Goldman Sachs an equity sale.

We can create two separate 10 custom transaction types to model this behavior, each in a different source.

When transactions are upserted into a portfolio, we set the source field on a transaction to reference the appropriate transaction type, enabling LUSID to apply the correct economic impact. However, we haven't altered the format or lineage of the imported data in any way, so it remains meaningful to the originating system.

Examining the source file

Let's assume we have amalgamated a stream of transactions from UBS and Goldman Sachs into a single CSV file, each with a particular txn_code:

We can see that:

  • The first transaction has a transaction code of FundsIn, which we can map directly to the built-in FundsIn transaction type to deposit £500 in the portfolio.
  • The second transaction is from UBS and has a transaction code of 10 to signal a purchase of 50 BP shares at a cost of £250.
  • The third transaction is from Goldman Sachs and has a transaction code of 10 to signal a sale of 25 BP shares for £125.

When all trades have settled, we would expect LUSID to generate a holdings report with 25 BP shares and £375.

Modelling the custom transaction types

We want our custom transaction types to have the following definitions (some components omitted for clarity):

Transaction type componentUBS (buy)Goldman Sachs (sell)Explanation
AliasType1010Transaction types have the same name.
DescriptionEquity purchase from UBSEquity sale from Goldman SachsTransaction types have different descriptions.
SourceUBSGoldmanSachsTransaction types are in different ‘scopes’.
ClassUBSGoldmanSachsTransaction types are not in the default Basic class, to prevent reporting clashes with the built-in transaction types.
Movement #1TypeStockMovementStockMovementUpdates the equity holding.
Direction1-1Increases/decreases units respectively.
SideSide1Side1Uses the built-in Side1 designed for instrument (non-cash) holdings.
Movement #2TypeCashCommitmentCashCommitmentUpdates the cash holding.
Direction-11Decreases/increases units respectively.
SideSide2Side2Uses the built-in Side2 designed for cash holdings.

Creating a custom transaction type for UBS to purchase an equity

We can call the SetTransactionType API with a source of UBS, a type of 10, and the ‘buy’ alias and movements defined in the table above.

Note: The PUT SetTransactionType API operates differently to the standard PUSH Upsert* model used elsewhere in LUSID. The transaction type is replaced rather than updated, so it's important to specify the entire definition each time.

Step 4 of the accompanying Jupyter Notebook demonstrates how to do this using the LUSID Python SDK. You can execute the Notebook in Jupyterhub for your LUSID domain and then sign in to the web app to see the custom transaction type on the System Settings > Transaction Types dashboard.

Note StockMovement is set to 1 to increase the equity holding and CashCommitment to -1 to decrease the cash holding, as per the built-in Buy transaction type:

Creating a custom transaction type for Goldman Sachs to sell an equity

We can call the SetTransactionType API again, this time with a source of GoldmanSachs, a type of 10, and the ‘sell’ alias and movements defined in the table above. 

Step 5 of the accompanying Jupyter Notebook demonstrates how to do this using the LUSID Python SDK. You can execute the Notebook in Jupyterhub for your LUSID domain and then sign in to the web app to see both types on the System Settings > Transaction Types dashboard.

Note StockMovement is set to -1 to decrease the equity holding and CashCommitment is 1 to increase the cash holding, as per the built-in Sell transaction type:

Loading transactions into a LUSID portfolio

We can now call the UpsertTransactions API to load our transaction source file into a suitable portfolio.

Both equity transactions have a type of 10. It's important therefore to set the source field on each transaction to reference the appropriate transaction type, so LUSID can apply the correct economic impact.

Step 6 of the accompanying Jupyter Notebook demonstrates how to do this using the LUSID Python SDK. You can execute the Notebook in Jupyterhub for your LUSID domain and then sign in to the web app to examine the Transactions dashboard.

Note that while the Type of upserted equity transactions is the same, the Description is different:

Generating a holdings report

We can now call the GetHoldings API to generate a holdings report.

Step 7 of the accompanying Jupyter Notebook demonstrates how to do this using the LUSID Python SDK. You can execute the Notebook in Jupyterhub for your LUSID domain and then sign in to the web app to examine the Holdings dashboard.

As expected, after all trades have settled, LUSID generates a holdings report with 25 BP shares and £375:


Use case 3: Reducing the cash balance by the cost of broker commission

Imagine we have a data provider that includes broker commission as a data point in a stream of equity purchase transactions.

We cannot model broker commission out-of-the-box in LUSID. There is no field on the transaction in which to store this data, and the built-in Buy transaction type does not define the economic impact.

We can add a custom property to transactions to store commission amounts, and create a custom transaction type to reduce our cash balance by the total cost of the commission, as well as by the total cost of the equities purchased.

Examining the source file

Let's assume we have transactions in a CSV file ready for upsert to LUSID:

We can see that:

  • The first transaction deposits £500 in the portfolio.
  • The second transaction purchases 25 BP shares at a cost of £125; the broker's commission is £3.75.
  • The third transaction purchases 10 BP shares at a cost of £50; the broker's commission is £1.50.

By default, LUSID cannot store or process the commission data. When all trades have settled, LUSID generates a holdings report with 35 BP shares and £325.

We can create a custom transaction type to reduce our GBP holding by the total cost of the commission as well as the total cost of the shares (£325 - £3.75 - £1.50 = £319.75). To do this, we need to:

  1. Store the commission data with each transaction. We can do this by creating a custom Commission property that we assign to each transaction with the appropriate value on upsert.
  2. Create a custom BuyWithCommission transaction type to model the following behavior for each equity transaction:
    • Increase the number of BP shares held by the units purchased (in the same way as the built-in Buy transaction type).
    • Decrease the GBP cash holding by the total consideration (in the same way as the built-in Buy transaction type).
    • Additionally decrease the GBP cash holding by the amount of the Commission property.

Modelling the custom transaction type

We can re-use the first two movements from the built-in Buy transaction type, and create a third movement to process the commission (some components omitted for clarity):

Transaction type componentValueExplanation
AliasTypeBuyWithCommissionDefines the name of the transaction type.
Movement #1TypeStockMovementUpdates an instrument (non-cash) holding.
Direction1Increases the number of units.
SideSide1Uses the built-in Side1 designed for instrument (non-cash) holdings.
Movement #2TypeCashCommitmentUpdates a cash holding.
Direction-1Decreases the number of units.
SideSide2Uses the built-in Side2 designed for cash holdings.
Movement #3TypeCashCommitmentUpdates a cash holding.
Direction-1Decreases the number of units.
SideReduceCashBalanceByCommissionDefines the name of the custom side.
 SecurityTxn:SettleCcyUpdates the same cash holding as Side2.
 CurrencyTxn:SettlementCurrencyUpdates the same cash holding as Side2.
 RateSettledToPortfolioRateUses the same exchange rate calculation as Side2.
 UnitsTransaction/FBNUniversity/CommissionDecreases the number of units on the holding by the number of units stored by the Commission property on a transaction (for a currency, the number of units is equal to the amount).
 AmountTransaction/FBNUniversity/CommissionDecreases the amount of the holding by the amount of the Commission property on a transaction.

In summary, for each transaction upserted to LUSID with the custom BuyWithCommission transaction type:

  • Movement #1 increases the number of units in the equity holding by the number purchased.
  • Movement #2 decreases the main GBP cash holding by the total consideration.
  • Movement #3 additionally decreases the main GBP cash holding by the broker's commission.

Creating a property definition for the Commission property

The first task is to call the CreatePropertyDefinition API to create a property definition for the Commission property, in order to establish the unique 3-stage property key that we'll need both to create the custom transaction type and upsert transactions with properties:

  • The first stage of the 3-stage property key must be in the Transaction domain, since the property belongs to transactions. The scope and code stages can be any intuitive strings, so for example Transaction/FBNUniversity/Commission.
  • The data type of the property should be a number, since we're storing cash amounts.

Step 4 of the accompanying Jupyter Notebook demonstrates how to do this using the LUSID Python SDK. You can execute the Notebook in Jupyterhub for your LUSID domain and then sign in to the web app to see the property definition on the Data Management > Properties dashboard:

Creating the custom BuyWithCommission transaction type

Creating the custom ReduceCashBalanceByCommission side

The first task is to call the SetSideDefinition API to create the custom ReduceCashBalanceByCommission side as per the definition in the table above, with the units and amount fields mapped to the Transaction/FBNUniversity/Commission property.

Note: The PUT SetSideDefinition API operates differently to the standard PUSH Upsert* model used elsewhere in LUSID. A side is replaced rather than updated, so it's important to specify the entire definition each time.

Step 5.1 of the accompanying Jupyter Notebook demonstrates how to do this using the LUSID Python SDK. You can execute the Notebook in Jupyterhub for your LUSID domain and then sign in to the web app to see the custom side on the System Settings > Transaction Types dashboard (navigate to the Sides tab):

Including the custom side in the transaction type definition

We can now call the SetTransactionType API with the alias and movements in the table above, and the custom side as the third movement.

Note: The PUT SetTransactionType API operates differently to the standard PUSH Upsert* model used elsewhere in LUSID. A transaction type is replaced rather than updated, so it's important to specify the entire definition each time.

Step 5.2 of the accompanying Jupyter Notebook demonstrates how to do this using the LUSID Python SDK. You can execute the Notebook in Jupyterhub for your LUSID domain and then sign in to the web app to see the custom transaction type on the System Settings > Transaction Types dashboard:

Loading transactions into a portfolio and storing the commission

We can now call the UpsertTransactions API to load our transactions into a suitable portfolio, assigning the Transaction/FBNUniversity/Commission property to each with the appropriate value from the commission column in the source file.

Step 6 of the accompanying Jupyter Notebook demonstrates how to do this using the LUSID Python SDK. You can execute the Notebook in Jupyterhub for your LUSID domain and then sign in to the web app to examine the Transactions dashboard.

To show the Broker commission property column in the LUSID web app, click the cog icon (highlighted in green), choose Add column, and then select the property to display from the Property tab. Note also the transaction Type of each equity purchase:

Generating a holdings report

We can now call the GetHoldings API to generate a holdings report.

Step 7 of the accompanying Jupyter Notebook demonstrates how to do this using the LUSID Python SDK. You can execute the Notebook in Jupyterhub for your LUSID domain and then sign in to the web app to examine the Holdings dashboard.

As expected, after all trades have settled, LUSID generates a holdings report with 35 BP shares and £319.75:

 

Use case 4: Breaking our broker commission as a separate cash holding

Imagine we have a data provider that includes broker commission as a data point in a stream of equity purchase transactions.

We cannot model broker commission out-of-the-box in LUSID. There is no field on the transaction in which to store this data, and the built-in Buy transaction type does not define the economic impact.

We can create a portfolio with a sub-holding key (SHK), which enables us to configure the default way in which LUSID generates and groups holdings.

We can then add a custom property to transactions to store commission amounts, and create a custom transaction type that maps commission to the SHK, in order to break it out as a separate cash holding line item in a holdings report.

Examining the source file

Let's assume we have transactions in a CSV file ready for upsert to LUSID:

We can see that:

  • The first transaction deposits £500 in the portfolio.
  • The second transaction purchases 25 BP shares at a cost of £125; the broker's commission is £3.75.
  • The third transaction purchases 10 BP shares at a cost of £50; the broker's commission is £1.50.

By default, LUSID cannot store or process the commission data. When all trades have settled, LUSID generates a holdings report with 35 BP shares and £325.

We can create a SHK to break out a separate holding, and a custom transaction type to map the commission to the SHK, such that the holdings report contains three line items: 35 BP shares, £325 in the main GBP cash holding, and -£3.75 + -£1.50 = -£5.25 in a separate GBP cash holding. To do this, we need to:

  1. Create a TrackCommission SHK (which is a standard property) and register it with a portfolio.
  2. Store the commission data with each transaction. We can do this by creating a custom Commission property that we assign to each transaction with the appropriate value on upsert.
  3. Create a custom BuyWithSeparateCommission transaction type to model the following behavior for each equity transaction:
    • Increase the number of BP shares held by the units purchased (in the same way as the built-in Buy transaction type).
    • Decrease the main GBP cash holding by the total consideration (in the same way as the built-in Buy transaction type).
    • Decrease a separate GBP cash holding maintained by the SHK by the amount of the Commission property.

Modelling the custom transaction type

We can re-use the first two movements from the built-in Buy transaction type, and create a third movement to process the commission (some components omitted for clarity):

Transaction type componentValueExplanation
AliasTypeBuyWithSeparateCommissionDefines the name of the transaction type.
Movement #1TypeStockMovementUpdates an instrument (non-cash) holding.
Direction1Increases the number of units.
SideSide1Uses the built-in Side1 designed for instrument (non-cash) holdings.
Movement #2TypeCashCommitmentUpdates a cash holding.
Direction-1Decreases the number of units.
SideSide2Uses the built-in Side2 designed for cash holdings.
Movement #3TypeCashCommitmentUpdates a cash holding.
Direction-1Decreases the number of units.
SideBreakOutCommissionSeparatelyDefines the name of the custom side.
 SecurityTxn:SettleCcyUpdates a cash holding in the same currency as Side2.
 CurrencyTxn:SettlementCurrencyUpdates a cash holding in the same currency as Side2.
 RateSettledToPortfolioRateUses the same exchange rate calculation as Side2.
 UnitsTransaction/FBNUniversity/CommissionDecreases the number of units on a cash holding by the number of units stored by the Commission property on a transaction (for a currency, the number of units is equal to the amount).
 AmountTransaction/FBNUniversity/CommissionDecreases the amount of a cash holding by the amount of the Commission property on a transaction.
MappingTransaction/FBNUniversity/TrackCommissionMaps the result of the movement not to the main GBP cash holding but to a separate GBP cash holding registered with the portfolio by the TrackCommission SHK.

In summary, for each transaction upserted to LUSID with the custom BuyWithSeparateCommission transaction type:

  • Movement #1 increases the number of units in the equity holding by the number purchased.
  • Movement #2 decreases the main GBP cash holding by the total consideration.
  • Movement #3 decreases a separate GBP cash holding by the broker's commission.

Creating a portfolio with a TrackCommission SHK

Since a SHK is just a custom property, the first step is to create a property definition. To do this, we call the LUSID CreatePropertyDefinition API and establish the unique 3-stage property key that we'll need when registering the SHK with the portfolio:

  • The first stage of the 3-stage property key must be in the Transaction domain, since the property belongs to transactions. The scope and code stages can be any intuitive strings, so for example Transaction/FBNUniversity/TrackCommission.
  • The data type of the property should be a string.

We can then call the LUSID CreatePortfolio API, specifying a suitable scope, code, name and base currency, and registering the SHK.

Step 3 of the accompanying Jupyter Notebook demonstrates how to do this using the LUSID Python SDK. You can execute the Notebook in Jupyterhub for your LUSID domain and then sign in to the web app to see the portfolio on the Data Management > Portfolios dashboard:

Creating a property definition for the Commission property

We need to call the CreatePropertyDefinition API again to create a property definition for the Commission property, establishing a unique 3-stage property key in the Transaction domain, for example Transaction/FBNUniversity/Commission. The data type should be a number, since we're storing cash amounts.

Step 4 of the accompanying Jupyter Notebook demonstrates how to do this using the LUSID Python SDK. You can execute the Notebook in Jupyterhub for your LUSID domain and then sign in to the web app to see the property definition on the Data Management > Properties dashboard:

Creating the custom BuyWithSeparateCommission transaction type

Creating the custom BreakOutCommissionSeparately side

The first task is to call the SetSideDefinition API to create the custom BreakOutCommissionSeparately side as per the definition in the table above, with the units and amount fields mapped to the Transaction/FBNUniversity/Commission property.

Note: The PUT SetSideDefinition API operates differently to the standard PUSH Upsert* model used elsewhere in LUSID. A side is replaced rather than updated, so it's important to specify the entire definition each time.

Step 5.1 of the accompanying Jupyter Notebook demonstrates how to do this using the LUSID Python SDK. You can execute the Notebook in Jupyterhub for your LUSID domain and then sign in to the web app to see the custom side on the System Settings > Transaction Types dashboard (navigate to the Sides tab):

Including the custom side in the transaction type definition

We can now call the SetTransactionType API with the alias and movements in the table above, and the custom side as the third movement.

Note: The PUT SetTransactionType API operates differently to the standard PUSH Upsert* model used elsewhere in LUSID. A transaction type is replaced rather than updated, so it's important to specify the entire definition each time.

Step 5.2 of the accompanying Jupyter Notebook demonstrates how to do this using the LUSID Python SDK. You can execute the Notebook in Jupyterhub for your LUSID domain and then sign in to the web app to see the custom transaction type on the System Settings > Transaction Types dashboard:

Loading transactions into a portfolio and storing the commission

We can call the UpsertTransactions API to load our transactions into a suitable portfolio, assigning the Transaction/FBNUniversity/Commission property to each with the appropriate value from the commission column in the source file.

Step 6 of the accompanying Jupyter Notebook demonstrates how to do this using the LUSID Python SDK. You can execute the Notebook in Jupyterhub for your LUSID domain and then sign in to the web app to examine the Transactions dashboard.

To show the Broker commission property column in the LUSID web app, click the cog icon (highlighted in green), choose Add column, and then select the property to display from the Property tab. Note also the transaction Type of each equity purchase:

Generating a holdings report

We can now call the GetHoldings API to generate a holdings report.

Step 7 of the accompanying Jupyter Notebook demonstrates how to do this using the LUSID Python SDK. You can execute the Notebook in Jupyterhub for your LUSID domain and then sign in to the web app to examine the Holdings dashboard.

As expected, after all trades have settled, LUSID generates a holdings report with 35 BP shares, £325 in the main GBP cash holding and -£5.25 in a separate GBP cash holding reflecting the total cost of broker commission: