/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.sesame.bond; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import org.threeten.bp.ZonedDateTime; import com.opengamma.analytics.financial.instrument.bond.BondFixedTransactionDefinition; import com.opengamma.analytics.financial.interestrate.bond.definition.BondFixedTransaction; import com.opengamma.analytics.financial.interestrate.bond.provider.BondSecurityDiscountingMethod; import com.opengamma.analytics.financial.interestrate.bond.provider.BondTransactionDiscountingMethod; import com.opengamma.analytics.financial.provider.calculator.discounting.PV01CurveParametersCalculator; import com.opengamma.analytics.financial.provider.calculator.generic.MarketQuoteSensitivityBlockCalculator; import com.opengamma.analytics.financial.provider.calculator.issuer.PresentValueCurveSensitivityIssuerCalculator; import com.opengamma.analytics.financial.provider.calculator.issuer.PresentValueIssuerCalculator; import com.opengamma.analytics.financial.provider.calculator.issuer.ZSpreadIssuerCalculator; import com.opengamma.analytics.financial.provider.curve.CurveBuildingBlockBundle; import com.opengamma.analytics.financial.provider.description.interestrate.IssuerProviderInterface; import com.opengamma.analytics.financial.provider.description.interestrate.ParameterIssuerProviderInterface; import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MultipleCurrencyParameterSensitivity; import com.opengamma.analytics.financial.provider.sensitivity.parameter.ParameterSensitivityParameterCalculator; import com.opengamma.analytics.math.matrix.DoubleMatrix1D; import com.opengamma.analytics.util.amount.ReferenceAmount; import com.opengamma.core.value.MarketDataRequirementNames; import com.opengamma.financial.analytics.DoubleLabelledMatrix1D; import com.opengamma.financial.analytics.conversion.BondAndBondFutureTradeConverter; import com.opengamma.financial.analytics.model.fixedincome.BucketedCurveSensitivities; import com.opengamma.sesame.CurveLabellingFn; import com.opengamma.sesame.CurveMatrixLabeller; import com.opengamma.sesame.Environment; import com.opengamma.sesame.IssuerProviderBundle; import com.opengamma.sesame.IssuerProviderFn; import com.opengamma.sesame.cache.CacheKey; import com.opengamma.sesame.cache.FunctionCache; import com.opengamma.sesame.marketdata.FieldName; import com.opengamma.sesame.marketdata.MarketDataFn; import com.opengamma.sesame.trade.BondTrade; 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; /** * Discounting function for calculating risk measures for bonds. */ public class DiscountingBondFn implements BondFn { private static final double BASIS_POINT_FACTOR = 1.0E-4; private final BondAndBondFutureTradeConverter _converter; private final IssuerProviderFn _issuerProviderFn; private final MarketDataFn _marketDataFn; private final FunctionCache _cache; private final CurveLabellingFn _curveLabellingFn; private final MarketQuoteSensitivityBlockCalculator<ParameterIssuerProviderInterface> _bucketedPv01Calculator = new MarketQuoteSensitivityBlockCalculator<>( new ParameterSensitivityParameterCalculator<>( PresentValueCurveSensitivityIssuerCalculator.getInstance())); private final PV01CurveParametersCalculator<ParameterIssuerProviderInterface> _pv01Calculator = new PV01CurveParametersCalculator<>( PresentValueCurveSensitivityIssuerCalculator.getInstance()); /** * @param converter converts bond trades to analytics objects * @param issuerProviderFn provides issuer curves * @param marketDataFn provides market data * @param curveLabellingFn provides labels for curve tenors used in building results objects * @param cache for caching the derivatives objects so they don't have to be rebuilt for every calculation */ public DiscountingBondFn(BondAndBondFutureTradeConverter converter, IssuerProviderFn issuerProviderFn, MarketDataFn marketDataFn, CurveLabellingFn curveLabellingFn, FunctionCache cache) { _curveLabellingFn = curveLabellingFn; _marketDataFn = ArgumentChecker.notNull(marketDataFn, "marketDataFn"); _issuerProviderFn = ArgumentChecker.notNull(issuerProviderFn, "issuerProviderFn"); _converter = ArgumentChecker.notNull(converter, "converter"); _cache = ArgumentChecker.notNull(cache, "cache"); } // TODO Java 8 - convert to Result.flatMap() @Override public Result<MultipleCurrencyAmount> calculatePresentValueFromCurves(Environment env, BondTrade trade) { Result<IssuerProviderBundle> bundleResult = _issuerProviderFn.getMulticurveBundle(env, trade.getTrade()); if (!bundleResult.isSuccess()) { return Result.failure(bundleResult); } IssuerProviderBundle bundle = bundleResult.getValue(); PresentValueIssuerCalculator calculator = PresentValueIssuerCalculator.getInstance(); return Result.success(derivative(env, trade).accept(calculator, bundle.getParameterIssuerProvider())); } @Override public Result<MultipleCurrencyAmount> calculatePresentValueFromCleanPrice(Environment env, BondTrade trade) { Result<Double> marketResult = calculateCleanPriceMarket(env, trade); Result<IssuerProviderBundle> bundleResult = _issuerProviderFn.getMulticurveBundle(env, trade.getTrade()); if (Result.anyFailures(marketResult, bundleResult)) { return Result.failure(marketResult, bundleResult); } ParameterIssuerProviderInterface curves = bundleResult.getValue().getParameterIssuerProvider(); BondTransactionDiscountingMethod calculator = BondTransactionDiscountingMethod.getInstance(); return Result.success(calculator.presentValueFromCleanPrice(derivative(env, trade), curves.getIssuerProvider(), marketResult.getValue())); } @Override public Result<MultipleCurrencyAmount> calculatePresentValueFromYield(Environment env, BondTrade trade) { Result<Double> marketResult = calculateYieldToMaturityMarket(env, trade); Result<IssuerProviderBundle> bundleResult = _issuerProviderFn.getMulticurveBundle(env, trade.getTrade()); if (Result.anyFailures(marketResult, bundleResult)) { return Result.failure(marketResult, bundleResult); } ParameterIssuerProviderInterface curves = bundleResult.getValue().getParameterIssuerProvider(); BondTransactionDiscountingMethod calculator = BondTransactionDiscountingMethod.getInstance(); return Result.success(calculator.presentValueFromYield(derivative(env, trade), curves.getIssuerProvider(), marketResult.getValue())); } @Override public Result<Double> calculateCleanPriceMarket(Environment env, BondTrade trade) { return _marketDataFn.getMarketValue(env, trade.getSecurity()); } @Override public Result<Double> calculateCleanPriceFromCurves(Environment env, BondTrade trade) { Result<IssuerProviderBundle> bundleResult = _issuerProviderFn.getMulticurveBundle(env, trade.getTrade()); if (!bundleResult.isSuccess()) { return Result.failure(bundleResult); } ParameterIssuerProviderInterface curves = bundleResult.getValue().getParameterIssuerProvider(); BondSecurityDiscountingMethod calculator = BondSecurityDiscountingMethod.getInstance(); return Result.success(calculator.cleanPriceFromCurves(derivative(env, trade).getBondStandard(), curves.getIssuerProvider())); } @Override public Result<Double> calculateCleanPriceFromYield(Environment env, BondTrade trade) { Result<Double> marketResult = calculateYieldToMaturityMarket(env, trade); if (!marketResult.isSuccess()) { return Result.failure(marketResult); } BondSecurityDiscountingMethod calculator = BondSecurityDiscountingMethod.getInstance(); return Result.success(calculator.cleanPriceFromYield(derivative(env, trade).getBondStandard(), marketResult.getValue())); } @Override public Result<Double> calculateYieldToMaturityFromCleanPrice(Environment env, BondTrade trade) { Result<Double> marketResult = calculateCleanPriceMarket(env, trade); if (!marketResult.isSuccess()) { return Result.failure(marketResult); } BondSecurityDiscountingMethod calculator = BondSecurityDiscountingMethod.getInstance(); return Result.success(calculator.yieldFromCleanPrice(derivative(env, trade).getBondStandard(), marketResult.getValue())); } @Override public Result<Double> calculateYieldToMaturityFromCurves(Environment env, BondTrade trade) { Result<IssuerProviderBundle> bundleResult = _issuerProviderFn.getMulticurveBundle(env, trade.getTrade()); if (!bundleResult.isSuccess()) { return Result.failure(bundleResult); } ParameterIssuerProviderInterface curves = bundleResult.getValue().getParameterIssuerProvider(); BondSecurityDiscountingMethod calculator = BondSecurityDiscountingMethod.getInstance(); return Result.success(calculator.yieldFromCurves(derivative(env, trade).getBondStandard(), curves.getIssuerProvider())); } @Override public Result<Double> calculateYieldToMaturityMarket(Environment env, BondTrade trade) { FieldName fieldName = FieldName.of(MarketDataRequirementNames.YIELD_YIELD_TO_MATURITY_MID); return _marketDataFn.getValue(env, trade.getSecurity(), fieldName, Double.class); } @Override public Result<BucketedCurveSensitivities> calculateBucketedPV01(Environment env, BondTrade trade) { Result<IssuerProviderBundle> bundleResult = _issuerProviderFn.getMulticurveBundle(env, trade.getTrade()); if (!bundleResult.isSuccess()) { return Result.failure(bundleResult); } ParameterIssuerProviderInterface curves = bundleResult.getValue().getParameterIssuerProvider(); CurveBuildingBlockBundle blocks = bundleResult.getValue().getCurveBuildingBlockBundle(); Set<String> curveNames = bundleResult.getValue().getCurveBuildingBlockBundle().getData().keySet(); Result<Map<String, CurveMatrixLabeller>> curveLabellersResult = _curveLabellingFn.getCurveLabellers(curveNames); if (!curveLabellersResult.isSuccess()) { return Result.failure(curveLabellersResult); } Map<String, CurveMatrixLabeller> curveLabellers = curveLabellersResult.getValue(); MultipleCurrencyParameterSensitivity sensitivity = _bucketedPv01Calculator.fromInstrument(derivative(env, trade), curves, blocks).multipliedBy(BASIS_POINT_FACTOR); Map<Pair<String, Currency>, DoubleLabelledMatrix1D> labelledMatrix1DMap = new HashMap<>(); for (Map.Entry<Pair<String, Currency>, DoubleMatrix1D> entry : sensitivity.getSensitivities().entrySet()) { CurveMatrixLabeller labeller = curveLabellers.get(entry.getKey().getFirst()); DoubleLabelledMatrix1D matrix = labeller.labelMatrix(entry.getValue()); labelledMatrix1DMap.put(entry.getKey(), matrix); } return Result.success(BucketedCurveSensitivities.of(labelledMatrix1DMap)); } @Override public Result<ReferenceAmount<Pair<String, Currency>>> calculatePV01(Environment env, BondTrade trade) { Result<IssuerProviderBundle> bundleResult = _issuerProviderFn.getMulticurveBundle(env, trade.getTrade()); if (!bundleResult.isSuccess()) { return Result.failure(bundleResult); } ParameterIssuerProviderInterface curves = bundleResult.getValue().getParameterIssuerProvider(); return Result.success(derivative(env, trade).accept(_pv01Calculator, curves)); } @Override public Result<Double> calculateZSpread(Environment env, BondTrade trade) { Result<Double> marketResult = calculateCleanPriceMarket(env, trade); Result<IssuerProviderBundle> bundleResult = _issuerProviderFn.getMulticurveBundle(env, trade.getTrade()); if (Result.anyFailures(marketResult, bundleResult)) { return Result.failure(marketResult, bundleResult); } ParameterIssuerProviderInterface curves = bundleResult.getValue().getParameterIssuerProvider(); Pair<IssuerProviderInterface, Double> pair = Pairs.of(curves.getIssuerProvider(), marketResult.getValue()); return Result.success(derivative(env, trade).accept(ZSpreadIssuerCalculator.getInstance(), pair)); } private BondFixedTransactionDefinition definition(final BondTrade trade) { // the definition only depends on the trade so that's the only thing in the cache key CacheKey cacheKey = CacheKey.of(this, trade); return _cache.get(cacheKey, new Callable<BondFixedTransactionDefinition>() { @Override public BondFixedTransactionDefinition call() throws Exception { return (BondFixedTransactionDefinition) _converter.convert(trade.getTrade()); } }); } private BondFixedTransaction derivative(Environment env, BondTrade trade) { final BondFixedTransactionDefinition definition = definition(trade); final ZonedDateTime valuationTime = env.getValuationTime(); // the derivative depends on the trade and the valuation time, so both are included in the cache key CacheKey cacheKey = CacheKey.of(this, valuationTime, trade); return _cache.get(cacheKey, new Callable<BondFixedTransaction>() { @Override public BondFixedTransaction call() throws Exception { return definition.toDerivative(valuationTime); } }); } }