/// <summary>
        /// Calculates the market quote sensitivities from parameter sensitivity.
        /// <para>
        /// This calculates the market quote sensitivities of fixed incomes.
        /// The input parameter sensitivities must be computed based on the legal entity discounting provider.
        ///
        /// </para>
        /// </summary>
        /// <param name="paramSensitivities">  the curve parameter sensitivities </param>
        /// <param name="provider">  the legal entity discounting provider, containing Jacobian calibration information </param>
        /// <returns> the market quote sensitivities </returns>
        public virtual CurrencyParameterSensitivities sensitivity(CurrencyParameterSensitivities paramSensitivities, LegalEntityDiscountingProvider provider)
        {
            ArgChecker.notNull(paramSensitivities, "paramSensitivities");
            ArgChecker.notNull(provider, "provider");

            CurrencyParameterSensitivities result = CurrencyParameterSensitivities.empty();

            foreach (CurrencyParameterSensitivity paramSens in paramSensitivities.Sensitivities)
            {
                // find the matching calibration info
                Curve curve = provider.findData(paramSens.MarketDataName).filter(v => v is Curve).map(v => (Curve)v).orElseThrow(() => new System.ArgumentException("Market Quote sensitivity requires curve: " + paramSens.MarketDataName));
                JacobianCalibrationMatrix info = curve.Metadata.findInfo(CurveInfoType.JACOBIAN).orElseThrow(() => new System.ArgumentException("Market Quote sensitivity requires Jacobian calibration information"));

                // calculate the market quote sensitivity using the Jacobian
                DoubleMatrix jacobian              = info.JacobianMatrix;
                DoubleArray  paramSensMatrix       = paramSens.Sensitivity;
                DoubleArray  marketQuoteSensMatrix = (DoubleArray)MATRIX_ALGEBRA.multiply(paramSensMatrix, jacobian);
                DoubleArray  marketQuoteSens       = marketQuoteSensMatrix;

                // split between different curves
                IDictionary <CurveName, DoubleArray> split = info.splitValues(marketQuoteSens);
                foreach (KeyValuePair <CurveName, DoubleArray> entry in split.SetOfKeyValuePairs())
                {
                    CurveName curveName = entry.Key;
                    CurrencyParameterSensitivity maketQuoteSens = provider.findData(curveName).map(c => c.createParameterSensitivity(paramSens.Currency, entry.Value)).orElse(CurrencyParameterSensitivity.of(curveName, paramSens.Currency, entry.Value));
                    result = result.combinedWith(maketQuoteSens);
                }
            }
            return(result);
        }
        // jacobian indirect, merging groups
        private static DoubleMatrix jacobianIndirect(DoubleMatrix res, DoubleMatrix pDmCurrentMatrix, int nbTrades, int totalParamsGroup, int totalParamsPrevious, ImmutableList <CurveParameterSize> orderPrevious, ImmutableMap <CurveName, JacobianCalibrationMatrix> jacobiansPrevious)
        {
            if (totalParamsPrevious == 0)
            {
                return(DoubleMatrix.EMPTY);
            }
//JAVA TO C# CONVERTER NOTE: The following call to the 'RectangularArrays' helper class reproduces the rectangular array initialization that is automatic in Java:
//ORIGINAL LINE: double[][] nonDirect = new double[totalParamsGroup][totalParamsPrevious];
            double[][] nonDirect = RectangularArrays.ReturnRectangularDoubleArray(totalParamsGroup, totalParamsPrevious);
            for (int i = 0; i < nbTrades; i++)
            {
                Array.Copy(res.rowArray(i), 0, nonDirect[i], 0, totalParamsPrevious);
            }
            DoubleMatrix pDpPreviousMatrix = (DoubleMatrix)MATRIX_ALGEBRA.scale(MATRIX_ALGEBRA.multiply(pDmCurrentMatrix, DoubleMatrix.copyOf(nonDirect)), -1d);

            // all curves: order and size
            int[] startIndexBefore = new int[orderPrevious.size()];
            for (int i = 1; i < orderPrevious.size(); i++)
            {
                startIndexBefore[i] = startIndexBefore[i - 1] + orderPrevious.get(i - 1).ParameterCount;
            }
            // transition Matrix: all curves from previous groups
//JAVA TO C# CONVERTER NOTE: The following call to the 'RectangularArrays' helper class reproduces the rectangular array initialization that is automatic in Java:
//ORIGINAL LINE: double[][] transition = new double[totalParamsPrevious][totalParamsPrevious];
            double[][] transition = RectangularArrays.ReturnRectangularDoubleArray(totalParamsPrevious, totalParamsPrevious);
            for (int i = 0; i < orderPrevious.size(); i++)
            {
                int paramCountOuter = orderPrevious.get(i).ParameterCount;
                JacobianCalibrationMatrix thisInfo = jacobiansPrevious.get(orderPrevious.get(i).Name);
                DoubleMatrix thisMatrix            = thisInfo.JacobianMatrix;
                int          startIndexInner       = 0;
                for (int j = 0; j < orderPrevious.size(); j++)
                {
                    int paramCountInner = orderPrevious.get(j).ParameterCount;
                    if (thisInfo.containsCurve(orderPrevious.get(j).Name))
                    {     // If not, the matrix stay with 0
                        for (int k = 0; k < paramCountOuter; k++)
                        {
                            Array.Copy(thisMatrix.rowArray(k), startIndexInner, transition[startIndexBefore[i] + k], startIndexBefore[j], paramCountInner);
                        }
                    }
                    startIndexInner += paramCountInner;
                }
            }
            DoubleMatrix transitionMatrix = DoubleMatrix.copyOf(transition);

            return((DoubleMatrix)MATRIX_ALGEBRA.multiply(pDpPreviousMatrix, transitionMatrix));
        }
Exemple #3
0
        //-------------------------------------------------------------------------
        internal static ScenarioMarketData marketData(CurveName curveName)
        {
            DoubleMatrix jacobian = DoubleMatrix.ofUnsafe(new double[][]
            {
                new double[] { 0.985d, 0.01d, 0d },
                new double[] { 0.01d, 0.98d, 0.01d },
                new double[] { 0.005d, 0.01d, 0.99d }
            });
            JacobianCalibrationMatrix jcm = JacobianCalibrationMatrix.of(ImmutableList.of(CurveParameterSize.of(curveName, 3)), jacobian);
            DoubleArray       time        = DoubleArray.of(0.1, 0.25, 0.5d);
            DoubleArray       rate        = DoubleArray.of(0.01, 0.015, 0.008d);
            Curve             curve       = InterpolatedNodalCurve.of(Curves.zeroRates(curveName, ACT_360).withInfo(CurveInfoType.JACOBIAN, jcm), time, rate, NATURAL_SPLINE);
            TestMarketDataMap md          = new TestMarketDataMap(VAL_DATE, ImmutableMap.of(FORWARD_CURVE_ID, curve, QUOTE_KEY, MARKET_PRICE), ImmutableMap.of());

            return(md);
        }
        //-------------------------------------------------------------------------
        // calculates the Jacobian and builds the result, called once per group
        // this uses, but does not alter, data from previous groups
        private ImmutableMap <CurveName, JacobianCalibrationMatrix> updateJacobiansForGroup(ImmutableRatesProvider provider, ImmutableList <ResolvedTrade> trades, ImmutableList <CurveParameterSize> orderGroup, ImmutableList <CurveParameterSize> orderPrev, ImmutableList <CurveParameterSize> orderAll, ImmutableMap <CurveName, JacobianCalibrationMatrix> jacobians)
        {
            // sensitivity to all parameters in the stated order
            int          totalParamsAll = orderAll.Select(e => e.ParameterCount).Sum();
            DoubleMatrix res            = derivatives(trades, provider, orderAll, totalParamsAll);

            // jacobian direct
            int          nbTrades            = trades.size();
            int          totalParamsGroup    = orderGroup.Select(e => e.ParameterCount).Sum();
            int          totalParamsPrevious = totalParamsAll - totalParamsGroup;
            DoubleMatrix pDmCurrentMatrix    = jacobianDirect(res, nbTrades, totalParamsGroup, totalParamsPrevious);

            // jacobian indirect: when totalParamsPrevious > 0
            DoubleMatrix pDmPrevious = jacobianIndirect(res, pDmCurrentMatrix, nbTrades, totalParamsGroup, totalParamsPrevious, orderPrev, jacobians);

            // add to the map of jacobians, one entry for each curve in this group
            ImmutableMap.Builder <CurveName, JacobianCalibrationMatrix> jacobianBuilder = ImmutableMap.builder();
            jacobianBuilder.putAll(jacobians);
            int startIndex = 0;

            foreach (CurveParameterSize order in orderGroup)
            {
                int paramCount = order.ParameterCount;
//JAVA TO C# CONVERTER NOTE: The following call to the 'RectangularArrays' helper class reproduces the rectangular array initialization that is automatic in Java:
//ORIGINAL LINE: double[][] pDmCurveArray = new double[paramCount][totalParamsAll];
                double[][] pDmCurveArray = RectangularArrays.ReturnRectangularDoubleArray(paramCount, totalParamsAll);
                // copy data for previous groups
                if (totalParamsPrevious > 0)
                {
                    for (int p = 0; p < paramCount; p++)
                    {
                        Array.Copy(pDmPrevious.rowArray(startIndex + p), 0, pDmCurveArray[p], 0, totalParamsPrevious);
                    }
                }
                // copy data for this group
                for (int p = 0; p < paramCount; p++)
                {
                    Array.Copy(pDmCurrentMatrix.rowArray(startIndex + p), 0, pDmCurveArray[p], totalParamsPrevious, totalParamsGroup);
                }
                // build final Jacobian matrix
                DoubleMatrix pDmCurveMatrix = DoubleMatrix.ofUnsafe(pDmCurveArray);
                jacobianBuilder.put(order.Name, JacobianCalibrationMatrix.of(orderAll, pDmCurveMatrix));
                startIndex += paramCount;
            }
            return(jacobianBuilder.build());
        }
        // build the map of additional info
        private CurveMetadata childMetadata(CurveMetadata metadata, CurveDefinition curveDefn, IDictionary <CurveName, JacobianCalibrationMatrix> jacobians, IDictionary <CurveName, DoubleArray> sensitivitiesMarketQuote)
        {
            JacobianCalibrationMatrix jacobian       = jacobians[curveDefn.Name];
            CurveMetadata             metadataResult = metadata;

            if (jacobian != null)
            {
                metadataResult = metadata.withInfo(CurveInfoType.JACOBIAN, jacobian);
            }
            DoubleArray sensitivity = sensitivitiesMarketQuote[curveDefn.Name];

            if (sensitivity != null)
            {
                metadataResult = metadataResult.withInfo(CurveInfoType.PV_SENSITIVITY_TO_MARKET_QUOTE, sensitivity);
            }
            return(metadataResult);
        }
        //-------------------------------------------------------------------------
        internal static ScenarioMarketData marketData()
        {
            CurveParameterSize        issuerSize   = CurveParameterSize.of(ISSUER_CURVE_ID.CurveName, 3);
            CurveParameterSize        repoSize     = CurveParameterSize.of(REPO_CURVE_ID.CurveName, 2);
            JacobianCalibrationMatrix issuerMatrix = JacobianCalibrationMatrix.of(ImmutableList.of(issuerSize, repoSize), DoubleMatrix.copyOf(new double[][]
            {
                new double[] { 0.95, 0.03, 0.01, 0.006, 0.004 },
                new double[] { 0.03, 0.95, 0.01, 0.005, 0.005 },
                new double[] { 0.03, 0.01, 0.95, 0.002, 0.008 }
            }));
            JacobianCalibrationMatrix repoMatrix = JacobianCalibrationMatrix.of(ImmutableList.of(issuerSize, repoSize), DoubleMatrix.copyOf(new double[][]
            {
                new double[] { 0.003, 0.003, 0.004, 0.97, 0.02 },
                new double[] { 0.003, 0.006, 0.001, 0.05, 0.94 }
            }));
            CurveMetadata issuerMetadata = Curves.zeroRates(ISSUER_CURVE_ID.CurveName, ACT_360).withInfo(CurveInfoType.JACOBIAN, issuerMatrix);
            CurveMetadata repoMetadata   = Curves.zeroRates(REPO_CURVE_ID.CurveName, ACT_360).withInfo(CurveInfoType.JACOBIAN, repoMatrix);
            Curve         issuerCurve    = InterpolatedNodalCurve.of(issuerMetadata, DoubleArray.of(1.0, 5.0, 10.0), DoubleArray.of(0.02, 0.04, 0.01), CurveInterpolators.LINEAR);
            Curve         repoCurve      = InterpolatedNodalCurve.of(repoMetadata, DoubleArray.of(0.5, 3.0), DoubleArray.of(0.005, 0.008), CurveInterpolators.LINEAR);

            return(new TestMarketDataMap(VALUATION_DATE, ImmutableMap.of(REPO_CURVE_ID, repoCurve, ISSUER_CURVE_ID, issuerCurve), ImmutableMap.of()));
        }
        //-------------------------------------------------------------------------
        // shift the curve
        private NodalCurve withShift(InterpolatedNodalCurve curve, IList <ParameterMetadata> parameterMetadata, DoubleMatrix sensitivity, bool computeJacobian, double shift)
        {
            int nNode = curve.ParameterCount;

            if (shift < curve.XValues.get(0))
            {
                //offset less than t value of 1st knot, so no knots are not removed
                double        eta      = curve.YValues.get(0) * shift;
                DoubleArray   time     = DoubleArray.of(nNode, i => curve.XValues.get(i) - shift);
                DoubleArray   rate     = DoubleArray.of(nNode, i => (curve.YValues.get(i) * curve.XValues.get(i) - eta) / time.get(i));
                CurveMetadata metadata = curve.Metadata.withParameterMetadata(parameterMetadata);
                if (computeJacobian)
                {
//JAVA TO C# CONVERTER NOTE: The following call to the 'RectangularArrays' helper class reproduces the rectangular array initialization that is automatic in Java:
//ORIGINAL LINE: double[][] transf = new double[nNode][nNode];
                    double[][] transf = RectangularArrays.ReturnRectangularDoubleArray(nNode, nNode);
                    for (int i = 0; i < nNode; ++i)
                    {
                        transf[i][0]  = -shift / time.get(i);
                        transf[i][i] += curve.XValues.get(i) / time.get(i);
                    }
                    DoubleMatrix jacobianMatrix        = (DoubleMatrix)MATRIX_ALGEBRA.multiply(DoubleMatrix.ofUnsafe(transf), MATRIX_ALGEBRA.getInverse(sensitivity));
                    JacobianCalibrationMatrix jacobian = JacobianCalibrationMatrix.of(ImmutableList.of(CurveParameterSize.of(curve.Name, nNode)), jacobianMatrix);
                    return(curve.withValues(time, rate).withMetadata(metadata.withInfo(CurveInfoType.JACOBIAN, jacobian)));
                }
                return(curve.withValues(time, rate).withMetadata(metadata));
            }
            if (shift >= curve.XValues.get(nNode - 1))
            {
                //new base after last knot. The new 'curve' has a constant zero rate which we represent with a nominal knot at 1.0
                double time     = 1d;
                double interval = curve.XValues.get(nNode - 1) - curve.XValues.get(nNode - 2);
                double rate     = (curve.YValues.get(nNode - 1) * curve.XValues.get(nNode - 1) - curve.YValues.get(nNode - 2) * curve.XValues.get(nNode - 2)) / interval;
                if (computeJacobian)
                {
//JAVA TO C# CONVERTER NOTE: The following call to the 'RectangularArrays' helper class reproduces the rectangular array initialization that is automatic in Java:
//ORIGINAL LINE: double[][] transf = new double[1][nNode];
                    double[][] transf = RectangularArrays.ReturnRectangularDoubleArray(1, nNode);
                    transf[0][nNode - 2] = -curve.XValues.get(nNode - 2) / interval;
                    transf[0][nNode - 1] = curve.XValues.get(nNode - 1) / interval;
                    DoubleMatrix jacobianMatrix        = (DoubleMatrix)MATRIX_ALGEBRA.multiply(DoubleMatrix.ofUnsafe(transf), MATRIX_ALGEBRA.getInverse(sensitivity));
                    JacobianCalibrationMatrix jacobian = JacobianCalibrationMatrix.of(ImmutableList.of(CurveParameterSize.of(curve.Name, nNode)), jacobianMatrix);
                    return(ConstantNodalCurve.of(curve.Metadata.withInfo(CurveInfoType.JACOBIAN, jacobian), time, rate));
                }
                return(ConstantNodalCurve.of(curve.Metadata, time, rate));
            }
            //offset greater than (or equal to) t value of 1st knot, so at least one knot must be removed
            int index = Arrays.binarySearch(curve.XValues.toArray(), shift);

            if (index < 0)
            {
                index = -(index + 1);
            }
            else
            {
                index++;
            }
            double        interval = curve.XValues.get(index) - curve.XValues.get(index - 1);
            double        tt1      = curve.XValues.get(index - 1) * (curve.XValues.get(index) - shift);
            double        tt2      = curve.XValues.get(index) * (shift - curve.XValues.get(index - 1));
            double        eta      = (curve.YValues.get(index - 1) * tt1 + curve.YValues.get(index) * tt2) / interval;
            int           m        = nNode - index;
            CurveMetadata metadata = curve.Metadata.withParameterMetadata(parameterMetadata.subList(index, nNode));
//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
//ORIGINAL LINE: final int indexFinal = index;
            int         indexFinal = index;
            DoubleArray time       = DoubleArray.of(m, i => curve.XValues.get(i + indexFinal) - shift);
            DoubleArray rate       = DoubleArray.of(m, i => (curve.YValues.get(i + indexFinal) * curve.XValues.get(i + indexFinal) - eta) / time.get(i));

            if (computeJacobian)
            {
//JAVA TO C# CONVERTER NOTE: The following call to the 'RectangularArrays' helper class reproduces the rectangular array initialization that is automatic in Java:
//ORIGINAL LINE: double[][] transf = new double[m][nNode];
                double[][] transf = RectangularArrays.ReturnRectangularDoubleArray(m, nNode);
                for (int i = 0; i < m; ++i)
                {
                    transf[i][index - 1] -= tt1 / (time.get(i) * interval);
                    transf[i][index]     -= tt2 / (time.get(i) * interval);
                    transf[i][i + index] += curve.XValues.get(i + index) / time.get(i);
                }
                DoubleMatrix jacobianMatrix        = (DoubleMatrix)MATRIX_ALGEBRA.multiply(DoubleMatrix.ofUnsafe(transf), MATRIX_ALGEBRA.getInverse(sensitivity));
                JacobianCalibrationMatrix jacobian = JacobianCalibrationMatrix.of(ImmutableList.of(CurveParameterSize.of(curve.Name, nNode)), jacobianMatrix);
                return(curve.withValues(time, rate).withMetadata(metadata.withInfo(CurveInfoType.JACOBIAN, jacobian)));
            }
            return(curve.withValues(time, rate).withMetadata(metadata));
        }
        //-------------------------------------------------------------------------
        /// <summary>
        /// Calibrates the ISDA compliant discount curve to the market data.
        /// <para>
        /// This creates the single discount curve for a specified currency.
        /// The curve nodes in {@code IsdaCreditCurveDefinition} should be term deposit or fixed-for-Ibor swap,
        /// and the number of nodes should be greater than 1.
        ///
        /// </para>
        /// </summary>
        /// <param name="curveDefinition">  the curve definition </param>
        /// <param name="marketData">  the market data </param>
        /// <param name="refData">  the reference data </param>
        /// <returns> the ISDA compliant discount curve </returns>
        public IsdaCreditDiscountFactors calibrate(IsdaCreditCurveDefinition curveDefinition, MarketData marketData, ReferenceData refData)
        {
//JAVA TO C# CONVERTER WARNING: Java wildcard generics have no direct equivalent in .NET:
//ORIGINAL LINE: java.util.List<? extends com.opengamma.strata.market.curve.IsdaCreditCurveNode> curveNodes = curveDefinition.getCurveNodes();
            IList <IsdaCreditCurveNode> curveNodes = curveDefinition.CurveNodes;
            int nNodes = curveNodes.Count;

            ArgChecker.isTrue(nNodes > 1, "the number of curve nodes must be greater than 1");
            LocalDate curveSnapDate      = marketData.ValuationDate;
            LocalDate curveValuationDate = curveDefinition.CurveValuationDate;
            DayCount  curveDayCount      = curveDefinition.DayCount;

            BasicFixedLeg[] swapLeg = new BasicFixedLeg[nNodes];
            double[]        termDepositYearFraction = new double[nNodes];
            double[]        curveNodeTime           = new double[nNodes];
            double[]        rates = new double[nNodes];
            ImmutableList.Builder <ParameterMetadata> paramMetadata = ImmutableList.builder();
            int       nTermDeposit  = 0;
            LocalDate curveSpotDate = null;

            for (int i = 0; i < nNodes; i++)
            {
                LocalDate           cvDateTmp;
                IsdaCreditCurveNode node = curveNodes[i];
                rates[i] = marketData.getValue(node.ObservableId);
                LocalDate adjMatDate = node.date(curveSnapDate, refData);
                paramMetadata.add(node.metadata(adjMatDate));
                if (node is DepositIsdaCreditCurveNode)
                {
                    DepositIsdaCreditCurveNode termDeposit = (DepositIsdaCreditCurveNode)node;
                    cvDateTmp                  = termDeposit.SpotDateOffset.adjust(curveSnapDate, refData);
                    curveNodeTime[i]           = curveDayCount.relativeYearFraction(cvDateTmp, adjMatDate);
                    termDepositYearFraction[i] = termDeposit.DayCount.relativeYearFraction(cvDateTmp, adjMatDate);
                    ArgChecker.isTrue(nTermDeposit == i, "TermDepositCurveNode should not be after FixedIborSwapCurveNode");
                    ++nTermDeposit;
                }
                else if (node is SwapIsdaCreditCurveNode)
                {
                    SwapIsdaCreditCurveNode swap = (SwapIsdaCreditCurveNode)node;
                    cvDateTmp        = swap.SpotDateOffset.adjust(curveSnapDate, refData);
                    curveNodeTime[i] = curveDayCount.relativeYearFraction(cvDateTmp, adjMatDate);
                    BusinessDayAdjustment busAdj = swap.BusinessDayAdjustment;
                    swapLeg[i] = new BasicFixedLeg(this, cvDateTmp, cvDateTmp.plus(swap.Tenor), swap.PaymentFrequency.Period, swap.DayCount, curveDayCount, busAdj, refData);
                }
                else
                {
                    throw new System.ArgumentException("unsupported cuve node type");
                }
                if (i > 0)
                {
                    ArgChecker.isTrue(curveNodeTime[i] - curveNodeTime[i - 1] > 0, "curve nodes should be ascending in terms of tenor");
                    ArgChecker.isTrue(cvDateTmp.Equals(curveSpotDate), "spot lag should be common for all of the curve nodes");
                }
                else
                {
                    ArgChecker.isTrue(curveNodeTime[i] >= 0d, "the first node should be after curve spot date");
                    curveSpotDate = cvDateTmp;
                }
            }
            ImmutableList <ParameterMetadata> parameterMetadata = paramMetadata.build();

            double[] ratesMod = Arrays.copyOf(rates, nNodes);
            for (int i = 0; i < nTermDeposit; ++i)
            {
                double dfInv = 1d + ratesMod[i] * termDepositYearFraction[i];
                ratesMod[i] = Math.Log(dfInv) / curveNodeTime[i];
            }
            InterpolatedNodalCurve curve = curveDefinition.curve(DoubleArray.ofUnsafe(curveNodeTime), DoubleArray.ofUnsafe(ratesMod));

            for (int i = nTermDeposit; i < nNodes; ++i)
            {
                curve = fitSwap(i, swapLeg[i], curve, rates[i]);
            }

            Currency     currency = curveDefinition.Currency;
            DoubleMatrix sensi    = quoteValueSensitivity(nTermDeposit, termDepositYearFraction, swapLeg, ratesMod, curve, curveDefinition.ComputeJacobian);

            if (curveValuationDate.isEqual(curveSpotDate))
            {
                if (curveDefinition.ComputeJacobian)
                {
                    JacobianCalibrationMatrix jacobian = JacobianCalibrationMatrix.of(ImmutableList.of(CurveParameterSize.of(curveDefinition.Name, nNodes)), MATRIX_ALGEBRA.getInverse(sensi));
                    NodalCurve curveWithParamMetadata  = curve.withMetadata(curve.Metadata.withInfo(CurveInfoType.JACOBIAN, jacobian).withParameterMetadata(parameterMetadata));
                    return(IsdaCreditDiscountFactors.of(currency, curveValuationDate, curveWithParamMetadata));
                }
                NodalCurve curveWithParamMetadata = curve.withMetadata(curve.Metadata.withParameterMetadata(parameterMetadata));
                return(IsdaCreditDiscountFactors.of(currency, curveValuationDate, curveWithParamMetadata));
            }
            double offset = curveDayCount.relativeYearFraction(curveSpotDate, curveValuationDate);

            return(IsdaCreditDiscountFactors.of(currency, curveValuationDate, withShift(curve, parameterMetadata, sensi, curveDefinition.ComputeJacobian, offset)));
        }
        /// <summary>
        /// Calibrate a single curve to 4 points. Use the resulting calibrated curves as starting point of the computation
        /// of a Jacobian. Compare the direct Jacobian and the one reconstructed from trades.
        /// </summary>
        public virtual void direct_two_curves()
        {
            JacobianCalibrationMatrix          jiObject = MULTICURVE_EUR_2_CALIBRATED.findData(EUR_DSCON_OIS).get().Metadata.findInfo(CurveInfoType.JACOBIAN).get();
            ImmutableList <CurveParameterSize> order    = jiObject.Order; // To obtain the order of the curves in the jacobian

            /* Create trades */
            IList <ResolvedTrade> tradesDsc = new List <ResolvedTrade>();

            for (int looptenor = 0; looptenor < TENORS_STD_2_OIS.Length; looptenor++)
            {
                ResolvedSwapTrade t0   = EUR_FIXED_1Y_EONIA_OIS.createTrade(VALUATION_DATE, TENORS_STD_2_OIS[looptenor], BuySell.BUY, 1.0, 0.0, REF_DATA).resolve(REF_DATA);
                double            rate = MARKET_QUOTE.value(t0, MULTICURVE_EUR_2_CALIBRATED);
                ResolvedSwapTrade t    = EUR_FIXED_1Y_EONIA_OIS.createTrade(VALUATION_DATE, TENORS_STD_2_OIS[looptenor], BuySell.BUY, 1.0, rate, REF_DATA).resolve(REF_DATA);
                tradesDsc.Add(t);
            }
            IList <ResolvedTrade> tradesE3 = new List <ResolvedTrade>();
            // Fixing
            IborFixingDepositConvention    c    = IborFixingDepositConvention.of(EUR_EURIBOR_6M);
            ResolvedIborFixingDepositTrade fix0 = c.createTrade(VALUATION_DATE, EUR_EURIBOR_6M.Tenor.Period, BuySell.BUY, 1.0, 0.0, REF_DATA).resolve(REF_DATA);
            double rateFixing = MARKET_QUOTE.value(fix0, MULTICURVE_EUR_2_CALIBRATED);
            ResolvedIborFixingDepositTrade fix = c.createTrade(VALUATION_DATE, EUR_EURIBOR_6M.Tenor.Period, BuySell.BUY, 1.0, rateFixing, REF_DATA).resolve(REF_DATA);

            tradesE3.Add(fix);
            // IRS
            for (int looptenor = 0; looptenor < TENORS_STD_2_IRS.Length; looptenor++)
            {
                ResolvedSwapTrade t0   = EUR_FIXED_1Y_EURIBOR_6M.createTrade(VALUATION_DATE, TENORS_STD_2_IRS[looptenor], BuySell.BUY, 1.0, 0.0, REF_DATA).resolve(REF_DATA);
                double            rate = MARKET_QUOTE.value(t0, MULTICURVE_EUR_2_CALIBRATED);
                ResolvedSwapTrade t    = EUR_FIXED_1Y_EURIBOR_6M.createTrade(VALUATION_DATE, TENORS_STD_2_IRS[looptenor], BuySell.BUY, 1.0, rate, REF_DATA).resolve(REF_DATA);
                tradesE3.Add(t);
            }
            IList <ResolvedTrade> trades = new List <ResolvedTrade>();

            if (order.get(0).Name.Equals(EUR_DSCON_OIS))
            {
                ((IList <ResolvedTrade>)trades).AddRange(tradesDsc);
                ((IList <ResolvedTrade>)trades).AddRange(tradesE3);
            }
            else
            {
                ((IList <ResolvedTrade>)trades).AddRange(tradesE3);
                ((IList <ResolvedTrade>)trades).AddRange(tradesDsc);
            }
            /* Par rate sensitivity */
            System.Func <ResolvedTrade, CurrencyParameterSensitivities> sensitivityFunction = (t) => MULTICURVE_EUR_2_CALIBRATED.parameterSensitivity((t is ResolvedSwapTrade) ? PRICER_SWAP_PRODUCT.parRateSensitivity(((ResolvedSwapTrade)t).Product, MULTICURVE_EUR_2_CALIBRATED).build() : PRICER_IBORFIX_PRODUCT.parRateSensitivity(((ResolvedIborFixingDepositTrade)t).Product, MULTICURVE_EUR_2_CALIBRATED));
            DoubleMatrix jiComputed    = CurveSensitivityUtils.jacobianFromMarketQuoteSensitivities(order, trades, sensitivityFunction);
            DoubleMatrix jiExpectedDsc = MULTICURVE_EUR_2_CALIBRATED.findData(EUR_DSCON_OIS).get().Metadata.getInfo(CurveInfoType.JACOBIAN).JacobianMatrix;
            DoubleMatrix jiExpectedE3  = MULTICURVE_EUR_2_CALIBRATED.findData(EUR_EURIBOR6M_IRS).get().Metadata.getInfo(CurveInfoType.JACOBIAN).JacobianMatrix;

            /* Comparison */
            assertEquals(jiComputed.rowCount(), jiExpectedDsc.rowCount() + jiExpectedE3.rowCount());
            assertEquals(jiComputed.columnCount(), jiExpectedDsc.columnCount());
            assertEquals(jiComputed.columnCount(), jiExpectedE3.columnCount());
            int shiftDsc = order.get(0).Name.Equals(EUR_DSCON_OIS) ? 0 : jiExpectedE3.rowCount();

            for (int i = 0; i < jiExpectedDsc.rowCount(); i++)
            {
                for (int j = 0; j < jiExpectedDsc.columnCount(); j++)
                {
                    assertEquals(jiComputed.get(i + shiftDsc, j), jiExpectedDsc.get(i, j), TOLERANCE_JAC);
                }
            }
            int shiftE3 = order.get(0).Name.Equals(EUR_DSCON_OIS) ? jiExpectedDsc.rowCount() : 0;

            for (int i = 0; i < jiExpectedE3.rowCount(); i++)
            {
                for (int j = 0; j < jiExpectedDsc.columnCount(); j++)
                {
                    assertEquals(jiComputed.get(i + shiftE3, j), jiExpectedE3.get(i, j), TOLERANCE_JAC);
                }
            }
        }
        internal virtual LegalEntitySurvivalProbabilities calibrate(IList <CdsIsdaCreditCurveNode> curveNodes, CurveName name, MarketData marketData, ImmutableCreditRatesProvider ratesProvider, DayCount definitionDayCount, Currency definitionCurrency, bool computeJacobian, bool storeTrade, ReferenceData refData)
        {
//JAVA TO C# CONVERTER TODO TASK: Method reference arbitrary object instance method syntax is not converted by Java to C# Converter:
//JAVA TO C# CONVERTER TODO TASK: Most Java stream collectors are not converted by Java to C# Converter:
            IEnumerator <StandardId> legalEntities = curveNodes.Select(CdsIsdaCreditCurveNode::getLegalEntityId).collect(Collectors.toSet()).GetEnumerator();
//JAVA TO C# CONVERTER TODO TASK: Java iterators are only converted within the context of 'while' and 'for' loops:
            StandardId legalEntityId = legalEntities.next();

//JAVA TO C# CONVERTER TODO TASK: Java iterators are only converted within the context of 'while' and 'for' loops:
            ArgChecker.isFalse(legalEntities.hasNext(), "legal entity must be common to curve nodes");
//JAVA TO C# CONVERTER TODO TASK: Most Java stream collectors are not converted by Java to C# Converter:
            IEnumerator <Currency> currencies = curveNodes.Select(n => n.Template.Convention.Currency).collect(Collectors.toSet()).GetEnumerator();
//JAVA TO C# CONVERTER TODO TASK: Java iterators are only converted within the context of 'while' and 'for' loops:
            Currency currency = currencies.next();

//JAVA TO C# CONVERTER TODO TASK: Java iterators are only converted within the context of 'while' and 'for' loops:
            ArgChecker.isFalse(currencies.hasNext(), "currency must be common to curve nodes");
            ArgChecker.isTrue(definitionCurrency.Equals(currency), "curve definition currency must be the same as the currency of CDS");
//JAVA TO C# CONVERTER TODO TASK: Most Java stream collectors are not converted by Java to C# Converter:
            IEnumerator <CdsQuoteConvention> quoteConventions = curveNodes.Select(n => n.QuoteConvention).collect(Collectors.toSet()).GetEnumerator();
//JAVA TO C# CONVERTER TODO TASK: Java iterators are only converted within the context of 'while' and 'for' loops:
            CdsQuoteConvention quoteConvention = quoteConventions.next();

//JAVA TO C# CONVERTER TODO TASK: Java iterators are only converted within the context of 'while' and 'for' loops:
            ArgChecker.isFalse(quoteConventions.hasNext(), "quote convention must be common to curve nodes");
            LocalDate valuationDate = marketData.ValuationDate;

            ArgChecker.isTrue(valuationDate.Equals(marketData.ValuationDate), "ratesProvider and marketDate must be based on the same valuation date");
            CreditDiscountFactors discountFactors = ratesProvider.discountFactors(currency);

            ArgChecker.isTrue(definitionDayCount.Equals(discountFactors.DayCount), "credit curve and discount curve must be based on the same day count convention");
            RecoveryRates recoveryRates = ratesProvider.recoveryRates(legalEntityId);

            int nNodes = curveNodes.Count;

            double[] coupons = new double[nNodes];
            double[] pufs    = new double[nNodes];
//JAVA TO C# CONVERTER NOTE: The following call to the 'RectangularArrays' helper class reproduces the rectangular array initialization that is automatic in Java:
//ORIGINAL LINE: double[][] diag = new double[nNodes][nNodes];
            double[][] diag = RectangularArrays.ReturnRectangularDoubleArray(nNodes, nNodes);
            ImmutableList.Builder <ResolvedCdsTrade> tradesBuilder = ImmutableList.builder();
            for (int i = 0; i < nNodes; i++)
            {
                CdsCalibrationTrade tradeCalibration = curveNodes[i].trade(1d, marketData, refData);
                ResolvedCdsTrade    trade            = tradeCalibration.UnderlyingTrade.resolve(refData);
                tradesBuilder.add(trade);
                double[] temp = getStandardQuoteForm(trade, tradeCalibration.Quote, valuationDate, discountFactors, recoveryRates, computeJacobian, refData);
                coupons[i] = temp[0];
                pufs[i]    = temp[1];
                diag[i][i] = temp[2];
            }
            ImmutableList <ResolvedCdsTrade> trades = tradesBuilder.build();
            NodalCurve nodalCurve = calibrate(trades, DoubleArray.ofUnsafe(coupons), DoubleArray.ofUnsafe(pufs), name, valuationDate, discountFactors, recoveryRates, refData);

            if (computeJacobian)
            {
                LegalEntitySurvivalProbabilities            creditCurve      = LegalEntitySurvivalProbabilities.of(legalEntityId, IsdaCreditDiscountFactors.of(currency, valuationDate, nodalCurve));
                ImmutableCreditRatesProvider                ratesProviderNew = ratesProvider.toBuilder().creditCurves(ImmutableMap.of(Pair.of(legalEntityId, currency), creditCurve)).build();
                System.Func <ResolvedCdsTrade, DoubleArray> sensiFunc        = quoteConvention.Equals(CdsQuoteConvention.PAR_SPREAD) ? getParSpreadSensitivityFunction(ratesProviderNew, name, currency, refData) : getPointsUpfrontSensitivityFunction(ratesProviderNew, name, currency, refData);
                DoubleMatrix sensi = DoubleMatrix.ofArrayObjects(nNodes, nNodes, i => sensiFunc(trades.get(i)));
                sensi = (DoubleMatrix)MATRIX_ALGEBRA.multiply(DoubleMatrix.ofUnsafe(diag), sensi);
                JacobianCalibrationMatrix jacobian = JacobianCalibrationMatrix.of(ImmutableList.of(CurveParameterSize.of(name, nNodes)), MATRIX_ALGEBRA.getInverse(sensi));
                nodalCurve = nodalCurve.withMetadata(nodalCurve.Metadata.withInfo(CurveInfoType.JACOBIAN, jacobian));
            }

            ImmutableList <ParameterMetadata> parameterMetadata;

            if (storeTrade)
            {
                parameterMetadata = IntStream.range(0, nNodes).mapToObj(n => ResolvedTradeParameterMetadata.of(trades.get(n), curveNodes[n].Label)).collect(Guavate.toImmutableList());
            }
            else
            {
                parameterMetadata = IntStream.range(0, nNodes).mapToObj(n => curveNodes[n].metadata(trades.get(n).Product.ProtectionEndDate)).collect(Guavate.toImmutableList());
            }
            nodalCurve = nodalCurve.withMetadata(nodalCurve.Metadata.withParameterMetadata(parameterMetadata));

            return(LegalEntitySurvivalProbabilities.of(legalEntityId, IsdaCreditDiscountFactors.of(currency, valuationDate, nodalCurve)));
        }