How do I load complex market data into LUSID?

Prev Next

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 to USD/JPY/FxFwdCurve.

  • LUSID saves an FX forward curve as FxForwardTenorCurveData if you enter a spot rate, or as FxForwardTenorPipsCurveData if 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?

scope

Any, for example MyCurves

✅ dataScope field

provider

Any valid, for example Lusid

✅ supplier field

marketAsset

Must follow the naming syntax for the category, for example GBP/GBPOIS for a GBP discount curve

✅ key field, for example Rates.GBP.GBPOIS or Rates.*.*

effectiveAt

A UTC datetime, or date and cut label

❌ but must be within quoteInterval

marketDataType

One of the categories of complex market data, for example DiscountFactorCurveData

❌

Other marketData fields

Depend on the marketDataType, for example baseDate, dates and discountFactors for a discount factor curve. Find out more about each category.

❌

The following fields are optional:

  • A priceSource indicating a sub-supplier to the provider, 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 its priceSource field.

  • A lineage in marketData providing additional information.

  • A marketDataOptions object in marketData setting 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 effectiveAt all 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 effectiveAt is 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's version.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:

  1. Load a copy of the object with the new values.

  2. Delete the original. In the web app, click Delete on its row in Data Management > Curves. Using the API, call DeleteComplexMarketData, specifying its scope and marketDataId.

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.