Modelling complex bonds in LUSID

LUSID has a ComplexBond instrument type designed to model most kinds of bond.

Kind of bond

LUSID instrument type

More information

Fixed rate with irregular coupons (other than the first)
Floating rate
Fixed-to-floating rate
Municipal
Callable
Puttable
Sinkable
Convertible
Mortgage-backed security (MBS)

ComplexBond

Continue reading this article.

Fixed rate with regular coupons (only the first can be irregular)
Zero coupon

Bond

Read this dedicated article.

Inflation-linked

InflationLinkedBond

Read this dedicated article.

Mastering an instrument

There are numerous methods you can use to master an instrument of type ComplexBond in the LUSID Security Master.

Some fields are common to all types of instrument, such as an intuitive name, the requirement to specify a set of identifiers, and the facility to store extra information as properties.

Fields in the economic definition object are specific to ComplexBond. For more information on these fields, select ComplexBond from the definition dropdown in the API documentation:

Understanding the economic definition of a complex bond

The composition of the definition object is highly dependent on the type of bond you want to master. Consider the following call to the UpsertInstruments API; note this is actually a no-op request since the schedules array must be populated:

curl -X POST "https://<your-domain>.lusid.com/api/api/instruments?scope=mycustominstrscope"
   -H "Content-Type: application/json-patch+json"
   -H "Authorization: Bearer <your-API-access-token>"
   -d '{
  "upsert-request-1": {
    "name": "UKT 0 â…œ 10/22/26",
    "identifiers": {"Figi": {"value": "BBG00ZF1T7P5"}},
    "definition": {
      "instrumentType": "ComplexBond",                   
      "calculationType": "Standard",
      "roundingConventions": [],
      "schedules": [],
      "assetBacked": "",
      "assetPoolIdentifier": ""
    }
  }
}'

Within the definition object:

  • The instrumentType must be ComplexBond.

  • The calculationType defaults to Standard. More information.

  • The roundingConventions array can contain one or more rounding conventions. If you omit this field, no rounding is applied. More information.

  • The assetBacked and assetPoolIdentifier fields are only applicable to MBS and can otherwise be omitted or set to empty strings. More information.

  • The schedules array must contain at least one schedule. Depending on the type of bond, you may need to specify multiple schedules, of the same type or a mix of different ones. For example, a callable fixed rate bond:

    • Must have at least one FixedSchedule

    • Cannot have a FloatSchedule

    • Can have a StepSchedule (or more than one)

    • Must have at least one OptionalitySchedule

    Type of LUSID schedule

    Type of bond

    Rate fixing

    Fixed rate

    Floating rate

    Fixed-to-floating rate

    FixedSchedule

    All

    1 or more

    0

    1 or more

    FloatSchedule

    All

    0

    1 or more

    1 or more

    StepSchedule

    Callable

    0 or more

    0 or more

    0 or more

    Puttable

    0 or more

    0 or more

    0 or more

    Sinkable

    1 or more

    1 or more

    1 or more

    Convertible

    0 or more

    0 or more

    0 or more

    Municipal

    0 or more

    0 or more

    0 or more

    OptionalitySchedule

    Callable

    1 or more

    1 or more

    1 or more

    Puttable

    1 or more

    1 or more

    1 or more

    Sinkable

    0 or more

    0 or more

    0 or more

    Convertible

    0 or more

    0 or more

    0 or more

    Municipal

    0 or more

    0 or more

    0 or more

Specifying a fixed schedule

You must specify a fixed schedule in the schedules array if a bond uses a fixed rate to calculate coupon payments for at least part of its lifetime.

You may need to specify more than one fixed schedule if some parameter of the bond other than the coupon rate changes, for example the payment frequency switches from semi-annually to quarterly. Note if only the coupon rate changes it may be easier to add a step schedule instead of another fixed schedule.

For more information on the fields in a fixed schedule, select FixedSchedule from the definition > schedules dropdown in the API documentation:

Consider the following example of a single fixed schedule in the schedules array; note the fields specified below are the minimum required:

