---
title: "Recipes: Locating market data"
slug: "recipes-locating-market-data"
updated: 2026-06-01T16:33:13Z
published: 2026-06-01T16:33:13Z
canonical: "support.lusid.com/recipes-locating-market-data"
stale: true
---

> ## 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.

# Recipes: Locating market data

A [recipe](/v1/docs/what-is-a-recipe) has a `market` object that you must populate so LUSID can locate and retrieve suitable [market data](/v1/docs/market-data) for each type of instrument you wish to value:

- For simple exchange-traded instruments such as equities, you are likely to require market prices held in the LUSID Quote Store. Note you can [override market prices](/v1/docs/recipes-locating-market-data#overriding-market-prices-or-falling-back-to-holding-pricing-data) in some circumstances using holding-level data such as unit cost or last traded price.
- For complex exchange-traded or OTC instruments, you are likely to require interest rate, discounting or credit curves and/or volatility surfaces held in the LUSID Complex Market Data Store.
- For any instrument (including a cash instrument) held in a different currency to the portfolio currency, you are likely to require FX rates held in the LUSID Quote Store.

> **Note**: You can call APIs to check the quality/quantity of market data required for the valuation operation you are trying to perform. [See how to do this](/v1/docs/what-market-data-does-my-valuation-require).

By default, a recipe must be able to locate market data that is valid ~ one day prior to each date in the [valuation schedule](/v1/docs/how-do-i-perform-a-valuation). The precise validity period is determined by the valuation time. For example, if you are valuing a portfolio containing a single equity on:

- 7 March 2022, the time defaults to 00:00:00 and the recipe must be able to locate at least one market price for that equity (and an FX rate, if applicable) effective 6 March 00:00:00 -> 7 March 00:00:00. Prices effective 5 March or earlier, or 7 March later than midnight are not valid.
- 7 March 2022 17:00:00, the recipe must be able to locate at least one market price effective 6 March 00:00:00 -> 7 March 17:00:00.
- 7 March 2022 23:59:59, the recipe must be able to locate at least one market price effective 6 March 00:00:00 -> 7 March 23:59:59.

In other words, there is an automatic look back, or ‘grace’, period of between 24 and 48 hours. You can specify an explicit `quoteInterval` to [look back further](/v1/docs/recipes-locating-market-data#more-information-on-the-quoteinterval-field) than this if you wish.

## Understanding market data rules

For each category of market data (that is, market price, FX rate, interest rate curve and so on) you are required to locate you should specify a *market data rule* in the `marketRules` array and/or `groupedMarketRules` array of the `market` object of your recipe. Consider the following example, of three market data rules in the `marketRules` array intended for a portfolio valuation on 7 March 2022:

![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/RecipeMarketDataRules (3).png)

Note the following:

- Each market data rule is self-contained and must contain all the information required to locate a particular category of market data in an appropriate store.
- Market data rules are processed in order. For each category of market data (as determined by its `key`), LUSID attempts to locate market data using the **first matching rule found**, unless [overridden for particular instruments](/v1/docs/recipes-locating-market-data#overriding-market-data-rules-for-particular-instruments). If no market data can be located (and no override is in place), LUSID tries the next matching rule.
- You can provide multiple rules for the same `key` in order to provide fallback logic. For example, you could have two rules for `Quote.Figi.*` that are identical except the first has a `supplier` of `DataScope` and the second a `supplier` of `Bloomberg`. Prices from Refinitiv would be preferred, but the recipe would fall back to Bloomberg if no Refinitiv prices could be found.
- You can provide more sophisticated fallback behavior by specifying market data rules in the `groupedMarketRules` array in addition to the `marketRules` array. [More information](/v1/docs/recipes-locating-market-data#grouping-market-data-rules-and-performing-preference-operations).
- You can perform operations such as taking the highest or latest price from a group of providers, or even synthesising an average price, by specifying market data rules in the `groupedMarketRules` array. [More information](/v1/docs/recipes-locating-market-data#grouping-market-data-rules-and-performing-preference-operations).
- You can specify ultimate fallback behaviour for market prices (not FX rates or complex market data) by using holding-level pricing data stored per portfolio, such as unit cost or last traded price. [More information](/v1/docs/recipes-locating-market-data#overriding-market-prices-or-falling-back-to-holding-pricing-data).

### Summary of fields in a market data rule

For information on all the fields and allowed values in a market data rule, examine the [API reference](https://www.lusid.com/docs/api#operation/UpsertConfigurationRecipe) (expand the `configurationRecipe/market/marketRules` section), or examine the [MarketDataKeyRule schema](https://www.lusid.com/docs/api/lusid/schemas/MarketDataKeyRule/):

![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image(187).png)

The following table provides a summary. Note in particular that:

- The `key` field defines the category of market data to locate.
- Given the category, all the other fields define the rule's criteria for retrieving appropriate data.

| **Market data rule field** | **Status** | **Applicable LUSID market data store(s)** | **Explanation** | **Matching market data field in store** |
| --- | --- | --- | --- | --- |
| `key` | Mandatory | Quote, Complex Market Data | A dot-separated string specifying the category of market data to locate, and implicitly the store in which to locate it. [See the table below for more information](/v1/docs/recipes-locating-market-data#more-information-on-the-key-field). | n/a |
| `supplier` | Mandatory | Quote, Complex Market Data | The market data vendor. This value must match the value of the market data `provider` field in the appropriate store. [See a list of valid vendors](/v1/docs/understanding-valid-market-data-provider-and-field-combinations). | `provider` |
| `priceSource` | Optional* | Quote, Complex Market Data | The sub-supplier to the market data vendor (above). This value must match the value of the market data `priceSource` field, for example `Tradeweb` or `RRPS` for Refinitiv `DataScope`. * If omitted, the value defaults to an empty string. This means LUSID will only locate market data with a matching null `priceSource` value (empty does not act as a wildcard). | `priceSource` |
| `dataScope` | Mandatory | Quote, Complex Market Data | This value must match the `scope` encapsulating the market data in the appropriate store. | `scope` |
| `quoteType` | Mandatory | Quote | If the rule locates market data in the Quote Store, this value must match the value of the `quoteType` field, for example `Price` or `Rate`. If the rule locates market data in the Complex Market Data Store, omit this field or provide a placeholder value. | `quoteType` |
| `field` | Mandatory | Quote | If the rule locates market data in the Quote Store, this value must match the value of the `field` field, for example `bid`, `mid` or `ask`. Note this value is case-sensitive, so `Bid` does not match `bid`. [See valid values for different vendors](/v1/docs/understanding-valid-market-data-provider-and-field-combinations). If the rule locates market data in the Complex Market Data Store, omit this field or provide a placeholder value. | `field` |
| `quoteInterval` | Optional | Quote, Complex Market Data | Specifies a 'look back' period for market data longer than the default value (if omitted) of `1D.0D`, which looks back to 00:00:00 on the day *before* the valuation date. [More information](/v1/docs/recipes-locating-market-data#more-information-on-the-quoteinterval-field). | n/a |
| `sourceSystem` | Optional | Quote, Complex Market Data | Defaults to `Lusid` to locate market data in either the Quote Store or Complex Market Data Store. Do not change this value unless you want to locate data in a 3rd party store. | n/a |

### More information on the `key` field

The following table explains the `key` field in a market data rule in more detail.

| **Market data category** | **LUSID market data store** | `key` field syntax | **Examples** |
| --- | --- | --- | --- |
| [Market price](/v1/docs/how-do-i-upload-a-market-price-or-fx-spot-rate-to-the-quote-store) or [interest rate fixing](/v1/docs/how-do-i-load-an-interest-rate-fixing-into-the-lusid-quote-store) | Quote Store | `Quote.&lt;IdentifierType&gt;.&lt;IdentifierValue&gt;` | - `Quote.RIC.*` to locate prices loaded with an [instrumentIdType](/v1/docs/how-do-i-upload-a-market-price-or-fx-spot-rate-to-the-quote-store#using-the-lusid-api) of `RIC`. - `Quote.LusidInstrumentId.*` to locate prices loaded with an `instrumentIdType` of `LusidInstrumentId`. - `Quote.Isin.GB0031348658` to locate prices loaded with an `instrumentIdType` of `Isin` and `instrumentId` of `GB0031348658`. > **Note**: `Quote.*.*` is maintained for backwards-compatibility and is equivalent to `Quote.LusidInstrumentId.*` Likewise, `Equity.*.*` is a synonym for `Quote.*.*` We recommend removing these key values from your recipes. |
| [FX spot rate](/v1/docs/how-do-i-load-fx-spot-rates-into-the-quote-store) | `Fx.&lt;FgnCcy&gt;.&lt;DomCcy&gt;` | - `Fx.USD.GBP` to locate rates loaded with an [instrumentIdType](/v1/docs/how-do-i-load-fx-spot-rates-into-the-quote-store) of `CurrencyPair` and `instrumentId` of `USD/GBP` (note forward slash). - `Fx.*.*` to locate any FX rate. |
| [Inflation fixing](/v1/docs/how-do-i-upload-an-inflation-fixing-to-the-lusid-quote-store) | Data loaded with `quoteType` of `Index` | `Inflation.InflationIndex.&lt;IdentifierType&gt;.&lt;IdentifierValue&gt;` | - `Inflation.InflationIndex.UKRPI` to locate index fixings loaded with an [instrumentIdType](/v1/docs/how-do-i-upload-an-inflation-fixing-to-the-lusid-quote-store#using-the-lusid-api) of `ClientInternal` and `instrumentId` of `UKRPI`. - `Inflation.InflationIndex.*` to locate any index fixing. |
| Data loaded with `quoteType` of `Ratio` | `Inflation.InflationRatio.&lt;IdentifierType&gt;.&lt;IdentifierValue&gt;` | - `Inflation.InflationRatio.ClientInternal.MX.UDI` to locate ratio fixings loaded with an [instrumentIdType](/v1/docs/how-do-i-upload-an-inflation-fixing-to-the-lusid-quote-store#using-the-lusid-api) of `ClientInternal` and `instrumentId` of `MX.UDI`. - `Inflation.InflationRatio.*` to locate any ratio fixing. |
| Data loaded with `quoteType` of `InflationAssumption` | `Inflation.InflationAssumption.&lt;IdentifierType&gt;.&lt;IdentifierValue&gt;` | - `Inflation.InflationAssumption.ClientInternal.IPCA` to locate assumption fixings loaded with an [instrumentIdType](/v1/docs/how-do-i-upload-an-inflation-fixing-to-the-lusid-quote-store#using-the-lusid-api) of `ClientInternal` and `instrumentId` of `IPCA`. - `Inflation.InflationAssumption.*` to locate any assumption fixing. |
| Discount factor curve | [Complex Market Data Store](/v1/docs/how-do-i-upload-an-object-to-the-complex-market-data-store) | `Rates.&lt;Ccy&gt;.&lt;Ccy&gt;OIS` | - `Rates.USD.USDOIS` to locate USD discount factor curves with a [marketAsset](/v1/docs/categories-of-complex-market-data) of `USD/USDOIS`. - `Rates.*.*` to locate any discounting curve. |
| FX forward curve | `FxForwards.&lt;DomCcy&gt;.&lt;FgnCcy&gt;.FxFwdCurve` | - `FxForwards.GBP.USD.FxFwdCurve` to locate a curve with a [marketAsset](/v1/docs/categories-of-complex-market-data) of `GBP/USD/FxFwdCurve`. - `FxForwards.*.*.*` to locate any curve. |
| Interest rate projection curve | `Rates.&lt;Ccy&gt;.&lt;Tenor&gt;.&lt;Index&gt;` | - `Rates.GBP.1Y.LIBOR` to locate one year GBP - LIBOR IR projection curves loaded with a [marketAsset](/v1/docs/categories-of-complex-market-data) of `GBP/1Y/LIBOR`. - `Rates.*.*.*` to locate any IR projection curve. |
| Credit curve | `Credit.&lt;REDCode&gt;.&lt;Ccy&gt;.&lt;Seniority&gt;.&lt;RestructuringClause&gt;` | - `Credit.FF667M.USD.SNR.MR` to locate senior CDS with modified restructuring loaded with a [marketAsset](/v1/docs/categories-of-complex-market-data) of `FF667M/USD/SNR/MR`. - `Credit.*.*.*.*` to locate any credit curve. |
| Equity volatility surface | `EquityVol.&lt;RIC&gt;.&lt;Ccy&gt;.&lt;N-or-LN&gt;` | - `EquityVol.AAPL.USD.LN` to locate Apple log normal volatility surfaces loaded with a [marketAsset](/v1/docs/categories-of-complex-market-data) of `AAPL/USD/LN`. - `EquityVol.*.*.*` to locate any equity volatility surface. |
| FX volatility surface | `FxVol.&lt;DomCcy&gt;.&lt;FgnCcy&gt;.&lt;N-or-LN&gt;` | - `FxVol.GBP.USD.N` to locate GBPUSD normal volatility surfaces loaded with a [marketAsset](/v1/docs/categories-of-complex-market-data) of `GBP/USD/N`. - `FxVol.*.*.*` to locate any FX volatility surface. |
| Interest rate volatility surface | `IrVol.&lt;Ccy&gt;.&lt;N-or-LN&gt;` | - `IrVol.USD.LN` to locate USD log-normal volatility surfaces loaded with a [marketAsset](/v1/docs/categories-of-complex-market-data) of `USD/LN`. - `IrVol.*.*` to locate any IR volatility surface. |

### More information on the `quoteInterval` field

Controls the time window in which LUSID searches for valid market data relative to the valuation date. Uses `&lt;startdate&gt;.&lt;enddate&gt;` [tenor syntax](/v1/docs/specifying-tenors-in-lusid), where `&lt;startdate&gt;` is how far back to look and `&lt;enddate&gt;` is the most recent point allowed. Both are expressed as offsets before the valuation datetime.

The implicit default is `1D.0D`, meaning LUSID accepts market data effective from 00:00:00 one calendar day before the valuation date up to the valuation datetime itself. If no matching data is found within the window, a [market data resolver error](/v1/docs/recipes-locating-market-data#resolving-market-data-errors) occurs. Common patterns are:

| `quoteInterval` value | LUSID accepts data… |
| --- | --- |
| `1D.0D` | From the start of the previous calendar day up to the valuation datetime. |
| `1W.0D` | From up to 7 days before the valuation date; useful for infrequently priced instruments. |
| `0D.0D` | From the start of the valuation date only; no look back to the previous day. |
| `2D.1D` | From 2 days ago, but not including the valuation date itself; useful when same-day prices should not be used. |

Note if you use `Bd` (business day) instead of `D` and your valuation schedule specifies a [holiday calendar](/v1/docs/using-calendars-in-lusid#using-calendars-to-perform-valuations-on-business-days), LUSID ignores non-business days and extends the window accordingly. This ensures weekends and holidays do not reduce the look-back period. Consider the following comparisons:

| `quoteInterval` value | Valuation date & time | `&lt;startdate&gt;` | `&lt;enddate&gt;` |
| --- | --- | --- | --- |
| `1D.0D` (the default) | Fri 07/10/2022 13:30 | Thu 06/10/2022 00:00 | Fri 07/10/2022 13:30 |
| Sat 08/10/2022 13:30 | Fri 07/10/2022 00:00 | Sat 08/10/2022 13:30 |
| Sun 09/10/2022 13:30 | Sat 08/10/2022 00:00 | Sun 09/10/2022 13:30 |
| Mon 10/10/2022 13:30 | Sun 09/10/2022 00:00 | Mon 10/10/2022 13:30 |
| Tue 11/10/2022 13:30 | Mon 10/10/2022 00:00 | Tue 11/10/2022 13:30 |
| `0D.0D` | Fri 07/10/2022 13:30 | Fri 07/10/2022 00:00 | Fri 07/10/2022 13:30 |
| Sat 08/10/2022 13:30 | Sat 08/10/2022 00:00 | Sat 08/10/2022 13:30 |
| Sun 09/10/2022 13:30 | Sun 09/10/2022 00:00 | Sun 09/10/2022 13:30 |
| Mon 10/10/2022 13:30 | Mon 10/10/2022 00:00 | Mon 10/10/2022 13:30 |
| Tue 11/10/2022 13:30 | Tue 11/10/2022 00:00 | Tue 11/10/2022 13:30 |
| `0Bd.0D` | Fri 07/10/2022 13:30 | Fri 07/10/2022 00:00 | Fri 07/10/2022 13:30 |
| Sat 08/10/2022 13:30 | Fri 07/10/2022 00:00 | Sat 08/10/2022 13:30 |
| Sun 09/10/2022 13:30 | Fri 07/10/2022 00:00 | Sun 09/10/2022 13:30 |
| Mon 10/10/2022 13:30 | Mon 10/10/2022 00:00 | Mon 10/10/2022 13:30 |
| Tue 11/10/2022 13:30 | Tue 11/10/2022 00:00 | Tue 11/10/2022 13:30 |
| `0Bd.0Bd` | Fri 07/10/2022 13:30 | Fri 07/10/2022 00:00 | Fri 07/10/2022 13:30 |
| Sat 08/10/2022 13:30 | Fri 07/10/2022 00:00 | Fri 07/10/2022 13:30 |
| Sun 09/10/2022 13:30 | Fri 07/10/2022 00:00 | Fri 07/10/2022 13:30 |
| Mon 10/10/2022 13:30 | Mon 10/10/2022 00:00 | Mon 10/10/2022 13:30 |
| Tue 11/10/2022 13:30 | Tue 11/10/2022 00:00 | Tue 11/10/2022 13:30 |
| `1Bd.0D` | Fri 07/10/2022 13:30 | Thu 06/10/2022 00:00 | Fri 07/10/2022 13:30 |
| Sat 08/10/2022 13:30 | Fri 07/10/2022 00:00 | Sat 08/10/2022 13:30 |
| Sun 09/10/2022 13:30 | Fri 07/10/2022 00:00 | Sun 09/10/2022 13:30 |
| Mon 10/10/2022 13:30 | Fri 07/10/2022 00:00 | Mon 10/10/2022 13:30 |
| Tue 11/10/2022 13:30 | Mon 10/10/2022 00:00 | Tue 11/10/2022 13:30 |
| `1Bd.1Bd` | Fri 07/10/2022 13:30 | Thu 06/10/2022 00:00 | Thu 06/10/2022 13:30 |
| Sat 08/10/2022 13:30 | Fri 07/10/2022 00:00 | Fri 07/10/2022 13:30 |
| Sun 09/10/2022 13:30 | Fri 07/10/2022 00:00 | Fri 07/10/2022 13:30 |
| Mon 10/10/2022 13:30 | Fri 07/10/2022 00:00 | Fri 07/10/2022 13:30 |
| Tue 11/10/2022 13:30 | Mon 10/10/2022 00:00 | Mon 10/10/2022 13:30 |

### Resolving market data errors

When you call the `GetValuation` API, you may see a `MarketResolverFailure` similar to the one below if one or more market data rules fails to locate suitable market data in an appropriate store. The first step is to make sure field values in the market data rule match those in market data objects according to the table above. If they do, you can try setting the `allowPartiallySuccessfulEvaluation` [pricing model option](/v1/docs/troubleshooting-valuation-errors) to `True` to relax validation and call the API again, though of course missing market data will not be used in calculations:

```json
{"name":"MarketResolverFailure","errorDetails":[{"id":"Failed to resolve market data item 
[Provider: Edi, PriceSource: , InstrumentId: BBG000C05BD1, InstrumentIdType: Figi, QuoteType: Price, Field: mid].
","detail":"Attempted to resolve with supplier [Edi] and sourceSystem [Lusid] in scope [recipe1] for effective date 
[07/03/2022 00:00:00], quoteInterval [None] and with as-date [2022-07-05T13:35:54.6077760+00:00], but failed 
because failed to find quote in store."}…]",…}
```

[More on troubleshooting valuation](/v1/docs/troubleshooting-valuation-errors).

## Overriding market data rules for particular instruments

You have the option to specify market data rules in the `specificRules` array that only apply to a matching set of instruments. If you do, these rules are preferred over rules in the `marketRules` array for just those instruments.

Consider the following example, of one standard [MarketDataKeyRule](https://www.lusid.com/docs/api/lusid/schemas/MarketDataKeyRule/) object in the `marketRules` array sourcing market prices for instruments from a particular market data provider, and two [MarketDataSpecificRule](https://www.lusid.com/docs/api/lusid/schemas/MarketDataSpecificRule/) objects in the `specificRules` array specifying preferential providers for:

- Equity instruments with a particular base currency.
- Instruments of any type with a particular property applied:

![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/Recipe.png)

Note the following:

- Rules in the `specificRules` array are processed first, before rules in the `marketRules` array.
- You can have any number of rules in the `specificRules` array.
- The order of rules in the `specificRules` array is significant; for each instrument, LUSID attempts to locate market data using the **first matching rule found**. If no market data can be located, LUSID tries the next matching rule.

For each rule, you can match instruments using any of the following characteristics in a [DependencySourceFilter](https://www.lusid.com/docs/api/lusid/schemas/DependencySourceFilter/) object:

| Characteristic | Field in `DependencySourceFilter` | Allowed values |
| --- | --- | --- |
| Instrument type | `instrumentType` | Any supported [LUSID instrument type](/v1/docs/supported-instruments-in-lusid). |
| Asset class | `assetClass` | LUSID automatically categorises instrument types into [asset classes](/v1/docs/how-does-lusid-categorise-instruments-into-asset-classes). |
| Instrument base currency | `domCcy` | Any ISO 4217 currency code. |
| Whether a holding in the instrument in a portfolio has positive units or negative units | `longOrShortIndicator` | `Long` or `Short`. |
| Instrument feature | `addressKeyFilters` | One or more OTC instrument features; see below. |
| Property | `addressKeyFilters` | One or more portfolio or instrument properties; see below. |

### Matching instruments on features and properties

You can match instruments on any combination of:

- [Properties](/v1/docs/properties) (custom, system or derived) in the `Portfolio` domain, for example `Portfolio/People/Manager`.
- Properties (custom, system or derived) in the `Instrument` domain, for example `Instrument/IBOR/CreditRating`.
- [Features for OTC instruments](/v1/docs/what-are-instrument-features), for example `Instrument/Features/ExerciseType`.

To do this, you can specify any number of [AddressKeyFilter](https://www.lusid.com/docs/api/lusid/schemas/AddressKeyFilter/) objects in the `addressKeyFilters` array in a `DependencySourceFilter` object. For each:

- The `left` field must reference the 3 stage key of the property or feature.
- The `operator` field must either be `eq` or `neq`.
- The `right` field must be a string value on which to match, with a `resultValueType` of `ResultValueString`. Note this means only properties with an underlying data type of `String` are currently supported.

Consider the following example, of three `AddressKeyFilter` objects that together combine to match only instruments that are American options with a credit rating other than B or B+:

```json
"specificRules": [
  {
    "key": "Quote.Figi.*",
    "supplier": "Lusid",
    "dataScope": "My-InHouse-Prices",
    "quoteType": "Price",
    "field": "mid",
    "dependencySourceFilter": {
      "addressKeyFilters": [
        {
          "left": "Instrument/Features/ExerciseType",
          "operator": "eq",
          "right": {
            "value": "American",
            "resultValueType": "ResultValueString"
          }
        },
        {
          "left": "Instrument/IBOR/CreditRating",
          "operator": "neq",
          "right": {
            "value": "B",
            "resultValueType": "ResultValueString"
          }
        },
        {
          "left": "Instrument/IBOR/CreditRating",
          "operator": "neq",
          "right": {
            "value": "B+",
            "resultValueType": "ResultValueString"
          }
        }
      ]
    }
  }
]
```

## **Grouping market data rules and performing preference operations**

You can specify market data rules in the `groupedMarketRules` array as well as, or instead of, the `marketRules` array:

- You might specify market data rules in both arrays in order to provide sophisticated fallback behavior from a preferred supplier of quotes to a group of secondary suppliers.
- You might specify market data rules in just the `groupedMarketRules` array to perform an operation such as taking the most recent or highest price from a group of suppliers, or even synthesising an average price.

You can of course combine these use cases to perform operations on a group of secondary suppliers.

Consider the following example, of a `market` object with one market data rule for prices in the `marketRules` array and three inside a single group in the `groupedMarketRules` array, intended for a portfolio valuation on 7 March 2022. Note the operation on the group of rules is `FirstLatest`:

![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/f997ef74-81c1-48f0-aeda-3c7949dece4b.png)

Note the following:

- A recipe should have at least one market data rule in order to be functional. It can be in the `marketRules` array, or on its own in a group in the `groupedMarketRules` array.
- You can have any number of market data rules in the `marketRules` array, and any number of rules in any number of groups in the `groupedMarketRules` array.
- LUSID reads the `marketRules` array first, then the `groupedMarketRules` array.
- Within the `marketRules` array, the order of market data rules is significant.
- Within the `groupedMarketRules` array, the order of groups is significant. The order of market data rules within a group is significant in some circumstances, depending on the operation being performed.
- Within the `groupedMarketRules` array, all market data rules must have the same `key` and `quoteType`. One exception is for `quoteType` with the `FirstLatest` operation, where you can mix the `Price` and `DirtyPrice` quote types.
- LUSID uses the **first matching rule found** for a particular category of market data.

The following `marketDataKeyRuleGroupOperations` are available:

| **Operation** | **Explanation** |
| --- | --- |
| `FirstLatest` | The rule retrieving the most recent market data item (market price, FX rate and so on) is used. If all items were loaded with exactly the same date and time, the rule specified first in the group is used. |
| `FirstMinimum` | The rule retrieving the lowest value market data item is used. If all items have exactly the same value, the rule specified first in the group is used. |
| `FirstMaximum` | The rule retrieving the highest value market data item is used. If all items have exactly the same value, the rule specified first in the group is used. |
| `AverageOfAllQuotes` | These operations are different in that LUSID synthesises a new market data item rather than using a rule to retrieve an existing item. See the section below. |
| `AverageOfQuotesFound` |

### Example: Synthesising an average market data item

You can request that LUSID retrieves market data items from all the rules in a group and synthesises a new market data item representing an average:

- Choose the `AverageOfAllQuotes` operation if you want the request to fail if a single rule in the group fails to retrieve a market data item.
- Choose the `AverageOfQuotesFound` operation to ignore failures unless all rules fail to retrieve items.

For example, imagine you have [upserted to the LUSID Quote Store](/v1/docs/how-do-i-upload-a-market-price-or-fx-spot-rate-to-the-quote-store) the following three market prices for a particular stock:

| **Data field** | **Price #1** | **Price #2** | **Price #3** | **Notes** |
| --- | --- | --- | --- | --- |
| `provider` | `DataScope` | `Lusid` | `Bloomberg` |  |
| `priceSource` | `Tradeweb` |  | `BBG` | This field is optional when upserting. |
| `instrumentIdType` | `RIC` | `RIC` | `RIC` | In order to synthesise an average, all instrument identifier types must be the same. |
| `instrumentId` | `ABC.N` | `ABC.N` | `ABC.N` | In order to synthesise an average, all values must identify the same instrument. |
| `quoteType` | `Price` | `Price` | `Price` | In order to synthesise an average, all quote types must be the same. |
| `field` | `open` | `bid` | `ask` |  |
| `effectiveAt` | `2024-01-02T09:00:00Z` | `2024-01-02T11:00:00Z` | `2024-01-02T12:00:00Z` |  |
| `metricValue.value` | `105` | `1.1` | `9.7` |  |
| `metricValue.unit` | `USD` | `USD` | `USD` | In order to synthesise an average, all currency units must be the same. |
| `scaleFactor` | `100` |  | `10` | This field is optional when upserting; it defaults to 1. |
| `lineage` | `A Refinitiv quote` |  | `A BBG quote` | This field is optional when upserting. |
| `uploadedBy` | `00ubs2temhwlYz2lI2p7` | `00ubk5b5apJVhOlAg2p8` | `00uhj7Gh8kklio92pl97` | This field is automatically populated by LUSID. |
| `asAt` | `2024-01-02T13:00:00Z` | `2024-01-02T14:00:00Z` | `2024-01-02T15:00:00Z` | This field is automatically populated by LUSID. |

...and created a recipe with the following three market data rules in a group with the `AverageOfAllQuotes` operation:

```json
"market": {
  "groupedMarketRules": [
    {
      "marketDataKeyRuleGroupOperation": "AverageOfAllQuotes",
      "marketRules": [
        {
          "key": "Quote.RIC.*",
          "supplier": "DataScope",
          "dataScope": "RefinitivPrices",
          "quoteType": "Price",
          "field": "open",
        },
        {
          "key": "Quote.RIC.*",
          "supplier": "Lusid",
          "dataScope": "LusidPrices",
          "quoteType": "Price",
          "field": "bid",
        },
        {
          "key": "Quote.RIC.*",
          "supplier": "Bloomberg",
          "dataScope": "BBGPrices",
          "quoteType": "Price",
          "field": "ask",
        }
      ]
    }
  ]
},
...
```

Assuming all three original market prices can be located, LUSID synthesises the following average price:

| **Data field** | **Synthesised value** | **Notes** |
| --- | --- | --- |
| `provider` | `Lusid` | The provider is always set to this value, irrespective of the original providers. |
| `priceSource` | `AverageOfAllQuotes('Tradeweb', '', 'BBG')` | The prefix is `AverageOfQuotesFound` if the other average operation is chosen. |
| `instrumentIdType` | `RIC` |  |
| `instrumentId` | `ABC.N` |  |
| `quoteType` | `Price` |  |
| `field` | `AverageOfAllQuotes('open', 'bid', 'ask')` | The prefix is `AverageOfQuotesFound` if the other average operation is chosen. |
| `effectiveAt` | `2024-01-02T12:00:00Z` | The latest original datetime is used. |
| `metricValue.value` | `1.04` | In this example, the original scale factors are different, so the synthesised value is unitised. The calculation is as follows: Original price #1: `105 / 100 = 1.05` Original price #2: `1.1 / 1 = 1.1` Original price #3: `9.8 / 10 = 0.97` Average price: `(1.05 + 1.1 + 0.97) / 3 = 1.04` |
| `metricValue.unit` | `USD` |  |
| `scaleFactor` | `1` | In this example, since the synthesised value is unitised, the scale factor is set to 1. If the original scale factors were the same and not 1, then this field is set to that scale factor. |
| `lineage` | `AverageOfAllQuotes('A Refinitiv quote', '', 'A BBG quote')` | The prefix is `AverageOfQuotesFound` if the other average operation is chosen. |
| `uploadedBy` | `SystemComputed-Average` | The user is always set to this value, irrespective of the original users. |
| `asAt` | `2024-01-03T17:00:00Z` | The *as at* datetime of the valuation operation is used. |

Note a synthesised market data item is not upserted to the Quote Store, so it cannot be [retrieved](/v1/docs/retrieving-data-from-the-quote-store) by an API such as `ListQuotesForScope`. However, it is available in the [manifest](/v1/docs/how-do-i-access-a-valuation-manifest) for a valuation operation.

## Overriding market prices or falling back to holding pricing data

If the valuation operation you want to perform requires a market price (note, not an FX rate or complex market data), you can optionally specify override and/or fallback rules to use holding-level pricing data such as unit cost or last traded price:

- You might specify an override rule to use holding-level data in preference to a market price stored in the Quote Store.
- You might specify a fallback rule to use holding-level data if a market price cannot be located in the Quote Store.

Rules can be global or specific; that is, apply to every type of instrument or just to a particular type or asset class. Note if you specify a global override rule and no specific overrides then LUSID never attempts to locate market prices in the Quote Store.

> **Note**: Holding-level pricing data may differ between portfolios, so if you are valuing multiple portfolios this may mean they are valued differently, even with equal positions in the same instruments.

To do this, specify a [HoldingPricingInfo](https://www.lusid.com/docs/api/lusid/schemas/HoldingPricingInfo/) object in the `pricing` object of a recipe (not the `market` object):

![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image(192).png)

Depending on the holding-level pricing option chosen below, LUSID manufactures a ‘virtual’ quote for the instrument appropriate to each portfolio being valued:

| Available pricing option | Virtual quote |
| --- | --- |
| `UnitCostClean` | The virtual quote price is calculated as `cost.amount / (units * contractSize)`, where `contractSize` is `1` if not explicitly specified in the instrument definition. The virtual quote type is set to `CleanPrice` to exclude accrued interest from the valuation. |
| `UnitCostDirty` | The virtual quote price is calculated as `cost.amount / (units * contractSize)`, where `contractSize` is `1` if not explicitly specified in the instrument definition. The virtual quote type is set to `DirtyPrice` to include accrued interest in the valuation. |
| `UnitAmortisedCostClean` | The virtual quote price is calculated as `amortisedCost.amount / (units * contractSize)`, where `contractSize` is `1` if not explicitly specified in the instrument definition. The virtual quote type is set to `CleanPrice` to exclude accrued interest from the valuation. |
| `UnitAmortisedCostDirty` | The virtual quote price is calculated as `amortisedCost.amount / (units * contractSize)`, where `contractSize` is `1` if not explicitly specified in the instrument definition. The virtual quote type is set to `DirtyPrice` to include accrued interest in the valuation. |
| `LastTradedPrice` | The virtual quote price is the `transactionPrice.price` of the most recent transaction contributing to the holding, divided by the `tradingConventions.priceScaleFactor` [specified in the instrument definition](/v1/docs/scaling-a-market-price) (which defaults to `1` if not applicable). The virtual quote type is taken from the `transactionPrice.type` of the most recent transaction, for example `CleanPrice` or `DirtyPrice`. Note that `Price` is equivalent to `CleanPrice`. |

### Example: Specifying override rules

In the following example, if the valuation operation requires market prices:

- Instruments of type `ComplexBond` are valued using `UnitAmortisedCostDirty`.
- Instruments of type `Equity` trading in euros are valued using `LastTradedPrice`.
- Instruments of every other type are valued using prices located in the Quote Store. Since no global `overrideField` is provided, a [market data error](/v1/docs/recipes-locating-market-data#resolving-market-data-errors) occurs if no such prices can be found:

```json
"pricing": {
  "holdingPricingInfo": {
    "overrideField": "None",
    "specificOverrides": [
      {
        "dependencySourceFilter": {
          "instrumentType": "ComplexBond"
        },
        "field": "UnitAmortisedCostDirty"
      },
      {
        "dependencySourceFilter": {
          "instrumentType": "Equity",
          "domCcy": "EUR"
        },
        "field": "LastTradedPrice"
      }
    ]
  },
  ...
}
```

### Example: Specifying fallback rules

In the following example, if the valuation operation requires market prices that cannot be located in the Quote Store:

- Instruments of type `Bond` are valued using `UnitCostClean`.
- Instruments of every other type are valued using `LastTradedPrice`:

```json
"pricing": {
  "holdingPricingInfo": {
    "fallbackField": "LastTradedPrice",
    "specificFallbacks": [
      {
        "dependencySourceFilter": {
          "instrumentType": "Bond"
        },
        "field": "UnitCostClean"
      }
    ]
  },
  ...
}
```
