Modelling inflation-linked bonds in LUSID

LUSID has an InflationLinkedBond instrument type designed to model various kinds of inflation-linked bond.

Kind of bond

LUSID instrument type

More information

Inflation-linked

InflationLinkedBond

Continue reading this article.

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

Bond

Read this dedicated article.

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

Read this dedicated article.

Mastering an instrument

There are numerous methods you can use to master an instrument of type InflationLinkedBond 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 InflationLinkedBond. For more information on these fields, select InflationLinkedBond from the definition dropdown in the API documentation:

For example, the following call to the UpsertInstruments API masters a bond in a custom instrument scope using a FIGI unique identifier. Note the fields specified below are the minimum required to master an instrument of type InflationLinkedBond:

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 '{
  "request_id_1": {
    "name": "UKT 0 â…œ 10/22/26",
    "identifiers": {"Figi": {"value": "BBG00ZF1T8P5"}},
    "definition": {
      "instrumentType": "InflationLinkedBond",
      "startDate": "2016-10-22T10:00:00.0000000+00:00",
      "maturityDate": "2026-10-22T10:00:00.0000000+00:00",
      "domCcy": "GBP",
      "couponRate": 0.00375,
      "principal": 1,
      "calculationType": "Standard",
      "flowConventions": {
        "currency": "GBP",
        "paymentFrequency": "6M",
        "dayCountConvention": "Actual365",
        "rollConvention": "22",
        "businessDayConvention": "NoAdjustment"
      },
      "inflationIndexConventions": {
        "inflationIndexName": "UKRPI",
        "currency": "GBP",
        "observationLag": "3M"
      }
    }
  }
}'

Note the following:

  • The instrumentType must be InflationLinkedBond.

  • The domCcy field must be an ISO 4217 currency code (and likewise all currency fields in nested objects).

  • 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 principal can be any number, but we advise setting it to 1 to unitise the security and specifying the face or purchase amount on the transaction.

  • The calculationType defaults to Standard. More information.

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

    • The paymentFrequency field 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. 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 inflation-linked bonds. The scope and code fields can be ignored unless you are loading a flow convention from a library.

  • The inflationIndexConventions object identifies a reference index to look up fixings:

    • The inflationIndexName 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 currency should be the same as the domCcy.

    • The observationLag must be a tenor with a unit of M for months, for example 3M.

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

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

Providing the request is successful, the response:

  • Confirms the globally-unique LUID for the instrument

  • Generates extra fields that are stored as part of the instrument definition and can be filtered on

  • Supplies default values for fields not explicitly specified in the request:

{
  "values": {
    "request_id_1": {
      "scope": "mycustominstrscope",
      "lusidInstrumentId": "LUID_00003DTU",
      "name": "UKT 0 â…œ 10/22/26",
      "identifiers": {
        "LusidInstrumentId": "LUID_00003DTU",
        "Figi": "BBG00ZF1T8P5"
      },
      "properties": [],
      "instrumentDefinition": {
        "startDate": "2016-10-22T10:00:00.0000000+00:00",
        "maturityDate": "2026-10-22T10:00:00.0000000+00:00",
        "flowConventions": {
          "currency": "GBP",
          "paymentFrequency": "6M",
          "dayCountConvention": "Actual365",
          "rollConvention": "22",
          "businessDayConvention": "None",
          "paymentCalendars": [],
          "resetCalendars": [],
          "settleDays": 0,
          "resetDays": 0,
          "leapDaysIncluded": true,
          "accrualDateAdjustment": "Adjusted",
          "accrualDayCountConvention": "Actual365"
        },
        "inflationIndexConventions": {
          "inflationIndexName": "UKRPI",
          "currency": "GBP",
          "observationLag": "3M",
          "inflationInterpolation": "Linear",
          "inflationFrequency": "1M",
          "inflationRollDay": 1
        },
        "couponRate": 0.00375,
        "principal": 1,
        "identifiers": {},
        "calculationType": "Standard",
        "indexPrecision": 5,
        "principalProtection": true,
        "stubType": "ShortFront",
        "roundingConventions": [],
        "instrumentType": "InflationLinkedBond"
      },
      "state": "Active",
      "assetClass": "Inflation",
      "domCcy": "GBP",
      "relationships": []
    }
  },
  ...
}

Loading fixings into the LUSID Quote Store and creating a recipe

You must load inflation fixings (or resets) 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). More information.

Consider the example of a bond linked to UKRPI with the following inflationIndexName in its instrument definition:

"inflationIndexConventions": {
  "inflationIndexName": "UKRPI",
   ...
}

The following call to the UpsertQuotes API loads two fixings for this bond for September and October 2023 into a MyFixings quote scope (in the URL):

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": "ClientInternal",
          "instrumentId": "UKRPI",
          "quoteType": "Index",
          "field": "mid"
        },
        "effectiveAt": "2023-09-01"
      },
      "metricValue": {
        "value": 378.4, "unit": "none"
      }
    },
    "Quote-0002": {
      "quoteId": {
        "quoteSeriesId": {
          "provider": "Lusid",
          "instrumentIdType": "ClientInternal",
          "instrumentId": "UKRPI",
          "quoteType": "Index",
          "field": "mid"
        },
        "effectiveAt": "2023-10-01"
      },
      "metricValue": {
        "value": 377.8, "unit": "none"
      }
    }
  }'

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

  • Must have an instrumentIdType of ClientInternal.

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

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

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

  • Should have a quoteType of Index except for certain kinds of Latin American bond.

  • Must have an effectiveAt date that is the first day of a month at midnight.

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": "Inflation.InflationIndex.UKRPI",
      "dataScope": "MyFixings",
      "supplier": "Lusid",
      "quoteType": "Index",
      "field": "mid"
    },
    ...
  ]
},

Note the following:

  • The key should have a prefix of Inflation.InflationIndex and a suffix of the bond's inflationIndexName except for certain kinds of Latin American bond.

  • 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).

Booking a transaction to establish a position

Once an instrument is mastered, you can book a transaction to record the acquisition of a quantity in a particular transaction portfolio. As mentioned above, we recommend unitising inflation-linked bond instruments and specifying the face or purchase amount on transactions.

For example, the following call to the BatchUpsertTransactions API acquires 75,000,000 units of a UK bond identified by its LUID:

curl -X POST 'https://<your-domain>.lusid.com/api/api/transactionportfolios/FixedIncome/UK/transactions/$batchUpsert?successMode=Partial&preserveProperties=true'
  -H 'Content-Type: application/json-patch+json'
  -H 'Authorization: Bearer <your-API-access-token>'
  -d '{
  "transactionRequest-1": {
    "transactionId": "uk_bond_purchase_001",
    "type": "StockIn",
    "instrumentIdentifiers": {"Instrument/default/LusidInstrumentId": "LUID_00003DTU"},
    "transactionDate": "2024-02-22T00:00:00.0000000+00:00",
    "settlementDate": "2024-02-25T00:00:00.0000000+00:00",
    "units": 75000000,
    "transactionPrice": {
      "price": 102,
      "type": "Price"
    },
    "totalConsideration": {
      "amount": 76594777.40,
      "currency": "GBP"
    },
    "properties": {
      "Transaction/default/BondInterest": {
        "key": "Transaction/default/BondInterest",
        "value": {
          "metricValue": {
            "value": 94777.40,
            "unit": ""
          }
        }
      }
    }
  },
}'

Note the following:

  • The type field invokes the built-in StockIn transaction type for simplicity, but you can create your own custom transaction type to determine the economic impact of bond purchases if you wish, perhaps using the BondInt and Side2WithoutBondInterest built-in sides designed for bond transactions.

  • The units field specifies the face or purchase amount.

  • The totalConsideration.amount field sets the settlement currency to GBP and specifies the total amount. This can be based upon the clean price but we recommend the dirty price; see below.

  • We recommend adding the BondInterest system property to bond transactions to record the amount of interest bought or sold. LUSID uses this property to calculate the clean price and makes it available on sides to help you customise the economic impact of bond transactions.

  • The transactionPrice object is optional and not used for cost calculations in LUSID. If the transaction currency is different to the settlement currency, specify the transactionCurrency and exchangeRate fields; otherwise LUSID considers the transaction and settlement currencies to be the same. If the transaction currency is different to the portfolio currency, add the TradeToPortfolioRate system property. More information.

You can confirm your position in a portfolio by calling the GetHoldings API; here the response is transformed to a table for clarity:

Valuing your position

Coming soon