/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.sesame.irs;
import java.security.InvalidParameterException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.threeten.bp.ZonedDateTime;
import com.google.common.collect.ImmutableMap;
import com.opengamma.analytics.financial.instrument.InstrumentDefinition;
import com.opengamma.analytics.financial.instrument.swap.SwapDefinition;
import com.opengamma.analytics.financial.interestrate.InstrumentDerivative;
import com.opengamma.analytics.financial.interestrate.InstrumentDerivativeVisitor;
import com.opengamma.analytics.financial.interestrate.InstrumentDerivativeVisitorAdapter;
import com.opengamma.analytics.financial.interestrate.payments.derivative.Payment;
import com.opengamma.analytics.financial.interestrate.swap.derivative.Swap;
import com.opengamma.analytics.financial.provider.calculator.discounting.CrossGammaMultiCurveCalculator;
import com.opengamma.analytics.financial.provider.calculator.discounting.CrossGammaSingleCurveCalculator;
import com.opengamma.analytics.financial.provider.calculator.discounting.PV01CurveParametersCalculator;
import com.opengamma.analytics.financial.provider.calculator.discounting.ParRateDiscountingCalculator;
import com.opengamma.analytics.financial.provider.calculator.discounting.ParSpreadMarketQuoteDiscountingCalculator;
import com.opengamma.analytics.financial.provider.calculator.discounting.PresentValueCurveSensitivityDiscountingCalculator;
import com.opengamma.analytics.financial.provider.calculator.discounting.PresentValueDiscountingCalculator;
import com.opengamma.analytics.financial.provider.calculator.discounting.PresentValueDiscountingMultipleInstrumentsCalculator;
import com.opengamma.analytics.financial.provider.calculator.generic.MarketQuoteSensitivityBlockCalculator;
import com.opengamma.analytics.financial.provider.curve.CurveBuildingBlockBundle;
import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount;
import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderInterface;
import com.opengamma.analytics.financial.provider.description.interestrate.ParameterProviderInterface;
import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MultipleCurrencyMulticurveSensitivity;
import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MultipleCurrencyParameterSensitivity;
import com.opengamma.analytics.financial.provider.sensitivity.parameter.ParameterSensitivityParameterCalculator;
import com.opengamma.analytics.math.matrix.CommonsMatrixAlgebra;
import com.opengamma.analytics.math.matrix.DoubleMatrix1D;
import com.opengamma.analytics.math.matrix.DoubleMatrix2D;
import com.opengamma.analytics.util.amount.ReferenceAmount;
import com.opengamma.financial.analytics.DoubleLabelledMatrix1D;
import com.opengamma.financial.analytics.DoubleLabelledMatrix2D;
import com.opengamma.financial.analytics.conversion.FixedIncomeConverterDataProvider;
import com.opengamma.financial.analytics.conversion.InterestRateSwapSecurityConverter;
import com.opengamma.financial.analytics.conversion.TradeFeeConverter;
import com.opengamma.financial.analytics.model.fixedincome.BucketedCrossSensitivities;
import com.opengamma.financial.analytics.model.fixedincome.BucketedCurveSensitivities;
import com.opengamma.financial.analytics.model.fixedincome.CashFlowDetailsCalculator;
import com.opengamma.financial.analytics.model.fixedincome.CashFlowDetailsProvider;
import com.opengamma.financial.analytics.model.fixedincome.SwapLegCashFlows;
import com.opengamma.financial.analytics.timeseries.HistoricalTimeSeriesBundle;
import com.opengamma.financial.security.irs.InterestRateSwapSecurity;
import com.opengamma.financial.security.irs.PayReceiveType;
import com.opengamma.sesame.CurveMatrixLabeller;
import com.opengamma.sesame.trade.ImmutableTrade;
import com.opengamma.sesame.trade.InterestRateSwapTrade;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.money.Currency;
import com.opengamma.util.money.MultipleCurrencyAmount;
import com.opengamma.util.result.Result;
import com.opengamma.util.tuple.Pair;
import com.opengamma.util.tuple.Pairs;
/**
* Calculator for Discounting swaps.
*/
public class DiscountingInterestRateSwapCalculator implements InterestRateSwapCalculator {
/**
* Calculator for cash flows.
*/
private static final CashFlowDetailsCalculator CFDC = new CashFlowDetailsCalculator();
/**
* Converter for fees defined in the Trade.
*/
private static final TradeFeeConverter TFC = TradeFeeConverter.getInstance();
/**
* Calculator for present value.
*/
private static final PresentValueDiscountingCalculator PVDC = PresentValueDiscountingCalculator.getInstance();
/**
* Calculator for present value for fees.
*/
private static final PresentValueDiscountingMultipleInstrumentsCalculator PVDMIC =
PresentValueDiscountingMultipleInstrumentsCalculator.getInstance();
/**
* Calculator for par rate.
*/
private static final ParRateDiscountingCalculator PRDC = ParRateDiscountingCalculator.getInstance();
/**
* Calculator for PV01
*/
private static final PV01CurveParametersCalculator<ParameterProviderInterface> PV01C =
new PV01CurveParametersCalculator<>(PresentValueCurveSensitivityDiscountingCalculator.getInstance());
/** The curve sensitivity calculator */
private static final InstrumentDerivativeVisitor<ParameterProviderInterface, MultipleCurrencyMulticurveSensitivity> PVCSDC =
PresentValueCurveSensitivityDiscountingCalculator.getInstance();
/** The parameter sensitivity calculator */
private static final ParameterSensitivityParameterCalculator<ParameterProviderInterface> PSC =
new ParameterSensitivityParameterCalculator<>(PVCSDC);
/** The market quote sensitivity calculator */
private static final MarketQuoteSensitivityBlockCalculator<ParameterProviderInterface> BUCKETED_PV01_CALCULATOR =
new MarketQuoteSensitivityBlockCalculator<>(PSC);
/** The par spread market quote calculator */
private static final ParSpreadMarketQuoteDiscountingCalculator PSMQC =
ParSpreadMarketQuoteDiscountingCalculator.getInstance();
/** The calculator which will compute intra-curve gammas */
private static final CrossGammaMultiCurveCalculator CGC = new CrossGammaMultiCurveCalculator(PVCSDC);
/** The calculator which will compute the sum of columns gammas */
private static final CrossGammaSingleCurveCalculator SUM_OF_COLUMNS_GAMMA = new CrossGammaSingleCurveCalculator(PVCSDC);
/** Matrix algebra tooling to permit matrix manipulation */
private static final CommonsMatrixAlgebra MA = new CommonsMatrixAlgebra();
/**
* Provides scaling to/from basis points.
*/
private static final double BASIS_POINT_FACTOR = 1.0E-4;
/**
* Derivative form of the security.
*/
private final InstrumentDerivative _derivative;
/**
* The multicurve bundle.
*/
private final MulticurveProviderInterface _bundle;
/**
* The curve building block bundle.
*/
private final CurveBuildingBlockBundle _curveBuildingBlockBundle;
/**
* The swap definition.
*/
private final SwapDefinition _definition;
/**
* The swap security.
*/
private final InterestRateSwapSecurity _security;
/**
* The swap trade. Can be null, as it is possible to use the InterestRateSwapSecurity
*/
private final ImmutableTrade _trade;
/**
* The ZonedDateTime valuation time.
*/
private final ZonedDateTime _valuationTime;
/**
* The curve labellers.
*/
private final Map<String, CurveMatrixLabeller> _curveLabellers;
/**
* Creates a calculator for a InterestRateSwapSecurity.
* @param security the swap to calculate values for
* @param bundle the multicurve bundle, including the curves
* @param curveBuildingBlockBundle the curve block building bundle
* @param swapConverter the InterestRateSwapSecurityConverter
* @param valuationTime the ZonedDateTime
* @param definitionConverter the FixedIncomeConverterDataProvider
* @param fixings the HistoricalTimeSeriesBundle, a collection of historical time-series objects
* @param curveLabellers the curve labellers
*/
public DiscountingInterestRateSwapCalculator(InterestRateSwapSecurity security,
MulticurveProviderInterface bundle,
CurveBuildingBlockBundle curveBuildingBlockBundle,
InterestRateSwapSecurityConverter swapConverter,
ZonedDateTime valuationTime,
FixedIncomeConverterDataProvider definitionConverter,
HistoricalTimeSeriesBundle fixings,
Map<String, CurveMatrixLabeller> curveLabellers) {
_security = ArgumentChecker.notNull(security, "security");
ArgumentChecker.notNull(swapConverter, "swapConverter");
_valuationTime = ArgumentChecker.notNull(valuationTime, "valuationTime");
ArgumentChecker.notNull(definitionConverter, "definitionConverter");
ArgumentChecker.notNull(fixings, "fixings");
_trade = null;
_definition = (SwapDefinition) security.accept(swapConverter);
_derivative = createInstrumentDerivative(security, valuationTime, definitionConverter, fixings);
_bundle = ArgumentChecker.notNull(bundle, "bundle");
_curveBuildingBlockBundle = ArgumentChecker.notNull(curveBuildingBlockBundle, "curveBuildingBlockBundle");
_curveLabellers = ArgumentChecker.notNull(curveLabellers, "curveLabellers");
ArgumentChecker.isTrue(curveLabellers.size() == curveBuildingBlockBundle.getData().size(),
"Require same number of curve labellers & definitions");
for (String curveName : curveBuildingBlockBundle.getData().keySet()) {
ArgumentChecker.isTrue(curveLabellers.containsKey(curveName), "curve labellers not present {}", curveName);
}
}
/**
* Creates a calculator for a InterestRateSwapSecurity.
*
* @param interestRateSwapTrade the swap to calculate values for
* @param bundle the multicurve bundle, including the curves
* @param curveBuildingBlockBundle the curve block building bundle
* @param valuationTime the ZonedDateTime
* @param curveLabellers the curve labellers
* @param definition the definition built from the swap
* @param derivative the derivative built from the swap
*/
public DiscountingInterestRateSwapCalculator(InterestRateSwapTrade interestRateSwapTrade,
MulticurveProviderInterface bundle,
CurveBuildingBlockBundle curveBuildingBlockBundle,
ZonedDateTime valuationTime,
Map<String, CurveMatrixLabeller> curveLabellers,
SwapDefinition definition,
InstrumentDerivative derivative) {
ArgumentChecker.notNull(interestRateSwapTrade, "interestRateSwapTrade");
_valuationTime = ArgumentChecker.notNull(valuationTime, "valuationTime");
_trade = interestRateSwapTrade.getTrade();
_security = interestRateSwapTrade.getSecurity();
_definition = ArgumentChecker.notNull(definition, "definition");
_derivative = ArgumentChecker.notNull(derivative, "derivative");
_bundle = ArgumentChecker.notNull(bundle, "bundle");
_curveBuildingBlockBundle = ArgumentChecker.notNull(curveBuildingBlockBundle, "curveBuildingBlockBundle");
_curveLabellers = ArgumentChecker.notNull(curveLabellers, "curveLabellers");
ArgumentChecker.isTrue(curveLabellers.size() == curveBuildingBlockBundle.getData().size(),
"Require same number of curve labellers & definitions");
for (String curveName : curveBuildingBlockBundle.getData().keySet()) {
ArgumentChecker.isTrue(curveLabellers.containsKey(curveName), "curve labellers not present {}", curveName);
}
}
@Override
public Result<MultipleCurrencyAmount> calculatePV() {
if (_trade != null) {
InstrumentDefinition<?> feeDefinition = TFC.convert(_trade);
if (feeDefinition != null) {
Pair<InstrumentDerivative[], MulticurveProviderInterface> data =
Pairs.of(new InstrumentDerivative[] {feeDefinition.toDerivative(_valuationTime) }, _bundle);
return Result.success(_derivative.accept(PVDMIC, data));
}
}
return Result.success(calculateResult(PVDC));
}
@Override
public Result<MultipleCurrencyAmount> calculatePv(MulticurveProviderInterface bundle) {
ArgumentChecker.notNull(bundle, "curve bundle");
return Result.success(_derivative.accept(PVDC, bundle));
}
@Override
public Result<Double> calculateRate() {
return Result.success(calculateResult(PRDC));
}
@Override
public Result<Double> calculateParSpread() {
return Result.success(calculateResult(PSMQC));
}
@Override
public Result<ReferenceAmount<Pair<String, Currency>>> calculatePV01() {
return Result.success(calculateResult(PV01C));
}
@Override
public Result<BucketedCurveSensitivities> calculateBucketedPV01() {
MultipleCurrencyParameterSensitivity sensitivity = BUCKETED_PV01_CALCULATOR
.fromInstrument(_derivative, _bundle, _curveBuildingBlockBundle)
.multipliedBy(BASIS_POINT_FACTOR);
Map<Pair<String, Currency>, DoubleLabelledMatrix1D> labelledMatrix1DMap = new HashMap<>();
for (Map.Entry<Pair<String, Currency>, DoubleMatrix1D> entry : sensitivity.getSensitivities().entrySet()) {
CurveMatrixLabeller curveMatrixLabeller = _curveLabellers.get(entry.getKey().getFirst());
DoubleLabelledMatrix1D matrix = curveMatrixLabeller.labelMatrix(entry.getValue());
labelledMatrix1DMap.put(entry.getKey(), matrix);
}
return Result.success(BucketedCurveSensitivities.of(labelledMatrix1DMap));
}
@Override
public Result<BucketedCrossSensitivities> calculateBucketedCrossGamma() {
HashMap<String, DoubleMatrix2D> crossGammas = CGC.calculateCrossGammaIntraCurve(_derivative, (MulticurveProviderDiscount) _bundle);
Map<String, DoubleLabelledMatrix2D> labelledMatrix2DMap = new HashMap<>();
for (Map.Entry<String, DoubleMatrix2D> entry : crossGammas.entrySet()) {
//Values returned from analytics need to be scaled appropriately: multiplied by 1 bp ^ 1bp
DoubleMatrix2D scaledValues = (DoubleMatrix2D) MA.scale(entry.getValue(), BASIS_POINT_FACTOR * BASIS_POINT_FACTOR);
CurveMatrixLabeller curveMatrixLabeller = _curveLabellers.get(entry.getKey());
DoubleLabelledMatrix2D matrix = curveMatrixLabeller.labelMatrix(scaledValues);
labelledMatrix2DMap.put(entry.getKey(), matrix);
}
return Result.success(BucketedCrossSensitivities.of(labelledMatrix2DMap));
}
@Override
public Result<BucketedCurveSensitivities> calculateBucketedGamma() {
MulticurveProviderDiscount singleCurveBundle = (MulticurveProviderDiscount) _bundle;
Set<String> curveNames = singleCurveBundle.getAllCurveNames();
if (curveNames.size() != 1) {
return Result.failure(
new InvalidParameterException("Only bucketed gamma on single-curve multicurve is currently supported"));
}
Set<Currency> currencies = singleCurveBundle.getCurrencies();
double[] rawGamma = SUM_OF_COLUMNS_GAMMA.calculateSumOfColumnsGamma(_derivative, singleCurveBundle);
for (int i = 0; i < rawGamma.length; ++i) {
rawGamma[i] *= BASIS_POINT_FACTOR * BASIS_POINT_FACTOR;
}
DoubleMatrix1D gamma = new DoubleMatrix1D(rawGamma);
Pair<String, Currency> sensitivityKey = Pairs.of(curveNames.iterator().next(), currencies.iterator().next());
CurveMatrixLabeller curveMatrixLabeller = _curveLabellers.get(sensitivityKey.getFirst());
DoubleLabelledMatrix1D matrix = curveMatrixLabeller.labelMatrix(gamma);
Map<Pair<String, Currency>, DoubleLabelledMatrix1D> labelledMatrix1DMap = ImmutableMap.of(sensitivityKey, matrix);
return Result.success(BucketedCurveSensitivities.of(labelledMatrix1DMap));
}
@Override
public Result<SwapLegCashFlows> calculatePayLegCashFlows() {
return Result.success(_derivative.accept(CFDC, createCashFlowDetailsProvider(PayReceiveType.PAY, false)));
}
@Override
public Result<SwapLegCashFlows> calculateReceiveLegCashFlows() {
return Result.success(_derivative.accept(CFDC, createCashFlowDetailsProvider(PayReceiveType.RECEIVE, false)));
}
@Override
public Result<SwapLegCashFlows> calculateFullPayLegCashFlows() {
return Result.success(_derivative.accept(CFDC, createCashFlowDetailsProvider(PayReceiveType.PAY, true)));
}
@Override
public Result<SwapLegCashFlows> calculateFullReceiveLegCashFlows() {
return Result.success(_derivative.accept(CFDC, createCashFlowDetailsProvider(PayReceiveType.RECEIVE, true)));
}
@Override
public Result<MultipleCurrencyAmount> calculatePayLegPv() {
/* We are not interested in fees here */
Swap<? extends Payment, ? extends Payment> swap = (Swap<? extends Payment, ? extends Payment>) _derivative;
InstrumentDerivative payLeg = isFirstLegPay() ?
swap.getFirstLeg() :
swap.getSecondLeg();
return Result.success(payLeg.accept(PVDC, _bundle));
}
@Override
public Result<MultipleCurrencyAmount> calculateReceiveLegPv() {
/* We are not interested in fees here */
Swap<? extends Payment, ? extends Payment> swap = (Swap<? extends Payment, ? extends Payment>) _derivative;
InstrumentDerivative receiveLeg = isFirstLegPay() ?
swap.getSecondLeg() :
swap.getFirstLeg();
return Result.success(receiveLeg.accept(PVDC, _bundle));
}
private <T> T calculateResult(InstrumentDerivativeVisitorAdapter<ParameterProviderInterface, T> calculator) {
return _derivative.accept(calculator, _bundle);
}
private ReferenceAmount<Pair<String, Currency>> calculateResult(InstrumentDerivativeVisitor<ParameterProviderInterface, ReferenceAmount<Pair<String, Currency>>> calculator) {
return _derivative.accept(calculator, _bundle);
}
private InstrumentDerivative createInstrumentDerivative(InterestRateSwapSecurity security,
ZonedDateTime valuationTime,
FixedIncomeConverterDataProvider definitionConverter,
HistoricalTimeSeriesBundle fixings) {
return definitionConverter.convert(security, _definition, valuationTime, fixings);
}
private CashFlowDetailsProvider createCashFlowDetailsProvider(PayReceiveType type, boolean full) {
return new CashFlowDetailsProvider(_bundle, _valuationTime, _definition, _security, type, full);
}
private boolean isFirstLegPay() {
// There is no information on the definition or derivative to tell us if the leg is pay or
// receive other than the sign of the notionals, but the first payment may be an exchange of notional,
// so if there are more than one payments we should take the second payment.
int paymentToCheck = _definition.getFirstLeg().getNumberOfPayments() > 1 ? 1 : 0;
return _definition.getFirstLeg().getNthPayment(paymentToCheck).getReferenceAmount() < 0;
}
}