"schedules": [
  {
    "scheduleType": "FixedSchedule",
    "startDate": "2021-10-22T00:00:00+00:00",
    "maturityDate": "2028-10-22T00:00:00+00:00",
    "paymentCurrency": "GBP",
    "notional": 100000,
    "couponRate": 0.025,
    "flowConventions": {
      "paymentFrequency": "6M",
      "dayCountConvention": "ActActIsma",
      "rollConvention": "22",
      "businessDayConvention": "Following",
      "currency": "GBP",
    }
  }
]

Note the following:

  • The scheduleType must be FixedSchedule.

  • The startDate should be the accrual start date; that is, the date from which interest is calculated.

  • The maximum maturityDate is 31 December 2140.

  • The couponRate should be expressed as a decimal rather than a percentage, so a bond paying:

    • 10% should have a couponRate of 0.1

    • 2.5% should have a couponRate of 0.025

    • 0.375% should have a couponRate of 0.00375.

  • The flowConventions object stores all the information necessary to determine coupon periods, accrued interest amounts and payment dates:

    • The paymentFrequency can be any tenor, in this case 6M to signify twice yearly. Supported day count conventions.

    • The rollConvention field should count back from maturity, so in this case 22 for a bond maturing on 22 October, which means the coupon dates are 22 October and 22 April each year. We also recommend setting a businessDayConvention to determine what should happen if this is not a good business day. More information on roll and business day conventions. Note business days in LUSID are determined by holiday calendars.

    • The accrualDateAdjustment field is optional and defaults to Adjusted. This is suitable for European treasury bonds but for US treasury bonds we recommend Unadjusted.

    • The settleDays field in a flow convention is deprecated, and resetDays is ignored for fixed schedules. The scope and code fields can be ignored unless you are loading a flow convention from a library.

  • If you do not specify an exDividendConfiguration object, a bond has no ex-dividend period. More information.

  • If you do not specify a stubType, the first and last coupon periods are expected to be regular. More information.

Specifying a floating schedule

You must specify a floating schedule in the schedules array if a bond uses a floating rate to calculate coupon payments for at least part of its lifetime.

Note: You must load reference rate observations (fixings) into the LUSID Quote Store and provide a recipe that can locate these fixings in order to calculate accrued interest. More information.

For more information on the fields in a floating schedule, select FloatSchedule from the definition > schedules dropdown in the API documentation:

Note that floating rate bonds can be grouped into three categories based on the nature of the underlying reference index and how it is used in coupon payment calculations:

  • Category A: The reference rate is observed once per coupon period. The observation can be made in advance of the interest rate period, or towards the end 'in arrears'. Older and LIBOR-referencing bonds are examples of this group.

  • Category B: The reference rate is observed more than once per coupon period, up to daily for overnight indices like SONIA or SOFR, with the implication that the final coupon payment can be calculated only at the end of the period.

  • Category C: The reference rate is a compounded index like the compounding SOFR index. These indices are published daily and they remove the necessity to capture daily fixings needed for a coupon amount calculation. The compounding indices are built from the daily SOFR fixings so the final coupon amount can be calculated only at the end of the coupon period.

Note: For all three categories, the basic principle of specifying a floating rate schedule is the same, but note these important differences.

Consider the following example, of a single floating schedule for a bond in Category A above; note the fields specified below are the minimum required:

"schedules": [
  {
    "scheduleType": "FloatSchedule",
    "startDate": "2021-10-22T00:00:00+00:00",
    "maturityDate": "2028-10-22T00:00:00+00:00",
    "paymentCurrency": "GBP",
    "notional": 100000,
    "indexConventions": {
      "fixingReference": "GBP1YBMK",
      "publicationDayLag": 1,
      "paymentTenor": "12M",
      "dayCountConvention": "ActActIsma",
      "currency": "GBP"
    },
    "flowConventions": {
      "paymentFrequency": "6M",
      "dayCountConvention": "ActActIsma",
      "rollConvention": "22",
      "businessDayConvention": "Following",
      "currency": "GBP",
    },
  }
]

Note the following:

  • The scheduleType must be FloatSchedule.

  • The startDate should be the accrual start date; that is, the date from which interest is calculated.

  • The maximum maturityDate is 31 December 2140.

  • Unlike a fixed schedule, a couponRate is not required. Instead, you specify an indexConvention object that identifies a reference index to look up rates and fixings:

    • The fixingReference should be an intuitive string that enables LUSID to look up fixing(s) in the Quote Store each time an accrued interest calculation is required. More information.

    • The indexName defaults to INDEX but you can change this to a more intuitive description, for example LIBOR or SOFR.

    • The paymentTenor must be a tenor specifying the length of the interest rate period, in this case 12M. For OIS, specify 1D. Supported day count conventions.

  • The spread field defaults to 0 but you can specify a percentage as a decimal, for example 0.01 to record a spread of 1%.

  • The resetConvention field defaults to InAdvance. You can change this to InArrears.

  • The flowConventions object stores all the information necessary to determine coupon periods, accrued interest amounts and payment dates:

    • The paymentFrequency can be any tenor, in this case 6M to signify twice yearly. Supported day count conventions.

    • The rollConvention field should count back from maturity, so in this case 22 for a bond maturing on 22 October, which means the coupon dates are 22 October and 22 April each year. We also recommend setting a businessDayConvention to determine what should happen if this is not a good business day. More information on roll and business day conventions. Note business days in LUSID are determined by holiday calendars.

    • The resetDays field defaults to 0. You can specify a number of reset days if appropriate, and optionally one or more ResetCalendars to determine whether these are good business days.

    • The accrualDateAdjustment field is optional and defaults to Adjusted. This is suitable for European treasury bonds but for US treasury bonds we recommend Unadjusted.

    • The settleDays field in a flow convention is deprecated. The scope and code fields can be ignored unless you are loading a flow convention from a library.

  • If you do not specify an exDividendConfiguration object, a bond has no ex-dividend period. More information.

  • If you do not specify a stubType, the first and last coupon periods are expected to be regular. More information.

Note the following differences between the three categories:

Parent object in floating schedule

Field in floating schedule

Category A

Category B

Category C

N/A

resetConvention

Can be InAdvance (the default) or InArrears.

Must be InArrears.

Must be InArrears.

indexConventions

paymentTenor

Can be any tenor.

Must be 1D.

Must be 1D.

compounding
(More information)

compoundingMethod

Do not set.

Can be Averaging or Compounding.

Must be CompoundedIndex.

spreadCompoundingMethod

Can be Straight, Flat or SpreadExclusive for Compounding. Do not set for Averaging.

Must be SpreadExclusive.

averagingMethod

Can be Weighted (the default) or Unweighted for Averaging. Do not set for Compounding.

Do not set.

resetFrequency

Can be any tenor.

Must be 1D.

calculationShiftMethod

Can be None, Lookback, NoShift, ObservationPeriodShift or Lockout.

Can be None or ObservationPeriodShift.

Loading fixings into the LUSID Quote Store

You must load the correct number of fixings into the Quote Store and provide a recipe that enables LUSID to locate these fixings every time an accrued interest calculation is required (which is each time you ask LUSID to value a holding in the bond or generate cashflows):

Category

Number of fixings to load

A

One per coupon period. This should be at the start if resetConvention is set to InAdvance, or at the end if InArrears.

B

One per day.

C

Two per calculation period, one at the start of the coupon period and one at the effective at datetime of the valuation or cashflow generation request. Note this means if you make such a request every day you will need to load a fixing every day.

Consider the example of a Category C bond with a quarterly coupon period and the following fixingReference:

"indexConventions": {
   "fixingReference": "USD-1D-SOFRINDEX",
   ...
 }

The following call to the UpsertQuotes API loads two fixings for this bond for Q1 2023 into a MyFixings quote scope (in the URL), one on the first day of this period and one on the last day:

curl -X POST "https://<your-domain>.lusid.com/api/api/quotes/MyFixings"
  -H "Authorization: Bearer <your-API-access-token>"
  -H "Content-Type: application/json-patch+json"
  -d '{
    "Quote-0001": {
      "quoteId": {
        "quoteSeriesId": {
          "provider": "Lusid",
          "instrumentIdType": "RIC",
          "instrumentId": "USD-1D-SOFRINDEX",
          "quoteType": "Rate",
          "field": "mid"
        },
        "effectiveAt": "2023-01-01T17:00:00Z"
      },
      "metricValue": {
        "value": 1.111648, "unit": "rate"
      }
    },
    "Quote-0002": {
      "quoteId": {
        "quoteSeriesId": {
          "provider": "Lusid",
          "instrumentIdType": "RIC",
          "instrumentId": "USD-1D-SOFRINDEX",
          "quoteType": "Rate",
          "field": "mid"
        },
        "effectiveAt": "2023-03-31T17:00:00Z"
      },
      "metricValue": {
        "value": 1.123209, "unit": "rate"
      }
    }
  }'

For general information on loading quotes into the Quote Store, see this article. Note that a quote for a fixing:

  • Must have an instrumentIdType of RIC.

  • Must have an instrumentId that is the fixingReference specified in the bond's index convention.

  • Should be loaded into a quote scope that you only use for fixings, to avoid clashes.

  • Should have a provider of Lusid, a quoteType of Rate and a field of mid, to avoid validation errors.

When you value a holding in this bond or generate cashflows, you must provide a recipe with a market data rule that is able to locate these fixings, for example:

"market": {
  "marketRules": [
    {
      "key": "Quote.RIC.USD-1D-SOFRINDEX",
      "dataScope": "MyFixings",
      "supplier": "Lusid",
      "quoteType": "Rate",
      "field": "mid"
    },
    ...
  ]
},

Note the following:

  • The key must have a prefix of Quote.RIC. and a suffix of the bond's fixingReference.

  • The dataScope must match the quote scope into which fixings were loaded.

  • The other fields must match their respective quote fields exactly (values are case-sensitive).

Specifying a step schedule

You must specify a step schedule in the schedules array to record notional change for a sinkable fixed rate, floating rate or fixed-to-floating rate bond.

Note: You can specify a step schedule for a bond whose coupon rate or spread changes during its lifetime.

For more information on the fields in a step schedule, select StepSchedule from the definition > schedules dropdown in the API documentation:

Consider the example of a bond whose original notional value of 1000 changes four times according to the following published schedule:

  • 800 on 15 November 2022

  • 500 on 15 November 2023

  • 600 on 15 November 2024

  • 200 on 17 November 2025.

One step is required in the schedule for each date on which the notional value changes.

You can capture notional change using one of four 'level types'. The one to choose depends largely on the information available to you and the convenience of translating it into LUSID:

LevelType field

How to calculate step quantities

Step quantities for this example

How LUSID calculates notionals from step quantities

15 Nov 2022

15 Nov 2023

15 Nov 2024

17 Nov 2025

Absolute

step quantity = new notional

800

500

600

200

new notional = step quantity

AbsoluteShift

step quantity = current notional - new notional

200

300

-100

400

new notional = current notional - step quantity

Percentage

step quantity = ((current notional - new notional) / original notional) * 100

20

30

-10

40

new notional = current notional - (step quantity / 100) * original notional

AbsolutePercentage

step quantity = (new notional / original notional) * 100

80

50

60

20

new notional = (step quantity / 100) * original notional

A step schedule to capture the notional change in this example using a LevelType of Absolute would look like this:

"schedules": [
  {
    "scheduleType": "StepSchedule",
    "levelType": "Absolute",
    "stepScheduleType": "Notional",
    "steps": [
      {"date": "2022-11-15", "quantity": 800},
      {"date": "2023-11-15", "quantity": 500},
      {"date": "2024-11-15", "quantity": 600},
      {"date": "2025-11-17", "quantity": 400}
    ]
  }
]

Note the following:

  • The scheduleType must be StepSchedule.

  • The stepScheduleType in this case is Notional but it can be Coupon or Spread.

  • The steps array must contain one step object (consisting of a date and quantity) for each change to the notional; that is, one fewer than the total number of coupon periods with different notional amounts.

Specifying an optionality schedule

You must specify an optionality schedule in the schedules array to capture exercise dates for a callable or puttable fixed rate, floating rate or fixed-to-floating rate bond.

For more information on the fields in an optionality schedule, select OptionalitySchedule from the definition > schedules dropdown in the API documentation:

More information coming soon