You can model a bond as either an instrument of type Bond, ComplexBond or InflationLinkedBond in LUSID. More information.
LUSID emits the following instrument lifecycle events for bonds, which you can handle to reduce workload and improve efficiency:
BondCouponEvent. This event is automatically emitted by LUSID each time a coupon is due. More information.BondPrincipalEvent. This event is automatically emitted by LUSID on the maturity date to return the principal. More information.MaturityEvent. This event is automatically emitted by LUSID on the maturity date to zero the holding. More information.
Mastering a bond instrument in LUSID and booking trades
Imagine we have a US Treasury bond paying 2.375% annually until 15 August 2026 modelled as a Bond as follows:
{
"instrumentType": "Bond",
"startDate": "2016-08-15T00:00:00.0000000+00:00",
"maturityDate": "2026-08-15T00:00:00.0000000+00:00",
"domCcy": "USD",
"flowConventions": {
"currency": "USD",
"paymentFrequency": "12M",
"dayCountConvention": "Actual365",
"rollConvention": "15",
"businessDayConvention": "Following",
"paymentCalendars": [],
"resetCalendars": [],
"accrualDateAdjustment": "Adjusted",
},
"principal": 1,
"couponRate": 0.02375,
"exDividendConfiguration": {
"exDividendDays": 5
}
}For much more information on modelling instruments as type Bond, see this article. Note with regard to instrument events:
The
maturityDatefield is set to the date on which the last coupon is paid and the principal returned: 15 August 2026.The
flowConventions.paymentFrequencyfield is set to12MandflowConventions.rollConventionto15to determine that coupons are paid annually on 15 August each year.The
exDividendConfiguration.exDividendDaysfield is set to5to specify that the ex-dividend date for the bond is 10 August each year.
For simplicity, business days and holiday calendars are ignored in this tutorial.
We have two historical transactions to book into a suitable transaction portfolio for this bond:
A purchase of 10,000 units on 7 August 2020, settling on 9 August 2020.
A sale of 5,000 units on 9 August 2023, settling on 11 August 2023.
Understanding lifecycle events LUSID emits for this bond
Given these transactions, and with respect to a date 'today' of 20 March 2024, LUSID automatically emits the following lifecycle events for this bond:
Date | Lifecycle event | Status with respect to 20 March 2024 | Eligible balance (known or estimate) | Coupon payment (known or estimate) | Notes |
|---|---|---|---|---|---|
15 August 2020 |
| Historical | 10000 | $237.50 | We are entitled to this coupon as the settlement date of the purchase transaction (9 August 2020) is before the ex-dividend date (10 August 2020). |
15 August 2021 |
| ||||
15 August 2022 |
| ||||
15 August 2023 | We are entitled to the full amount of this coupon as the settlement date of the sell transaction (11 August 2023) is after the ex-dividend date (10 August 2023). | ||||
15 August 2024 | Future | 5000 | $118.75 |
| |
15 August 2025 |
| ||||
15 August 2026 | Final coupon. | ||||
15 August 2026 |
| N/A |
| ||
15 August 2026 |
| N/A | N/A |
|
Handling bond coupons
LUSID automatically emits a BondCouponEvent each time a coupon is due. You should handle this event to increase an appropriate cash balance in the portfolio by the coupon amount.
Examining the default transaction template for BondCouponEvent
LUSID provides a default transaction template for every type of event. In this tutorial we’ll use the default template for BondCouponEvent, but note you can create a custom transaction template to configure the process if you wish.
We can call the GetTransactionTemplate API to examine this default template, which at the time of writing is as follows:
{
"instrumentType": "Bond",
"instrumentEventType": "BondCouponEvent",
"description": "LUSID default template for automatically generated transactions in respect of Bond Coupon instrument events.",
"scope": "default",
"componentTransactions": [
{
"displayName": "Bond Income",
"transactionFieldMap": {
"transactionId": "{{instrumentEventId}}-{{holdingId}}",
"type": "BondCoupon",
"source": "default",
"instrument": "{{instrument}}",
"transactionDate": "{{BondCouponEvent.exDate}}",
"settlementDate": "{{BondCouponEvent.paymentDate}}",
"units": "{{eligibleBalance}}",
"transactionPrice": {
"price": "{{BondCouponEvent.couponPerUnit}}",
"type": "CashFlowPerUnit"
},
"transactionCurrency": "{{BondCouponEvent.currency}}",
"exchangeRate": "1",
"totalConsideration": {
"currency": "{{BondCouponEvent.currency}}",
"amount": "{{BondCouponEvent.couponAmount}}"
}
},
"transactionPropertyMap": []
}
],
...
}Note the following:
This template handles instrument events of type
BondCouponEventemitted by instruments of typeBond.It is domiciled in the
default(that is, system) transaction templatescope.It contains instructions for automatically generating a single output transaction for each portfolio with a holding in the underlying instrument. Note it is possible for a template to generate multiple output transactions.
The generated transaction has no
condition; that is, it is always produced when LUSID emits a bond coupon event.The generated transaction belongs to a
BondCoupontransaction type grouped in thedefaulttransaction typesource.The
transactionDateis the ex-dividend date and thesettlementDateis the payment date.The quantity held (
units) is assessed dynamically per-portfolio and per-coupon.The
transactionPrice.priceis the coupon per unit, which is calculated as the annual coupon rate multiplied by the principal and divided by the number of coupons per year.The
transactionCurrencyis the currency specified in the bond economic definition, and anexchangeRateof1means the settlement currency is the same.The
totalConsideration.Amountis the coupon per unit scaled by the quantity held.
Note: This template does not add the
Transaction/default/TradeToPortfolioRatesystem property to the generated transaction. If the portfolio base currency is different to the transaction currency we recommend specifying theTxn:TradeToPortfolioRatecalculation in the transaction type to look up a spot rate dynamically in the LUSID Quote Store. See how to do this.
Creating a suitable BondCoupon transaction type
The default transaction template mandates a BondCoupon transaction type grouped in the default transaction type source. We must create this transaction type if it does not exist.
Note: Transaction types are grouped in sources and reside in scopes. For more information on the difference, see this article.
We can call the SetTransactionType API as follows:
curl -X PUT 'https://<your-domain>.lusid.com/api/api/transactionconfiguration/types/default/BondCoupon?scope=default'
-H 'Content-Type: application/json-patch+json'
-H 'Authorization: Bearer <your-API-access-token>'
-d '{
"aliases": [
{
"type": "BondCoupon",
"description": "Transaction type for bond coupon event",
"transactionClass": "Basic",
"transactionRoles": "AllRoles",
"isDefault": false
}
],
"movements": [
{
"name": "Add coupon to separate portfolio cash holding",
"movementTypes": "CashAccrual",
"side": "Side2",
"direction": 1,
"mappings": [
{
"propertyKey": "Transaction/SHKs/BondCoupons",
"setTo": "BondCoupons"
}
]
},
{
"name": "Report the coupon as income",
"movementTypes": "Carry",
"side": "Side1",
"direction": 1
}
]
}'Note the following:
The transaction type alias is
BondCoupon, to comply with the default transaction template.The transaction type source is
default(specified in the URL), to comply with the default transaction template.You can design a transaction type to have any economic impact you like. This one has the following two movements:
The first is a
CashAccrualmovement that maps gross coupon amounts to aBondCouponssub-holding key (SHK), to report accrual separately to other cash holdings in the bond currency (USD). You could omit themappingsarray to add coupons to the main USD cash holding instead. See how to calculate a net amount after tax.The second is a
Carrymovement that reports the coupon as income in an A2B report.
Handling return of principal
LUSID automatically emits a BondPrincipalEvent on the maturity date. You should handle this event to increase an appropriate cash balance in the portfolio by the principal amount.
Examining the default transaction template for BondPrincipalEvent
We can call the GetTransactionTemplate API to examine the default transaction templates for BondPrincipalEvent, which at the time of writing is as follows:
{
"instrumentType": "Bond",
"instrumentEventType": "BondPrincipalEvent",
"description": "LUSID default template for automatically generated transactions in respect of Bond Principal instrument events",
"scope": "default",
"componentTransactions": [
{
"displayName": "Bond Principal",
"transactionFieldMap": {
"transactionId": "{{instrumentEventId}}-{{holdingId}}",
"type": "BondPrincipal",
"source": "default",
"instrument": "{{instrument}}",
"transactionDate": "{{BondPrincipalEvent.paymentDate}}",
"settlementDate": "{{BondPrincipalEvent.paymentDate}}",
"units": "{{eligibleBalance}}",
"transactionPrice": {
"price": "{{BondPrincipalEvent.principalPerUnit}}",
"type": "CashFlowPerUnit"
},
"transactionCurrency": "{{BondPrincipalEvent.currency}}",
"exchangeRate": "1",
"totalConsideration": {
"currency": "{{BondPrincipalEvent.currency}}",
"amount": "{{BondPrincipalEvent.principalAmount}}"
}
},
"transactionPropertyMap": []
}
],
...
}Note the following:
This template handles instrument events of type
BondPrincipalEventemitted by instruments of typeBond.It is domiciled in the
default(that is, system) transaction template scope.It contains instructions for automatically generating a single output transaction in every portfolio with a holding in the bond instrument.
The generated transaction belongs to a
BondPrincipaltransaction type grouped in thedefaulttransaction type source.
Creating a suitable BondPrincipal transaction type
The default transaction template mandates a BondPrincipal transaction type grouped in the default transaction type source. We must create this transaction type if it does not exist, for example by calling the SetTransactionType API as follows:
curl -X PUT 'https://<your-domain>.lusid.com/api/api/transactionconfiguration/types/default/BondPrincipal?scope=default'
-H 'Content-Type: application/json-patch+json'
-H 'Authorization: Bearer <your-API-access-token>'
-d '{
"aliases": [
{
"type": "BondPrincipal",
"description": "Transaction type for bond principal event",
"transactionClass": "Basic",
"transactionRoles": "AllRoles",
"isDefault": false
}
],
"movements": [
{
"name": "Return principal to separate cash balance",
"movementTypes": "CashReceivable",
"side": "Side2",
"direction": 1,
"mappings": [
{
"propertyKey": "Transaction/SHKs/BondPrincipal",
"setTo": "BondPrincipal"
}
]
},
{
"name": "Set cost to zero without reducing units",
"movementTypes": "StockMovement",
"side": "Side1",
"direction": -1,
"movementOptions": ["Virtual"]
}
]
}'Note the following:
The transaction type alias is
BondPrincipal, to comply with the default transaction template.The transaction type source is
default(specified in the URL), to comply with the default transaction template.Our recommended transaction type has two movements:
The first is a
CashReceivablemovement that maps the principal amount to aBondPrincipalSHK, to report it separately to any other cash balance in the bond currency (USD). You could omit themappingsarray to add the principal to the main USD cash holding instead.The second is a
StockMovementthat uses theVirtualoption to set the cost of the holding to zero without actually reducing the units (this is handled byMaturityEvent; see below).
Handling instrument maturity
LUSID automatically emits a MaturityEvent on the maturity date. You should handle this event to set your position to zero units in the portfolio, so it drops out of holding and valuation reports.
Examining the default transaction template for MaturityEvent
We can call the GetTransactionTemplate API to examine the default transaction templates for MaturityEvent, which at the time of writing is as follows:
{
"instrumentType": "Bond",
"instrumentEventType": "MaturityEvent",
"description": "LUSID default template for automatically generated transactions in respect of instrument maturity events.",
"scope": "default",
"componentTransactions": [
{
"displayName": "Maturity",
"transactionFieldMap": {
"transactionId": "{{instrumentEventId}}-{{holdingId}}",
"type": "Maturity",
"source": "default",
"instrument": "{{instrument}}",
"transactionDate": "{{MaturityEvent.maturityDate}}",
"settlementDate": "{{MaturityEvent.maturityDate}}",
"units": "{{eligibleBalance}}",
"transactionPrice": {
"price": "0",
"type": "Price"
},
"transactionCurrency": "{{holdingCurrency}}",
"exchangeRate": "0",
"totalConsideration": {
"currency": "{{holdingCurrency}}",
"amount": "0"
}
},
"transactionPropertyMap": []
}
],
...
}Note the following:
This template handles instrument events of type
MaturityEventemitted by instruments of typeBond.It is domiciled in the
default(that is, system) transaction template scope.It contains instructions for automatically generating a single output transaction in every portfolio with a holding in the bond instrument.
The generated transaction belongs to a
Maturitytransaction type grouped in thedefaulttransaction type source.
Creating a suitable Maturity transaction type
The default transaction template mandates a Maturity transaction type grouped in the default transaction type source. We must create this transaction type if it does not exist, for example by calling the SetTransactionType API as follows:
curl -X PUT 'https://<your-domain>.lusid.com/api/api/transactionconfiguration/types/default/Maturity?scope=default'
-H 'Content-Type: application/json-patch+json'
-H 'Authorization: Bearer <your-API-access-token>'
-d '{
"aliases": [
{
"type": "Maturity",
"description": "Transaction type for instrument maturity event",
"transactionClass": "Basic",
"transactionRoles": "AllRoles",
"isDefault": false
}
],
"movements": [
"name": "Set units to zero",
"movementTypes": "StockMovement",
"side": "Side1",
"direction": -1
]
}'Note the following:
The transaction type alias is
Maturity, to comply with the default transaction template.The transaction type source is
default(specified in the URL), to comply with the default transaction template.Our recommended transaction type has a single
StockMovementto reduce the number of units to zero (redcuing the cost to zero has already been handled byBondPrincipalEvent; see above).
Examining the impact of instrument lifecycle events on the portfolio
9 August 2020
Calling the GetHoldings API before the first coupon payment reveals one holding in the portfolio, for 10,000 settled units of the bond instrument:
.png?sv=2022-11-02&spr=https&st=2025-11-01T11%3A46%3A47Z&se=2025-11-01T12%3A07%3A47Z&sr=c&sp=r&sig=G6NaoePz2bHluQ1dn99cp5OVRADcqvmYhWn59uBA9fs%3D)
We can call the BuildTransactions API with a suitable window to examine the single output transaction that has contributed to this position:
.png?sv=2022-11-02&spr=https&st=2025-11-01T11%3A46%3A47Z&se=2025-11-01T12%3A07%3A47Z&sr=c&sp=r&sig=G6NaoePz2bHluQ1dn99cp5OVRADcqvmYhWn59uBA9fs%3D)
20 March 2024
Calling the GetHoldings API again 'today' reveals we now have two holdings:
A holding in the bond instrument for 5000 settled units, reflecting the sale of half the units in August 2023.
A USD cash holding categorized by a
BondCouponsSHK, representing coupon accrual to date:
.png?sv=2022-11-02&spr=https&st=2025-11-01T11%3A46%3A47Z&se=2025-11-01T12%3A07%3A47Z&sr=c&sp=r&sig=G6NaoePz2bHluQ1dn99cp5OVRADcqvmYhWn59uBA9fs%3D)
We can call the BuildTransactions API with a suitable window to examine the output transactions that have contributed to these positions:
.png?sv=2022-11-02&spr=https&st=2025-11-01T11%3A46%3A47Z&se=2025-11-01T12%3A07%3A47Z&sr=c&sp=r&sig=G6NaoePz2bHluQ1dn99cp5OVRADcqvmYhWn59uBA9fs%3D)
1 January 2030
We can fast-forward LUSID and call the GetHoldings API again after the bond instrument has matured, at which point (and assuming no other activity) we will have two holdings in our portfolio:
A USD cash holding categorized by a
BondCouponsSHK, representing total coupon accrual.A USD cash holding categorized by a
BondPrincipalSHK, representing the return of remaining principal.
Note we no longer have a holding in the bond instrument:
.png?sv=2022-11-02&spr=https&st=2025-11-01T11%3A46%3A47Z&se=2025-11-01T12%3A07%3A47Z&sr=c&sp=r&sig=G6NaoePz2bHluQ1dn99cp5OVRADcqvmYhWn59uBA9fs%3D)
We can also fast-forward the BuildTransactions API to examine the output transactions that have contributed to these positions:
.png?sv=2022-11-02&spr=https&st=2025-11-01T11%3A46%3A47Z&se=2025-11-01T12%3A07%3A47Z&sr=c&sp=r&sig=G6NaoePz2bHluQ1dn99cp5OVRADcqvmYhWn59uBA9fs%3D)