You can load an object such as a discount factor curve, an FX forward curve or a volatility surface into the Complex Market Data (CMD) Store, for example a GBP OIS discount curve for 1 October 2026.
Note the following:
Complex market data is required to value most instruments with a pricing model that discounts cashflows, projects interest or FX rates, or values options. See the categories of complex market data.
Each version of an object is a complete curve or surface for one effective date. To build a history, load the same object again with a new effective date each time. Find out more about versions.
Choices you make when uploading market data impact the composition of the recipe you must create in order to use that data. In particular, the name you give each object must follow the naming syntax for its category.
Methods
Navigate to Data Management > Curves, click the New curve button and choose a curve type. Enter the data in the editor, then click Create and specify a Scope, Market Asset, Provider and Effective Date.
Note the following:
You can create FX forward curves, discount factor curves, yield curves (from swaps and FRAs), FX volatility surfaces and equity volatility surfaces (lognormal). To load any other category, use the API or Luminesce.
You type the Market Asset yourself, so make sure it follows the naming syntax for the category. For example, the FX forward curve editor suggests
USD/JPY, which you must change toUSD/JPY/FxFwdCurve.LUSID saves an FX forward curve as
FxForwardTenorCurveDataif you enter a spot rate, or asFxForwardTenorPipsCurveDataif you do not.You cannot set a price source or curve options in the web app. To set either, use the API or Luminesce.

Call the UpsertComplexMarketData API to load up to 2000 objects in a single request. You must:
Encapsulate all the objects in a single
scope(namespace) in the URL.For each object, specify an ephemeral ID (to track errors in the response).
In the following example, a GBP discount factor curve with curve options and a EUR/USD FX forward curve quoted in forward points are loaded together into a MyCurves scope:
curl -X POST "https://<your-domain>.lusid.com/api/api/complexmarketdata/MyCurves"
-H "Authorization: Bearer <your-API-access-token>"
-H "Content-Type: application/json-patch+json"
-d '{
"CMD-0001": {
"marketDataId": {
"provider": "Lusid",
"effectiveAt": "2026-10-01T00:00:00Z",
"marketAsset": "GBP/GBPOIS"
},
"marketData": {
"marketDataType": "DiscountFactorCurveData",
"baseDate": "2026-10-01T00:00:00Z",
"dates": ["2027-01-01T00:00:00Z", "2027-10-01T00:00:00Z", "2028-10-01T00:00:00Z", "2031-10-01T00:00:00Z"],
"discountFactors": [0.9902, 0.9612, 0.9238, 0.8187],
"marketDataOptions": {
"marketDataOptionsType": "CurveOptions",
"interpolationType": "LinearOnLogDiscountFactors",
"backExtrapolationType": "Flat"
}
}
},
"CMD-0002": {
"marketDataId": {
"provider": "Lusid",
"effectiveAt": "2026-10-01T00:00:00Z",
"marketAsset": "EUR/USD/FxFwdCurve"
},
"marketData": {
"marketDataType": "FxForwardTenorPipsCurveData",
"baseDate": "2026-10-01T00:00:00Z",
"domCcy": "EUR",
"fgnCcy": "USD",
"tenors": ["1M", "3M", "6M", "1Y"],
"pipRates": [6.75, 27.32, 49.20, 104.91]
}
}
}'We recommend checking the failed section of the response for any objects LUSID could not load.
For a FX forward curve, you can write a Luminesce SQL query using the dedicated Lusid.ComplexMarketData.FxForwardCurve provider and execute it using a tool such as the LUSID web app.
For any other object, use the Lusid.ComplexMarketData.Writer provider, specifying a Scope and DefinitionJson for each containing the same marketDataId and marketData as one object in the query:
@table_of_data = select 'MyCurves' as Scope,
'{"marketDataId": {"provider": "Lusid", "effectiveAt": "2026-10-01T00:00:00Z", "marketAsset": "GBP/GBPOIS"},
"marketData": {"marketDataType": "DiscountFactorCurveData", "baseDate": "2026-10-01T00:00:00Z",
"dates": ["2027-01-01T00:00:00Z", "2027-10-01T00:00:00Z", "2028-10-01T00:00:00Z"],
"discountFactors": [0.9902, 0.9612, 0.9238]}}' as DefinitionJson;
select * from Lusid.ComplexMarketData.Writer where ToWrite = @table_of_data;We recommend examining the WriteErrorCode, WriteError and WriteErrorDetail fields in the results to check each object loaded successfully.
Data fields
This section supplements the API documentation and on-screen help text in the LUSID web app.
Mandatory field or parameter | Allowed values | Must be matched in recipe? |
|---|---|---|
| Any, for example | |
| Any valid, for example | |
| Must follow the naming syntax for the category, for example |
|
| A UTC datetime, or date and cut label | |
| One of the categories of complex market data, for example | |
Other | Depend on the |
The following fields are optional:
A
priceSourceindicating a sub-supplier to theprovider, for example Tradeweb or RRPS for Refinitiv DataScope. Match this using the priceSource field in a recipe. Note that if omitted, your recipe must also omit itspriceSourcefield.A
lineageinmarketDataproviding additional information.A
marketDataOptionsobject inmarketDatasetting curve options such as interpolation and extrapolation, for categories that accept them.
Subsequent updates
LUSID identifies each object by its scope, provider, priceSource and marketAsset. An object can have many versions, each with a different effectiveAt, so one object can hold a complete history of a curve or surface. When you load an object, LUSID compares these four fields and the effectiveAt with the objects already in the CMD Store:
If the four fields and the
effectiveAtall match an existing version, LUSID replaces that version with the new one. It replaces the whole version, so include every field you want to keep, not only the ones you changed.If the four fields match but the
effectiveAtis new, LUSID adds a version to the existing object and leaves its other versions in place. It does not create a new object. LUSID increases the object'sversion.asAtVersionNumber, which counts every change to the object at any effective date. This number does not tell you which version a valuation uses.If any of the four fields differs, LUSID creates a new object.
A valuation uses the version with the latest effectiveAt on or before the valuation date, provided it falls within the quoteInterval in your recipe. For example, if an object has versions effective on 1 October and 8 October, a valuation on 3 October uses the 1 October version and a valuation on 9 October uses the 8 October version. To see the version in force on a particular date, call GetComplexMarketData with that date as the effectiveAt. The date does not need to match a version exactly.
You cannot rename an object, move it to another scope, or change its provider or price source. Instead:
Load a copy of the object with the new values.
Delete the original. In the web app, click Delete on its row in Data Management > Curves. Using the API, call DeleteComplexMarketData, specifying its
scopeandmarketDataId.
If you do not delete the original, both objects remain in the CMD Store and a valuation may use the one you meant to replace.
To add a point to the end of an existing FX forward curve without loading the whole curve again, call the UpsertAppendComplexMarketData API. The effectiveAt you specify must exactly match an existing version.
Editing in the LUSID web app
When you click Edit on an object in Data Management > Curves and then Save, the web app asks for the Scope, Market Asset, Provider and Effective Date again. To change the original object rather than create a new one, keep the Scope, Market Asset and Provider the same. The Effective Date then decides what happens: the same date replaces that version, and a different date adds a new version. The web app fills in the Effective Date from the curve's base date, which may differ from the version's effective date, and saves it without a time, so check it before you click Save.
Note: The web app does not keep an object's price source or curve options. If the object you want to edit has either, edit it using the API or Luminesce instead. Saving it in the web app creates a separate object without the price source, or replaces the version and resets its curve options to the defaults.