1. Help Center
  2. Getting started

How do I create holdings in LUSID?

Learn how to set up a portfolio and load transactions into LUSID

This document will guide you through loading transactions into LUSID to produce a holdings report. This document assumes that you have basic understanding of LUSID and have a good understanding of portfolio and asset management concepts.

Looking ahead, we will show you how to load transactions for a long-only UK Equity Portfolio denominated in GBP. The portfolio will invest in 3 stocks in the Food & Retail sector: Tesco, Sainsbury’s and Morrisons.

Here is a snapshot of those transactions. You can see there are two transactions on the 01-January, two on the 02-January and one on the 03-January. These transactions will be used to build holdings. You can see a reference implementation of this example in the linked Jupyter notebook (click here).

Figure 1: Transactions file for upload 

image1

Table of contents

  1. Introduction to key concepts
  2. Preparing LUSID for the transactions
  3. Extensible data model
  4. Loading portfolios
  5. Loading an instrument master
  6. Loading a transaction file
  7. Configuring transaction types
  8. Generating holdings
  9. Reconfigure holdings use sub-holdings keys

Introduction to key concepts

Before we start, let’s define some key concepts:

  • Data entity - these are the core data building blocks of the platform which includes instruments, transactions, holdings (and many more).
  • Portfolio – a portfolio is a container for transactions and holdings.
  • Transaction – a Transaction in LUSID is an economic event that occurs in a portfolio causing its state  to change. This could be a purchase of an instrument, redemption of cash by a client, or a corporate action such as a dividend payment.
  • Movement Engine – the Movement Engine aggregates the economic impact from transactions to build a set of holdings.
  • ScopeScopes are used to partition data. Two data entities with the same unique code can co-exist within different scopes. For example, we could store transactions for the same portfolio in two scopes: one for the Front-Office IBOR and one for the Custodian. We can also assign entitlements based on scope.

Preparing LUSID for the transactions

To prepare LUSID for the transactions upload, we seed the environment with two sets of data:

  1. A portfolio (or portfolios) in which to put the new transactions
  2. A set of instruments for booking the transactions

Broadly speaking, there are two ways to put portfolios and instruments data into LUSID:

  • Call the API methods to create instruments and portfolios (see documentation)
  • Use our tools which have wrapped the APIs in the SDKs with some easy-to-use functions. For example, you can use a method in our Python lusidtools package called load_from_data_frame to load a pandas DataFrame containing portfolios and instruments into LUSID.

Extensible data model

At this point, it is worth highlighting LUSID’s extensible data model. LUSID only requires a minimal number of fields to upload a data entity. These fields are the fields required by the system’s underlying business logic. Beyond that, any number of additional fields can be painted on as properties

New properties need to be defined in your LUSID domain with a create property definition call before they can be assigned to data entities. 

Loading portfolios

We load a portfolio in LUSID using the create portfolio endpoint. To setup a portfolio in LUSID, a minimum of 4 data fields must be provided: scope, displayName, code and a baseCurrency. You are then free to add as many additional properties as you require. These additional properties could be anything from regulatory data points (e.g. LEI for MiFID) or details about the underlying client.

In our UK Equity example, we create a portfolio with code UK_EQUITY which has a baseCurrency of GBP. We then add additional properties such as “Portfolio Manager Name” and “Portfolio Manager ID”. 

Request 1: Create portfolio POST request

{
"displayName": "Portfolio UK",
"description": "Portfolio for UK market",
"code": "PortfolioUk",
"created": "2018-03-05T12:00:00.0000000+00:00",
"baseCurrency": "GBP",
"corporateActionSourceId": {
"scope": "Sources",
"code": "Vendor1"
},
"accountingMethod": "Default",
"subHoldingKeys": [],
"properties": {
"Portfolio/FrontOffice/ManagerName": {
"key": "Portfolio/FrontOffice/ManagerName",
"value": {
"labelValue": "Matt Smith"
},
"effectiveFrom": "2018-03-05T12:00:00.0000000+00:00"
},
"Portfolio/Manager/Id": {
"key": "Portfolio/FrontOffice/ManagerName",
"value": {
"metricValue": {
"value": 1628483,
"unit": "NoUnits"
}
},
"effectiveFrom": "2018-03-05T12:00:00.0000000+00:00"
}
}
}

Now that our portfolio is in LUSID and we have somewhere to store our transactions. Next let’s look at instruments.

