Modelling vanilla fixed-rate bonds in LUSID

Prev Next

You can model a simple fixed-rate bond as an instrument of type Bond in LUSID. See all supported instruments.

Kind of bond

LUSID instrument type

More information

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

Bond

Continue reading this article.

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

ComplexBond

Read this dedicated article.

Mortgage-backed security (MBS)

Read this dedicated article.

PIK

Dedicated article coming soon

Inflation-linked

InflationLinkedBond

Read this dedicated article.

The rest of this article explains the lifecycle of a vanilla government bond issue with a regular coupon schedule and fixed principal. There is also an accompanying Jupyter Notebook that further demonstrates many of the operations and concepts.

Mastering an instrument

Note: You should use the ComplexBond instrument type if your coupon schedule is irregular. The Bond instrument type can only accept an irregular first coupon period; all other coupons must be regular.

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

For example, the following call to the UpsertInstruments API masters a UK gilt 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 Bond:

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": "BBG00ZF1T9P5"}},
    "definition": {
      "instrumentType": "Bond",
      "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,
      "flowConventions": {
        "currency": "GBP",
        "paymentFrequency": "6M",
        "dayCountConvention": "Actual365",
        "rollConvention": "22",
        "businessDayConvention": "Following"
      },
      "tradingConventions": {
        "priceScaleFactor": 100
      }
    }
  }
}'

Note the following:

  • The instrumentType must be Bond.

  • 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 flowConventions object stores all the information necessary to determine payment schedules.

  • The tradingConventions.priceScaleFactor field is set to 100 so bond prices can be expressed as a percentage of par without scaling every quote loaded into LUSID.

  • If you omit the exDividendConfiguration object, a bond has no ex-dividend period.

  • If you omit the roundingConventions array, no rounding conventions are applied at the instrument level.

  • If the first coupon payment is irregular, specify the firstCouponPayDate field.

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_00003DS1",
      "name": "UKT 0 ⅜ 10/22/26",
      "identifiers": {
        "LusidInstrumentId": "LUID_00003DS1",
        "Figi": "BBG00ZF1T9P5"
      },
      "properties": [],
      "instrumentDefinition": {
        "startDate": "2016-10-22T10:00:00.0000000+00:00",
        "maturityDate": "2026-10-22T10:00:00.0000000+00:00",
        "domCcy": "GBP",
        "flowConventions": {
          "currency": "GBP",
          "paymentFrequency": "6M",
          "dayCountConvention": "Actual365",
          "rollConvention": "22",
          "businessDayConvention": "Following",
          "paymentCalendars": [],
          "resetCalendars": [],
          "settleDays": 0,
          "resetDays": 0,
          "leapDaysIncluded": true,
          "accrualDateAdjustment": "Adjusted",
        },
        "principal": 1,
        "couponRate": 0.00375,
        "identifiers": {},
        "calculationType": "Standard",
        "roundingConventions": [],
        "tradingConventions": {
          "priceScaleFactor": 100,
          "minimumOrderSize": 0,
          "minimumOrderIncrement": 0
        },
        "instrumentType": "Bond"
      },
      "state": "Active",
      "assetClass": "Credit",
      "domCcy": "GBP",
      "relationships": []
    }
  },
  ...
}

Understanding how LUSID determines coupon periods

LUSID uses the information in the flow convention to build a payment schedule. It implicitly assumes the last coupon period is regular, and counts back from the maturity date according to the payment frequency.

Note: Any bond with an irregular last coupon period must be mastered using the ComplexBond instrument type. More information.

If the bond has an irregular first coupon period you can set the optional firstCouponPayDate field to create either a long or short first coupon period.

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 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 gilt uniquely 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_gilt_purchase_001",
    "type": "BuyBond",
    "instrumentIdentifiers": {"Instrument/default/LusidInstrumentId": "LUID_00003DS1"},
    "transactionDate": "2024-02-22T00:00:00.0000000+00:00",
    "settlementDate": "2024-02-25T00:00:00.0000000+00:00",
    "units": 75000000,
    "transactionPrice": {
      "price": 102,
      "type": "CleanPrice"
    },
    "totalConsideration": {
      "amount": 0,
      "currency": "GBP"
    }
  }
}'

Note the following:

  • The type field invokes a custom BuyBond transaction type to confer a particular economic impact (see below).

  • The units field specifies the face or purchase amount.

  • The transactionPrice object records the clean market price (not including bond interest)as a percentage of par. This is used by LUSID to automatically calculate the gross consideration (see below).

  • The totalConsideration object:

    • Sets the settlement currency to GBP.

    • Specifies a cost of 0 to enable LUSID to automatically derive the total consideration (see below).

  • LUSID has a transaction fee engine to help calculate fees and either capitalise or expense them.

Note: This example assumes the transaction, settlement and portfolio currencies are the same. If not, you can specify exchange rates.

You might create a BuyBond transaction type as follows:

