/** * Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.measure.cms; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.Set; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.opengamma.strata.basics.ReferenceData; import com.opengamma.strata.basics.currency.Currency; import com.opengamma.strata.basics.index.IborIndex; import com.opengamma.strata.basics.index.Index; import com.opengamma.strata.calc.Measure; import com.opengamma.strata.calc.runner.CalculationFunction; import com.opengamma.strata.calc.runner.CalculationParameters; import com.opengamma.strata.calc.runner.FunctionRequirements; import com.opengamma.strata.collect.result.FailureReason; import com.opengamma.strata.collect.result.Result; import com.opengamma.strata.data.scenario.ScenarioMarketData; import com.opengamma.strata.measure.Measures; import com.opengamma.strata.measure.rate.RatesMarketDataLookup; import com.opengamma.strata.measure.rate.RatesScenarioMarketData; import com.opengamma.strata.measure.swaption.SwaptionMarketDataLookup; import com.opengamma.strata.measure.swaption.SwaptionScenarioMarketData; import com.opengamma.strata.product.cms.Cms; import com.opengamma.strata.product.cms.CmsTrade; import com.opengamma.strata.product.cms.ResolvedCmsTrade; /** * Perform calculations on a single {@code CmsTrade} for each of a set of scenarios. * <p> * This uses SABR swaption volatilities, which must be specified using {@link SwaptionMarketDataLookup}. * An instance of {@link RatesMarketDataLookup} and {@link CmsSabrExtrapolationParams} must also be specified. * <p> * The supported built-in measures are: * <ul> * <li>{@linkplain Measures#PRESENT_VALUE Present value} * <li>{@linkplain Measures#PV01_CALIBRATED_SUM PV01 calibrated sum on rate curves} * <li>{@linkplain Measures#PV01_CALIBRATED_BUCKETED PV01 calibrated bucketed on rate curves} * <li>{@linkplain Measures#PV01_MARKET_QUOTE_SUM PV01 market quote sum on rate curves} * <li>{@linkplain Measures#PV01_MARKET_QUOTE_BUCKETED PV01 market quote bucketed on rate curves} * <li>{@linkplain Measures#CURRENCY_EXPOSURE Currency exposure} * <li>{@linkplain Measures#CURRENT_CASH Current cash} * <li>{@linkplain Measures#RESOLVED_TARGET Resolved trade} * </ul> * <p> * The "natural" currency is determined from the CMS leg. */ public class CmsTradeCalculationFunction implements CalculationFunction<CmsTrade> { /** * The calculations by measure. */ private static final ImmutableMap<Measure, SingleMeasureCalculation> CALCULATORS = ImmutableMap.<Measure, SingleMeasureCalculation>builder() .put(Measures.PRESENT_VALUE, CmsMeasureCalculations::presentValue) .put(Measures.PV01_CALIBRATED_SUM, CmsMeasureCalculations::pv01RatesCalibratedSum) .put(Measures.PV01_CALIBRATED_BUCKETED, CmsMeasureCalculations::pv01RatesCalibratedBucketed) .put(Measures.PV01_MARKET_QUOTE_SUM, CmsMeasureCalculations::pv01RatesMarketQuoteSum) .put(Measures.PV01_MARKET_QUOTE_BUCKETED, CmsMeasureCalculations::pv01RatesMarketQuoteBucketed) .put(Measures.CURRENCY_EXPOSURE, CmsMeasureCalculations::currencyExposure) .put(Measures.CURRENT_CASH, CmsMeasureCalculations::currentCash) .put(Measures.RESOLVED_TARGET, (c, rt, smd, m) -> rt) .build(); private static final ImmutableSet<Measure> MEASURES = CALCULATORS.keySet(); /** * Creates an instance. */ public CmsTradeCalculationFunction() { } //------------------------------------------------------------------------- @Override public Class<CmsTrade> targetType() { return CmsTrade.class; } @Override public Set<Measure> supportedMeasures() { return MEASURES; } @Override public Optional<String> identifier(CmsTrade target) { return target.getInfo().getId().map(id -> id.toString()); } @Override public Currency naturalCurrency(CmsTrade trade, ReferenceData refData) { return trade.getProduct().getCmsLeg().getCurrency(); } //------------------------------------------------------------------------- @Override public FunctionRequirements requirements( CmsTrade trade, Set<Measure> measures, CalculationParameters parameters, ReferenceData refData) { // extract data from product Cms product = trade.getProduct(); Set<Currency> currencies = product.allPaymentCurrencies(); IborIndex cmsIndex = trade.getProduct().getCmsLeg().getUnderlyingIndex(); Set<Index> payIndices = trade.getProduct().allRateIndices(); Set<Index> indices = ImmutableSet.<Index>builder().add(cmsIndex).addAll(payIndices).build(); // use lookup to build requirements RatesMarketDataLookup ratesLookup = parameters.getParameter(RatesMarketDataLookup.class); FunctionRequirements ratesReqs = ratesLookup.requirements(currencies, indices); SwaptionMarketDataLookup swaptionLookup = parameters.getParameter(SwaptionMarketDataLookup.class); FunctionRequirements swaptionReqs = swaptionLookup.requirements(cmsIndex); return ratesReqs.combinedWith(swaptionReqs); } //------------------------------------------------------------------------- @Override public Map<Measure, Result<?>> calculate( CmsTrade trade, Set<Measure> measures, CalculationParameters parameters, ScenarioMarketData scenarioMarketData, ReferenceData refData) { // expand the trade once for all measures and all scenarios ResolvedCmsTrade resolved = trade.resolve(refData); RatesMarketDataLookup ratesLookup = parameters.getParameter(RatesMarketDataLookup.class); RatesScenarioMarketData ratesMarketData = ratesLookup.marketDataView(scenarioMarketData); SwaptionMarketDataLookup swaptionLookup = parameters.getParameter(SwaptionMarketDataLookup.class); SwaptionScenarioMarketData swaptionMarketData = swaptionLookup.marketDataView(scenarioMarketData); CmsSabrExtrapolationParams cmsParams = parameters.getParameter(CmsSabrExtrapolationParams.class); CmsMeasureCalculations calculations = new CmsMeasureCalculations(cmsParams); // loop around measures, calculating all scenarios for one measure Map<Measure, Result<?>> results = new HashMap<>(); for (Measure measure : measures) { results.put(measure, calculate(measure, resolved, calculations, ratesMarketData, swaptionMarketData)); } return results; } // calculate one measure private Result<?> calculate( Measure measure, ResolvedCmsTrade trade, CmsMeasureCalculations calculations, RatesScenarioMarketData ratesMarketData, SwaptionScenarioMarketData swaptionMarketData) { SingleMeasureCalculation calculator = CALCULATORS.get(measure); if (calculator == null) { return Result.failure(FailureReason.UNSUPPORTED, "Unsupported measure for SwaptionTrade: {}", measure); } return Result.of(() -> calculator.calculate(calculations, trade, ratesMarketData, swaptionMarketData)); } //------------------------------------------------------------------------- @FunctionalInterface interface SingleMeasureCalculation { public abstract Object calculate( CmsMeasureCalculations calculations, ResolvedCmsTrade trade, RatesScenarioMarketData ratesMarketData, SwaptionScenarioMarketData swaptionMarketData); } }