Loading and resolving instruments

Creating new instruments

Instruments in LUSID are mastered using the upsert instruments endpoint. At a minimum, mastering an instrument requires two fields: the instrument name and a unique identifier. To see the list of unique identifiers, users can call the get identifier types endpoint.

Request 2: Create an instrument POST request

{
"request_id_1": {
"name": "Instrument name",
"identifiers": {
"ClientInternal": {
"value": "some-identifier",
"effectiveAt": "0001-01-01T00:00:00.0000000+00:00"
},
"Figi": {
"value": "some-figi-code",
"effectiveAt": "0001-01-01T00:00:00.0000000+00:00"
}
},
"properties": [
{
"key": "Instrument/default/Isin",
"value": {
"labelValue": "US0378331005"
},
"effectiveFrom": "2018-06-18T09:00:00.0000000+00:00"
}
],
"lookThroughPortfolioId": {
"scope": "MyScope",
"code": "portfolio-code"
},
"definition": {
"instrumentFormat": "some-format",
"content": "{\"some-key\": \"some-value\"}"
}
},
"request_id_2": {
"name": "Instrument name",
"identifiers": {
"ClientInternal": {
"value": "some-identifier-2",
"effectiveAt": "0001-01-01T00:00:00.0000000+00:00"
},
"Figi": {
"value": "some-figi-code-2",
"effectiveAt": "0001-01-01T00:00:00.0000000+00:00"
}
},
"properties": [],
"lookThroughPortfolioId": {
"scope": "MyScope",
"code": "portfolio-code"
},
"definition": {
"instrumentFormat": "some-format",
"content": "{\"some-key\": \"some-value\"}"
}
}
}

Upon mastering, LUSID also creates a LUID which is a unique LUSID specific identifier. This LUID employs the naming convention of “LUID_” followed by a string of 8 alphanumeric characters.

Example: LUID_AI36GRS8.

Resolving instruments

When booking a transaction or holding, you supply a map of identifier scheme to identifier value. LUSID then checks that the provided identifier (such as a Client ID or ISIN) is mapped to a unique LUID. If LUSID cannot resolve the provided identifier to a LUID, then the transaction is booked against the “unknown instrument” identifier called LUID_ZZZZZZZZ.

When resolving instruments, it’s worth highlighting that that unique identifiers resolve to a single LUID and non-unique identifiers can resolve to multiple LUIDs. The following page provides more details on resolving instruments.

Loading a transaction file

At this point, we now have a portfolio and instruments loaded into LUSID and are ready to upload (or upsert) the transactions. As above, broadly speaking, we have two options to load the data:

  • Call the API methods to create transaction
  • Use our pre-defined tools

Like the other data entities in LUSID, the transaction has an extensible data model. You only need to provide the following to upsert a transaction:

  1. Scope
  2. Code
  3. Transaction ID
  4. Transaction Type (e.g. Buy, Sell, Close, FundsIn)
  5. Instrument ID (e.g. LUID, ISIN)
  6. Transaction Date
  7. Settlement Date
  8. Units
  9. Transaction Price
  10. Total Consideration

You can then view the transactions using your preferred method – the Web API, Excel, Jupyter Notebooks, or calling the Get Transactions API directly with your own software.

Configuring transaction types

LUSID has a flexible transaction modelling system. This means we allow you to configure the economic impact of your own transaction types. For example, you could define a transaction code of “Buy”, “B” or “IBOR_BUY” which all have the same meaning - increase the amount of an instrument in a portfolio and decrease cash. If, at a later date, a new source of data is added to LUSID which has a new transaction type of “Purchase”, but has the same underlying economic requirement (increase amount of the instrument, decrease cash) then that type can simply be added to the current configuration.

The idea here is simple but powerful – LUSID is not concerned with naming conventions of transaction codes in external systems, we only care about the economic impact of these transaction codes.

Structure of a transaction type request

A transaction type request is composed of 4 components, which all determine how LUSID interprets a transaction:

  1. Alias
  2. Movements
  3. Sides
  4. Properties

For illustration, let’s consider these components with reference to the “Buy” transaction discussed above. At its core, a “Buy” transaction in the IBOR should do the following to a portfolio’s trade dated cash and positions:

  • Increase the amount of what is being purchased.
  • Decrease cash which represents the value of the purchase.
  • Decrease cash which is paid to the broker as a broker fee for this purchase.

