Skip to content

Fixings are thrown out, breaking valuation of several instruments #295

@gbfredrik

Description

@gbfredrik

Hi,
A bug seems to have been introduced with ORE 13 which is causing fixings to be disregarded because they are not registered as required. This means it is no longer possible to value a portfolio of e.g. FX average forwards . I believe the behavior stems from this commit, specifically in MarketDataLoader::populateFixings: d0e0c00.

The cause
After the changes, the default behavior is no longer to load all fixings provided. Instead, it:

  1. asks the portfolio for its required fixings. (See lines 170-175 below). This always returns an empty map because the trades have not been built yet, consistent with the warning in the docstring of Trade::fixings.

  2. asks some market data objects (specifically IndexForwardingCurves, Zero/YoYInflationCurves, and CommodityCurves) if they require fixings (see lines 178-180 below). If you have, say, USD-SOFR under your IndexForwardingCurves, then your SOFR fixings will be marked as necessary. On the other hand, if your market only has discounting and generic yield curves, no fixings are flagged for usage.

Then, only if steps 1 and/or 2 yielded any required fixings, the condition on line 191 will evaluate to true. If so, MarketDataCsvLoaderImpl::retrieveFixings will trigger and by default load any other fixings provided to ORE in the CSV.

Thus, if I run a portfolio of only FX average forwards (assuming no index, inflation, or commodity curves are used), it will not keep any of the FX fixings provided and then they cannot be priced. This can happen for any portfolio not using the curve types mentioned.

void MarketDataLoader::populateFixings(
const std::vector<QuantLib::ext::shared_ptr<ore::data::TodaysMarketParameters>>& todaysMarketParameters,
const std::set<QuantLib::Date>& loaderDates) {
LOG("Asking portfolio for its required fixings");
FixingMap portfolioFixings;
std::map<std::pair<std::string, QuantLib::Date>, std::set<QuantLib::Date>> lastAvailableFixingLookupMap;
// portfolio fixings will warn if missing
if (inputs_->portfolio()) {
portfolioFixings = inputs_->portfolio()->fixings();
LOG("The portfolio depends on fixings from " << portfolioFixings.size() << " indices");
for (const auto& it : portfolioFixings)
addRelevantFixings(it, lastAvailableFixingLookupMap);
}
LOG("Add fixings possibly required for bootstrapping TodaysMarket");
for (const auto& tmp : todaysMarketParameters) {
for (const auto d : loaderDates)
addMarketFixingDates(d, fixings_, *tmp);
LOG("Add fixing possibly required for equity index delta risk decomposition")
additional_equity_fixings(fixings_, *tmp, inputs_->refDataManager(),
inputs_->curveConfigs().get());
}
if (inputs_->eomInflationFixings()) {
LOG("Adjust inflation fixing dates to the end of the month before the request");
amendInflationFixingDates(fixings_);
}
if (fixings_.size() > 0 && impl_)
impl()->retrieveFixings(loader_, fixings_, lastAvailableFixingLookupMap);
applyFixings(loader_->loadFixings());

The solution
Hoping for your input here! I imagine step 1 should be able to retrieve the trades' required fixings, then the condition would correctly evaluate to true and load them in. I can naively avoid the issue like this for the time being by forcing fixings to be retrieved (but that's likely not the way to go for a proper solution):

Image

Any feedback on this would be much appreciated. I can provide a minimal input data set to replicate the issue, if you're interested.

I also want to offer a major thank you for the work that went into the 13th release; it is very valuable to us!

Kind regards,
Fredrik

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions