In this tutorial we'll see how to construct a recipe to value a GBP-denominated portfolio containing US and UK equities, using market prices and FX spot rates from Bloomberg pre-loaded into the LUSID Quote Store.
A recipe must have a scope and code that together uniquely identify it, and optionally a description. The core framework of a recipe is thus a JSON document as follows:
{
"scope": "my-recipe-scope",
"code": "my-recipe-code",
"description": "A simple recipe to value equities using price * units",
"market": {},
"pricing": {},
"aggregation": {},
"holding": {}
}
In order to be functional, a recipe must have at least one market data rule in the market
section. Every other aspect of a recipe defaults to a sensible value. To value a basket of equities, typically there is no need to change pricing models (that is, populate the pricing
section) or set options (the aggregation
and holding
sections).
Note: You are likely to want to change pricing models and set options to value instruments of types other than
Equity
. This impacts the market data required, and therefore the composition of themarket
section.
Constructing a market data rule for market prices
We must construct a market data rule for each category of market data to locate; that is, one rule for market prices, and a second rule for FX rates.
To do this for market prices, we need to understand how they were loaded into the LUSID Quote Store, for example:
Market price in LUSID Quote Store | Matching market data rule field in recipe | Notes | |
---|---|---|---|
Data field | Example value | ||
|
|
| The recipe value must match the Quote Store value exactly. |
|
|
| The recipe value must match the Quote Store value exactly. More information. |
|
|
| The recipe |
|
| ||
|
|
| The recipe value must match the Quote Store value exactly. More information |
|
|
| The recipe value must match the Quote Store value exactly. More information. |
|
| n/a | There is no matching field in the recipe, but if the valuation date is >24 hours after the effective date, you might need to set the |
Our first market data rule to locate prices might look like this:
{
"scope": "my-recipe-scope",
"code": "my-recipe-code",
"description": "A simple recipe to value equities using price * units",
"market": {
"marketRules": [
{
"key": "Quote.Figi.*",
"supplier": "Bloomberg",
"dataScope": "My-BBG-Prices",
"quoteType": "Price",
"field": "mid"
}
]
},
"pricing": {},
"aggregation": {},
"holding": {}
}
Constructing a market data rule for FX rates
In exactly the same way, we need to understand how FX rates were loaded into the LUSID Quote Store, for example:
FX rate in LUSID Quote Store | Matching market data rule field in recipe | Notes | |
---|---|---|---|
Data field | Example value | ||
|
|
| The recipe value must match the Quote Store value exactly. |
|
|
| The recipe value must match the Quote Store value exactly. More information. |
|
|
| The recipe |
|
| ||
|
|
| The recipe value must match the Quote Store value exactly. More information |
|
|
| The recipe value must match the Quote Store value exactly. More information. |
|
| n/a | There is no matching field in the recipe, but if the valuation date is >24 hours after the effective date, you might need to set the |
Our second market data rule to locate FX rates might look like this:
{
"scope": "my-recipe-scope",
"code": "my-recipe-code",
"description": "A simple recipe to value equities using price * units",
"market": {
"marketRules": [
{
"key": "Quote.Figi.*",
"supplier": "Bloomberg",
"dataScope": "My-BBG-Prices",
"quoteType": "Price",
"field": "mid"
},
{
"key": "Fx.USD.GBP",
"supplier": "Bloomberg",
"dataScope": "My-BBG-Rates",
"quoteType": "Rate",
"field": "mid"
}
]
},
"pricing": {},
"aggregation": {},
"holding": {}
}
Note that a recipe is a hierarchical document, and LUSID reads the market data rule for prices before the market data rule for FX rates. Since these rules are for different categories of market data, there is no impact. However, if you specify two rules for the same category of market data (that is, with the same key, for example one rule for Bloomberg prices and a second rule for Refinitiv prices), then prices from Bloomberg are preferred. You can use this feature to set up a hierarchy of preferred suppliers.
Synthesising a mid price from a bid and ask price
What if Bloomberg does not always supply a mid price, but instead provides separate bid and ask prices? By default, the valuation will fail, because the first market data rule is looking exclusively for mid prices. However, we can fall back to synthesising an average from the bid and ask, and use that instead.
Imagine bid and ask prices like these have been loaded into the Quote Store:
Data field | Example bid price | Example ask price | Notes |
---|---|---|---|
|
|
|
|
|
|
|
|
|
|
| To synthesise an average, instrument identifier types must be the same. |
|
|
| To synthesise an average, values must identify the same instrument. |
|
|
| To synthesise an average, quote types must be the same. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| To synthesise an average, currency units must be the same. |
We can group market data rules and perform an operation on them such as synthesising an average value, or taking the latest or highest value.
To do this, we add a groupedMarketRules
section and nominate an operation, in this case AverageOfAllQuotes
. Note the composition of market data rules themselves within a group is identical, but that other rules apply to groups:
{
"scope": "my-recipe-scope",
"code": "my-recipe-code",
"description": "A simple recipe to value equities using price * units",
"market": {
"marketRules": [
{
"key": "Quote.Figi.*",
"supplier": "Bloomberg",
"dataScope": "My-BBG-Prices",
"quoteType": "Price",
"field": "mid"
},
{
"key": "Fx.USD.GBP",
"supplier": "Bloomberg",
"dataScope": "My-BBG-Rates",
"quoteType": "Rate",
"field": "mid"
}
],
"groupedMarketRules": [
{
"marketDataKeyRuleGroupOperation": "AverageOfAllQuotes",
"marketRules": [
{
"key": "Quote.Figi.*",
"supplier": "Bloomberg",
"dataScope": "My-BBG-Prices",
"quoteType": "Price",
"field": "bid"
},
{
"key": "Quote.Figi.*",
"supplier": "Bloomberg",
"dataScope": "My-BBG-Prices",
"quoteType": "Price",
"field": "ask"
}
]
}
]
},
"pricing": {},
"aggregation": {},
"holding": {}
}
When we value the portfolio now, an equity without a mid price is valued using an average of the bid and ask prices.
With our recipe complete, we can upsert it to LUSID using either:
The UpsertConfigurationRecipe API.
The LUSID web app, by navigating to the Data Management > Recipes dashboard, clicking the Create recipe button, and pasting the JSON document into the box.