/** * Compute the spread to be added to the rate of the instrument for which the present value of the * instrument is zero. The "rate" can be a "rate" or a "yield" and will depend of each instrument. * * @deprecated {@link YieldCurveBundle} is deprecated */ @Deprecated public final class ParSpreadRateCalculator extends InstrumentDerivativeVisitorAdapter<YieldCurveBundle, Double> { /** The unique instance of the calculator. */ private static final ParSpreadRateCalculator INSTANCE = new ParSpreadRateCalculator(); /** * Gets the calculator instance. * * @return The calculator. */ public static ParSpreadRateCalculator getInstance() { return INSTANCE; } /** Constructor. */ private ParSpreadRateCalculator() {} /** The methods and calculators. */ private static final PresentValueMCACalculator PVMCC = PresentValueMCACalculator.getInstance(); private static final PresentValueBasisPointCalculator PVBPC = PresentValueBasisPointCalculator.getInstance(); private static final CashDiscountingMethod METHOD_DEPOSIT = CashDiscountingMethod.getInstance(); private static final DepositZeroDiscountingMethod METHOD_DEPOSIT_ZERO = DepositZeroDiscountingMethod.getInstance(); private static final ForwardRateAgreementDiscountingMethod METHOD_FRA = ForwardRateAgreementDiscountingMethod.getInstance(); // private static final InterestRateFutureTransactionDiscountingMethod // METHOD_IR_FUTURES_TRANSACTION = InterestRateFutureTransactionDiscountingMethod.getInstance(); private static final InterestRateFutureSecurityDiscountingMethod METHOD_IR_FUTURES_SECURITY = InterestRateFutureSecurityDiscountingMethod.getInstance(); @Override public Double visitCash(final Cash deposit, final YieldCurveBundle curves) { return METHOD_DEPOSIT.parSpread(deposit, curves); } @Override public Double visitDepositZero(final DepositZero deposit, final YieldCurveBundle curves) { return METHOD_DEPOSIT_ZERO.parSpread(deposit, curves); } /** * For swaps the ParSpread is the spread to be added on each coupon of the first leg to obtain a * present value of zero. It is computed as the opposite of the present value of the swap divided * by the present value of a basis point of the first leg (as computed by the * PresentValueBasisPointCalculator). * * @param swap The swap. * @param curves The yield curve bundle. * @return The par spread. */ @Override public Double visitSwap(final Swap<?, ?> swap, final YieldCurveBundle curves) { Validate.notNull(curves); Validate.notNull(swap); return -curves .getFxRates() .convert(swap.accept(PVMCC, curves), swap.getFirstLeg().getCurrency()) .getAmount() / swap.getFirstLeg().accept(PVBPC, curves); } @Override public Double visitFixedCouponSwap(final SwapFixedCoupon<?> swap, final YieldCurveBundle curves) { return visitSwap(swap, curves); } /** * For ForwardRateAgreement the ParSpread is the spread to be added to the fixed rate to obtain a * present value of zero. * * @param fra The forward rate agreement. * @param curves The yield curve bundle. * @return The par spread. */ @Override public Double visitForwardRateAgreement( final ForwardRateAgreement fra, final YieldCurveBundle curves) { Validate.notNull(curves); Validate.notNull(fra); return METHOD_FRA.parSpread(fra, curves); } /** * For InterestRateFutures the ParSpread is the spread to be added to the reference price to * obtain a present value of zero. * * @param future The futures. * @param curves The yield curve bundle. * @return The par spread. */ @Override public Double visitInterestRateFutureTransaction( final InterestRateFutureTransaction future, final YieldCurveBundle curves) { return -(METHOD_IR_FUTURES_SECURITY.price(future.getUnderlying(), curves) - future.getReferencePrice()); } // /** // * For InterestRateFutures the ParSpread is the spread to be added to the reference price to // obtain a present value of zero. // * @param future The futures. // * @param curves The yield curve bundle. // * @return The par spread. // */ // @Override // public Double visitInterestRateFutureSecurity(final InterestRateFutureSecurity future, final // YieldCurveBundle curves) { // return -(METHOD_IR_FUTURES_SECURITY.presentValue(future, curves).getAmount()); // } }
/** * For an instrument, this calculates the sensitivity of the present value (PV) to points on the * yield curve(s) (i.e. dPV/dR at every point the instrument has sensitivity). The return format is * a map with curve names (String) as keys and List of DoublesPair as the values; each list holds * set of time (corresponding to point of the yield curve) and sensitivity pairs (i.e. dPV/dR at * that time). <b>Note:</b> The length of the list is instrument dependent and may have repeated * times (with the understanding the sensitivities should be summed). */ public class PresentValueCurveSensitivityCalculator extends AbstractInstrumentDerivativeVisitor<YieldCurveBundle, Map<String, List<DoublesPair>>> { // TODO: Change the output format from Map to InterestRateCurveSensitivity, which wraps the map // and adds common functionality. /** The method unique instance. */ private static final PresentValueCurveSensitivityCalculator INSTANCE = new PresentValueCurveSensitivityCalculator(); /** * Return the unique instance of the class. * * @return The instance. */ public static PresentValueCurveSensitivityCalculator getInstance() { return INSTANCE; } /** Constructor. */ PresentValueCurveSensitivityCalculator() {} /** The method used for OIS coupons. */ private static final CouponOISDiscountingMethod METHOD_OIS = CouponOISDiscountingMethod.getInstance(); private static final CouponIborDiscountingMethod METHOD_IBOR = CouponIborDiscountingMethod.getInstance(); private static final CouponIborGearingDiscountingMethod METHOD_IBOR_GEARING = CouponIborGearingDiscountingMethod.getInstance(); private static final CashDiscountingMethod METHOD_DEPOSIT = CashDiscountingMethod.getInstance(); private static final DepositZeroDiscountingMethod METHOD_DEPOSIT_ZERO = DepositZeroDiscountingMethod.getInstance(); private static final BillSecurityDiscountingMethod METHOD_BILL_SECURITY = BillSecurityDiscountingMethod.getInstance(); private static final BillTransactionDiscountingMethod METHOD_BILL_TRANSACTION = BillTransactionDiscountingMethod.getInstance(); @Override public Map<String, List<DoublesPair>> visit( final InstrumentDerivative instrument, final YieldCurveBundle curves) { return instrument.accept(this, curves); } @Override public Map<String, List<DoublesPair>> visitCash(final Cash cash, final YieldCurveBundle curves) { return METHOD_DEPOSIT.presentValueCurveSensitivity(cash, curves).getSensitivities(); } @Override public Map<String, List<DoublesPair>> visitDepositZero( final DepositZero deposit, final YieldCurveBundle curves) { return METHOD_DEPOSIT_ZERO.presentValueCurveSensitivity(deposit, curves).getSensitivities(); } @Override public Map<String, List<DoublesPair>> visitForwardRateAgreement( final ForwardRateAgreement fra, final YieldCurveBundle curves) { final ForwardRateAgreementDiscountingMethod method = ForwardRateAgreementDiscountingMethod.getInstance(); return method.presentValueCurveSensitivity(fra, curves).getSensitivities(); } /** {@inheritDoc} Future transaction pricing without convexity adjustment. */ @Override public Map<String, List<DoublesPair>> visitInterestRateFuture( final InterestRateFuture future, final YieldCurveBundle curves) { final InterestRateFutureDiscountingMethod method = InterestRateFutureDiscountingMethod.getInstance(); return method.presentValueCurveSensitivity(future, curves).getSensitivities(); } @Override public Map<String, List<DoublesPair>> visitBondFixedSecurity( final BondFixedSecurity bond, final YieldCurveBundle curves) { final BondSecurityDiscountingMethod method = BondSecurityDiscountingMethod.getInstance(); return method.presentValueCurveSensitivity(bond, curves).getSensitivities(); } @Override public Map<String, List<DoublesPair>> visitBondFixedTransaction( final BondFixedTransaction bond, final YieldCurveBundle curves) { final BondTransactionDiscountingMethod method = BondTransactionDiscountingMethod.getInstance(); return method.presentValueSensitivity(bond, curves).getSensitivities(); } @Override public Map<String, List<DoublesPair>> visitBondIborTransaction( final BondIborTransaction bond, final YieldCurveBundle curves) { final BondTransactionDiscountingMethod method = BondTransactionDiscountingMethod.getInstance(); return method.presentValueSensitivity(bond, curves).getSensitivities(); } @Override public Map<String, List<DoublesPair>> visitBillSecurity( final BillSecurity bill, final YieldCurveBundle curves) { return METHOD_BILL_SECURITY.presentValueCurveSensitivity(bill, curves).getSensitivities(); } @Override public Map<String, List<DoublesPair>> visitBillTransaction( final BillTransaction bill, final YieldCurveBundle curves) { return METHOD_BILL_TRANSACTION.presentValueCurveSensitivity(bill, curves).getSensitivities(); } @Override public Map<String, List<DoublesPair>> visitBondFuture( final BondFuture bondFuture, final YieldCurveBundle curves) { Validate.notNull(curves); Validate.notNull(bondFuture); final BondFutureDiscountingMethod method = BondFutureDiscountingMethod.getInstance(); return method.presentValueCurveSensitivity(bondFuture, curves).getSensitivities(); } @Override public Map<String, List<DoublesPair>> visitSwap( final Swap<?, ?> swap, final YieldCurveBundle curves) { final Map<String, List<DoublesPair>> senseR = visit(swap.getSecondLeg(), curves); final Map<String, List<DoublesPair>> senseP = visit(swap.getFirstLeg(), curves); return addSensitivity(senseR, senseP); } @Override public Map<String, List<DoublesPair>> visitFixedCouponSwap( final SwapFixedCoupon<?> swap, final YieldCurveBundle curves) { return visitSwap(swap, curves); } @Override public Map<String, List<DoublesPair>> visitTenorSwap( final TenorSwap<? extends Payment> swap, final YieldCurveBundle curves) { return visitSwap(swap, curves); } @Override public Map<String, List<DoublesPair>> visitFloatingRateNote( final FloatingRateNote frn, final YieldCurveBundle curves) { return visitSwap(frn, curves); } @Override public Map<String, List<DoublesPair>> visitCrossCurrencySwap( final CrossCurrencySwap ccs, final YieldCurveBundle curves) { final Map<String, List<DoublesPair>> senseD = visit(ccs.getDomesticLeg(), curves); final Map<String, List<DoublesPair>> senseF = visit(ccs.getForeignLeg(), curves); // Note the sensitivities subtract rather than add here because the CCS is set up as domestic // FRN minus a foreign FRN return addSensitivity(senseD, multiplySensitivity(senseF, -ccs.getSpotFX())); } @Override public Map<String, List<DoublesPair>> visitForexForward( final ForexForward fx, final YieldCurveBundle curves) { final Map<String, List<DoublesPair>> senseP1 = visit(fx.getPaymentCurrency1(), curves); final Map<String, List<DoublesPair>> senseP2 = visit(fx.getPaymentCurrency2(), curves); // Note the sensitivities add rather than subtract here because the FX Forward is set up as a // notional in one currency PLUS a notional in another with the opposite sign return InterestRateCurveSensitivityUtils.addSensitivity( senseP1, multiplySensitivity(senseP2, fx.getSpotForexRate())); } @Override public Map<String, List<DoublesPair>> visitGenericAnnuity( final Annuity<? extends Payment> annuity, final YieldCurveBundle data) { final Map<String, List<DoublesPair>> map = new HashMap<String, List<DoublesPair>>(); for (final Payment p : annuity.getPayments()) { final Map<String, List<DoublesPair>> tempMap = visit(p, data); for (final String name : tempMap.keySet()) { if (!map.containsKey(name)) { map.put(name, tempMap.get(name)); } else { final List<DoublesPair> tempList = map.get(name); tempList.addAll(tempMap.get(name)); map.put(name, tempList); } } } return map; } @Override public Map<String, List<DoublesPair>> visitFixedPayment( final PaymentFixed payment, final YieldCurveBundle data) { final String curveName = payment.getFundingCurveName(); final YieldAndDiscountCurve curve = data.getCurve(curveName); final double t = payment.getPaymentTime(); final DoublesPair s = new DoublesPair(t, -t * payment.getAmount() * curve.getDiscountFactor(t)); final List<DoublesPair> list = new ArrayList<DoublesPair>(); list.add(s); final Map<String, List<DoublesPair>> result = new HashMap<String, List<DoublesPair>>(); result.put(curveName, list); return result; } @Override public Map<String, List<DoublesPair>> visitCouponIborSpread( final CouponIborSpread payment, final YieldCurveBundle data) { final String fundingCurveName = payment.getFundingCurveName(); final String liborCurveName = payment.getForwardCurveName(); final YieldAndDiscountCurve fundCurve = data.getCurve(fundingCurveName); final YieldAndDiscountCurve liborCurve = data.getCurve(liborCurveName); final double tPay = payment.getPaymentTime(); final double tStart = payment.getFixingPeriodStartTime(); final double tEnd = payment.getFixingPeriodEndTime(); final double dfPay = fundCurve.getDiscountFactor(tPay); final double dfStart = liborCurve.getDiscountFactor(tStart); final double dfEnd = liborCurve.getDiscountFactor(tEnd); final double forward = (dfStart / dfEnd - 1) / payment.getFixingYearFraction(); final double notional = payment.getNotional(); final Map<String, List<DoublesPair>> result = new HashMap<String, List<DoublesPair>>(); List<DoublesPair> temp = new ArrayList<DoublesPair>(); DoublesPair s; s = new DoublesPair( tPay, -tPay * dfPay * notional * (forward + payment.getSpread()) * payment.getPaymentYearFraction()); temp.add(s); if (!liborCurveName.equals(fundingCurveName)) { result.put(fundingCurveName, temp); temp = new ArrayList<DoublesPair>(); } final double ratio = notional * dfPay * dfStart / dfEnd * payment.getPaymentYearFraction() / payment.getFixingYearFraction(); s = new DoublesPair(tStart, -tStart * ratio); temp.add(s); s = new DoublesPair(tEnd, tEnd * ratio); temp.add(s); result.put(liborCurveName, temp); return result; } @Override public Map<String, List<DoublesPair>> visitCouponOIS( final CouponOIS payment, final YieldCurveBundle data) { final Map<String, List<DoublesPair>> result = METHOD_OIS.presentValueCurveSensitivity(payment, data).getSensitivities(); return result; } @Override public Map<String, List<DoublesPair>> visitFixedCouponAnnuity( final AnnuityCouponFixed annuity, final YieldCurveBundle data) { return visitGenericAnnuity(annuity, data); } @Override public Map<String, List<DoublesPair>> visitCouponFixed( final CouponFixed payment, final YieldCurveBundle data) { return visitFixedPayment(payment.toPaymentFixed(), data); } @Override public Map<String, List<DoublesPair>> visitForwardLiborAnnuity( final AnnuityCouponIbor annuity, final YieldCurveBundle data) { return visitGenericAnnuity(annuity, data); } @Override public Map<String, List<DoublesPair>> visitFixedFloatSwap( final FixedFloatSwap swap, final YieldCurveBundle data) { return visitFixedCouponSwap(swap, data); } @Override public Map<String, List<DoublesPair>> visitCouponCMS( final CouponCMS payment, final YieldCurveBundle data) { final CouponCMSDiscountingMethod method = CouponCMSDiscountingMethod.getInstance(); return method.presentValueSensitivity(payment, data).getSensitivities(); } @Override public Map<String, List<DoublesPair>> visitCouponIbor( final CouponIbor coupon, final YieldCurveBundle curves) { return METHOD_IBOR.presentValueCurveSensitivity(coupon, curves).getSensitivities(); } @Override public Map<String, List<DoublesPair>> visitCouponIborGearing( final CouponIborGearing coupon, final YieldCurveBundle curves) { return METHOD_IBOR_GEARING.presentValueCurveSensitivity(coupon, curves).getSensitivities(); } /** * Compute the sensitivity of the discount factor at a given time. * * @param curveName The curve name associated to the discount factor. * @param curve The curve from which the discount factor should be computed. * @param time The time * @return The sensitivity. */ public static Map<String, List<DoublesPair>> discountFactorSensitivity( final String curveName, final YieldAndDiscountCurve curve, final double time) { final DoublesPair s = new DoublesPair(time, -time * curve.getDiscountFactor(time)); final List<DoublesPair> list = new ArrayList<DoublesPair>(); list.add(s); final Map<String, List<DoublesPair>> result = new HashMap<String, List<DoublesPair>>(); result.put(curveName, list); return result; } }
/** * Constructs a single yield curve and its Jacobian from an FX-implied yield curve calculation * configuration and a yield curve definition that contains <b>only</b> {@link * StripInstrumentType#CASH} strips. The transformation of the yield curve allows risk to be * displayed with respect to implied deposit rates, not FX forwards. */ public class ImpliedDepositCurveFunction extends AbstractFunction { /** The calculation method property value */ public static final String IMPLIED_DEPOSIT = "ImpliedDeposit"; /** The Cash instrument method */ private static final CashDiscountingMethod METHOD_CASH = CashDiscountingMethod.getInstance(); /** Calculates the par rate */ private static final ParRateCalculator PAR_RATE_CALCULATOR = ParRateCalculator.getInstance(); /** Calculates the sensitivity of the par rate to the curves */ private static final ParRateCurveSensitivityCalculator PAR_RATE_SENSITIVITY_CALCULATOR = ParRateCurveSensitivityCalculator.getInstance(); /** The business day convention used for FX forward dates computation * */ private static final BusinessDayConvention MOD_FOL = BusinessDayConventions.MODIFIED_FOLLOWING; /** The logger */ private static final Logger s_logger = LoggerFactory.getLogger(ImpliedDepositCurveFunction.class); /** The curve name */ private final String _curveCalculationConfig; private ConfigSourceQuery<MultiCurveCalculationConfig> _multiCurveCalculationConfig; private ConfigSourceQuery<YieldCurveDefinition> _yieldCurveDefinition; /** @param curveCalculationConfig The curve name, not null */ public ImpliedDepositCurveFunction(final String curveCalculationConfig) { ArgumentChecker.notNull(curveCalculationConfig, "curve name"); _curveCalculationConfig = curveCalculationConfig; } @Override public void init(final FunctionCompilationContext context) { _multiCurveCalculationConfig = ConfigSourceQuery.init(context, this, MultiCurveCalculationConfig.class); _yieldCurveDefinition = ConfigSourceQuery.init(context, this, YieldCurveDefinition.class); } @Override public CompiledFunctionDefinition compile( final FunctionCompilationContext context, final Instant atInstant) { final MultiCurveCalculationConfig impliedConfiguration = _multiCurveCalculationConfig.get(_curveCalculationConfig); if (impliedConfiguration == null) { throw new OpenGammaRuntimeException( "Multi-curve calculation called " + _curveCalculationConfig + " was null"); } ComputationTarget target = context.getComputationTargetResolver().resolve(impliedConfiguration.getTarget()); if (!(target.getValue() instanceof Currency)) { throw new OpenGammaRuntimeException( "Target of curve calculation configuration was not a currency"); } final Currency impliedCurrency = (Currency) target.getValue(); if (!IMPLIED_DEPOSIT.equals(impliedConfiguration.getCalculationMethod())) { throw new OpenGammaRuntimeException( "Curve calculation method was not " + IMPLIED_DEPOSIT + " for configuration called " + _curveCalculationConfig); } final String[] impliedCurveNames = impliedConfiguration.getYieldCurveNames(); if (impliedCurveNames.length != 1) { throw new OpenGammaRuntimeException( "Can only handle configurations with a single implied curve"); } final LinkedHashMap<String, String[]> originalConfigurationName = impliedConfiguration.getExogenousConfigData(); if (originalConfigurationName == null || originalConfigurationName.size() != 1) { throw new OpenGammaRuntimeException("Need a configuration with one exogenous configuration"); } final Map.Entry<String, String[]> entry = Iterables.getOnlyElement(originalConfigurationName.entrySet()); final String[] originalCurveNames = entry.getValue(); if (originalCurveNames.length != 1) { s_logger.warn("Found more than one exogenous configuration name; using only the first"); } final MultiCurveCalculationConfig originalConfiguration = _multiCurveCalculationConfig.get(entry.getKey()); if (originalConfiguration == null) { throw new OpenGammaRuntimeException( "Multi-curve calculation called " + entry.getKey() + " was null"); } target = context.getComputationTargetResolver().resolve(originalConfiguration.getTarget()); if (!(target.getValue() instanceof Currency)) { throw new OpenGammaRuntimeException( "Target of curve calculation configuration was not a currency"); } final Currency originalCurrency = (Currency) target.getValue(); if (!originalCurrency.equals(impliedCurrency)) { throw new OpenGammaRuntimeException( "Currency targets for configurations " + _curveCalculationConfig + " and " + entry.getKey() + " did not match"); } final YieldCurveDefinition impliedDefinition = _yieldCurveDefinition.get(impliedCurveNames[0] + "_" + impliedCurrency.getCode()); if (impliedDefinition == null) { throw new OpenGammaRuntimeException( "Could not get implied definition called " + impliedCurveNames[0] + "_" + impliedCurrency.getCode()); } final Set<FixedIncomeStrip> strips = impliedDefinition.getStrips(); for (final FixedIncomeStrip strip : strips) { if (strip.getInstrumentType() != StripInstrumentType.CASH) { throw new OpenGammaRuntimeException( "Can only handle yield curve definitions with CASH strips"); } } final ZonedDateTime atZDT = ZonedDateTime.ofInstant(atInstant, ZoneOffset.UTC); return new MyCompiledFunction( atZDT.with(LocalTime.MIDNIGHT), atZDT.plusDays(1).with(LocalTime.MIDNIGHT).minusNanos(1000000), impliedConfiguration, impliedDefinition, originalConfiguration, originalCurveNames[0]); }; private class MyCompiledFunction extends AbstractInvokingCompiledFunction { /** The definition of the implied curve */ private final YieldCurveDefinition _impliedDefinition; /** The implied curve calculation configuration */ private final MultiCurveCalculationConfig _impliedConfiguration; /** The original curve calculation configuration */ private final MultiCurveCalculationConfig _originalConfiguration; /** The implied curve name */ private final String _impliedCurveName; /** The original curve name */ private final String _originalCurveName; /** The currency */ private final Currency _currency; /** The interpolator */ private final String _interpolatorName; /** The left extrapolator */ private final String _leftExtrapolatorName; /** The right extrapolator */ private final String _rightExtrapolatorName; public MyCompiledFunction( final ZonedDateTime earliestInvokation, final ZonedDateTime latestInvokation, final MultiCurveCalculationConfig impliedConfiguration, final YieldCurveDefinition impliedDefinition, final MultiCurveCalculationConfig originalConfiguration, final String originalCurveName) { super(earliestInvokation, latestInvokation); _impliedConfiguration = impliedConfiguration; _impliedDefinition = impliedDefinition; _originalConfiguration = originalConfiguration; _impliedCurveName = impliedDefinition.getName(); _originalCurveName = originalCurveName; _currency = impliedDefinition.getCurrency(); _interpolatorName = impliedDefinition.getInterpolatorName(); _leftExtrapolatorName = impliedDefinition.getLeftExtrapolatorName(); _rightExtrapolatorName = impliedDefinition.getRightExtrapolatorName(); } @Override public Set<ComputedValue> execute( final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues) throws AsynchronousExecution { final Object originalCurveObject = inputs.getValue(YIELD_CURVE); if (originalCurveObject == null) { throw new OpenGammaRuntimeException("Could not get original curve"); } ValueProperties resultCurveProperties = null; String absoluteToleranceName = null; String relativeToleranceName = null; String iterationsName = null; String decompositionName = null; String useFiniteDifferenceName = null; for (final ValueRequirement desiredValue : desiredValues) { if (desiredValue.getValueName().equals(YIELD_CURVE)) { absoluteToleranceName = desiredValue.getConstraint( MultiYieldCurvePropertiesAndDefaults.PROPERTY_ROOT_FINDER_ABSOLUTE_TOLERANCE); relativeToleranceName = desiredValue.getConstraint( MultiYieldCurvePropertiesAndDefaults.PROPERTY_ROOT_FINDER_RELATIVE_TOLERANCE); iterationsName = desiredValue.getConstraint( MultiYieldCurvePropertiesAndDefaults.PROPERTY_ROOT_FINDER_MAX_ITERATIONS); decompositionName = desiredValue.getConstraint( MultiYieldCurvePropertiesAndDefaults.PROPERTY_DECOMPOSITION); useFiniteDifferenceName = desiredValue.getConstraint( MultiYieldCurvePropertiesAndDefaults.PROPERTY_USE_FINITE_DIFFERENCE); resultCurveProperties = desiredValue.getConstraints().copy().get(); break; } } if (resultCurveProperties == null) { throw new OpenGammaRuntimeException("Could not get result curve properties"); } final ValueProperties resultJacobianProperties = resultCurveProperties.withoutAny(CURVE); ZonedDateTime valuationDateTime = executionContext .getValuationTime() .atZone(executionContext.getValuationClock().getZone()); final HolidaySource holidaySource = OpenGammaExecutionContext.getHolidaySource(executionContext); final ConventionSource conventionSource = OpenGammaExecutionContext.getConventionSource(executionContext); final Calendar calendar = CalendarUtils.getCalendar(holidaySource, _currency); final DepositConvention convention = conventionSource.getSingle( ExternalId.of(SCHEME_NAME, getConventionName(_currency, DEPOSIT)), DepositConvention.class); final int spotLag = convention.getSettlementDays(); final ExternalId conventionSettlementRegion = convention.getRegionCalendar(); ZonedDateTime spotDate; if (spotLag == 0 && conventionSettlementRegion == null) { spotDate = valuationDateTime; } else { spotDate = ScheduleCalculator.getAdjustedDate(valuationDateTime, spotLag, calendar); ; } final YieldCurveBundle curves = new YieldCurveBundle(); final String fullYieldCurveName = _originalCurveName + "_" + _currency; curves.setCurve(fullYieldCurveName, (YieldAndDiscountCurve) originalCurveObject); final int n = _impliedDefinition.getStrips().size(); final double[] t = new double[n]; final double[] r = new double[n]; int i = 0; final DayCount dayCount = DayCountFactory.INSTANCE.getDayCount( "Act/360"); // TODO: Get the convention from the curve. final String impliedDepositCurveName = _curveCalculationConfig + "_" + _currency.getCode(); final List<InstrumentDerivative> derivatives = new ArrayList<>(); for (final FixedIncomeStrip strip : _impliedDefinition.getStrips()) { final Tenor tenor = strip.getCurveNodePointTime(); final ZonedDateTime paymentDate = ScheduleCalculator.getAdjustedDate( spotDate, tenor.getPeriod(), MOD_FOL, calendar, true); final double startTime = TimeCalculator.getTimeBetween(valuationDateTime, spotDate); final double endTime = TimeCalculator.getTimeBetween(valuationDateTime, paymentDate); final double accrualFactor = dayCount.getDayCountFraction(spotDate, paymentDate, calendar); final Cash cashFXCurve = new Cash(_currency, startTime, endTime, 1, 0, accrualFactor, fullYieldCurveName); final double parRate = METHOD_CASH.parRate(cashFXCurve, curves); final Cash cashDepositCurve = new Cash(_currency, startTime, endTime, 1, 0, accrualFactor, impliedDepositCurveName); derivatives.add(cashDepositCurve); t[i] = endTime; r[i++] = parRate; } final CombinedInterpolatorExtrapolator interpolator = CombinedInterpolatorExtrapolatorFactory.getInterpolator( _interpolatorName, _leftExtrapolatorName, _rightExtrapolatorName); final double absoluteTolerance = Double.parseDouble(absoluteToleranceName); final double relativeTolerance = Double.parseDouble(relativeToleranceName); final int iterations = Integer.parseInt(iterationsName); final Decomposition<?> decomposition = DecompositionFactory.getDecomposition(decompositionName); final boolean useFiniteDifference = Boolean.parseBoolean(useFiniteDifferenceName); final LinkedHashMap<String, double[]> curveNodes = new LinkedHashMap<>(); final LinkedHashMap<String, Interpolator1D> interpolators = new LinkedHashMap<>(); curveNodes.put(impliedDepositCurveName, t); interpolators.put(impliedDepositCurveName, interpolator); final FXMatrix fxMatrix = new FXMatrix(); final YieldCurveBundle knownCurve = new YieldCurveBundle(); final MultipleYieldCurveFinderDataBundle data = new MultipleYieldCurveFinderDataBundle( derivatives, r, knownCurve, curveNodes, interpolators, useFiniteDifference, fxMatrix); final NewtonVectorRootFinder rootFinder = new BroydenVectorRootFinder( absoluteTolerance, relativeTolerance, iterations, decomposition); final Function1D<DoubleMatrix1D, DoubleMatrix1D> curveCalculator = new MultipleYieldCurveFinderFunction(data, PAR_RATE_CALCULATOR); final Function1D<DoubleMatrix1D, DoubleMatrix2D> jacobianCalculator = new MultipleYieldCurveFinderJacobian(data, PAR_RATE_SENSITIVITY_CALCULATOR); final double[] fittedYields = rootFinder.getRoot(curveCalculator, jacobianCalculator, new DoubleMatrix1D(r)).getData(); final DoubleMatrix2D jacobianMatrix = jacobianCalculator.evaluate(new DoubleMatrix1D(fittedYields)); final YieldCurve impliedDepositCurve = new YieldCurve( impliedDepositCurveName, InterpolatedDoublesCurve.from(t, fittedYields, interpolator)); final ValueSpecification curveSpec = new ValueSpecification(YIELD_CURVE, target.toSpecification(), resultCurveProperties); final ValueSpecification jacobianSpec = new ValueSpecification( YIELD_CURVE_JACOBIAN, target.toSpecification(), resultJacobianProperties); return Sets.newHashSet( new ComputedValue(curveSpec, impliedDepositCurve), new ComputedValue(jacobianSpec, jacobianMatrix)); } @Override public ComputationTargetType getTargetType() { return ComputationTargetType.CURRENCY; } @Override public Set<ValueSpecification> getResults( final FunctionCompilationContext compilationContext, final ComputationTarget target) { final ValueProperties curveProperties = getCurveProperties(_impliedCurveName, _impliedConfiguration.getCalculationConfigName()); final ValueProperties jacobianProperties = getJacobianProperties(_impliedConfiguration.getCalculationConfigName()); final ComputationTargetSpecification targetSpec = target.toSpecification(); final ValueSpecification curve = new ValueSpecification(YIELD_CURVE, targetSpec, curveProperties); final ValueSpecification jacobian = new ValueSpecification(YIELD_CURVE_JACOBIAN, targetSpec, jacobianProperties); return Sets.newHashSet(curve, jacobian); } @Override public Set<ValueRequirement> getRequirements( final FunctionCompilationContext compilationContext, final ComputationTarget target, final ValueRequirement desiredValue) { final ValueProperties constraints = desiredValue.getConstraints(); final Set<String> rootFinderAbsoluteTolerance = constraints.getValues(PROPERTY_ROOT_FINDER_ABSOLUTE_TOLERANCE); if (rootFinderAbsoluteTolerance == null || rootFinderAbsoluteTolerance.size() != 1) { return null; } final Set<String> rootFinderRelativeTolerance = constraints.getValues(PROPERTY_ROOT_FINDER_RELATIVE_TOLERANCE); if (rootFinderRelativeTolerance == null || rootFinderRelativeTolerance.size() != 1) { return null; } final Set<String> maxIterations = constraints.getValues(PROPERTY_ROOT_FINDER_MAX_ITERATIONS); if (maxIterations == null || maxIterations.size() != 1) { return null; } final Set<String> decomposition = constraints.getValues(PROPERTY_DECOMPOSITION); if (decomposition == null || decomposition.size() != 1) { return null; } final Set<String> useFiniteDifference = constraints.getValues(PROPERTY_USE_FINITE_DIFFERENCE); if (useFiniteDifference == null || useFiniteDifference.size() != 1) { return null; } if (!_originalConfiguration.getTarget().equals(target.toSpecification())) { s_logger.info( "Invalid target, was {} - expected {}", target, _originalConfiguration.getTarget()); return null; } final ValueProperties properties = ValueProperties.builder() .with(CURVE_CALCULATION_METHOD, _originalConfiguration.getCalculationMethod()) .with(CURVE_CALCULATION_CONFIG, _originalConfiguration.getCalculationConfigName()) .with(CURVE, _originalCurveName) .get(); return Collections.singleton( new ValueRequirement(YIELD_CURVE, target.toSpecification(), properties)); } /** * Gets the properties of the implied yield curve. * * @param curveName The implied curve name * @return The properties */ private ValueProperties getCurveProperties( final String curveName, final String curveCalculationConfig) { return createValueProperties() .with(CURVE_CALCULATION_METHOD, IMPLIED_DEPOSIT) .with(CURVE, curveName) .with(CURVE_CALCULATION_CONFIG, curveCalculationConfig) .withAny(PROPERTY_ROOT_FINDER_ABSOLUTE_TOLERANCE) .withAny(PROPERTY_ROOT_FINDER_RELATIVE_TOLERANCE) .withAny(PROPERTY_ROOT_FINDER_MAX_ITERATIONS) .withAny(PROPERTY_DECOMPOSITION) .withAny(PROPERTY_USE_FINITE_DIFFERENCE) .with(InterpolatedDataProperties.X_INTERPOLATOR_NAME, _interpolatorName) .with(InterpolatedDataProperties.LEFT_X_EXTRAPOLATOR_NAME, _leftExtrapolatorName) .with(InterpolatedDataProperties.RIGHT_X_EXTRAPOLATOR_NAME, _rightExtrapolatorName) .get(); } /** * Gets the properties of the Jacobian with no values set. * * @return The properties. */ private ValueProperties getJacobianProperties(final String curveCalculationConfig) { return createValueProperties() .with(CURVE_CALCULATION_METHOD, IMPLIED_DEPOSIT) .with(CURVE_CALCULATION_CONFIG, curveCalculationConfig) .withAny(PROPERTY_ROOT_FINDER_ABSOLUTE_TOLERANCE) .withAny(PROPERTY_ROOT_FINDER_RELATIVE_TOLERANCE) .withAny(PROPERTY_ROOT_FINDER_MAX_ITERATIONS) .withAny(PROPERTY_DECOMPOSITION) .withAny(PROPERTY_USE_FINITE_DIFFERENCE) .with(InterpolatedDataProperties.X_INTERPOLATOR_NAME, _interpolatorName) .with(InterpolatedDataProperties.LEFT_X_EXTRAPOLATOR_NAME, _leftExtrapolatorName) .with(InterpolatedDataProperties.RIGHT_X_EXTRAPOLATOR_NAME, _rightExtrapolatorName) .get(); } } }