Handling bond coupon, principal and maturity instrument lifecycle events

Prev Next

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 to 12M and flowConventions.rollConvention to 15 to determine that coupons are paid annually on 15 August each year.

  • The exDividendConfiguration.exDividendDays field is set to 5 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

BondCouponEvent

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

BondPrincipalEvent

N/A

 

15 August 2026

MaturityEvent

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 type Bond.

  • It is domiciled in the default (that is, system) transaction template scope.

  • 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 the default transaction type source.

  • The transactionDate is the ex-dividend date and the settlementDate 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 an exchangeRate of 1 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 the Txn: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 a BondCoupons sub-holding key (SHK), to report accrual separately to other cash holdings in the bond currency (USD). You could omit the mappings 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 type Bond.

  • 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 the default 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 a BondPrincipal SHK, to report it separately to any other cash balance in the bond currency (USD). You could omit the mappings array to add the principal to the main USD cash holding instead.

    • The second is a StockMovement that uses the Virtual option to set the cost of the holding to zero without actually reducing the units (this is handled by MaturityEvent; 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 type Bond.

  • 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 the default 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 by BondPrincipalEvent; 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: