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
maturityDate
field is set to the date on which the last coupon is paid and the principal returned: 15 August 2026.The
flowConventions.paymentFrequency
field is set to12M
andflowConventions.rollConvention
to15
to determine that coupons are paid annually on 15 August each year.The
exDividendConfiguration.exDividendDays
field is set to5
to 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
BondCouponEvent
emitted 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
BondCoupon
transaction type grouped in thedefault
transaction typesource
.The
transactionDate
is the ex-dividend date and thesettlementDate
is the payment date.The quantity held (
units
) is assessed dynamically per-portfolio and per-coupon.The
transactionPrice.price
is 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
transactionCurrency
is the currency specified in the bond economic definition, and anexchangeRate
of1
means the settlement currency is the same.The
totalConsideration.Amount
is the coupon per unit scaled by the quantity held.
Note: This template does not add the
Transaction/default/TradeToPortfolioRate
system property to the generated transaction. If the portfolio base currency is different to the transaction currency we recommend specifying theTxn:TradeToPortfolioRate
calculation 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
CashAccrual
movement that maps gross coupon amounts to aBondCoupons
sub-holding key (SHK), to report accrual separately to other cash holdings in the bond currency (USD). You could omit themappings
array to add coupons to the main USD cash holding instead. See how to calculate a net amount after tax.The second is a
Carry
movement 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
BondPrincipalEvent
emitted 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
BondPrincipal
transaction type grouped in thedefault
transaction 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
CashReceivable
movement that maps the principal amount to aBondPrincipal
SHK, to report it separately to any other cash balance in the bond currency (USD). You could omit themappings
array to add the principal to the main USD cash holding instead.The second is a
StockMovement
that uses theVirtual
option 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
MaturityEvent
emitted 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
Maturity
transaction type grouped in thedefault
transaction 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
StockMovement
to 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:
We can call the BuildTransactions API with a suitable window to examine the single output transaction that has contributed to this position:
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
BondCoupons
SHK, representing coupon accrual to date:
We can call the BuildTransactions
API with a suitable window to examine the output transactions that have contributed to these positions:
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
BondCoupons
SHK, representing total coupon accrual.A USD cash holding categorized by a
BondPrincipal
SHK, representing the return of remaining principal.
Note we no longer have a holding in the bond instrument:
We can also fast-forward the BuildTransactions
API to examine the output transactions that have contributed to these positions: