/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.financial.analytics.model.curve.interestrate; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.threeten.bp.Clock; import org.threeten.bp.ZonedDateTime; import com.google.common.collect.Sets; import com.opengamma.OpenGammaRuntimeException; import com.opengamma.analytics.financial.forex.method.FXMatrix; import com.opengamma.analytics.financial.instrument.InstrumentDefinition; import com.opengamma.analytics.financial.instrument.future.InterestRateFutureTransactionDefinition; import com.opengamma.analytics.financial.interestrate.InstrumentDerivative; import com.opengamma.analytics.financial.interestrate.InstrumentDerivativeVisitor; import com.opengamma.analytics.financial.interestrate.MultipleYieldCurveFinderDataBundle; import com.opengamma.analytics.financial.interestrate.MultipleYieldCurveFinderFunction; import com.opengamma.analytics.financial.interestrate.MultipleYieldCurveFinderJacobian; import com.opengamma.analytics.financial.interestrate.ParRateCalculator; import com.opengamma.analytics.financial.interestrate.ParRateCurveSensitivityCalculator; import com.opengamma.analytics.financial.interestrate.PresentValueCalculator; import com.opengamma.analytics.financial.interestrate.PresentValueCouponSensitivityCalculator; import com.opengamma.analytics.financial.interestrate.PresentValueCurveSensitivityCalculator; import com.opengamma.analytics.financial.interestrate.YieldCurveBundle; import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve; import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurve; import com.opengamma.analytics.financial.provider.calculator.generic.LastTimeCalculator; import com.opengamma.analytics.math.curve.InterpolatedDoublesCurve; import com.opengamma.analytics.math.function.Function1D; import com.opengamma.analytics.math.interpolation.Interpolator1D; import com.opengamma.analytics.math.linearalgebra.DecompositionFactory; import com.opengamma.analytics.math.matrix.DoubleMatrix1D; import com.opengamma.analytics.math.matrix.DoubleMatrix2D; import com.opengamma.analytics.math.rootfinding.newton.BroydenVectorRootFinder; import com.opengamma.analytics.math.rootfinding.newton.NewtonVectorRootFinder; import com.opengamma.core.holiday.HolidaySource; import com.opengamma.core.region.RegionSource; import com.opengamma.core.security.SecuritySource; import com.opengamma.engine.ComputationTarget; import com.opengamma.engine.ComputationTargetSpecification; import com.opengamma.engine.function.AbstractFunction; import com.opengamma.engine.function.FunctionCompilationContext; import com.opengamma.engine.function.FunctionExecutionContext; import com.opengamma.engine.function.FunctionInputs; import com.opengamma.engine.target.ComputationTargetType; import com.opengamma.engine.value.ComputedValue; import com.opengamma.engine.value.ValueProperties; import com.opengamma.engine.value.ValuePropertyNames; import com.opengamma.engine.value.ValueRequirement; import com.opengamma.engine.value.ValueRequirementNames; import com.opengamma.engine.value.ValueSpecification; import com.opengamma.financial.OpenGammaCompilationContext; import com.opengamma.financial.OpenGammaExecutionContext; import com.opengamma.financial.analytics.conversion.FixedIncomeConverterDataProvider; import com.opengamma.financial.analytics.conversion.InterestRateInstrumentTradeOrSecurityConverter; import com.opengamma.financial.analytics.fixedincome.FixedIncomeInstrumentCurveExposureHelper; import com.opengamma.financial.analytics.ircurve.FixedIncomeStripWithSecurity; import com.opengamma.financial.analytics.ircurve.InterpolatedYieldCurveSpecificationWithSecurities; import com.opengamma.financial.analytics.ircurve.YieldCurveData; import com.opengamma.financial.analytics.ircurve.YieldCurveFunction; import com.opengamma.financial.analytics.timeseries.HistoricalTimeSeriesBundle; import com.opengamma.financial.convention.ConventionBundleSource; import com.opengamma.financial.security.FinancialSecurity; import com.opengamma.master.historicaltimeseries.HistoricalTimeSeriesResolver; import com.opengamma.util.ParallelArrayBinarySort; import com.opengamma.util.money.Currency; import com.opengamma.util.tuple.DoublesPair; /** * @deprecated @see MultiYieldCurveFunction */ @Deprecated public class MarketInstrumentImpliedYieldCurveFunction extends AbstractFunction.NonCompiledInvoker { /** The logger */ private static final Logger s_logger = LoggerFactory.getLogger(MarketInstrumentImpliedYieldCurveFunction.class); private static final LastTimeCalculator LAST_DATE_CALCULATOR = LastTimeCalculator.getInstance(); private static final String RESULT_PROPERTY_TYPE = "Type"; private static final String REQUIREMENT_PROPERTY_TYPE = ValuePropertyNames.OUTPUT_RESERVED_PREFIX + RESULT_PROPERTY_TYPE; private static final String TYPE_FORWARD = "Forward"; private static final String TYPE_FUNDING = "Funding"; /** Label setting this function to use the par rate of the instruments in root-finding */ public static final String PAR_RATE_STRING = "ParRate"; /** Label setting this function to use the present value of the instruments in root-finding */ public static final String PRESENT_VALUE_STRING = "PresentValue"; private final InstrumentDerivativeVisitor<YieldCurveBundle, Double> _calculator; private final InstrumentDerivativeVisitor<YieldCurveBundle, Map<String, List<DoublesPair>>> _sensitivityCalculator; private final PresentValueCouponSensitivityCalculator _couponSensitivityCalculator; private final String _calculationType; private final boolean _calcTypeParRate; private FixedIncomeConverterDataProvider _definitionConverter; public MarketInstrumentImpliedYieldCurveFunction(final String calculationType) { _calculationType = calculationType; if (calculationType.equals(PAR_RATE_STRING)) { _calculator = ParRateCalculator.getInstance(); _sensitivityCalculator = ParRateCurveSensitivityCalculator.getInstance(); _couponSensitivityCalculator = null; _calcTypeParRate = true; } else if (calculationType.equals(PRESENT_VALUE_STRING)) { _calculator = PresentValueCalculator.getInstance(); _sensitivityCalculator = PresentValueCurveSensitivityCalculator.getInstance(); _couponSensitivityCalculator = PresentValueCouponSensitivityCalculator.getInstance(); _calcTypeParRate = false; } else { throw new IllegalArgumentException("Unrecognized calculator type: " + calculationType + ". In order of preference, try ParRate then PresentValue"); } } protected String getCalculationType() { return _calculationType; } protected InstrumentDerivativeVisitor<YieldCurveBundle, Double> getCalculator() { return _calculator; } protected InstrumentDerivativeVisitor<YieldCurveBundle, Map<String, List<DoublesPair>>> getSensitivityCalculator() { return _sensitivityCalculator; } protected PresentValueCouponSensitivityCalculator getCouponSensitivityCalculator() { return _couponSensitivityCalculator; } protected InterestRateInstrumentTradeOrSecurityConverter getSecurityConverter(final FunctionExecutionContext context) { final HolidaySource holidaySource = OpenGammaExecutionContext.getHolidaySource(context); final RegionSource regionSource = OpenGammaExecutionContext.getRegionSource(context); final ConventionBundleSource conventionSource = OpenGammaExecutionContext.getConventionBundleSource(context); final SecuritySource securitySource = OpenGammaExecutionContext.getSecuritySource(context); return new InterestRateInstrumentTradeOrSecurityConverter(holidaySource, conventionSource, regionSource, securitySource, true, context.getComputationTargetResolver().getVersionCorrection()); } protected FixedIncomeConverterDataProvider getDefinitionConverter() { return _definitionConverter; } @Override public void init(final FunctionCompilationContext context) { final HolidaySource holidaySource = OpenGammaCompilationContext.getHolidaySource(context); if (holidaySource == null) { throw new UnsupportedOperationException("A holiday source is required"); } final RegionSource regionSource = OpenGammaCompilationContext.getRegionSource(context); if (regionSource == null) { throw new UnsupportedOperationException("A region source is required"); } final ConventionBundleSource conventionSource = OpenGammaCompilationContext.getConventionBundleSource(context); if (conventionSource == null) { throw new UnsupportedOperationException("A convention bundle source is required"); } final SecuritySource securitySource = OpenGammaCompilationContext.getSecuritySource(context); if (securitySource == null) { throw new UnsupportedOperationException("A security source is required"); } final HistoricalTimeSeriesResolver timeSeriesResolver = OpenGammaCompilationContext.getHistoricalTimeSeriesResolver(context); if (timeSeriesResolver == null) { throw new UnsupportedOperationException("A historical time series resolver is required"); } _definitionConverter = new FixedIncomeConverterDataProvider(conventionSource, securitySource, timeSeriesResolver); } @Override public ComputationTargetType getTargetType() { return ComputationTargetType.CURRENCY; } @Override public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target) { final Set<ValueSpecification> results = Sets.newHashSetWithExpectedSize(4); final ComputationTargetSpecification targetSpec = target.toSpecification(); results.add(new ValueSpecification(ValueRequirementNames.YIELD_CURVE, targetSpec, createValueProperties().withAny(ValuePropertyNames.CURVE) .with(ValuePropertyNames.CURVE_CALCULATION_METHOD, getCalculationType()).withAny(YieldCurveFunction.PROPERTY_FORWARD_CURVE).withAny(YieldCurveFunction.PROPERTY_FUNDING_CURVE) .with(RESULT_PROPERTY_TYPE, TYPE_FORWARD).get())); results.add(new ValueSpecification(ValueRequirementNames.YIELD_CURVE, targetSpec, createValueProperties().withAny(ValuePropertyNames.CURVE) .with(ValuePropertyNames.CURVE_CALCULATION_METHOD, getCalculationType()).withAny(YieldCurveFunction.PROPERTY_FORWARD_CURVE).withAny(YieldCurveFunction.PROPERTY_FUNDING_CURVE) .with(RESULT_PROPERTY_TYPE, TYPE_FUNDING).get())); results.add(new ValueSpecification(ValueRequirementNames.YIELD_CURVE_JACOBIAN, targetSpec, createValueProperties().withAny(YieldCurveFunction.PROPERTY_FORWARD_CURVE) .withAny(YieldCurveFunction.PROPERTY_FUNDING_CURVE).with(ValuePropertyNames.CURVE_CALCULATION_METHOD, getCalculationType()).get())); if (getCalculationType().equals(PRESENT_VALUE_STRING)) { results.add(new ValueSpecification(ValueRequirementNames.PRESENT_VALUE_COUPON_SENSITIVITY, targetSpec, createValueProperties().withAny(YieldCurveFunction.PROPERTY_FORWARD_CURVE) .withAny(YieldCurveFunction.PROPERTY_FUNDING_CURVE).get())); } return results; } @Override public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ValueRequirement desiredValue) { final String forwardCurveName; final String fundingCurveName; if (ValueRequirementNames.YIELD_CURVE.equals(desiredValue.getValueName())) { final Set<String> curveNames = desiredValue.getConstraints().getValues(ValuePropertyNames.CURVE); if ((curveNames == null) || (curveNames.size() != 1)) { return null; } final String curveName = curveNames.iterator().next(); final Set<String> fundingCurveNames = desiredValue.getConstraints().getValues(YieldCurveFunction.PROPERTY_FUNDING_CURVE); if ((fundingCurveNames == null) || fundingCurveNames.isEmpty()) { fundingCurveName = curveName; } else { if (fundingCurveNames.size() != 1) { return null; } fundingCurveName = fundingCurveNames.iterator().next(); } final Set<String> forwardCurveNames = desiredValue.getConstraints().getValues(YieldCurveFunction.PROPERTY_FORWARD_CURVE); if ((forwardCurveNames == null) || forwardCurveNames.isEmpty()) { forwardCurveName = curveName; } else { if (forwardCurveNames.size() != 1) { return null; } forwardCurveName = forwardCurveNames.iterator().next(); } } else { // Jacobian and Coupon sensitivities must specify a funding and forward curve (possibly the same name) final Set<String> fundingCurveNames = desiredValue.getConstraints().getValues(YieldCurveFunction.PROPERTY_FUNDING_CURVE); if ((fundingCurveNames == null) || (fundingCurveNames.size() != 1)) { return null; } final Set<String> forwardCurveNames = desiredValue.getConstraints().getValues(YieldCurveFunction.PROPERTY_FORWARD_CURVE); if ((forwardCurveNames == null) || (forwardCurveNames.size() != 1)) { return null; } fundingCurveName = fundingCurveNames.iterator().next(); forwardCurveName = forwardCurveNames.iterator().next(); } final Set<ValueRequirement> requirements = new HashSet<>(); final ComputationTargetSpecification targetSpec = target.toSpecification(); requirements.add(new ValueRequirement(ValueRequirementNames.YIELD_CURVE_INSTRUMENT_CONVERSION_HISTORICAL_TIME_SERIES, targetSpec, ValueProperties.with(ValuePropertyNames.CURVE, fundingCurveName) .with(YieldCurveFunction.PROPERTY_FUNDING_CURVE, fundingCurveName).with(YieldCurveFunction.PROPERTY_FORWARD_CURVE, forwardCurveName).get())); if (forwardCurveName.equals(fundingCurveName)) { requirements.add(new ValueRequirement(ValueRequirementNames.YIELD_CURVE_DATA, targetSpec, ValueProperties.with(ValuePropertyNames.CURVE, fundingCurveName).get())); } else { requirements.add(new ValueRequirement(ValueRequirementNames.YIELD_CURVE_DATA, targetSpec, ValueProperties.with(ValuePropertyNames.CURVE, fundingCurveName) .withOptional(REQUIREMENT_PROPERTY_TYPE).with(REQUIREMENT_PROPERTY_TYPE, TYPE_FUNDING).get())); requirements.add(new ValueRequirement(ValueRequirementNames.YIELD_CURVE_DATA, targetSpec, ValueProperties.with(ValuePropertyNames.CURVE, forwardCurveName) .withOptional(REQUIREMENT_PROPERTY_TYPE).with(REQUIREMENT_PROPERTY_TYPE, TYPE_FORWARD).get())); requirements.add(new ValueRequirement(ValueRequirementNames.YIELD_CURVE_INSTRUMENT_CONVERSION_HISTORICAL_TIME_SERIES, targetSpec, ValueProperties .with(ValuePropertyNames.CURVE, forwardCurveName) .with(YieldCurveFunction.PROPERTY_FUNDING_CURVE, fundingCurveName).with(YieldCurveFunction.PROPERTY_FORWARD_CURVE, forwardCurveName).get())); } return requirements; } @Override public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target, final Map<ValueSpecification, ValueRequirement> inputs) { String forwardCurveName = null; String fundingCurveName = null; for (final Map.Entry<ValueSpecification, ValueRequirement> input : inputs.entrySet()) { if (ValueRequirementNames.YIELD_CURVE_DATA.equals(input.getKey().getValueName())) { final String curveName = input.getKey().getProperty(ValuePropertyNames.CURVE); final String type = input.getValue().getConstraint(REQUIREMENT_PROPERTY_TYPE); if (type == null) { assert forwardCurveName == null; assert fundingCurveName == null; forwardCurveName = curveName; fundingCurveName = curveName; } else { if (TYPE_FORWARD.equals(type)) { assert forwardCurveName == null; forwardCurveName = curveName; } else { assert TYPE_FUNDING.equals(type); assert fundingCurveName == null; fundingCurveName = curveName; } } } } assert forwardCurveName != null; assert fundingCurveName != null; final Set<ValueSpecification> results = Sets.newHashSetWithExpectedSize(4); final ComputationTargetSpecification targetSpec = target.toSpecification(); final ValueProperties.Builder properties = createValueProperties().with(ValuePropertyNames.CURVE_CALCULATION_METHOD, getCalculationType()) .with(YieldCurveFunction.PROPERTY_FORWARD_CURVE, forwardCurveName).with(YieldCurveFunction.PROPERTY_FUNDING_CURVE, fundingCurveName); results.add(new ValueSpecification(ValueRequirementNames.YIELD_CURVE_JACOBIAN, targetSpec, properties.get())); if (getCalculationType().equals(PRESENT_VALUE_STRING)) { results.add(new ValueSpecification(ValueRequirementNames.PRESENT_VALUE_COUPON_SENSITIVITY, targetSpec, createValueProperties().withAny(YieldCurveFunction.PROPERTY_FORWARD_CURVE) .withAny(YieldCurveFunction.PROPERTY_FUNDING_CURVE).get())); } results.add(new ValueSpecification(ValueRequirementNames.YIELD_CURVE, targetSpec, properties.with(ValuePropertyNames.CURVE, forwardCurveName).get())); if (!forwardCurveName.equals(fundingCurveName)) { results.add(new ValueSpecification(ValueRequirementNames.YIELD_CURVE, targetSpec, properties.withoutAny(ValuePropertyNames.CURVE).with(ValuePropertyNames.CURVE, fundingCurveName).get())); } return results; } @Override public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues) { String forwardCurveName = null; String fundingCurveName = null; boolean createForward = false; boolean createFunding = false; boolean createJacobian = false; boolean createSensitivities = false; for (final ValueRequirement desiredValue : desiredValues) { if (forwardCurveName == null) { forwardCurveName = desiredValue.getConstraint(YieldCurveFunction.PROPERTY_FORWARD_CURVE); fundingCurveName = desiredValue.getConstraint(YieldCurveFunction.PROPERTY_FUNDING_CURVE); } if (ValueRequirementNames.YIELD_CURVE.equals(desiredValue.getValueName())) { final String curveName = desiredValue.getConstraint(ValuePropertyNames.CURVE); if (curveName.equals(forwardCurveName)) { assert !createForward; createForward = true; } if (curveName.equals(fundingCurveName)) { assert !createFunding; createFunding = true; } } else if (ValueRequirementNames.YIELD_CURVE_JACOBIAN.equals(desiredValue.getValueName())) { assert !createJacobian; createJacobian = true; } else if (ValueRequirementNames.PRESENT_VALUE_COUPON_SENSITIVITY.equals(desiredValue.getValueName())) { assert !createSensitivities; createSensitivities = true; } else { assert false; } } assert forwardCurveName != null; assert fundingCurveName != null; YieldCurveData fundingCurveData = null; YieldCurveData forwardCurveData = null; HistoricalTimeSeriesBundle fundingTimeSeries = null; HistoricalTimeSeriesBundle forwardTimeSeries = null; for (final ComputedValue input : inputs.getAllValues()) { final String curveName = input.getSpecification().getProperty(ValuePropertyNames.CURVE); if (ValueRequirementNames.YIELD_CURVE_DATA.equals(input.getSpecification().getValueName())) { if (curveName.equals(fundingCurveName)) { assert fundingCurveData == null; fundingCurveData = (YieldCurveData) input.getValue(); } if (curveName.equals(forwardCurveName)) { assert forwardCurveData == null; forwardCurveData = (YieldCurveData) input.getValue(); } } else if (ValueRequirementNames.YIELD_CURVE_INSTRUMENT_CONVERSION_HISTORICAL_TIME_SERIES.equals(input.getSpecification().getValueName())) { if (curveName.equals(fundingCurveName)) { assert fundingTimeSeries == null; fundingTimeSeries = (HistoricalTimeSeriesBundle) input.getValue(); } if (curveName.equals(forwardCurveName)) { assert forwardTimeSeries == null; forwardTimeSeries = (HistoricalTimeSeriesBundle) input.getValue(); } } } assert fundingCurveData != null; assert forwardCurveData != null; if (forwardCurveName.equals(fundingCurveName)) { return execute(executionContext, target.toSpecification(), forwardCurveName, forwardCurveData, forwardTimeSeries, createForward, createJacobian, createSensitivities); } return execute(executionContext, target.toSpecification(), forwardCurveName, forwardCurveData, forwardTimeSeries, fundingCurveName, fundingCurveData, fundingTimeSeries, createForward, createFunding, createJacobian, createSensitivities); } private static Interpolator1D getInterpolator(final InterpolatedYieldCurveSpecificationWithSecurities specification) { return specification.getInterpolator(); } private Set<ComputedValue> execute(FunctionExecutionContext executionContext, ComputationTargetSpecification targetSpec, String curveName, YieldCurveData curveData, HistoricalTimeSeriesBundle timeSeries, boolean createYieldCurve, boolean createJacobian, boolean createSensitivities) { final Clock snapshotClock = executionContext.getValuationClock(); final ZonedDateTime now = ZonedDateTime.now(snapshotClock); final List<InstrumentDerivative> derivatives = new ArrayList<>(); final int n = curveData.getCurveSpecification().getStrips().size(); final double[] initialRatesGuess = new double[n]; final double[] nodeTimes = new double[n]; final double[] marketValues = new double[n]; int i = 0; final InterestRateInstrumentTradeOrSecurityConverter securityConverter = getSecurityConverter(executionContext); for (final FixedIncomeStripWithSecurity strip : curveData.getCurveSpecification().getStrips()) { Double marketValue = curveData.getDataPoint(strip.getSecurityIdentifier()); if (marketValue == null) { throw new NullPointerException("Could not get market data for " + strip); } InstrumentDerivative derivative; final FinancialSecurity financialSecurity = (FinancialSecurity) strip.getSecurity(); final String[] curveNames = FixedIncomeInstrumentCurveExposureHelper.getCurveNamesForFundingCurveInstrument(strip.getInstrumentType(), curveName, curveName); final InstrumentDefinition<?> definition = securityConverter.visit(financialSecurity); if (strip.getSecurity().getSecurityType().equals("FUTURE")) { marketValue = 1 - marketValue; // transform to rate for initial rates guess } try { derivative = getDefinitionConverter().convert(financialSecurity, definition, now, curveNames, timeSeries); } catch (final OpenGammaRuntimeException ogre) { s_logger.error("Error thrown by convertor for security {}, definition {}, time {}, curveNames {}, dataSource {}", financialSecurity, definition, now, curveNames, timeSeries); throw ogre; } if (derivative == null) { throw new NullPointerException("Had a null InterestRateDefinition for " + strip); } if (_calcTypeParRate) { marketValues[i] = marketValue; } derivatives.add(derivative); initialRatesGuess[i] = marketValue; nodeTimes[i] = derivative.accept(LAST_DATE_CALCULATOR); i++; } ParallelArrayBinarySort.parallelBinarySort(nodeTimes, initialRatesGuess); final LinkedHashMap<String, double[]> curveKnots = new LinkedHashMap<>(); curveKnots.put(curveName, nodeTimes); final LinkedHashMap<String, double[]> curveNodes = new LinkedHashMap<>(); final LinkedHashMap<String, Interpolator1D> interpolators = new LinkedHashMap<>(); curveNodes.put(curveName, nodeTimes); interpolators.put(curveName, getInterpolator(curveData.getCurveSpecification())); // TODO have use finite difference or not as an input [FIN-147] final Currency currency = Currency.of(targetSpec.getUniqueId().getValue()); final MultipleYieldCurveFinderDataBundle data = new MultipleYieldCurveFinderDataBundle(derivatives, marketValues, null, curveNodes, interpolators, false, new FXMatrix(currency)); final Function1D<DoubleMatrix1D, DoubleMatrix1D> curveCalculator = new MultipleYieldCurveFinderFunction(data, getCalculator()); final Function1D<DoubleMatrix1D, DoubleMatrix2D> jacobianCalculator = new MultipleYieldCurveFinderJacobian(data, getSensitivityCalculator()); NewtonVectorRootFinder rootFinder; double[] yields = null; try { // TODO have the decomposition as an optional input [FIN-146] rootFinder = new BroydenVectorRootFinder(1e-7, 1e-7, 100, DecompositionFactory.getDecomposition(DecompositionFactory.SV_COLT_NAME)); final DoubleMatrix1D result = rootFinder.getRoot(curveCalculator, jacobianCalculator, new DoubleMatrix1D(initialRatesGuess)); yields = result.getData(); } catch (final Exception eLU) { try { s_logger.warn("Could not find root using LU decomposition and present value method for curve " + curveName + "; trying SV. Error was: " + eLU.getMessage()); rootFinder = new BroydenVectorRootFinder(1e-7, 1e-7, 100, DecompositionFactory.getDecomposition(DecompositionFactory.SV_COMMONS_NAME)); yields = rootFinder.getRoot(curveCalculator, jacobianCalculator, new DoubleMatrix1D(initialRatesGuess)).getData(); } catch (final Exception eSV) { s_logger.warn("Could not find root using SV decomposition and present value method for curve " + curveName + ". Error was: " + eSV.getMessage()); throw new OpenGammaRuntimeException(eSV.getMessage()); } } final YieldAndDiscountCurve curve; if (createSensitivities || createYieldCurve) { curve = YieldCurve.from(InterpolatedDoublesCurve.from(nodeTimes, yields, getInterpolator(curveData.getCurveSpecification()))); } else { curve = null; } final Set<ComputedValue> result = Sets.newHashSetWithExpectedSize(4); final ValueProperties.Builder properties = createValueProperties().with(ValuePropertyNames.CURVE_CALCULATION_METHOD, getCalculationType()) .with(YieldCurveFunction.PROPERTY_FORWARD_CURVE, curveName).with(YieldCurveFunction.PROPERTY_FUNDING_CURVE, curveName); if (createJacobian) { final DoubleMatrix2D jacobianMatrix = jacobianCalculator.evaluate(new DoubleMatrix1D(yields)); result.add(new ComputedValue(new ValueSpecification(ValueRequirementNames.YIELD_CURVE_JACOBIAN, targetSpec, properties.get()), jacobianMatrix.getData())); } if (createSensitivities) { final double[] couponSensitivities = new double[derivatives.size()]; int ii = 0; final String[] curveNames = new String[] {curveName, curveName }; final YieldAndDiscountCurve[] curves = new YieldAndDiscountCurve[] {curve, curve }; final YieldCurveBundle curveBundle = new YieldCurveBundle(curveNames, curves); for (final InstrumentDerivative derivative : derivatives) { couponSensitivities[ii++] = derivative.accept(getCouponSensitivityCalculator(), curveBundle); } result.add(new ComputedValue(new ValueSpecification(ValueRequirementNames.PRESENT_VALUE_COUPON_SENSITIVITY, targetSpec, properties.get()), new DoubleMatrix1D(couponSensitivities))); } if (createYieldCurve) { result.add(new ComputedValue(new ValueSpecification(ValueRequirementNames.YIELD_CURVE, targetSpec, properties.with(ValuePropertyNames.CURVE, curveName).get()), curve)); } return result; } private Set<ComputedValue> execute(FunctionExecutionContext executionContext, ComputationTargetSpecification targetSpec, String forwardCurveName, YieldCurveData forwardCurveData, HistoricalTimeSeriesBundle forwardTimeSeries, String fundingCurveName, YieldCurveData fundingCurveData, HistoricalTimeSeriesBundle fundingTimeSeries, boolean createForwardYieldCurve, boolean createFundingYieldCurve, boolean createJacobian, boolean createSensitivities) { final Clock snapshotClock = executionContext.getValuationClock(); final ZonedDateTime now = ZonedDateTime.now(snapshotClock); final List<InstrumentDerivative> derivatives = new ArrayList<>(); final int nFunding = fundingCurveData.getCurveSpecification().getStrips().size(); final int nForward = forwardCurveData.getCurveSpecification().getStrips().size(); final double[] initialRatesGuess = new double[nFunding + nForward]; final double[] fundingNodeTimes = new double[nFunding]; final double[] forwardNodeTimes = new double[nForward]; final double[] marketValues = new double[nFunding + nForward]; int i = 0, fundingIndex = 0, forwardIndex = 0; final InterestRateInstrumentTradeOrSecurityConverter securityConverter = getSecurityConverter(executionContext); for (final FixedIncomeStripWithSecurity strip : fundingCurveData.getCurveSpecification().getStrips()) { final Double fundingMarketValue = fundingCurveData.getDataPoint(strip.getSecurityIdentifier()); if (fundingMarketValue == null) { throw new OpenGammaRuntimeException("Could not get funding market data for " + strip); } final double marketValue = fundingMarketValue; final FinancialSecurity financialSecurity = (FinancialSecurity) strip.getSecurity(); InstrumentDerivative derivative; final String[] curveNames = FixedIncomeInstrumentCurveExposureHelper.getCurveNamesForFundingCurveInstrument(strip.getInstrumentType(), fundingCurveName, forwardCurveName); final InstrumentDefinition<?> definition = securityConverter.visit(financialSecurity); if (strip.getSecurity().getSecurityType().equals("FUTURE")) { throw new OpenGammaRuntimeException("We do not currently support FundingCurves containing FUTURES. Contact QR if you desire this."); } derivative = getDefinitionConverter().convert(financialSecurity, definition, now, curveNames, fundingTimeSeries); if (derivative == null) { throw new OpenGammaRuntimeException("Had a null InterestRateDefinition for " + strip); } if (_calcTypeParRate) { // set market value to the rate marketValues[i] = marketValue; } // else PV, leave at 0 derivatives.add(derivative); initialRatesGuess[i] = marketValue; i++; fundingNodeTimes[fundingIndex] = derivative.accept(LAST_DATE_CALCULATOR); fundingIndex++; } for (final FixedIncomeStripWithSecurity strip : forwardCurveData.getCurveSpecification().getStrips()) { final Double forwardMarketValue = forwardCurveData.getDataPoint(strip.getSecurityIdentifier()); if (forwardMarketValue == null) { throw new OpenGammaRuntimeException("Could not get forward market data for " + strip); } double marketValue = forwardMarketValue; final FinancialSecurity financialSecurity = (FinancialSecurity) strip.getSecurity(); InstrumentDerivative derivative = null; final String[] curveNames = FixedIncomeInstrumentCurveExposureHelper.getCurveNamesForForwardCurveInstrument(strip.getInstrumentType(), fundingCurveName, forwardCurveName); try { InstrumentDefinition<?> definition = securityConverter.visit(financialSecurity); if (strip.getSecurity().getSecurityType().equals("FUTURE")) { if (!_calcTypeParRate) { // Scale notional to 1 - this is to better condition the jacobian matrix // Set trade price to current market value - so the present value will be zero once fit definition = ((InterestRateFutureTransactionDefinition) definition).withNewNotionalAndTransactionPrice(1.0, marketValue); } marketValue = 1 - marketValue; // transform to rate for initial rates guess } derivative = getDefinitionConverter().convert(financialSecurity, definition, now, curveNames, forwardTimeSeries); } catch (final Exception e) { s_logger.error("Caught exception for " + financialSecurity, e); } if (derivative == null) { throw new OpenGammaRuntimeException("Had a null InterestRateDefinition for " + strip); } if (_calcTypeParRate) { // set market value to the rate, else leave at 0 marketValues[i] = marketValue; } derivatives.add(derivative); initialRatesGuess[i] = marketValue; i++; forwardNodeTimes[forwardIndex] = derivative.accept(LAST_DATE_CALCULATOR); forwardIndex++; } final LinkedHashMap<String, double[]> curveNodes = new LinkedHashMap<>(); final LinkedHashMap<String, Interpolator1D> interpolators = new LinkedHashMap<>(); curveNodes.put(fundingCurveName, fundingNodeTimes); interpolators.put(fundingCurveName, getInterpolator(fundingCurveData.getCurveSpecification())); curveNodes.put(forwardCurveName, forwardNodeTimes); interpolators.put(forwardCurveName, getInterpolator(forwardCurveData.getCurveSpecification())); // TODO have use finite difference or not as an input [FIN-147] final Currency currency = Currency.of(targetSpec.getUniqueId().getValue()); final MultipleYieldCurveFinderDataBundle data = new MultipleYieldCurveFinderDataBundle(derivatives, marketValues, null, curveNodes, interpolators, false, new FXMatrix(currency)); // TODO have the calculator and sensitivity calculators as an input [FIN-144], [FIN-145] final Function1D<DoubleMatrix1D, DoubleMatrix1D> curveCalculator = new MultipleYieldCurveFinderFunction(data, getCalculator()); final Function1D<DoubleMatrix1D, DoubleMatrix2D> jacobianCalculator = new MultipleYieldCurveFinderJacobian(data, getSensitivityCalculator()); NewtonVectorRootFinder rootFinder; double[] yields = null; // TODO have the decomposition as an optional input [FIN-146] try { rootFinder = new BroydenVectorRootFinder(1e-4, 1e-4, 10000, DecompositionFactory.getDecomposition(DecompositionFactory.SV_COLT_NAME)); yields = rootFinder.getRoot(curveCalculator, jacobianCalculator, new DoubleMatrix1D(initialRatesGuess)).getData(); } catch (final Exception eSV) { s_logger.warn("Could not find root using SV decomposition and " + _calculationType + " method for curves " + fundingCurveName + " and " + forwardCurveName + ". Error was: " + eSV.getMessage()); throw new OpenGammaRuntimeException("Could not find curves " + fundingCurveName + " (" + targetSpec.getUniqueId().getValue() + "), " + forwardCurveName + " (" + targetSpec.getUniqueId().getValue() + ") using SV decomposition and calculation method " + _calculationType); } final YieldAndDiscountCurve fundingCurve; if (createSensitivities || createFundingYieldCurve) { final double[] fundingYields = Arrays.copyOfRange(yields, 0, fundingNodeTimes.length); fundingCurve = YieldCurve.from(InterpolatedDoublesCurve.from(fundingNodeTimes, fundingYields, getInterpolator(fundingCurveData.getCurveSpecification()))); } else { fundingCurve = null; } final YieldAndDiscountCurve forwardCurve; if (createSensitivities || createForwardYieldCurve) { final double[] forwardYields = Arrays.copyOfRange(yields, fundingNodeTimes.length, yields.length); forwardCurve = YieldCurve.from(InterpolatedDoublesCurve.from(forwardNodeTimes, forwardYields, getInterpolator(forwardCurveData.getCurveSpecification()))); } else { forwardCurve = null; } final Set<ComputedValue> result = Sets.newHashSetWithExpectedSize(4); final ValueProperties.Builder properties = createValueProperties().with(ValuePropertyNames.CURVE_CALCULATION_METHOD, getCalculationType()) .with(YieldCurveFunction.PROPERTY_FORWARD_CURVE, forwardCurveName).with(YieldCurveFunction.PROPERTY_FUNDING_CURVE, fundingCurveName); if (createJacobian) { final DoubleMatrix2D jacobian = jacobianCalculator.evaluate(new DoubleMatrix1D(yields)); result.add(new ComputedValue(new ValueSpecification(ValueRequirementNames.YIELD_CURVE_JACOBIAN, targetSpec, properties.get()), jacobian.getData())); } if (createSensitivities) { // calcType is PresentValue. Compute CouponSens ( dPrice / dParRate ) used in conjunction with Jacobian to get Yield Curve Node (Par Rate) Sensitivities final double[] couponSensitivities = new double[derivatives.size()]; int ii = 0; final String[] curveNames = new String[] {forwardCurveName, fundingCurveName }; final YieldAndDiscountCurve[] curves = new YieldAndDiscountCurve[] {forwardCurve, fundingCurve }; final YieldCurveBundle curveBundle = new YieldCurveBundle(curveNames, curves); for (final InstrumentDerivative derivative : derivatives) { couponSensitivities[ii++] = derivative.accept(getCouponSensitivityCalculator(), curveBundle); } final ValueProperties couponProperties = createValueProperties().with(YieldCurveFunction.PROPERTY_FORWARD_CURVE, forwardCurveName) .with(YieldCurveFunction.PROPERTY_FUNDING_CURVE, fundingCurveName).get(); result.add(new ComputedValue(new ValueSpecification(ValueRequirementNames.PRESENT_VALUE_COUPON_SENSITIVITY, targetSpec, couponProperties), new DoubleMatrix1D(couponSensitivities))); } if (createForwardYieldCurve) { result.add(new ComputedValue(new ValueSpecification(ValueRequirementNames.YIELD_CURVE, targetSpec, properties.with(ValuePropertyNames.CURVE, forwardCurveName).get()), forwardCurve)); } if (createFundingYieldCurve) { result.add(new ComputedValue(new ValueSpecification(ValueRequirementNames.YIELD_CURVE, targetSpec, properties.withoutAny(ValuePropertyNames.CURVE) .with(ValuePropertyNames.CURVE, fundingCurveName).get()), fundingCurve)); } return result; } }