Looking back to the example portfolio in the introduction.

Figure 2: Transactions file for upload

image2

In that example we buy 100,000 units of Tesco @ £2.56 (into the portfolio UK_EQUITY). In LUSID we capture the following movements:

  • Increase the amount of “Tesco” in UK_EQUITY by 100,000.
  • Decrease CCY_GBP by £256,000 which represents the purchase.
  • Decrease CCY_GBP by an additional £128 which represents the broker fee.

In this section, we will setup the Transaction Type components with reference to this “Tesco” example using the following JSON configuration.

{
"transactionConfigs": [
{
"aliases": [
{
"type": "string",
"description": "string",
"transactionClass": "string",
"transactionGroup": "string",
"transactionRoles": "None"
}
],
"movements": [
{
"movementTypes": "Settlement",
"side": "string",
"direction": 0,
"properties": {
"property1": {
"key": "string",
"value": {
"labelValue": "string",
"metricValue": {
"value": 0,
"unit": "string"
}
}
},
"property2": {
"key": "string",
"value": {
"labelValue": "string",
"metricValue": {
"value": 0,
"unit": "string"
}
}
}
},
"mappings": [
{
"propertyKey": "string",
"mapFrom": "string",
"setTo": {}
}
]
}
],
"properties": {
"property1": {
"key": "string",
"value": {
"labelValue": "string",
"metricValue": {
"value": 0,
"unit": "string"
}
}
},
"property2": {
"key": "string",
"value": {
"labelValue": "string",
"metricValue": {
"value": 0,
"unit": "string"
}
}
}
}
}
],
"sideDefinitions": [
{
"side": "string",
"security": "string",
"currency": "string",
"rate": "string",
"units": "string",
"amount": "string",
"links": [
{
"relation": "string",
"href": "string",
"description": "string",
"method": "string"
}
]
}
],
"links": [
{
"relation": "string",
"href": "string",
"description": "string",
"method": "string"
}
]
}

Alias

The first component of a transaction type request is the alias. The alias configuration determines how external transaction types are mapped and grouped together. The alias has 5 key attributes.

For the purposes of this tutorial, we have one source of transaction data. Therefore, the Transaction Group, Transaction Class and Transaction Role don’t drive any functionality in our example. That said, these data points provide powerful functionality which allows users to organise a variety of complex transaction types from multiple sources. We will demonstrate that functionality in another tutorial.

Attribute

Description

Type

This is the string by which an external source knows the transaction type. For example, you could add “BUY” for your ABOR and “B” for your IBOR, and “Purchase” for a client file. The “Type” is what LUSID uses to map an external string – like “BUY” - to our internal logic.

Description

This is a plain text description of the “Type”. It does not drive any LUSID functionality. In the example of a “Buy” you could add a description of “A purchase transaction from System Y.

Transaction Class

Transaction Class is used to group similar transaction types together across different transaction groups (or sources). For example, you could group all transaction types for Fixed leg Swap together (PayFix, ReceiveFix) under a class of “FixedSwap”. 

(For the purposes of our example, we use the default class of “Basic”. This does not drive any functionality in our example as we only have one source of transaction data.)

To see sample use-cases, please refer to the following article HERE.

Transaction Group

Transaction group is used to group different transaction types from the same source or system together. For more details on Transaction Group, please see our support page HERE.

(For the purposes of our example, we use the default group of “default”. This does not drive any functionality in our example as we only have one source of transaction data.)

Transaction Role

The transaction role is a pre-defined label used to categorise the movement of a transaction within a Transaction Class. Valid values include:  LongLonger, Longer, ShortShorter, Shorter. As a rule, you should not have more than one of these transaction roles within the same Transaction Class. The transaction role is used to link different transaction types in different transaction groups, which have the same underlying economic movement.

For example, you might have a “BUY” from your ABOR and a “B” from your IBOR which both map to the same Transaction Role of “LongLonger”

To see sample use-cases, please refer to the following article HERE.

 

Movements

While the “alias” describes the transaction type and its relation to other transaction types, it is the movement which drives the economic impact of a transaction in LUSID. The “movement” in LUSID is composed of the following attributes:

 

Attribute

Description

Movement Type

This drives how the transaction will be grouped in the movements engine. For example, a movement of “CashCommitment” will modify the cash lines in LUID while “StockMovement” will adjust the quantity. The Movement Type will ultimately drive the “holdingType” groupings in a get holdings request.

