You can model certain interest rate swap contracts as instruments of type InterestRateSwap in LUSID. See all supported instruments.
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.
Note there is an accompanying Jupyter notebook that demonstrates many of the operations and concepts in this article.
Note: This article explains how to master an instrument and then load a transaction using the LUSID API. It is possible to simply load a transaction in the LUSID web app and have LUSID master the instrument for you. More information.
Mastering an instrument
There are numerous tools 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 of identifiers, and the facility to store extra information as properties:

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 API; note this is actually a no-op request, since the legs 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": "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 onto SDK models:
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-1D-SOFRINDEX",
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 schema. Note in particular the following:
The
instrumentTypemust beInterestRateSwap.The
startDateis typically the transaction date but can be before or after if required.The
legsarray must contain two legs, one withpayReceiveset toPayand the other toReceive. These can both be a fixed leg, both be a floating leg, or one of each. The order does not matter.You can record
additionalPaymentsif 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:
{
"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 schema. Note in particular the following:
payReceiveis set toReceive. One leg must receive payment.For a fixed leg,
rateOrSpreadrefers to the fixed interest rate expressed as a decimal rather than a percentage, so:10% should be specified as
0.12.5% should be specified as
0.020.375% should be specified as
0.00375.
startDateandmaturityDateare typically the same as the instrument itself.notionalis set to the full amount and subsequent transactions are for a single unit, but you can reverse this if desired.notionalExchangeTypeis set toNoneto not swap notionals, but this could beInitial,FinalorBothfor a cross-currency swap.conventionsspecifies all the information necessary to determine payment schedules. In particular,rollConventionspecifies 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 SOFRINDEX compounded index that pays once a year on the 15th:
{
"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-1D-SOFRINDEX",
"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 schema. Some fields are common to all swaps; note in particular the following:
payReceiveis set toPay. One leg in a swap must pay out.For a floating leg,
rateOrSpreadrefers to a spread on the index, if any. For example, a spread of 50bps should be specified as0.005.For a vanilla swap,
notional,notionalExchangeType,stubTypeandconventionsare typically the same as the fixed leg, but can differ if required (ie. for a cross-currency swap).indexConventionsspecifies all the information necessary to calculate accrued interest amounts 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 field | Category A | Category B | Category C |
|---|---|---|---|
| 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 |
Examining the response
Providing the request is successful, the response:
Confirms the globally-unique LUID for the instrument;
Provides version information;
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": {
"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-1D-SOFRINDEX",
"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 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 |
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:
"indexConvention": {
"fixingReference": "USD-1D-SOFRINDEX",
...
}The following call to the UpsertQuotes API loads two fixings for this index into a particular quote scope:
curl -X POST "https://<your-domain>.lusid.com/api/api/quotes/MySOFRFixings"
-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": "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-1D-SOFRINDEX",
"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. Note the following about this example:
Both fixings are loaded into a
MySOFRFixingsquote scope (specified in the URL) that is only used for fixings, to avoid clashes.The
instrumentIdTypeof each fixing must beRICorClientInternal.The
instrumentIdmust be thefixingReferencespecified in the index convention, in this caseUSD-1D-SOFRINDEX.The
provideris set toLusidand thefieldtomid, to avoid validation errors.The
quoteTypemust be eitherIndexorRate.The
scaleFactoris set to100to scale the rate down but you could omit this and specifymetricValue.valueas a decimal rather than a percentage, for example0.0122834029.
Creating a suitable recipe
Your recipe must have a market data rule able to locate these fixings, for example:
"market": {
"marketRules": [
{
"key": "Quote.RIC.USD-1D-SOFRINDEX",
"dataScope": "MySOFRFixings",
"supplier": "Lusid",
"quoteType": "Index",
"field": "mid"
},
...
]
},For general information on recipes, start with this article. Note the following:
The
keyshould be constructed as follows:Quote.<instrumentIdType>.<instrumentId>.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).
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.
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 in a particular portfolio, for example using the BatchUpsertTransactions API:
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
typefield invokes a customEnterIRStransaction type to enter into a position without a cost (see below).The
transactionDateandsettlementDatematch thestartDatein the instrument definition, but can be before or after if required.unitsis1becausenotionalis recorded in the instrument definition. You can reverse this by settingnotionalto1and recording the number of units in the transaction if you wish.The
transactionPrice.priceandtotalConsideration.amount(cost) are set to0to 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.
We might create a custom EnterIRS transaction type as follows:
curl -X PUT 'https://<your-domain>.lusid.com/api/api/transactionconfiguration/types/default/EnterIRS?scope=default'
-H 'Content-Type: application/json-patch+json'
-H 'Authorization: Bearer <your-API-access-token>'
-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 on the transaction date to see that we hold one unit at no cost:

Auditing LUSID’s automatically-generated transactions
We can examine the output transaction that generates this holding:

Valuing your position
To value a position in an InterestRateSwap in a portfolio, work through our valuation checklist.
Note: You can override LUSID’s calculation of cashflows or PV by loading pre-determined structured result data. For more information, see this article and this Jupyter Notebook.
Available pricing model (see how to change) | Market data required | Notes |
|---|---|---|
|
| Cannot split by legs. |
|
| Can split by legs if |
| Fixings for floating leg in Quote Store | Note PV is simply a sum of projected cashflows. Can split by legs if |
For example, to value the portfolio above on 13 May 2026 using SimpleStatic:
Make sure fixings for a floating leg are loaded into the Quote Store.
Load market prices for the valuation date as either
CleanPriceorDirtyPriceinto a particular quote scope in the Quote Store, for example:curl -X POST "https://<your-domain>.lusid.com/api/api/quotes/MyIRSPrices" -H "Authorization: Bearer <your-API-access-token>" -H "Content-Type: application/json-patch+json" -d '{ "Quote-0001": { "quoteId": { "quoteSeriesId": { "provider": "Lusid", "instrumentIdType": "ClientInternal", "instrumentId": "IRS-15Jan30", "quoteType": "DirtyPrice", "field": "mid" }, "effectiveAt": "2026-05-13" }, "metricValue": { "value": 0.09787, "unit": "USD" } }, }'Make sure the recipe you use has a market data rule able to locate these market prices, for example:
"market": { "marketRules": [ { "key": "Quote.ClientInternal.*", "dataScope": "MyIRSPrices", "supplier": "Lusid", "quoteType": "DirtyPrice", "field": "mid" }, ... ] },Generate a valuation report with appropriate metrics, for example:

Handling the lifecycle of the instrument
An InterestRateSwap is tightly integrated into LUSID’s instrument event framework. To enable this for your domain, you must:
Register a recipe with every portfolio holding an
InterestRateSwap.Create transaction types to determine the economic impact of the transactions automatically generated by
InterestRateSwapevents.
For much more information, see how to handle instrument events for interest rate swaps.
Instrument event | Event emission criteria | If emitted, effect of LUSID default transaction template |
|---|---|---|
| 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. |
| This event is automatically emitted by LUSID for each leg where | One transaction for a cash amount is automatically generated for each leg. |
| 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 that you can use to both book a trade and simultaneously master an InterestRateSwap instrument, which may be a simpler user experience. To enable this:
Contact Technical Support for the required license.
In the transaction type you use to enter into a swap, add the
TransactionConfiguration/default/UIClasssystem property and set the value toPMS - IRS.Navigate to Portfolio Management > Transactions
Click the Create transaction button and open the IRS tab.