You can model a mortgage-backed security (MBS) contract as a special kind of ComplexBond
in LUSID. See other uses for this instrument type.
Mastering an instrument
There are numerous tools you can use to master a MBS contract as an instrument as 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, select ComplexBond from the definition dropdown in the API documentation:
In this tutorial, we’ll master a simple fixed schedule MBS contract by calling the UpsertInstruments API as follows:
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": "MBS",
"identifiers": {"ClientInternal": {"value": "MBS"}},
"definition": {
"instrumentType": "ComplexBond",
"assetBacked": true,
"assetPoolIdentifier": "MyMBSMarketDataIdentifier",
"schedules": [
{
"scheduleType": "FixedSchedule",
"startDate": "2024-12-01T00:00:00+00:00",
"maturityDate": "2026-11-20T00:00:00+00:00",
"paymentCurrency": "USD",
"notional": 1,
"couponRate": 0.05,
"flowConventions": {
"paymentFrequency": "1M",
"dayCountConvention": "Act365",
"rollConvention": "20",
"businessDayConvention": "Following",
"paymentCalendars": ["USD"],
"currency": "USD",
"accrualDateAdjustment": "Unadjusted",
},
"exDividendConfiguration": {
"exDividendDays": 19,
"applyThirty360PayDelay": true,
}
}
]
}
}
}'
Note the following:
We’ve chosen to master this instrument in a custom instrument scope (specified in the URL).
The
identifiers
field uniquely identifies the instrument using aClientInternal
identifier.In the economic
definition
object:The
instrumentType
must beComplexBond
.The
assetBacked
field must betrue
to distinguish a MBS contract from other non-backed instruments mastered as typeComplexBond
.The
assetPoolIdentifier
should be set to an intuitive string that represents a market data identifier for pool factors loaded into the LUSID Quote Store (required for calculating principal repayments).The
notional
should be unitised (set to1
) and the amount bought or sold specified on the transaction.The
couponRate
should be expressed as a decimal rather than a percentage, so a bond paying:10% should have a
couponRate
of0.1
5.0% should have a
couponRate
of0.05
0.375% should have a
couponRate
of0.00375
and so on.
The
flowConventions
object stores all the information necessary to determine coupon periods, accrued interest amounts and payment dates:The
paymentFrequency
can be any tenor, in this case1M
to signify once a month. Supported day count conventions.The
rollConvention
field should count back from maturity, so in this case20
for a bond maturing on 20 November, which means the coupon dates are the 20th of every month. We also recommend setting abusinessDayConvention
to 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
accrualDateAdjustment
field is optional and defaults toAdjusted
. This is suitable for European bonds but for US bonds we recommendUnadjusted
.The
settleDays
field in a flow convention is deprecated, andresetDays
is ignored for fixed schedules. Thescope
andcode
fields can be ignored unless you are loading a flow convention from a library.
The
exDividendConfiguration
object configures a special ex-div period for a MBS contract:The
exDividendDays
field should record the difference between the coupon date and the first of the month (when pool factors are recorded in LUSID), so in this case20 - 1 = 19
.The
applyThirty360PayDelay
field should be set totrue
to delay coupon and principal payments to the following month.
If you do not specify a
stubType
object, the first and last coupon periods are expected to be regular. More information coming soon.The
tradingConventions.priceScaleFactor
field is set to 100 so market prices can be entered as a percentage of par.If you do not specify a
roundingConventions
object, no rounding is applied at the instrument level.
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": {
"upsert-request-1": {
"scope": "mycustominstrscope",
"lusidInstrumentId": "LUID_00003EDZ",
"version": {
"asAtCreated": "2025-04-24T11:10:25.8725820+00:00",
"userIdCreated": "00u91lo2d7X42sdse2p7",
"requestIdCreated": "2025042411-35bc0fe68ef8414f9766ca62e6613d4b",
"asAtModified": "2025-04-24T11:20:41.4671350+00:00",
"userIdModified": "00u91lo2d7X42sdse2p7",
"requestIdModified": "2025042411-65bd46cf085f4f43899b9d3004a3cff9",
"asAtVersionNumber": 1,
"entityUniqueId": "9c64bc2b-fe0b-4e95-b91c-7f8e5b11cc02"
},
"name": "MBS",
"identifiers": {
"LusidInstrumentId": "LUID_00003EDZ",
"ClientInternal": "MBS"
},
"properties": [],
"instrumentDefinition": {
"identifiers": {},
"calculationType": "Standard",
"schedules": [
{
"startDate": "2024-12-01T00:00:00.0000000+00:00",
"maturityDate": "2026-11-20T00:00:00.0000000+00:00",
"flowConventions": {
"currency": "USD",
"paymentFrequency": "1M",
"dayCountConvention": "Actual365",
"rollConvention": "20",
"paymentCalendars": [
"USD"
],
"resetCalendars": [],
"settleDays": 0,
"resetDays": 0,
"leapDaysIncluded": true,
"accrualDateAdjustment": "Unadjusted",
"businessDayConvention": "F",
"accrualDayCountConvention": "Actual365"
},
"couponRate": 0.05,
"notional": 1,
"paymentCurrency": "USD",
"stubType": "None",
"exDividendConfiguration": {
"useBusinessDays": false,
"exDividendDays": 19,
"returnNegativeAccrued": false,
"applyThirty360PayDelay": true
},
"scheduleType": "FixedSchedule"
}
],
"roundingConventions": [],
"assetBacked": true,
"assetPoolIdentifier": "MyMBSMarketDataIdentifier",
"tradingConventions": {
"priceScaleFactor": 100,
"minimumOrderSize": 0,
"minimumOrderIncrement": 0
},
"instrumentType": "ComplexBond"
},
"state": "Active",
"assetClass": "Credit",
"domCcy": "USD",
"relationships": []
}
},
...
}
Booking a transaction to establish a position
Once an instrument is mastered, there are numerous ways we can book a transaction to record the acquisition of a quantity in a suitable portfolio, for example calling the BatchUpsertTransactions
API as follows:
curl -X POST 'https://<your-domain>.lusid.com/api/api/transactionportfolios/FixedIncome/US/transactions/$batchUpsert?successMode=Partial&preserveProperties=true'
-H 'Content-Type: application/json-patch+json'
-H 'Authorization: Bearer <your-API-access-token>'
-d '{
"transactionRequest-1": {
"transactionId": "mbs_purchase_001",
"type": "BuyMBS",
"instrumentIdentifiers": {"Instrument/default/ClientInternal": "MBS"},
"transactionDate": "2025-01-10T00:00:00.0000000+00:00",
"settlementDate": "2025-01-13T00:00:00.0000000+00:00",
"units": 100000,
"transactionPrice": {
"price": 0.98,
"type": "CleanPrice"
},
"totalConsideration": {
"amount": 0,
"currency": "USD"
},
"properties": {
"Transaction/MyProperties/TotalCapitalisedFees": {
"key": "Transaction/MyProperties/TotalCapitalisedFees",
"value": {
"metricValue": {
"value": 200,
"unit": "USD"
}
}
},
"Transaction/default/CurrentFace": {
"key": "Transaction/default/CurrentFace",
"value": {
"metricValue": {
"value": 95000
}
}
}
}
}
}'
Note the following:
The
instrumentIdentifiers
field uses theClientInternal
identifier to resolve the transaction to the correct MBS instrument (but it could use the LUID).The
type
field invokes a customBuyMBS
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 accrued interest). This is used by LUSID to automatically calculate gross consideration (see below).The
totalConsideration
object:Sets the settlement currency to
USD
.Specifies a cost of
0
to trigger LUSID to automatically derive total consideration (see below).
The trade fee is recorded using a custom property.
The outstanding notional is recorded as a non-unitised amount using the
Transaction/default/CurrentFace
system property, in this case100000 × 0.95 = 95000
.
Note: This example assumes the transaction, settlement and portfolio currencies are the same. If not, you can specify exchange rates.
We can call the SetTransactionType API to create a BuyMBS
transaction type as follows:
curl -X PUT 'https://<your-domain>.lusid.com/api/api/transactionconfiguration/types/default/BuyMBS?scope=default'
-H 'Content-Type: application/json-patch+json'
-H 'Authorization: Bearer <your-API-access-token>'
-d '{
"aliases": [
{
"type": "BuyMBS",
"description": "Transaction type for MBS bond purchases",
"transactionClass": "Basic",
"transactionRoles": "AllRoles",
"isDefault": false
}
],
"movements": [
{
"name": "Increase units of security",
"movementTypes": "StockMovement",
"side": "Side1WithCurrentFace",
"direction": 1
},
{
"name": "Decrease cash balance",
"movementTypes": "CashCommitment",
"side": "Side2",
"direction": -1
}
],
"calculations": [
{
"type": "Txn:BondInterest"
},
{
"type": "Txn:GrossConsideration"
},
{
"type": "DeriveTotalConsideration",
"formula": "Txn:GrossConsideration + Properties[Transaction/MyProperties/TotalCapitalisedFees]"
}
]
}'
Note the following:
The
Txn:BondInterest
calculation automatically calculates the amount of accrued interest bought or sold and stores the result in theTransaction/default/BondInterest
system property.As a clean price is specified, the
Txn:GrossConsideration
calculation automatically calculates gross consideration according to the formula(units * price * Transaction/default/CurrentFace) + Transaction/default/BondInterest
, and stores the result in theTransaction/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 thetotalConsideration.amount
of the transaction.The
StockMovement
uses aSide1WithCurrentFace
custom side (see below) to establish a holding in the instrument with the specified number of units at a cost derived from the total consideration, and a record of the starting current face.The
CashCommitment
movement uses the built-inSide2
to decrease the instrument currency holding by the total consideration.
We can call the SetSideDefinition API to create a Side1WithCurrentFace
custom side as follows:
curl -X PUT 'https://<your-domain>.lusid.com/api/api/transactionconfiguration/sides/Side1WithCurrentFace?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:TradeAmount",
"currentFace": "Txn:CurrentFace"
}'
Note this is the same as the built-in Side1
except the currentFace
field is set to Txn:CurrentFace
, which returns the value of the Transaction/default/CurrentFace
system property added to the transaction.
Auditing LUSID’s calculations of transaction amounts
We can navigate to Portfolio Management > Transactions in Output mode in the LUSID web app to call the BuildTransactions API with a suitable window and examine the output transaction generated by LUSID:
The gross transaction amount is
units * price * Transaction/default/CurrentFace
, so100000 × 0.98 × 0.95 = 93100
The amount of accrued interest purchased is
164.38
Since the price is clean, the gross consideration is the gross transaction amount plus accrued interest:
93100 + 164.38 = 93264.38
The total consideration is gross consideration plus fees:
93264.38 + 200 = 93464.38
:
Confirming positions on the settlement date
We can navigate to Portfolio Management > Holdings to call the GetHoldings API on the settlement date and see that we have:
100,000 units of the MBS instrument with a cost calculated as total consideration minus accrued interest:
93464.38 - 164.38 = 93300
A USD currency holding with a negative amount reflecting the total consideration paid: change Effective dropdown to show Settlement date…
Valuing your position
To value your position, work through our valuation checklist.
Pricing models
The following pricing models are available for instruments of type ComplexBond
. Note your choice impacts the market data required and the composition of your recipe:
BondLookupPricer (strongly recommended)
Note: The default pricing model for bonds is currently
ConstantTimeValueOfMoney
, but we no longer recommend it. You should change this to one of the alternatives above. See how to do this.
Metrics
LUSID can report many hundreds of metrics in a valuation report. Note the following:
Valuation/PV*
metrics calculate PV according to a formula specific to the pricing model.Valuation/Accrued*
metrics calculate accrual according to the flow convention defined in the underlying instrument. In most cases this increases in amount until the payment date, at which point it resets to zero and starts to accrue again the next day; there is no date on which the accrued appears as the full value of the coupon because it is assumed to be paid the day a period completes.Valuation/Diagnostics/Accrual/*
metrics diagnose accrual calculations.Valuation/CleanPV*
metrics calculate PV minus accrual.ProfitAndLoss/*
metrics calculate unrealised profit/loss as the difference between the PV and the holding cost at the start and again at the end of the valuation period.Analytic/*
metrics calculate yield to maturity and duration.
Market data
To value a MBS instrument position using the recommended BondLookupPricer
model, load the following market data for the valuation date:
A market price. This should be a clean price specified as a percentage of par. The quote can resolve to the MBS instrument using any identifier. The
scaleFactor
should be100
.A pool factor. This must have a
quoteType
ofPoolFactor
and aninstrumentIdType
ofIsin
but theinstrumentId
should be theassetPoolIdentifier
value specified in the instrument economic definition. ThescaleFactor
should be100
.
For example, to value our position on 13 January 2025:
Load two quotes into a particular quote scope (specified in the URL) with the
effectiveAt
field set to the valuation date:curl -X POST 'https://<your-domain>.lusid.com/api/api/quotes/MyMBSQuotes' -H 'Authorization: Bearer <your-API-access-token>' -H 'Content-Type: application/json-patch+json' -d '{ "Quote-0001": { "quoteId": { "quoteSeriesId": { "provider": "Lusid", "instrumentIdType": "ClientInternal", "instrumentId": "MBS", "quoteType": "Price", "field": "mid" }, "effectiveAt": "2025-01-13T00:00:00Z" }, "metricValue": { "value": 98, "unit": "USD" }, "scaleFactor": 100, }, "Quote-0002": { "quoteId": { "quoteSeriesId": { "provider": "Lusid", "instrumentIdType": "Isin", "instrumentId": "MyMBSMarketDataIdentifier", "quoteType": "PoolFactor", "field": "mid" }, "effectiveAt": "2025-01-13T00:00:00Z" }, "metricValue": { "value": 95, "unit": "USD" }, "scaleFactor": 100, } }'
Create a recipe to locate this market data and change the default pricing model, for example:
curl -X POST 'https://<your-domain>.lusid.com/api/api/recipes' -H 'Content-Type: application/json-patch+json' -H 'Authorization: Bearer <your-API-access-token>' -d '{ "configurationRecipe": { "scope": "MyRecipes", "code": "MyBasicRecipe", "market": { "marketRules": [ { "key": "Quote.ClientInternal.*", "dataScope": "MyMBSQuotes", "supplier": "Lusid", "quoteType": "Price", "field": "mid" }, { "key": "Quote.Isin.*", "dataScope": "MyMBSQuotes", "supplier": "Lusid", "quoteType": "PoolFactor", "field": "mid" } ] }, "pricing": { "modelRules": [ { "instrumentType": "ComplexBond", "modelName": "BondLookupPricer" } ] } } }'
Generate a valuation report with appropriate metrics, for example:
Assessing risk
LUSID provides an exposure calculation and supports both analytic and bump and valuation mechanisms for assessing risk.
Monitoring the lifecycle of the instrument
A MBS instrument ‘expires’ on the maturity date specified in the instrument economic definition.
Note: An instrument in LUSID is still available post-expiry, although the valuation is zero. If you set your holding to zero it no longer appears in reports unless you are deliberately backdating.
Handling automatic lifecycle events
LUSID is transitioning to a system where it automatically emits lifecycle events for supported instruments. We provide default transaction templates that you can use as-is to automatically generate transactions in portfolios with holdings, and recommendations for transaction types that deliver appropriate economic impacts for those generated transactions.
The following events are available for MBS instruments:
Instrument event type | Event emission criteria | If emitted, default transaction template generates… | Recommendations for transaction types |
---|---|---|---|
| This event is automatically emitted by LUSID each time a coupon is due. | A transaction for the coupon amount. | More information coming soon |
| This event is automatically emitted by LUSID each time a principal repayment is due. | A transaction for the principal amount. | |
| |||
| |||
| |||
| This event is automatically emitted by LUSID on the maturity date of the instrument. | A transaction for all the units in the holding at a cost of zero. |
Manually loading settlement transactions
If you do not want to turn on automatic instrument lifecycle events you can continue to monitor upcoming cashflows using Portfolio Management > CashLadder in the LUSID web app, or by calling the GetPortfolioCashLadder API directly.
You can call the GetUpsertablePortfolioCashFlows API to  return imminent cashflows as upsertable DTOs ready to manually load into LUSID as input transactions.