See the attached support page and the API documentation for a full list of movement types.

Side

When you configure a Transaction Type in LUSID you will need to specify a Side for each Movement. Put simply, the Side tells LUSID which attributes (required, optional and/or properties) to use from the transaction to specify values for the units, security, currency, exchange rate and total amount used in generating the holdings for a portfolio.

See linked support page for more details.

Direction

This configuration determines the economic direction of a transaction. The value values are:

  • 1 = an increase movement (example: a “Buy” increases stock)
  • -1 = a decrease movement (example: a “Buy” decreases committed cash)

A 0 direction can also be used if you want to capture a movement as a memo event with no economic impact.

Mapping

This allows you to map a property onto a transaction based on the transaction movement. To illustrate, each time a movement is booked into LUSID, this mapping could do one of two things:

(1) Automatically set the PropertyKey to a predefined value. For example, consider the movement which captures the broker commission from the transaction in our UK_EQUITY example. As a result, you might want to stamp that movement with a “Transaction/MyScope/asset_type” value of “Commission”

(2) Alternatively, you might want to take that Transaction/MyScope/asset_type” value from another property on the transaction.

 

Sides

The side determines which of the fields from our Transaction are used to generate the movement. Let’s dive into this further by looking at two different side configurations from our example.

But first, a quick note on “Side” syntax.

There are two options:

  • Use one of the “Txn:” keys such as "Txn:TradeCurrency" - see the full list on this page HERE.
  • Use a property in the standard “domain/scope/code” format

Now back to our two examples.

First, let’s look at Side1. This is a system default which is used for increasing or decreasing stock. In our example above, we can see that a side of “Side1” has been assigned to the “Buy” transaction type. Here LUSID knows it needs to increase the LusidInstrumentId from the transaction by the number of units on the same transaction.

{
"side": "Side1",
"security": "Txn:LusidInstrumentId",
"currency": "Txn:TradeCurrency",
"rate": "Txn:TradeToPortfolioRate",
"units": "Txn:Units",
"amount": "Txn:TradeAmount"
}

However, side configurations provide a lot more flexibility. Imagine you wanted to capture broker commission from a column in your transaction file. In that case you could configure a new side called “TradeCommissions” which takes a commission from the broker_comms property and allocates that to the source LUID. Please see the following notebook which contains an example of a side configured to capture broker commission.

Properties

Client defined properties can be applied to transaction types data entities and on the movements assigned to those transaction types entities. These properties follow LUSID’s standard property schema. See more details HERE.

 

Get holdings

Now we are ready to generate some holdings. But first, let’s review what we have covered so far. Throughout this tutorial, we have:

  1. Created a portfolio called UK_EQUITY and mastered some instruments.
  2. Booked 6 transactions across 3 separate days.
  3. Configured transaction types.

To generate holdings in LUSID, we call the get holdings endpoint. As above, you can call that endpoint in various ways:

  • Write code directly against our APIs
  • Use one of the SDKs on GitHub
  • View via the LUSID Dashboard

Figure 3: Sample get_holdings function in Python SDK on GitHub

image3

Once the get holdings endpoint is called, LUSID will build your holdings in “real-time” using the transactions and transaction type configurations in the movements engine. This is a key implementation point. LUSID does not store your portfolio holdings with one fixed “state”. Instead it stores all the transaction “events” in an event store and then builds the state of the portfolio when you call “get holdings”.

Let’s “get holdings” on our UK_EQUITY example above. We will get holdings for two separate dates 01-January and 03-Januay using the web dashboard.

As an aside, the web tool, SDKs and Jupyter notebooks all use the exact same set of underlying APIs.

Get holdings on 01-January

When we call get holdings on 01-January, the holdings engine looks through the portfolio’s events store and finds 4 movements:

  • + £1mm CCY_GBP from the initial fund subscription
  • + 100,000 units in Tesco
  • - £256,000 CCY_GBP which is the cost of Tesco
  • - £128 CCY_GBP which is the transaction cost or broker commission

These movements are used to build two holdings:

  • 100,000 units in Tesco
  • 743,872 units in CCY_GBP**

**The “units” of a CCY holding are always equal to the nominal value of that holding since the “price” of a CCY holding is always “1” in LUSID. For example, 100 units of CCY_GBP will always be valued at £100 in LUSID.

