@@ -143,14 +143,16 @@ DecomposedSensitivityStream::decomposeSurvivalProbability(const SensitivityRecor
143143}
144144
145145std::vector<SensitivityRecord> DecomposedSensitivityStream::decomposeEquityRisk (const SensitivityRecord& sr) const {
146- auto eqRefData = refDataManager_->getData (" EquityIndex" , sr.key_1 .name );
147- auto decomposeEquityIndexData = boost::dynamic_pointer_cast<ore::data::EquityIndexReferenceDatum>(eqRefData);
148- if (decomposeEquityIndexData != nullptr ) {
149- auto decompResults =
150- decomposeEqComIndexRisk (sr.delta , decomposeEquityIndexData, ore::data::CurveSpec::CurveType::Equity);
151- scaleFxRisk (decompResults.fxRisk , decomposeEquityIndexData->id ());
152- return createDecompositionRecords (decompResults.equityDelta , decompResults.fxRisk , decompResults.indexCurrency ,
153- sr);
146+ std::string indexName = sr.key_1 .name ;
147+ auto indexCurrency = curveCurrency (indexName, ore::data::CurveSpec::CurveType::Equity);
148+ if (refDataManager_->hasData (" EquityIndex" , indexName)) {
149+ auto refDatum = refDataManager_->getData (" EquityIndex" , indexName);
150+ auto indexRefDatum = boost::dynamic_pointer_cast<ore::data::IndexReferenceDatum>(refDatum);
151+ auto decompResults = decomposeIndex (sr.delta , indexRefDatum, ore::data::CurveSpec::CurveType::Equity);
152+ scaleFxRisk (decompResults.fxRisk , indexName);
153+ convertFxRiskToBaseCurrency (decompResults.fxRisk , indexCurrency);
154+ return createDecompositionRecords (
155+ decompResults.equityDelta , decompResults.fxRisk , decompResults.indexCurrency , sr);
154156 } else {
155157 auto subFields = std::map<std::string, std::string>({{" tradeId" , sr.tradeId }});
156158 StructuredAnalyticsErrorMessage (" CRIF Generation" , " Equity index decomposition failed" ,
@@ -164,53 +166,54 @@ std::vector<SensitivityRecord> DecomposedSensitivityStream::decomposeEquityRisk(
164166
165167std::vector<SensitivityRecord>
166168DecomposedSensitivityStream::decomposeCurrencyHedgedIndexRisk (const SensitivityRecord& sr) const {
169+
170+ auto indexName = sr.key_1 .name ;
171+ auto indexCurrency = curveCurrency (indexName, ore::data::CurveSpec::CurveType::Equity);
167172
168173 boost::shared_ptr<ore::data::CurrencyHedgedEquityIndexDecomposition> decomposeCurrencyHedgedIndexHelper;
169174 decomposeCurrencyHedgedIndexHelper =
170- loadCurrencyHedgedIndexDecomposition (sr. key_1 . name , refDataManager_, curveConfigs_);
175+ loadCurrencyHedgedIndexDecomposition (indexName , refDataManager_, curveConfigs_);
171176 if (decomposeCurrencyHedgedIndexHelper != nullptr ) {
172177 QL_REQUIRE (currencyHedgedIndexQuantities_.count (sr.tradeId ) > 0 ,
173178 " CurrencyHedgedIndexDecomposition failed, there is no index quantity for trade "
174- << sr.tradeId << " and equity index EQ-" << sr. key_1 . name );
175- QL_REQUIRE (currencyHedgedIndexQuantities_.at (sr.tradeId ).count (" EQ-" + sr. key_1 . name ) > 0 ,
179+ << sr.tradeId << " and equity index EQ-" << indexName );
180+ QL_REQUIRE (currencyHedgedIndexQuantities_.at (sr.tradeId ).count (" EQ-" + indexName ) > 0 ,
176181 " CurrencyHedgedIndexDecomposition failed, there is no index quantity for trade "
177- << sr.tradeId << " and equity index EQ-" << sr. key_1 . name );
182+ << sr.tradeId << " and equity index EQ-" << indexName );
178183 QL_REQUIRE (todaysMarket_ != nullptr ,
179184 " CurrencyHedgedIndexDecomposition failed, there is no market given quantity for trade "
180185 << sr.tradeId );
181186
182187 Date today = QuantLib::Settings::instance ().evaluationDate ();
183188
184- auto quantity = currencyHedgedIndexQuantities_.at (sr.tradeId ).find (" EQ-" + sr. key_1 . name )->second ;
189+ auto quantity = currencyHedgedIndexQuantities_.at (sr.tradeId ).find (" EQ-" + indexName )->second ;
185190
186191 QL_REQUIRE (quantity != QuantLib::Null<double >(),
187192 " CurrencyHedgedIndexDecomposition failed, index quantity cannot be NULL." );
188193
189- double hedgedExposure = sr.delta / eqRiskShiftSize (sr. key_1 . name , ssd_);
194+ double hedgedExposure = sr.delta / eqRiskShiftSize (indexName , ssd_);
190195
191196 double unhedgedExposure =
192197 decomposeCurrencyHedgedIndexHelper->unhedgedSpotExposure (hedgedExposure, quantity, today, todaysMarket_);
193198
194- double unhedgedDelta = unhedgedExposure * eqRiskShiftSize (sr. key_1 . name , ssd_);
199+ double unhedgedDelta = unhedgedExposure * eqRiskShiftSize (indexName , ssd_);
195200
196- auto decompResults =
197- decomposeEqComIndexRisk (unhedgedDelta, decomposeCurrencyHedgedIndexHelper->underlyingRefData (),
198- ore::data::CurveSpec::CurveType::Equity);
201+ auto decompResults = decomposeIndex (unhedgedDelta, decomposeCurrencyHedgedIndexHelper->underlyingRefData (),
202+ ore::data::CurveSpec::CurveType::Equity);
199203 scaleFxRisk (decompResults.fxRisk , decomposeCurrencyHedgedIndexHelper->indexName ());
200204 // Correct FX Delta from FxForwards
201205 for (const auto & [ccy, fxRisk] :
202206 decomposeCurrencyHedgedIndexHelper->fxSpotRiskFromForwards (quantity, today, todaysMarket_, 1.0 )) {
203- decompResults.fxRisk [ccy] =
204- decompResults.fxRisk [ccy] -
205- fxRisk *
206- fxRiskShiftSize (ccy, baseCurrency_, ssd_); // *todaysMarket_->fxSpot(ccy + baseCurrency_)->value();
207+ decompResults.fxRisk [ccy] = decompResults.fxRisk [ccy] - fxRisk * fxRiskShiftSize (ccy, baseCurrency_, ssd_);
207208 }
209+ // Convert into the correct currency
210+ convertFxRiskToBaseCurrency (decompResults.fxRisk , indexCurrency);
208211 return createDecompositionRecords (decompResults.equityDelta , decompResults.fxRisk ,
209- decomposeCurrencyHedgedIndexHelper-> indexCurrency () , sr);
212+ indexCurrency, sr);
210213 } else {
211214 auto subFields = std::map<std::string, std::string>({{" tradeId" , sr.tradeId }});
212215 StructuredAnalyticsErrorMessage (" CRIF Generation" , " Equity index decomposition failed" ,
213- " Cannot decompose equity index delta (" + sr. key_1 . name +
216+ " Cannot decompose equity index delta (" + indexName +
214217 " ) for trade: no reference data found. Continuing without decomposition." ,
215218 subFields)
216219 .log ();
@@ -219,14 +222,16 @@ DecomposedSensitivityStream::decomposeCurrencyHedgedIndexRisk(const SensitivityR
219222}
220223
221224std::vector<SensitivityRecord> DecomposedSensitivityStream::decomposeCommodityRisk (const SensitivityRecord& sr) const {
222- if (refDataManager_->hasData (" CommodityIndex" , sr.key_1 .name )) {
223- auto refDatum = refDataManager_->getData (" CommodityIndex" , sr.key_1 .name );
225+ std::string indexName = sr.key_1 .name ;
226+ if (refDataManager_->hasData (" CommodityIndex" , indexName)) {
227+ auto refDatum = refDataManager_->getData (" CommodityIndex" , indexName);
224228 auto indexRefDatum = boost::dynamic_pointer_cast<ore::data::IndexReferenceDatum>(refDatum);
225- auto decompResults =
226- decomposeEqComIndexRisk (sr.delta , indexRefDatum, ore::data::CurveSpec::CurveType::Commodity);
227- scaleFxRisk (decompResults.fxRisk , indexRefDatum->id ());
228- return createDecompositionRecords (decompResults.equityDelta , decompResults.fxRisk , decompResults.indexCurrency ,
229- sr);
229+ auto decompResults = decomposeIndex (sr.delta , indexRefDatum, ore::data::CurveSpec::CurveType::Commodity);
230+ scaleFxRisk (decompResults.fxRisk , indexName);
231+ auto indexCurrency = curveCurrency (indexName, ore::data::CurveSpec::CurveType::Commodity);
232+ convertFxRiskToBaseCurrency (decompResults.fxRisk , indexCurrency);
233+ return createDecompositionRecords (
234+ decompResults.equityDelta , decompResults.fxRisk , decompResults.indexCurrency , sr);
230235 } else {
231236 auto subFields = std::map<std::string, std::string>({{" tradeId" , sr.tradeId }});
232237 StructuredAnalyticsErrorMessage (" CRIF Generation" , " Equity index decomposition failed" ,
@@ -244,48 +249,34 @@ void DecomposedSensitivityStream::reset() {
244249 itCurrent_ = decomposedRecords_.begin ();
245250}
246251
247- DecomposedSensitivityStream::EqComIndexDecompositionResults
248- DecomposedSensitivityStream::decomposeEqComIndexRisk (double delta,
249- const boost::shared_ptr<ore::data::IndexReferenceDatum>& ird,
250- ore::data::CurveSpec::CurveType curveType) const {
252+ DecomposedSensitivityStream::DecompositionResults
253+ DecomposedSensitivityStream::decomposeIndex (double delta, const boost::shared_ptr<ore::data::IndexReferenceDatum>& ird,
254+ ore::data::CurveSpec::CurveType curveType) const {
251255 QL_REQUIRE (ird, " Can not decompose equity risk, no EquityIndexReferenceData giving" );
252256 QL_REQUIRE (curveConfigs_, " Can not decompose equity risk, no CurveConfig giving" );
253257 QL_REQUIRE (curveType == ore::data::CurveSpec::CurveType::Equity ||
254258 curveType == ore::data::CurveSpec::CurveType::Commodity,
255259 " internal error decomposeEquityRisk supports only Equity and Commodity curves" );
256260
257- EqComIndexDecompositionResults results;
258-
259- if (curveConfigs_->has (curveType, ird->id ())) {
260- results.indexCurrency = curveType == ore::data::CurveSpec::CurveType::Equity
261- ? curveConfigs_->equityCurveConfig (ird->id ())->currency ()
262- : curveConfigs_->commodityCurveConfig (ird->id ())->currency ();
263- } else if (curveConfigs_->hasEquityCurveConfig (ird->id ())) {
264- // if we use a equity curve as proxy fall back to lookup the currency from the proxy config
265- results.indexCurrency = curveConfigs_->equityCurveConfig (ird->id ())->currency ();
266- } else {
267- QL_FAIL (" Cannot perform equity risk decomposition find currency index " + ird->id () + " from curve configs." );
268- }
261+ DecompositionResults results;
262+ results.indexCurrency = curveCurrency (ird->id (), curveType);
263+ QL_REQUIRE (!results.indexCurrency .empty (),
264+ " Cannot perform equity risk decomposition find currency index " + ird->id () + " from curve configs." );
269265
270- for (auto c : ird->underlyings ()) {
271- results.equityDelta [c. first ] += delta * c. second ;
266+ for (const auto & [constituent, weight] : ird->underlyings ()) {
267+ results.equityDelta [constituent ] += delta * weight ;
272268 // try look up currency in reference data and add if FX delta risk if necessary
273- std::string constituentCcy = results.indexCurrency ;
274- if (curveConfigs_->has (curveType, c.first )) {
275- auto constituentCcy = curveType == ore::data::CurveSpec::CurveType::Equity
276- ? curveConfigs_->equityCurveConfig (c.first )->currency ()
277- : curveConfigs_->commodityCurveConfig (c.first )->currency ();
278-
279- } else {
269+ std::string constituentCcy = curveCurrency (constituent, curveType);
270+ if (constituentCcy.empty ()) {
271+ constituentCcy = results.indexCurrency ;
280272 StructuredAnalyticsErrorMessage (" CRIF Generation" , " Equity index decomposition" ,
281- " Cannot find currency for equity " + c. first +
273+ " Cannot find currency for equity " + constituent +
282274 " from curve configs, fallback to use index currency (" +
283275 results.indexCurrency + " )" )
284276 .log ();
285277 }
286278 if (constituentCcy != baseCurrency_) {
287- results.fxRisk [constituentCcy] +=
288- delta * c.second ; // * todaysMarket_->fxSpot(constituentCcy+baseCurrency_)->value();
279+ results.fxRisk [constituentCcy] += delta * weight;
289280 }
290281 }
291282 return results;
@@ -320,5 +311,27 @@ void DecomposedSensitivityStream::scaleFxRisk(std::map<std::string, double>& fxR
320311 }
321312}
322313
314+ void DecomposedSensitivityStream::convertFxRiskToBaseCurrency (std::map<std::string, double >& fxRisk,
315+ const std::string& indexCurrency) const {
316+ // Eq/Comm Shift to FX Shift Conversion
317+ auto fxSpot = todaysMarket_->fxSpot (indexCurrency + baseCurrency_)->value ();
318+ for (auto & [ccy, fxdelta] : fxRisk) {
319+ fxdelta *= fxSpot;
320+ }
321+ }
322+
323+ std::string DecomposedSensitivityStream::curveCurrency (const std::string& name,
324+ ore::data::CurveSpec::CurveType curveType) const {
325+ std::string curveCurrency;
326+ if (curveConfigs_->has (curveType, name)) {
327+ curveCurrency = curveType == ore::data::CurveSpec::CurveType::Equity
328+ ? curveConfigs_->equityCurveConfig (name)->currency ()
329+ : curveConfigs_->commodityCurveConfig (name)->currency ();
330+ } else if (curveConfigs_->hasEquityCurveConfig (name)) {
331+ // if we use a equity curve as proxy fall back to lookup the currency from the proxy config
332+ curveCurrency = curveConfigs_->equityCurveConfig (name)->currency ();
333+ }
334+ return curveCurrency;
335+ }
323336} // namespace analytics
324337} // namespace ore
0 commit comments