curl -X PUT 'https://<your-domain>.lusid.com/api/api/transactionconfiguration/types/default/BuyBond?scope=default'
  -H 'Content-Type: application/json-patch+json'
  -H 'Authorization: Bearer <your-API-access-token>'
  -d '{
  "aliases": [
    {
      "type": "BuyBond",
      "description": "Transaction type for bond purchases",
      "transactionClass": "Trading",
      "transactionRoles": "LongLonger",
      "isDefault": false
    }
  ],
  "movements": [
    {
      "name": "Increase units of security",
      "movementTypes": "StockMovement",
      "side": "Side1",
      "direction": 1
    },
    {
      "name": "Decrease cash balance",
      "movementTypes": "CashCommitment",
      "side": "Side2",
      "direction": -1
    },
    {
      "name": "Report bond interest bought as a flow of value out of the security",
      "movementTypes": "Carry",
      "side": "BondInterest",
      "direction": -1
    }
  ],
  "calculations": [
    {
      "type": "Txn:BondInterest"
    },
    {
      "type": "Txn:GrossConsideration"
    },
    {
      "type": "DeriveTotalConsideration",
      "formula": "Txn:GrossConsideration + Txn:TotalFees"
    }
  ]
}'

Note the following:

  • The Txn:BondInterest calculation automatically calculates the amount of bond interest bought or sold and stores the result in the Transaction/default/BondInterest system property, available for use in sides.

  • If a clean price is specified, the Txn:GrossConsideration calculation automatically calculates gross consideration according to the formula (price * units) + Txn:BondInterest, and stores the result in the Transaction/default/GrossConsideration system property.

  • The DeriveTotalConsideration calculation automatically calculates total consideration according to the given, user-defined formula, which in this case sums gross consideration and total fees; this is stored as the totalConsideration.amount of the transaction.

  • The StockMovement uses the built-in Side1 side to establish a holding in the instrument with the specified number of units at a cost derived from the total consideration.

  • The CashCommitment movement uses the built-in Side2 to decrease the instrument currency holding by the total consideration.

  • The Carry movement uses a custom BondInterest side to record a flow of value out of the instrument equivalent to the interest purchased.

You might create a BondInterest custom side as follows:

curl -X PUT 'https://<your-domain>.lusid.com/api/api/transactionconfiguration/sides/BondInterest?scope=default'
  -H 'Content-Type: application/json-patch+json'
  -H 'Authorization: Bearer <your-API-access-token>'
  -d '{
  "security": "Txn:LusidInstrumentId",
  "currency": "Txn:TradeCurrency",
  "rate": "Txn:TradeToPortfolioRate",
  "units": "Txn:Units",
  "amount": "Txn:BondInterest"
}'

Note this is the same as Side1 except the amount field is set to Txn:BondInterest instead of Txn:TradeAmount.

Confirming positions

We can generate a holdings report for the settlement date to see the impact of the transaction on security and cash holdings:

Auditing LUSID’s calculations of transaction amounts

We can examine output transactions automatically generated by LUSID up to the settlement date to audit calculated amounts:

Valuing your position

To value your position, work through our valuation checklist.

Note: We no longer recommend using the ConstantTimeValueOfMoney or SimpleStatic pricing models. Please change the pricing model to one of the alternatives below.

Available pricing model

Instrument types

Market data required

Notes

BondLookupPricer (recommended)

Bond, ComplexBond, InflationLinkedBond

Market price for instrument in Quote Store with a quoteType of Price or DirtyPrice.

In addition, to calculate accrual for a:

A model option must be set to enable risk metrics, and extra complex market data is required.

Discounting

Bond, ComplexBond

Discount factor curve in Complex Market Data (CMD) Store with a marketAsset of <Ccy>/<Ccy>OIS.

In addition, for a ComplexBond with a floating schedule, an interest rate projection curve in CMD Store with a marketAsset of <Ccy>/<Tenor>/<Index>.

A superset of risk metrics is available for HullWhite1F.

HullWhite1F

Callable or puttable ComplexBond

InflationForward

InflationLinkedBond

  1. Inflation curve in CMD Store.

  2. Discount factor curve in CMD Store with a marketAsset of <Ccy>/<Ccy>OIS.

Only for use with risk metrics, not for calculating PV.

OverrideOnlyPricer

Bond, ComplexBond, InflationLinkedBond

N/A

Only for use with external valuation results from the Structured Result Store.

LUSID can report many hundreds of metrics in a valuation report. Note the following:

Monitoring the lifecycle of the instrument

A Bond is tightly integrated into LUSID’s instrument event framework. To enable this for your domain, you must:

  1. Register a recipe with every portfolio holding a Bond.

  2. Create transaction types to determine the economic impact of the transactions automatically generated by Bond events.

Recommended reading: Handling instrument events for bonds

The following events are automatically emitted by LUSID but there are many more available to manually load to impact the lifecycle, such as EarlyRedemptionEvent and BondDefaultEvent.

Instrument event

Event emission criteria

Effect of default transaction template

BondCouponEvent

Automatically emitted by LUSID each time a bond coupon is due.

One transaction is automatically generated for a cash amount.

BondPrincipalEvent

Automatically emitted by LUSID on the maturity date, though processed before MaturityEvent.

One transaction is automatically generated for a cash amount.

MaturityEvent

Automatically emitted by LUSID on the maturity date.

One transaction is automatically generated for all units at zero cost to reduce the holding to zero.