Figure 4: Holdings report in LUSID's web tool

img4

Get holdings on 03-January

Fast forward two days to 03-January, and we can see that the holdings have been adjusted further by three subsequent transactions:

  • Another purchase of more Tesco for 20,000 units
  • A purchase of 120,000 units of Morrisons
  • A purchase of 200,000 units of Sainsbury’s

Figure 5: Holdings report after purchase of Morrisons and Sainsbury's

img5

Holding type

A holding can be one of the following types depending on the instrument or on the stage of a holding in its life cycle (example: settled versus unsettled):

Holding Type

Code

Description

Position

P

A holding in an instrument.

Cash Balance

B

The settled cash balance. For example, the cash from a SELL trade which settled last week is included in this balance.

Cash Commitment

C

Future cash payment in/out of the portfolio from trading activity. For example, the unsettled cash from yesterday's BUY trade. Each separate unsettled trade is represented as a unique line in the response.

Cash Receivable

R

Future cash flow into the portfolio from non-trading activity such subscriptions, redemptions, fees, taxes etc. This label of "Receivable" is used to capture the class of holding types which represent both Receivables and Payables.

Cash Accrual

A

Future cash flow that has been classified as an accrual (e.g. accrued dividend from a corporate action)

Forward FX

F

Future cash flow from an FX forward transaction. These will be represented in pairs, one for each leg of the FX Forward.

 

Sub holding keys

By default, LUSID groups holdings at the instrument level. Consider the example above where we had two Transactions in “Tesco”:

  • On 01-January the portfolio purchased 100,000 units
  • On 03-January the portfolio purchased 20,000 units

When we call “get holdings” for effective date of 03 January, LUSID subtotals these positions to return one holding of 120,000 units. In most circumstances this is the desired behaviour. Portfolio managers and clients generally want to see one line per instrument in their holdings and valuations report.

However, there are various scenarios where you might want to separate holdings further. Two examples:

  1. You want to segregate holdings in a single instrument based on a strategy tag supplied with the transaction
  2. You want to account for different types of cash balances (e.g. principal, interest or margin)

This brings us to sub-holding keys (or SHKs for short).

Example 1 – strategy tagging

Let’s review our example using SHKs for strategy tagging. Imagine the portfolio UK_EQUITY has two strategies:

  • benchmarkRebalance for bringing the portfolio in-line with its benchmark requirements as specified in the IMA (Investment Management Agreement)
  • quantitativeSignal which is used by the fund manager’s models to make decisions based on quantitative analysis (e.g. an earnings announcement)

To implement this strategy tagging on LUSID, we need to apply three configurations:

(1) Create the new property

First, we create a new transaction property. We call the property “Transaction/MyScope/strategy_tag”. This property is created using the create property definition endpoint.

(2) Assign property to portfolio as a SHK

Then we create the portfolio with the new property as a Sub-holding Key in the “upsert portfolio” request. It’s important to note that a portfolio’s sub-holding keys need to be defined at portfolio creation. They cannot be retrospectively added with a subsequent “update portfolio” request.

(3) Upload transactions with properties for SHK

For step 3, we upsert some transactions with a strategy_tag property value of either benchmarkRebalance or quantitativeSignal. In terms of key-value pairs, the mapping is one of the following:

  • Transaction/MyScope/strategy_tag=benchmarkRebalance
  • Transaction/MyScope/strategy_tag =quantitativeSignal

Here is the source data with new strategy_tag column:

Figure 6: Transaction file with strategy column

img6

Once the transactions have been uploaded, and we call get holdings, the results will be split out per strategy_tag. Looking at our holdings from the example, we see that the second Tesco trade of 20,000 units has been categorised under the “quantitativeSignal” bucket. There is also a second CCY_GBP cash line to track cash movements from trades in the “quantitativeSignal” bucket.

Figure 7: Holding split by sub-holding key

img7

Example 2 - track trading commissions

Another use-case for sub-holding keys is the splitting out the commission from a cash balance. Recall the example above where we mapped an “asset_type” for each movement on the buy:

These asset_type sub-holding keys are used to split out the “Commission” in a holding report.

Figure 8: Holdings report split by commission 

img8

Conclusion

We hope you found this tutorial useful. Here at FINBOURNE we believe that technology products should be easy-to-use and accessible for end-users. If you have any feedback on this tutorial or suggestions for more documentation, please send us an email on the below:

info@finbourne.com