--- title: "Modelling interest rate swaps in LUSID" slug: "modelling-interest-rate-swaps-in-lusid" tags: ["IRS"] updated: 2026-10-01T11:42:18Z published: 2026-10-01T11:42:18Z canonical: "support.lusid.com/modelling-interest-rate-swaps-in-lusid" --- > ## Documentation Index > Fetch the complete documentation index at: https://support.lusid.com/llms.txt > Use this file to discover all available pages before exploring further. # Modelling interest rate swaps in LUSID You can model certain interest rate swap contracts as instruments of type `InterestRateSwap` in LUSID. [See all supported instruments](/v1/docs/what-instruments-does-lusid-support). - Vanilla swaps with a fixed and a floating leg, or two fixed or two floating legs if required. - Cross-currency swaps with any combination of fixed/floating legs and notional exchange. - Basis swaps with floating legs referencing different indices or tenors. - Amortising swaps with any combination of fixed/floating legs and decreasing notionals. This article explains how to use the LUSID API to first master an instrument and then load a transaction: - You can simplify setup by loading a transaction in the web app and have LUSID [master the instrument for you](/v1/docs/modelling-interest-rate-swaps-in-lusid#appendix-using-the-lusid-web-app-to-simplify-repurchase-agreements). - If you enable instrument events you must create transaction types to [handle those events](/v1/docs/handling-instrument-events-for-interest-rate-swaps). - For an implementation using the Python SDK see [this Jupyter Notebook](https://github.com/finbourne/finbourne-notebooks/blob/main/V3/features/instrument-events/Interest-rate-swap-and-instrument-events.ipynb). ## Mastering an instrument There are [numerous tools](/v1/docs/how-do-i-master-an-instrument) you can use to master an `InterestRateSwap` 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](/v1/docs/understanding-instrument-identifiers) of `identifiers`, and the facility to [store extra information](/v1/docs/properties) as `properties`: ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image-UC6SW9YY.png?sv=2026-02-06&spr=https&st=2026-11-09T06%3A00%3A24Z&se=2026-11-09T07%3A00%3A24Z&sr=c&sp=r&sig=B61nxufv7dTRkCk8sGcCFl7Kmp3YM5DiawuMACjXww0%3D) ### Understanding the economic definition of an interest rate swap Fields in the economic `definition` object are specific to an `InterestRateSwap`. The composition of this object is dependent on the kind of swap you want to master. Consider the following call to the [UpsertInstruments](https://www.lusid.com/docs/api/lusid/endpoints/instruments/UpsertInstruments) API; note this is actually a no-op request, since the `legs` array must be populated: REST APIPython SDK ```json curl -X POST "https://.lusid.com/api/api/instruments?scope=mycustominstrscope"   -H "Content-Type: application/json-patch+json"   -H "Authorization: Bearer "   -d '{  "upsert_request_1": {    "name": "IRS-15Jan30",    "identifiers": {      "ClientInternal": {"value": "IRS-15Jan30"}    },    "definition": {      "instrumentType": "InterestRateSwap",      "startDate": "2025-01-15T00:00:00Z",      "maturityDate": "2030-01-15T00:00:00Z",      "legs": [],      "additionalPayments": []    }  } }' ``` The economic definition of the entire swap in this article is given in the example below, since it is not always clear how API schemas map to SDK models: ```python instruments_api = lusid_api_factory.build(lusid.api.InstrumentsApi) instrument_request = {    "upsert_request_1": lusid.models.InstrumentDefinition(        name = "IRS-15Jan30",        identifiers = {"ClientInternal": lusid.models.InstrumentIdValue(value = "IRS-15Jan30")},        definition = lusid.models.InterestRateSwap(            instrumentType = "InterestRateSwap",            startDate = "2025-01-15T00:00:00Z",            maturityDate = "2030-01-15T00:00:00Z",            legs = [                lusid.models.FixedLeg(                    instrumentType = "FixedLeg",                    startDate = "2025-01-15T00:00:00Z",                    maturityDate = "2030-01-15T00:00:00Z",                    notional = 1000000,                    legDefinition = lusid.models.LegDefinition(                        notionalExchangeType = "None",                        payReceive = "Receive",                        rateOrSpread = 0.05,                        stubType = "None",                        conventions=lm.FlowConventions(                            currency = "USD",                            payment_frequency = "12M",                            day_count_convention = "Actual365",                            roll_convention = "15",                            business_day_convention = "Following",                            payment_calendars = [],                            reset_calendars = []                        )                    )                ),                lusid.models.FloatingLeg(                    instrumentType = "FloatingLeg",                    startDate = "2025-01-15T00:00:00Z",                    maturityDate = "2030-01-15T00:00:00Z",                    notional = 1000000,                    legDefinition = lusid.models.LegDefinition(                        notionalExchangeType = "None",                        payReceive = "Pay",                        rateOrSpread = 0,                        stubType = "None",                        conventions=lusid.models.FlowConventions(                            currency = "USD",                            payment_frequency = "12M",                            day_count_convention = "Actual365",                            roll_convention = "15",                            business_day_convention = "Following",                            payment_calendars = [],                            reset_calendars = []                        ),                        index_convention = lusid.models.IndexConvention(                            fixing_reference = "USD-SOFR-COMPOUNDED-INDEX",                            publication_day_lag = 0,                            payment_tenor = '1D',                            day_count_convention= 'Actual365',                            currency = 'USD',                            index_name = 'SOFRINDEX'                        ),                        resetConvention="InArrears",                        compounding=lusid.models.Compounding(                            compoundingMethod="CompoundedIndex",                            spreadCompoundingMethod="SpreadExclusive",                            resetFrequency="1D"                        )                    )                )            ]        )    ) } try:    instrument_response = instruments_api.upsert_instruments(        request_body = instrument_request,        scope = "mycustominstrscope"    ) except lusid.ApiException as e:    print(e) ``` For information on every field, examine the [InterestRateSwap](https://www.lusid.com/docs/api/lusid/schemas/InterestRateSwap/) schema. Note in particular the following: - The `instrumentType` must be `InterestRateSwap`. - The `startDate` is typically the transaction date but can be before or after if required. - The `legs` array **must** contain two legs, one with `payReceive` set to `Pay` and the other to `Receive`. These can both be a fixed leg, both be a [floating leg](/v1/docs/modelling-interest-rate-swaps-in-lusid#specifying-a-floating-leg), or one of each. The order does not matter. - You can record `additionalPayments` if you wish but this has no downstream impact at present. ### Specifying a fixed leg Consider the following example of a fixed leg set to receive 5% on a £100,000 notional once a year on the 15th: ```json {  "instrumentType": "FixedLeg",  "startDate": "2025-01-15T00:00:00Z",  "maturityDate": "2030-01-15T00:00:00Z",  "notional": 100000,  "legDefinition": {    "notionalExchangeType": "None",    "payReceive": "Receive",    "rateOrSpread": 0.05,    "stubType": "None",    "conventions": {      "currency": "USD",      "paymentFrequency": "12M",      "dayCountConvention": "Actual365",      "rollConvention": "15",      "businessDayConvention": "Following",      "paymentCalendars": [],      "resetCalendars": []    }  } } ``` For information on all fields, examine the [FixedLeg](https://www.lusid.com/docs/api/lusid/schemas/FixedLeg/) schema. Note in particular the following: - `payReceive` is set to `Receive`. One leg **must** receive payment. - For a fixed leg, `rateOrSpread` refers to the fixed interest rate expressed as a decimal rather than a percentage, so: - 10% should be specified as `0.1` - 2.5% should be specified as `0.02` - 0.375% should be specified as `0.00375`. - `startDate` and `maturityDate` are typically the same as the instrument itself. - `notional` is set to the full amount and subsequent [transactions](/v1/docs/modelling-interest-rate-swaps-in-lusid#booking-a-transaction-to-establish-a-position) are for a single unit, but you can reverse this if desired. - `notionalExchangeType` is set to `None` to *not* swap notionals, but this could be `Initial`, `Final` or `Both` for a cross-currency swap. - `conventions` specifies all the information necessary to determine [payment schedules](/v1/docs/flow-conventions-and-index-conventions#specifying-a-flow-convention). In particular, `rollConvention` specifies the payment day of the month. ### Specifying a floating leg You must first decide which category of reference index the floating leg should observe: | Category | Explanation | Example index | | --- | --- | --- | | A | The reference rate is observed once per interest rate period. The observation can be made in advance of a period, or towards the end 'in arrears'. | LIBOR | | B | The reference rate is observed more than once per period (up to daily for overnight indices), with the implication that the final payment amount can be calculated only at the end of a period | SOFR, SONIA | | C | The reference rate is a compounded index. These indices are published daily and they remove the necessity to capture daily fixings needed for a calculation. The compounding indices are built from daily fixings so the final payment amount can only be calculated at the end of a period. | SOFRINDEX | Consider the following example of a floating leg observing the SOFR compounded index that pays once a year on the 15th: ```json {  "instrumentType": "FloatingLeg",  "startDate": "2025-01-15T00:00:00Z",  "maturityDate": "2030-01-15T00:00:00Z",  "notional": 100000,  "legDefinition": {    "notionalExchangeType": "None",    "payReceive": "Pay",    "rateOrSpread": 0,    "stubType": "None",    "conventions": {      "currency": "USD",      "paymentFrequency": "12M",      "dayCountConvention": "Actual365",      "rollConvention": "15",      "businessDayConvention": "Following",      "paymentCalendars": [],      "resetCalendars": []    },    "indexConvention": {      "fixingReference": "USD-SOFR-COMPOUNDED-INDEX",      "publicationDayLag": 0,      "paymentTenor": "1D",      "dayCountConvention": "Actual365",      "currency": "USD",      "indexName": "SOFRINDEX"    },    "resetConvention": "InArrears",    "compounding": {      "compoundingMethod": "CompoundedIndex",      "spreadCompoundingMethod": "SpreadExclusive",      "resetFrequency": "1D"    }  } } ``` For information on all fields, examine the [FloatingLeg](https://www.lusid.com/docs/api/lusid/schemas/FloatingLeg/) schema. Some fields are common to all swaps; note in particular the following: - `payReceive` is set to `Pay`. One leg in a swap **must** pay out. - For a floating leg, `rateOrSpread` refers to a spread on the index, if any. For example, a spread of 50bps should be specified as `0.005`. - For a vanilla swap, `notional`, `notionalExchangeType`, `stubType` and `conventions` are typically the same as the fixed leg, but can have different values if required (ie. for a cross-currency swap). - `indexConventions` specifies all the information necessary to calculate [accrued interest amounts](/v1/docs/flow-conventions-and-index-conventions#specifying-an-index-convention) from observed rates in a reference index. Other fields in a floating leg definition are dependent on the category of the reference index chosen: | [LegDefinition](https://www.lusid.com/docs/api/lusid/schemas/LegDefinition/) field | **Category A** | **Category B** | **Category C** | | --- | --- | --- | --- | | `resetConvention` | Can be `InAdvance` (the default) or `InArrears`. | Must be `InArrears`. | Must be `InArrears`. | | `indexConvention.paymentTenor` | Can be any [tenor](/v1/docs/specifying-tenors-in-lusid). | Must be `1D`. | Must be `1D`. | | `compounding.compoundingMethod` ([more information](/v1/docs/how-does-lusid-calculate-compound-interest)) | Do not set. | Can be `Averaging` or `Compounding`. | Must be `CompoundedIndex`. | | `compounding.spreadCompoundingMethod` | Can be `Straight`, `Flat` or `SpreadExclusive` for `Compounding`. Do not set for `Averaging`. | Must be `SpreadExclusive`. | | `compounding.averagingMethod` | Can be `Weighted` (the default) or `Unweighted` for `Averaging`. Do not set for `Compounding`. | Do not set. | | `compounding.resetFrequency` | Can be any [tenor](/v1/docs/specifying-tenors-in-lusid). | Must be `1D`. | | `compounding.calculationShiftMethod` | Can be `Lookback`, `NoShift`, `ObservationPeriodShift` or `Lockout`. | Can be `NoShift`or `ObservationPeriodShift`. | ### Examining the response Providing the request is successful, the response: - Confirms the globally-unique [LUID](/v1/docs/what-is-a-lusid-instrument-id-or-luid) for the instrument; - Provides version information; - Generates extra fields that are stored as part of the instrument definition and can be [filtered on](/v1/docs/filtering-information-retrieved-from-lusid); - Supplies default values for fields not explicitly specified in the request: ```json {  "values": {    "upsert_request_1": {      "scope": "mycustominstrumentscope",      "lusidInstrumentId": "LUID_00003H0L",      "version": {        "effectiveFrom": "0001-01-01T00:00:00.0000000+00:00",        "asAtDate": "2026-05-13T11:17:30.4381300+00:00",        "asAtCreated": "2026-05-13T11:17:30.4381300+00:00",        "userIdCreated": "00u91lo2d7X42sdse2p7",        "requestIdCreated": "2026051311-1544deac41e442849923be5091f8a00d",        "reasonCreated": "",        "asAtModified": "2026-05-13T11:17:30.4381300+00:00",        "userIdModified": "00u91lo2d7X42sdse2p7",        "requestIdModified": "2026051311-1544deac41e442849923be5091f8a00d",        "reasonModified": "",        "asAtVersionNumber": 1,        "entityUniqueId": "60a46c85-58cb-40cc-9840-affb724dc72d"      },      "name": "IRS-15Jan30",      "identifiers": {        "ClientInternal": "IRS-15Jan30",        "LusidInstrumentId": "LUID_00003H0L"      },      "properties": [],      "instrumentDefinition": {        "startDate": "2025-01-15T00:00:00.0000000+00:00",        "maturityDate": "2030-01-15T00:00:00.0000000+00:00",        "isNonDeliverable": false,        "legs": [          {            "startDate": "2025-01-15T00:00:00.0000000+00:00",            "maturityDate": "2030-01-15T00:00:00.0000000+00:00",            "legDefinition": {              "conventions": {                "currency": "USD",                "paymentFrequency": "12M",                "dayCountConvention": "Actual365",                "rollConvention": "15",                "paymentCalendars": [],                "resetCalendars": [],                "settleDays": 0,                "resetDays": 0,                "leapDaysIncluded": true,                "accrualDateAdjustment": "Adjusted",                "businessDayConvention": "F",                "accrualDayCountConvention": "Actual365"              },              "notionalExchangeType": "None",              "payReceive": "Receive",              "rateOrSpread": 0.05,              "resetConvention": "InAdvance",              "stubType": "None",              "firstCouponType": "ProRata",              "lastCouponType": "ProRata",              "intermediateNotionalExchange": false            },            "notional": 100000,            "overrides": {},            "instrumentType": "FixedLeg"          },          {            "startDate": "2025-01-15T00:00:00.0000000+00:00",            "maturityDate": "2030-01-15T00:00:00.0000000+00:00",            "legDefinition": {              "conventions": {                "currency": "USD",                "paymentFrequency": "12M",                "dayCountConvention": "Actual365",                "rollConvention": "15",                "paymentCalendars": [],                "resetCalendars": [],                "settleDays": 0,                "resetDays": 0,                "leapDaysIncluded": true,                "accrualDateAdjustment": "Adjusted",                "businessDayConvention": "F",                "accrualDayCountConvention": "Actual365"              },              "indexConvention": {                "fixingReference": "USD-SOFR-COMPOUNDED-INDEX",                "publicationDayLag": 0,                "paymentTenor": "1D",                "dayCountConvention": "Actual365",                "currency": "USD",                "indexName": "SOFRINDEX"              },              "notionalExchangeType": "None",              "payReceive": "Pay",              "rateOrSpread": 0,              "resetConvention": "InArrears",              "stubType": "None",              "compounding": {                "calculationShiftMethod": "NoShift",                "compoundingMethod": "CompoundedIndex",                "resetFrequency": "1D",                "shift": 0,                "spreadCompoundingMethod": "SpreadExclusive"              },              "firstCouponType": "ProRata",              "lastCouponType": "ProRata",              "intermediateNotionalExchange": false            },            "notional": 100000,            "overrides": {},            "instrumentType": "FloatingLeg"          }        ],        "additionalPayments": [],        "instrumentType": "InterestRateSwap"      },      "state": "Active",      "assetClass": "InterestRates",      "domCcy": "USD",      "relationships": [],      "dataModelMembership": {        "membership": []      }    }  },  "staged": {},  "failed": {},  ... } ``` ## Providing interest rate fixings If an `InterestRateSwap` has a floating leg, you must [load the correct number of fixings](/v1/docs/how-do-i-load-an-interest-rate-fixing-into-the-lusid-quote-store) into the Quote Store and provide a recipe that enables LUSID to locate them when an accrued interest calculation is required (which is every time you ask LUSID to value a holding or generate cashflows): | **Category** | **Number of fixings to load** | | --- | --- | | A | One per interest rate 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 interest rate period and one at the effective 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 swap with the following `fixingReference`: ```json "indexConvention": {  "fixingReference": "USD-SOFR-COMPOUNDED-INDEX",  ... } ``` The following call to the [UpsertQuotes](https://www.lusid.com/docs/api#operation/UpsertQuotes) API loads two fixings for this index into a particular quote scope: ```json curl -X POST "https://.lusid.com/api/api/quotes/MySOFRFixings"  -H "Authorization: Bearer "  -H "Content-Type: application/json-patch+json"  -d '{    "Quote-0001": {      "quoteId": {        "quoteSeriesId": {          "provider": "Lusid",          "instrumentIdType": "RIC",          "instrumentId": "USD-SOFR-COMPOUNDED-INDEX",          "quoteType": "Index",          "field": "mid"        },        "effectiveAt": "2025-01-15"      },      "metricValue": {        "value": 1.17692687, "unit": "none"      },      "scaleFactor": 100    },    "Quote-0002": {      "quoteId": {        "quoteSeriesId": {          "provider": "Lusid",          "instrumentIdType": "RIC",          "instrumentId": "USD-SOFR-COMPOUNDED-INDEX",          "quoteType": "Index",          "field": "mid"        },        "effectiveAt": "2026-01-15"      },      "metricValue": {        "value": 1.22834029, "unit": "none"      },      "scaleFactor": 100    }  }' ``` For general information on loading fixings into the Quote Store, see [this article](/v1/docs/how-do-i-load-an-interest-rate-fixing-into-the-lusid-quote-store). Note the following about this example: - Both fixings are loaded into a `MySOFRFixings` quote scope (specified in the URL) that is only used for fixings, to avoid clashes. - The `instrumentIdType` of each fixing must be `RIC` or `ClientInternal`. - The `instrumentId` must be the `fixingReference` specified in the index convention, in this case `USD-SOFR-COMPOUNDED-INDEX`. - The `provider` is set to `Lusid` and the `field` to `mid`, to avoid validation errors. - The `quoteType` must be either `Index` or `Rate`. - The `scaleFactor` is set to `100` to scale the rate down but you could omit this and specify `metricValue.value` as a decimal rather than a percentage, for example `0.0122834029`. ### Creating a suitable recipe Your recipe must have a [market data rule](/v1/docs/recipes-locating-market-data) able to locate these fixings, for example: ```json "market": {  "marketRules": [    {      "key": "Quote.RIC.USD-SOFR-COMPOUNDED-INDEX",      "dataScope": "MySOFRFixings",      "supplier": "Lusid",      "quoteType": "Index",      "field": "mid"    },    ...  ] }, ``` For general information on recipes, start with [this article](/v1/docs/what-is-a-recipe). Note the following: - The `key` should be constructed as follows: `Quote.<instrumentIdType>.<instrumentId>`. - 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). ### Registering the recipe with portfolios You must register the recipe with every portfolio in which you intend to hold an `InterestRateSwap`. [See how to do this](/v1/docs/setting-a-portfolio-recipe). You can use the same recipe for valuation operations if you wish. ## Booking a transaction to establish a position Once an `InterestRateSwap` instrument is mastered, you can [book a transaction](/v1/docs/how-do-i-create-or-update-a-transaction) in a particular portfolio, for example using the [BatchUpsertTransactions](https://www.lusid.com/docs/api/lusid/endpoints/transaction-portfolios/BatchUpsertTransactions/) API: ```json curl -X POST 'https://mydomain.lusid.com/api/api/transactionportfolios/MyPortfolioScope/MyPortfolioCode/transactions/$batchUpsert?successMode=Partial&preserveProperties=true'  -H 'Content-Type: application/json-patch+json'  -H 'Authorization: Bearer myAPIAccessToken'  -d '{  "transactionRequest-1": {    "transactionId": "Txn01",    "type": "EnterIRS",    "instrumentIdentifiers": {"Instrument/default/ClientInternal": "IRS-15Jan30"},    "transactionDate": "2025-01-15T00:00:00.0000000+00:00",    "settlementDate": "2025-01-15T00:00:00.0000000+00:00",    "units": 1,    "transactionPrice": {      "price": 0,      "type": "Price"    },    "totalConsideration": {      "amount": 0,      "currency": "GBP"    }  } }' ``` Note the following about this example, which demonstrates an OTC contract with no broker or exchange fees: - The `type` field invokes a custom `EnterIRS` [transaction type](/v1/docs/what-is-a-transaction-type) to enter into a position without a cost (see below). - The `transactionDate` and `settlementDate` match the `startDate` in the instrument definition, but can be before or after if required. - `units` is `1` because `notional` is recorded in the instrument definition. You can reverse this by setting `notional` to `1` and recording the number of units in the transaction if you wish. - The `transactionPrice.price` and `totalConsideration.amount` (cost) are set to `0` to infer that this contract has no current market value, but you can specify a clean or dirty price if buying or selling a live swap. > **Note**: This example assumes the transaction, settlement and portfolio currencies are all the same. If not, you can [specify exchange rates](/v1/docs/transactions-and-exchange-rates). We might create a custom `EnterIRS` transaction type as follows: ```json curl -X PUT 'https://.lusid.com/api/api/transactionconfiguration/types/default/EnterIRS?scope=default'  -H 'Content-Type: application/json-patch+json'  -H 'Authorization: Bearer '  -d '{  "aliases": [    {      "type": "EnterIRS",      "description": "Transaction type for entering into an IRS",      "transactionClass": "Basic",      "transactionRoles": "AllRoles",      "isDefault": false    }  ],  "movements": [    {      "name": "Increase units of security",      "movementTypes": "StockMovement",      "side": "Side1",      "direction": 1    }  ] }' ``` ### Confirming positions We can [generate a holdings report](/v1/docs/how-do-i-generate-a-holdings-report) on the transaction date to see that we hold one unit at no cost: ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image-2326X3T8.png?sv=2026-02-06&spr=https&st=2026-11-09T06%3A00%3A24Z&se=2026-11-09T07%3A00%3A24Z&sr=c&sp=r&sig=B61nxufv7dTRkCk8sGcCFl7Kmp3YM5DiawuMACjXww0%3D) ### Auditing LUSID’s automatically-generated transactions We can examine the [output transaction](/v1/docs/input-and-output-transactions) that generates this holding: ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image-6TECBOVU.png?sv=2026-02-06&spr=https&st=2026-11-09T06%3A00%3A24Z&se=2026-11-09T07%3A00%3A24Z&sr=c&sp=r&sig=B61nxufv7dTRkCk8sGcCFl7Kmp3YM5DiawuMACjXww0%3D) ## Valuing your position To value your position, work through our [valuation checklist](/v1/docs/valuation-checklist). > **Note**: You can override LUSID’s calculation of cashflows or PV by loading external valuation results. For more information, see [this article](/v1/docs/what-is-structured-result-data) and [this Jupyter Notebook](https://github.com/finbourne/finbourne-notebooks/blob/main/V1/features/instruments/Interest%20Rate%20Swap.ipynb). | [Pricing model](/v1/docs/recipes-changing-the-default-pricing-models-for-instrument-types) | Applicability | [PV calculation](/v1/docs/how-does-lusid-calculate-pv) | Market data required | [Selected available metrics](/v1/docs/what-is-a-metric) | Notes | | --- | --- | --- | --- | --- | --- | | Store | Type | [Recipe key(s)](/v1/docs/recipes-locating-market-data#more-information-on-the-key-field) | | `SimpleStatic` (default) | Any | Market price multiplied by units held. | Quote | 1. [Market price](/v1/docs/how-do-i-upload-a-market-price-or-fx-spot-rate-to-the-quote-store) for instrument 2. [Fixings](/v1/docs/how-do-i-load-an-interest-rate-fixing-into-the-lusid-quote-store) for the floating leg's current period, used to calculate accrued interest | `Quote.*.*` | `Valuation/PV` `Valuation/Accrued` | Values the swap as a whole, so cannot split by legs. Leg-level metrics are not available. See the model options below to skip the fixings, or to load a price quoted relative to par. | | `Discounting` (recommended) | Fixed-to-float same-currency | Each leg's cashflows are discounted on the funding curve for that leg's payment currency. Floating-leg rates not yet fixed are projected off the curve for the leg's index. PV is the net of both legs' discounted cashflows. | 1. CMD 2. CMD 3. Quote | 1. [Discount factor curve](/v1/docs/categories-of-complex-market-data) per currency 2. [Interest rate projection curve](/v1/docs/categories-of-complex-market-data) per floating leg 3. [Fixings](/v1/docs/how-do-i-load-an-interest-rate-fixing-into-the-lusid-quote-store) already set on or before the valuation date | 1. `Rates.<CCY>.<CCY>OIS`, for example `Rates.USD.USDOIS` 2. `Rates.<CCY>.<Tenor>.<IndexName>`, for example `Rates.EUR.3M.EURIBOR` 3. `Quote.*.*` | `Valuation/PV` `Valuation/Accrued` `Valuation/Leg1/PV` `Valuation/Leg2/PV` `Valuation/Leg1/Accrued` `Valuation/Leg2/Accrued` `Valuation/Leg1/Notional` `Valuation/Leg2/Notional` `Valuation/Leg1/CurrentNotional` `Valuation/Leg2/CurrentNotional` `Valuation/ParRate` `Valuation/Annuity` | If you do not load a projection curve, LUSID projects off the discount curve instead. Can split by legs if `produceSeparateResultForLinearOtcLegs=True` in recipe. For a demonstration of loading the required complex market data, see [this Jupyter Notebook](https://github.com/finbourne/finbourne-notebooks/blob/main/V1/features/instruments/Interest%20Rate%20Swap.ipynb). | | Cross-currency, NDF or FX-linked notional reset | As above. Each leg is discounted in its own payment currency. | As above, plus Quote | As above for each currency, plus [FX rates](/v1/docs/how-do-i-upload-a-market-price-or-fx-spot-rate-to-the-quote-store) between the currencies involved | As above, plus `Fx.CurrencyPair.*` | As above, except `Valuation/ParRate` and `Valuation/Annuity` | As above | | `ConstantTimeValueOfMoney` | Any | Sum of projected cashflows, with no discounting. Floating-leg rates not yet fixed use the last observed fixing. | Quote | 1. [Fixings](/v1/docs/how-do-i-load-an-interest-rate-fixing-into-the-lusid-quote-store) already set on or before the valuation date 2. [FX rates](/v1/docs/how-do-i-upload-a-market-price-or-fx-spot-rate-to-the-quote-store) between the currencies involved, for cross-currency swaps | 1. `Quote.*.*` 2. `Fx.CurrencyPair.*` | As for `Discounting` | Can split by legs if `produceSeparateResultForLinearOtcLegs=True` in recipe. Useful when you have fixings but no curves. | | `OverrideOnlyPricer` | Any | N/A | N/A | N/A | Utilises external valuation results held in the [Structured Result Store](https://support.lusid.com/v1/docs/what-is-structured-result-data). | ### Notes on market data - **Fixings.** `Discounting` and `ConstantTimeValueOfMoney` both need every fixing whose reset date is on or before the valuation date. Once a swap has passed its first reset date, it needs at least the fixing for the current period. A forward-starting swap valued before its first reset date needs no fixings. - **Single-curve valuation.** If you load a discount curve but no projection curve, `Discounting` uses the discount curve to project floating rates as well. This is a quick way to get started, for example valuing a SOFR swap with just a `USDOIS` curve. - **Choosing a different discount curve.** By default, LUSID discounts each currency on the `<CCY>OIS` curve. To use a different curve for a currency, set the `fundingCurveByCurrency` pricing option in your recipe, for example `{"GBP": "GBPOIS-USDCOLL"}`. The discount curve key then becomes `Rates.GBP.GBPOIS-USDCOLL`. ### Notes on metrics - **Splitting by leg.** `Discounting` and `ConstantTimeValueOfMoney` return one row per leg if you set `produceSeparateResultForLinearOtcLegs` to `true` in the recipe's pricing options. `SimpleStatic` always returns a single row. - `Valuation/ParRate` **and** `Valuation/Annuity`**.** LUSID returns these only for a swap with exactly two legs, one of them fixed, in a single currency. They are not returned for float-float, fixed-fixed or cross-currency swaps, or when results are split by leg. Where they do not apply, LUSID leaves them out rather than reporting an error. ### `SimpleStatic` model options You can set the following options in the `modelOptions` of a `SimpleStatic` vendor model rule, using `SimpleModelOptions`: - `assumeAccruedIsZero`: set to `true` to skip calculating accrued interest. LUSID then does not ask for fixings, so you only need the swap's market price. - `priceIsParOffset`: set to `true` if your price is quoted relative to par, as swap marks often are. LUSID values one unit of notional as `(price - 100) / 100`, so a price of `100` gives a value of zero. If the quote has its own scale factor, LUSID uses that instead of `100`. The quote type must be `Price` (giving clean PV) or `DirtyPrice` (giving dirty PV). Quote the swap as it is booked: a swap booked the other way round should be quoted on the other side of par. This option is available for interest rate swaps only. ## Assessing risk > **Required prior reading**: [Valuing your position](/v1/docs/modelling-interest-rate-swaps-in-lusid#valuing-your-position) LUSID publishes an [exposure calculation](/v1/docs/how-does-lusid-calculate-exposure) that you can request when valuing your position. The following risk-aware pricing models produce analytic measures (such as yields, durations and spreads) and risk measures (sensitivities to market data): - **Analytic measures** (`Analytic/*`) are solved from the model's own calculated value. - **Risk measures** (`Risk/*`) are calculated by bumping each curve the pricing model uses, then revaluing the swap. Every curve the model uses therefore produces the full family of measures for its asset class; the table below shows a selection. If a model does not use a particular curve, the result for that curve is blank, not zero. - **Aliases.** `Risk/Rates/Pv01` and `Risk/Rates/Br01` are the same as `Risk/Rates/Delta/Central/Parallel`, and `Risk/Rates/Krd` is the same as `Risk/Rates/Delta/Central/PointWise`. `Risk/Rates/Br01` only gives a different number from `Risk/Rates/Pv01` if you load collateral-adjusted discount curves, named `<CCY>OIS-<REF>COLL` (for example `GBPOIS-USDCOLL`), each of which appears in its own column. - **Curves only.** LUSID never bumps quotes or published fixings, only curves (and FX spot rates). | Risk-aware pricing model | Applicability | Priced off | Available metrics | Notes | | --- | --- | --- | --- | --- | | `Analytic` domain | `Risk` domain | | `Discounting` | Single-currency swap (fixed-to-float or basis) | Model value off the discount and projection curves | `Analytic/ModifiedDuration` `Analytic/MacaulayDuration` | `Risk/Rates/Pv01` `Risk/Rates/Krd` `Risk/Rates/Gamma/Parallel` `Risk/Rates/Difference/Ladder` | Each projection curve's risk appears separately from the discount curve's. Leg-level metrics such as `Risk/Rates/Leg1/Pv01` are refused, because one curve moves both legs. | | `Discounting` | Cross-currency swap | As above | As above | As above, plus: `Risk/Rates/Leg1/Pv01` `Risk/Rates/Leg2/Pv01` `Risk/Fx/Delta/Central/Parallel` | Leg-level metrics are only available if each leg is in a different currency and each curve belongs to exactly one leg. For example, they are refused if one leg is indexed to the other leg's currency. | | `Discounting` | FX-linked notional reset | As above | As above | As above for a single-currency or cross-currency swap, plus: `Risk/Fx/Delta/Central/Parallel` | Both rates and FX bumps move the value, because future FX resets are projected from the curves and spot rate. | | `Discounting` | Non-deliverable swap | As above | As above | As for a single-currency swap | LUSID currently values a non-deliverable swap as if it were deliverable, so `Risk/Fx/*` is zero. | | `ConstantTimeValueOfMoney` | Any | Undiscounted sum of cashflows | `Analytic/ModifiedDuration` `Analytic/MacaulayDuration` | Cross-currency or FX-linked swap only: `Risk/Fx/Delta/Central/Parallel` | Uses no curves, so there is no rates risk. | Note the following: - **Duration.** `Analytic/ModifiedDuration` and `Analytic/MacaulayDuration` are the sum of the two legs' signed durations, not a bond-style duration. They return an error before the swap's start date. - **Units and signs.** Rates risk measures are the change in value for a 1bp move, in the curve's own currency. `Risk/Fx/Delta/Central/Parallel` (also available as `Risk/Fx/SpotDelta`) is the change in value for a 0.0001 move in the FX spot rate as quoted, in the swap's valuation currency. For a move relative to the spot level, request `Risk/Fx/Delta/Central/Parallel/Relative`. - **Key rate buckets.** To choose your own key rate buckets, pass at least two tenors in the metric's `options`, for example `{"key": "Risk/Rates/Krd", "op": "Value", "options": {"Buckets": "2Y,5Y,10Y"}}`. Putting the buckets in the key itself is rejected. - **Bump sizes.** To change the bump size or the shifts used for ladders, set the `riskBumpOptions` pricing option in your recipe, for example `deltaShift` or `ladderShiftOverrides`. - **Risk engine.** By default, LUSID calculates risk by bumping curves. For a swap that resets in advance and is valued with `Discounting`, you can set the `riskEngine` pricing option to `Adjoint` to calculate `Risk/Rates/Pv01` and `Risk/Rates/Krd` analytically instead, which is faster for large books. `Parity` calculates both and checks they agree. Swaps that reset in arrears always use bumping. - **Carry.** `Risk/All/Difference/Carry` returns the change in value from today to the next business day, using today's market data. If your curves are yield curves, LUSID rolls them forward a day, so the result includes rolldown; with discount factor curves it reflects the passage of time only. It is available under `Discounting`. ## Handling the lifecycle of the instrument An `InterestRateSwap` is tightly integrated into LUSID’s [instrument event framework](/v1/docs/understanding-instrument-lifecycle-events-emitted-by-lusid). To enable this for your domain, you must: 1. Register a recipe with every portfolio holding an `InterestRateSwap`. 2. Create transaction types to determine the economic impact of the transactions automatically generated by `InterestRateSwap` events. For much more information, see how to [handle instrument events for interest rate swaps](/v1/docs/handling-instrument-events-for-interest-rate-swaps). | **Instrument event** | Event emission criteria | **If emitted, effect of LUSID default transaction template** | | --- | --- | --- | | `SwapCashFlowEvent` | This event is automatically emitted by LUSID for each leg on each payment date to exchange cashflows. Note market data is required for a floating leg. | One transaction for a cash amount is automatically generated for each leg. | | `SwapPrincipalEvent` | This event is automatically emitted by LUSID for each leg where `notionalExchangeType` is not `None` in the instrument definition, to exchange notionals. | One transaction for a cash amount is automatically generated for each leg. | | `MaturityEvent` | This event is automatically emitted by LUSID on the maturity date to zero the holding. | One transaction for all units at zero cost is automatically generated. | ## Appendix: Using the LUSID web app to simplify setup A dashboard is available in the LUSID web app to both book a trade and simultaneously master an `InterestRateSwap` instrument, which may be a simpler user experience. To enable this: 1. Contact [Technical Support](/v1/docs/how-do-i-use-the-support-centre) for the required license. 2. In the transaction type you use to [enter into a swap](/v1/docs/modelling-interest-rate-swaps-in-lusid#booking-a-transaction-to-establish-a-position), add the `TransactionConfiguration/default/UIClass` system property and set the value to `PMS - IRS`. 3. Navigate to **Portfolio Management > Transactions** 4. Click the **Create transaction** button and open the **IRS** tab.