You can typically model a bond contract as an instrument of type ComplexBond in LUSID. See all supported instruments.
Note LUSID has other instrument types better suited to certain kinds of bond:
Kind of bond | LUSID instrument type | More information |
|---|---|---|
Fixed rate with irregular coupons (other than the first) |
| Continue reading this article. |
Mortgage-backed security (MBS) | ||
Fixed rate with regular coupons (only the first can be irregular) |
| |
Inflation-linked |
|
Mastering an instrument
There are numerous tools 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, examine the ComplexBond schema or alternatively select ComplexBond from the definition dropdown in the UpsertInstruments API reference:
.png?sv=2022-11-02&spr=https&st=2025-11-02T03%3A24%3A01Z&se=2025-11-02T03%3A49%3A01Z&sr=c&sp=r&sig=%2FgF1JtNY78w9XuOfz0hlHJrHbjR96gvOPoFjPROX%2Fhc%3D)
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
instrumentTypemust beComplexBond.The
calculationTypedefaults toStandard. More information.If you omit the
roundingConventionsarray, no rounding conventions are applied at the instrument level.The
assetBackedandassetPoolIdentifierfields are only applicable to an MBS contract and can otherwise be omitted or set to empty strings.The
schedulesarray 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:A bond must have at least one
FixedScheduleor at least oneFloatingSchedule, except a fixed-to-floating rate bond which must have at least one of each.A bond can additionally have at least one
StepSchedule, except a Sinkable bond which must have at least one.A bond can additionally have at least one
OptionalitySchedule, except a Callable or Puttable bond which must have at least one.
Type of LUSID schedule
Type of bond
Rate fixing
Fixed rate
Floating rate
Fixed-to-floating rate
All
1 or more
0
1 or more
All
0
1 or more
1 or more
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
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, examine the FixedSchedule schema, or alternatively select FixedSchedule from the definition > schedules dropdown in the UpsertInstruments API reference:
.png?sv=2022-11-02&spr=https&st=2025-11-02T03%3A24%3A01Z&se=2025-11-02T03%3A49%3A01Z&sr=c&sp=r&sig=%2FgF1JtNY78w9XuOfz0hlHJrHbjR96gvOPoFjPROX%2Fhc%3D)
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": 1,
"couponRate": 0.025,
"flowConventions": {
"paymentFrequency": "6M",
"dayCountConvention": "ActActIsma",
"rollConvention": "22",
"currency": "GBP",
"paymentCalendars": [],
"resetCalendars": []
}
}
]Note the following:
The
scheduleTypemust beFixedSchedule.The
startDateshould be the accrual start date; that is, the date from which interest is calculated.The maximum
maturityDateis 31 December 2140.The
couponRateshould be expressed as a decimal rather than a percentage, so a bond paying:10% should have a
couponRateof0.12.5% should have a
couponRateof0.0250.375% should have a
couponRateof0.00375.
The
notionalcan be any number, but we advise setting it to1to unitise the security and specifying the face or purchase amount on the transaction.The
flowConventionsobject stores all the information necessary to determine coupon periods, accrued interest amounts and payment dates:The
paymentFrequencycan be any tenor, in this case6Mto signify twice yearly. Supported day count conventions.The
rollConventionfield should count back from maturity, so in this case22for a bond maturing on 22 October, which means the coupon dates are 22 October and 22 April each year. We also recommend setting abusinessDayConventionto 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
accrualDateAdjustmentfield is optional and defaults toAdjusted. This is suitable for European treasury bonds but for US treasury bonds we recommendUnadjusted.The
settleDaysfield in a flow convention is deprecated, andresetDaysis ignored for fixed schedules. Thescopeandcodefields can be ignored unless you are loading a flow convention from a library.
If the bond has an ex-dividend period, specify an
exDividendConfigurationobject, and within it (at a minimum) a number ofexDividendDays. Do not specify theexDividendDaysfield on its own (that is, outside this object).The
stubTypefield is deprecated so you should omit it but you can model irregular first or last coupon periods (or both) using multiple schedules with different start and maturity dates. See how to do this.
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, examine the FloatSchedule schema, or alternatively select FloatSchedule from the definition > schedules dropdown in the UpsertInstruments API reference:
.png?sv=2022-11-02&spr=https&st=2025-11-02T03%3A24%3A01Z&se=2025-11-02T03%3A49%3A01Z&sr=c&sp=r&sig=%2FgF1JtNY78w9XuOfz0hlHJrHbjR96gvOPoFjPROX%2Fhc%3D)
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": 1,
"indexConventions": {
"fixingReference": "GBP1YBMK",
"publicationDayLag": 1,
"paymentTenor": "12M",
"dayCountConvention": "ActActIsma",
"currency": "GBP"
},
"flowConventions": {
"paymentFrequency": "6M",
"dayCountConvention": "ActActIsma",
"rollConvention": "22",
"currency": "GBP",
"paymentCalendars": [],
"resetCalendars": []
}
}
]Note the following:
The
scheduleTypemust beFloatSchedule.The
startDateshould be the accrual start date; that is, the date from which interest is calculated.The maximum
maturityDateis 31 December 2140.Unlike a fixed schedule, a
couponRateis not required. Instead, you specify anindexConventionobject that identifies a reference index to look up rates and fixings:The
fixingReferenceshould 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
indexNamedefaults toINDEXbut you can change this to a more intuitive description, for exampleLIBORorSOFR.The
paymentTenormust be a tenor specifying the length of the interest rate period, in this case12M. For OIS, specify1D. Supported day count conventions.
The
spreadfield defaults to0but you can specify a percentage as a decimal, for example0.01to record a spread of 1%.The
resetConventionfield defaults toInAdvance. You can change this toInArrears.The
flowConventionsobject stores all the information necessary to determine coupon periods, accrued interest amounts and payment dates:The
paymentFrequencycan be any tenor, in this case6Mto signify twice yearly. Supported day count conventions.The
rollConventionfield should count back from maturity, so in this case22for a bond maturing on 22 October, which means the coupon dates are 22 October and 22 April each year. We also recommend setting abusinessDayConventionto 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
resetDaysfield defaults to0. You can specify a number of reset days if appropriate, and optionally one or moreResetCalendarsto determine whether these are good business days.The
accrualDateAdjustmentfield is optional and defaults toAdjusted. This is suitable for European treasury bonds but for US treasury bonds we recommendUnadjusted.The
settleDaysfield in a flow convention is deprecated. Thescopeandcodefields can be ignored unless you are loading a flow convention from a library.
If the bond has an ex-dividend period, specify an
exDividendConfigurationobject, and within it (at a minimum) a number ofexDividendDays. Do not specify theexDividendDaysfield on its own (that is, outside this object).The
stubTypefield is deprecated so you should omit it but you can model irregular first or last coupon periods (or both) using multiple schedules with different start and maturity dates. See how to do this.
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 |
| Can be | Must be | Must be |
|
| Can be any tenor. | Must be | Must be |
|
| Do not set. | Can be | Must be |
| Can be | Must be | ||
| Can be | Do not set. | ||
| Can be any tenor. | Must be | ||
| Can be | Can be |
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 |
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
instrumentIdTypeofRIC.Must have an
instrumentIdthat is thefixingReferencespecified 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
providerofLusid, aquoteTypeofRateand afieldofmid, 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
keymust have a prefix ofQuote.RIC.and a suffix of the bond'sfixingReference.The
dataScopemust 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 at least one step schedule in the schedules array to record notional change for a Sinkable bond, in addition to its FixedSchedule and/or FloatingSchedule. You can optionally specify a step schedule for any other type of bond, perhaps if the coupon rate or spread changes during its lifetime.
For more information on the fields, examine the StepSchedule schema, or alternatively select StepSchedule from the definition > schedules dropdown in the UpsertInstruments API reference:
.png?sv=2022-11-02&spr=https&st=2025-11-02T03%3A24%3A01Z&se=2025-11-02T03%3A49%3A01Z&sr=c&sp=r&sig=%2FgF1JtNY78w9XuOfz0hlHJrHbjR96gvOPoFjPROX%2Fhc%3D)
Consider the example of a Sinakable 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:
| 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 | |||
| step quantity = new notional | 800 | 500 | 600 | 200 | new notional = step quantity |
| step quantity = current notional - new notional | 200 | 300 | -100 | 400 | new notional = current notional - step quantity |
| step quantity = ((current notional - new notional) / original notional) * 100 | 20 | 30 | -10 | 40 | new notional = current notional - (step quantity / 100) * original notional |
| 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
scheduleTypemust beStepSchedule.The
stepScheduleTypein this case isNotionalbut it can beCouponorSpread.The
stepsarray must contain one step object (consisting of adateandquantity) 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 at least one optionality schedule in the schedules array to capture exercise dates for a Callable or Puttable bond, in addition to its FixedSchedule and/or FloatingSchedule. You can optionally specify an optionality schedule for any other type of bond.
For more information on the fields, examine the OptionalitySchedule schema, or alternatively select OptionalitySchedule from the definition > schedules dropdown in the UpsertInstruments API reference:
.png?sv=2022-11-02&spr=https&st=2025-11-02T03%3A24%3A01Z&se=2025-11-02T03%3A49%3A01Z&sr=c&sp=r&sig=%2FgF1JtNY78w9XuOfz0hlHJrHbjR96gvOPoFjPROX%2Fhc%3D)
More information coming soon
Appendix A: Understanding calculation types
By default, a ComplexBond has a calculationType of Standard, which means a coupon is calculated as rate * units / frequency. You can change this to:
DayCountCouponto calculate a coupon asrate * units * yearfraction(accrualStart, accrualEnd).BrazilFixedCouponto apply the Brazilian compounding rule to the rate and pay semi-annually (for example, if the rate is 10% on the instrument then this is converted to semi-annual payments that compound annually to 10%, instead of 5% twice). The formula isadjustedRate = (1+r)^1/2 - 1, so 10% is approximately 4.88% semi-annually.
Please do not specify NoCalculationFloater or StandardWithCappedAccruedInterest at this time.
Appendix B: Modelling irregular first or last coupon periods
Consider the example of a fixed-rate bond with a coupon paid on the 20th of each month but an irregular first coupon period of fifteen days. To model this, we can set two FixedSchedule objects in the schedules array; note they must be specified in this order:
From the start date of the bond (5 January 2025) to the first coupon date (20 January 2025). Note the
maturityDateis set to the first coupon date and the flow convention has apaymentFrequencyof1TFrom the first coupon date (20 January 2025) to the maturity date of the bond (20 December 2026). Note the
startDateis set to the first coupon date and the flow convention has apaymentFrequencyof1M
"schedules": [
{
"scheduleType": "FixedSchedule",
"startDate": "2024-01-05T00:00:00+00:00",
"maturityDate": "2024-01-20T00:00:00+00:00",
"paymentCurrency": "GBP",
"notional": 1,
"couponRate": 0.05,
"flowConventions": {
"paymentFrequency": "1T",
"dayCountConvention": "Actual365",
"rollConvention": "20",
"currency": "GBP",
"paymentCalendars": [],
"resetCalendars": []
}
},
{
"scheduleType": "FixedSchedule",
"startDate": "2024-01-20T00:00:00+00:00",
"maturityDate": "2026-12-20T00:00:00+00:00",
"paymentCurrency": "GBP",
"notional": 1,
"couponRate": 0.05,
"flowConventions": {
"paymentFrequency": "1M",
"dayCountConvention": "Actual365",
"rollConvention": "20",
"currency": "GBP",
"paymentCalendars": [],
"resetCalendars": []
}
}
]