/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.provider.calculator.discounting; import java.util.Map; import com.opengamma.analytics.ShiftType; import com.opengamma.analytics.financial.instrument.index.IborIndex; import com.opengamma.analytics.financial.instrument.index.IndexON; import com.opengamma.analytics.financial.interestrate.InstrumentDerivative; import com.opengamma.analytics.financial.interestrate.InstrumentDerivativeVisitor; import com.opengamma.analytics.financial.interestrate.InstrumentDerivativeVisitorSameMethodAdapter; import com.opengamma.analytics.financial.legalentity.LegalEntity; import com.opengamma.analytics.financial.legalentity.LegalEntityFilter; import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve; import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurve; import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurveUtils; import com.opengamma.analytics.financial.provider.description.interestrate.IssuerProviderDiscount; import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount; import com.opengamma.analytics.financial.provider.description.interestrate.ParameterProviderInterface; import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MultipleCurrencyMulticurveSensitivity; import com.opengamma.analytics.util.amount.ReferenceAmount; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.money.Currency; import com.opengamma.util.tuple.Pair; import com.opengamma.util.tuple.Pairs; /** * Returns the change in PV01 of an instrument due to a parallel 1bp move of <b>all</b> the curves to which the instrument is sensitive. * @param <T> The type of the multi-curve provider */ public final class GammaPV01CurveParametersCalculator<T extends ParameterProviderInterface> extends InstrumentDerivativeVisitorSameMethodAdapter<T, Double> { /** * The size of the scaling: 1 basis point. */ private static final double BP1 = 1.0E-4; /** * The PV01 calculator. */ private final InstrumentDerivativeVisitor<T, ReferenceAmount<Pair<String, Currency>>> _pv01Calculator; /** * Constructs a PV01 calculator that uses a particular sensitivity calculator. * @param curveSensitivityCalculator The curve sensitivity calculator, not null */ public GammaPV01CurveParametersCalculator(final InstrumentDerivativeVisitor<T, MultipleCurrencyMulticurveSensitivity> curveSensitivityCalculator) { ArgumentChecker.notNull(curveSensitivityCalculator, "curve sensitivity calculator"); _pv01Calculator = new PV01CurveParametersCalculator<>(curveSensitivityCalculator); } /** * Calculates the change in PV01 of an instrument due to a parallel move of each yield curve the instrument is sensitive to, scaled so that the move is 1bp. * @param ird The instrument, not null * @param multicurves The multi-curves provider, not null * @return The scaled sensitivity for each curve/currency. */ @Override public Double visit(final InstrumentDerivative ird, final T multicurves) { ArgumentChecker.notNull(ird, "derivative"); ArgumentChecker.notNull(multicurves, "multicurves"); final T bumped = getBumpedProvider(multicurves); final ReferenceAmount<Pair<String, Currency>> pv01 = ird.accept(_pv01Calculator, multicurves); final ReferenceAmount<Pair<String, Currency>> up = ird.accept(_pv01Calculator, bumped); double gammaPV01 = 0; for (final Map.Entry<Pair<String, Currency>, Double> entry : pv01.getMap().entrySet()) { final Pair<String, Currency> bumpedNameCurrency = Pairs.of(entry.getKey().getFirst() + YieldCurveUtils.PARALLEL_SHIFT_NAME, entry.getKey().getSecond()); if (!(up.getMap().containsKey(bumpedNameCurrency))) { throw new IllegalStateException("Have bumped PV01 for curve / currency pair " + entry.getKey() + " but no PV01"); } gammaPV01 += (up.getMap().get(bumpedNameCurrency) - entry.getValue()) / BP1; } return gammaPV01; } @Override public Double visit(final InstrumentDerivative derivative) { throw new UnsupportedOperationException("Need curve data"); } /** * Bumps every curve in a provider. This method will be replaced when the curve providers * have been refactored. * @param multicurves The curves * @return A provider with each curve bumped by +1 bp */ @SuppressWarnings("unchecked") private T getBumpedProvider(final T multicurves) { if (multicurves instanceof MulticurveProviderDiscount) { final MulticurveProviderDiscount discount = ((MulticurveProviderDiscount) multicurves); final MulticurveProviderDiscount bumped = new MulticurveProviderDiscount(discount.getFxRates()); for (final Map.Entry<Currency, YieldAndDiscountCurve> entry : discount.getDiscountingCurves().entrySet()) { if (!(entry.getValue() instanceof YieldCurve)) { throw new IllegalArgumentException("Can only bump YieldCurves"); } bumped.setCurve(entry.getKey(), YieldCurveUtils.withParallelShift((YieldCurve) entry.getValue(), BP1, ShiftType.ABSOLUTE)); } for (final Map.Entry<IborIndex, YieldAndDiscountCurve> entry : discount.getForwardIborCurves().entrySet()) { if (!(entry.getValue() instanceof YieldCurve)) { throw new IllegalArgumentException("Can only bump YieldCurves"); } bumped.setCurve(entry.getKey(), YieldCurveUtils.withParallelShift((YieldCurve) entry.getValue(), BP1, ShiftType.ABSOLUTE)); } for (final Map.Entry<IndexON, YieldAndDiscountCurve> entry : discount.getForwardONCurves().entrySet()) { if (!(entry.getValue() instanceof YieldCurve)) { throw new IllegalArgumentException("Can only bump YieldCurves"); } bumped.setCurve(entry.getKey(), YieldCurveUtils.withParallelShift((YieldCurve) entry.getValue(), BP1, ShiftType.ABSOLUTE)); } return (T) bumped; } else if (multicurves instanceof IssuerProviderDiscount) { final IssuerProviderDiscount discount = ((IssuerProviderDiscount) multicurves); final MulticurveProviderDiscount multicurveProvider = discount.getMulticurveProvider(); final IssuerProviderDiscount bumped = new IssuerProviderDiscount(new MulticurveProviderDiscount(multicurveProvider.getFxRates())); for (final Map.Entry<Currency, YieldAndDiscountCurve> entry : multicurveProvider.getDiscountingCurves().entrySet()) { if (!(entry.getValue() instanceof YieldCurve)) { throw new IllegalArgumentException("Can only bump YieldCurves"); } bumped.getMulticurveProvider().setCurve(entry.getKey(), YieldCurveUtils.withParallelShift((YieldCurve) entry.getValue(), BP1, ShiftType.ABSOLUTE)); } for (final Map.Entry<IborIndex, YieldAndDiscountCurve> entry : multicurveProvider.getForwardIborCurves().entrySet()) { if (!(entry.getValue() instanceof YieldCurve)) { throw new IllegalArgumentException("Can only bump YieldCurves"); } bumped.getMulticurveProvider().setCurve(entry.getKey(), YieldCurveUtils.withParallelShift((YieldCurve) entry.getValue(), BP1, ShiftType.ABSOLUTE)); } for (final Map.Entry<IndexON, YieldAndDiscountCurve> entry : multicurveProvider.getForwardONCurves().entrySet()) { if (!(entry.getValue() instanceof YieldCurve)) { throw new IllegalArgumentException("Can only bump YieldCurves"); } bumped.getMulticurveProvider().setCurve(entry.getKey(), YieldCurveUtils.withParallelShift((YieldCurve) entry.getValue(), BP1, ShiftType.ABSOLUTE)); } for (final Map.Entry<Pair<Object, LegalEntityFilter<LegalEntity>>, YieldAndDiscountCurve> entry : discount.getIssuerCurves().entrySet()) { if (!(entry.getValue() instanceof YieldCurve)) { throw new IllegalArgumentException("Can only bump YieldCurves"); } bumped.setCurve(entry.getKey(), YieldCurveUtils.withParallelShift((YieldCurve) entry.getValue(), BP1, ShiftType.ABSOLUTE)); } return (T) bumped; } throw new UnsupportedOperationException("Cannot bump curves of type " + multicurves.getClass()); } }