//-------------------------------------------------------------------------
        /// <summary>
        /// Computes the present value curve sensitivity by simple forward rate estimation.
        /// </summary>
        /// <param name="cmsPeriod">  the CMS </param>
        /// <param name="provider">  the rates provider </param>
        /// <returns> the present value sensitivity </returns>
        public virtual PointSensitivityBuilder presentValueSensitivity(CmsPeriod cmsPeriod, RatesProvider provider)
        {
            Currency  ccy           = cmsPeriod.Currency;
            LocalDate valuationDate = provider.ValuationDate;

            if (valuationDate.isAfter(cmsPeriod.PaymentDate))
            {
                return(PointSensitivityBuilder.none());
            }
            LocalDate fixingDate = cmsPeriod.FixingDate;
            double    dfPayment  = provider.discountFactor(ccy, cmsPeriod.PaymentDate);

            if (!fixingDate.isAfter(valuationDate))
            {     // Using fixing
                double?fixedRate = provider.timeSeries(cmsPeriod.Index).get(fixingDate);
                if (fixedRate.HasValue)
                {
                    double payoff = 0d;
                    switch (cmsPeriod.CmsPeriodType)
                    {
                    case CAPLET:
                        payoff = Math.Max(fixedRate.Value - cmsPeriod.Strike, 0d);
                        break;

                    case FLOORLET:
                        payoff = Math.Max(cmsPeriod.Strike - fixedRate.Value, 0d);
                        break;

                    case COUPON:
                        payoff = fixedRate.Value;
                        break;

                    default:
                        throw new System.ArgumentException("unsupported CMS type");
                    }
                    return(provider.discountFactors(ccy).zeroRatePointSensitivity(cmsPeriod.PaymentDate).multipliedBy(payoff * cmsPeriod.Notional * cmsPeriod.YearFraction));
                }
                else if (fixingDate.isBefore(valuationDate))
                {
                    throw new System.ArgumentException(Messages.format("Unable to get fixing for {} on date {}, no time-series supplied", cmsPeriod.Index, fixingDate));
                }
            }
            if (!cmsPeriod.CmsPeriodType.Equals(CmsPeriodType.COUPON))
            {
                throw new System.ArgumentException("Unable to price cap or floor in this pricer");
            }
            // Using forward
            ResolvedSwap        swap        = cmsPeriod.UnderlyingSwap;
            ZeroRateSensitivity dfPaymentdr = provider.discountFactors(ccy).zeroRatePointSensitivity(cmsPeriod.PaymentDate);
            double forward = swapPricer.parRate(swap, provider);
            PointSensitivityBuilder forwardSensi = swapPricer.parRateSensitivity(swap, provider);

            return(forwardSensi.multipliedBy(dfPayment).combinedWith(dfPaymentdr.multipliedBy(forward)).multipliedBy(cmsPeriod.Notional * cmsPeriod.YearFraction));
        }
        public virtual void test_presentValueSensitivityRatesStickyStrike_parity()
        {
            CurrencyParameterSensitivities pvSensiRecLong  = RATE_PROVIDER.parameterSensitivity(PRICER_SWAPTION.presentValueSensitivityRatesStickyStrike(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS).build());
            CurrencyParameterSensitivities pvSensiRecShort = RATE_PROVIDER.parameterSensitivity(PRICER_SWAPTION.presentValueSensitivityRatesStickyStrike(SWAPTION_REC_SHORT, RATE_PROVIDER, VOLS).build());
            CurrencyParameterSensitivities pvSensiPayLong  = RATE_PROVIDER.parameterSensitivity(PRICER_SWAPTION.presentValueSensitivityRatesStickyStrike(SWAPTION_PAY_LONG, RATE_PROVIDER, VOLS).build());
            CurrencyParameterSensitivities pvSensiPayShort = RATE_PROVIDER.parameterSensitivity(PRICER_SWAPTION.presentValueSensitivityRatesStickyStrike(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS).build());

            assertTrue(pvSensiRecLong.equalWithTolerance(pvSensiRecShort.multipliedBy(-1d), NOTIONAL * TOL));
            assertTrue(pvSensiPayLong.equalWithTolerance(pvSensiPayShort.multipliedBy(-1d), NOTIONAL * TOL));

            double forward = PRICER_SWAP.parRate(RSWAP_REC, RATE_PROVIDER);
            PointSensitivityBuilder forwardSensi = PRICER_SWAP.parRateSensitivity(RSWAP_REC, RATE_PROVIDER);
            double annuityCash      = PRICER_SWAP.LegPricer.annuityCash(RSWAP_REC.getLegs(SwapLegType.FIXED).get(0), forward);
            double annuityCashDeriv = PRICER_SWAP.LegPricer.annuityCashDerivative(RSWAP_REC.getLegs(SwapLegType.FIXED).get(0), forward).getDerivative(0);
            double discount         = RATE_PROVIDER.discountFactor(USD, SETTLE_DATE);
            PointSensitivityBuilder        discountSensi = RATE_PROVIDER.discountFactors(USD).zeroRatePointSensitivity(SETTLE_DATE);
            PointSensitivities             expecedPoint  = discountSensi.multipliedBy(annuityCash * (forward - STRIKE)).combinedWith(forwardSensi.multipliedBy(discount * annuityCash + discount * annuityCashDeriv * (forward - STRIKE))).build();
            CurrencyParameterSensitivities expected      = RATE_PROVIDER.parameterSensitivity(expecedPoint);

            assertTrue(expected.equalWithTolerance(pvSensiPayLong.combinedWith(pvSensiRecLong.multipliedBy(-1d)), NOTIONAL * TOL));
            assertTrue(expected.equalWithTolerance(pvSensiRecShort.combinedWith(pvSensiPayShort.multipliedBy(-1d)), NOTIONAL * TOL));
        }
        /// <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_one_curve()
        {
            /* Create trades */
            IList <ResolvedTrade> trades    = new List <ResolvedTrade>();
            IList <LocalDate>     nodeDates = new List <LocalDate>();

            for (int looptenor = 0; looptenor < TENORS_STD_1.Length; looptenor++)
            {
                ResolvedSwapTrade t0   = EUR_FIXED_1Y_EURIBOR_6M.createTrade(VALUATION_DATE, TENORS_STD_1[looptenor], BuySell.BUY, 1.0, 0.0, REF_DATA).resolve(REF_DATA);
                double            rate = MARKET_QUOTE.value(t0, MULTICURVE_EUR_SINGLE_CALIBRATED);
                ResolvedSwapTrade t    = EUR_FIXED_1Y_EURIBOR_6M.createTrade(VALUATION_DATE, TENORS_STD_1[looptenor], BuySell.BUY, 1.0, rate, REF_DATA).resolve(REF_DATA);
                nodeDates.Add(t.Product.EndDate);
                trades.Add(t);
            }
            /* Par rate sensitivity */
            System.Func <ResolvedTrade, CurrencyParameterSensitivities> sensitivityFunction = (t) => MULTICURVE_EUR_SINGLE_CALIBRATED.parameterSensitivity(PRICER_SWAP_PRODUCT.parRateSensitivity(((ResolvedSwapTrade)t).Product, MULTICURVE_EUR_SINGLE_CALIBRATED).build());
            DoubleMatrix jiComputed = CurveSensitivityUtils.jacobianFromMarketQuoteSensitivities(LIST_CURVE_NAMES_1, trades, sensitivityFunction);
            DoubleMatrix jiExpected = MULTICURVE_EUR_SINGLE_CALIBRATED.findData(EUR_SINGLE_NAME).get().Metadata.findInfo(CurveInfoType.JACOBIAN).get().JacobianMatrix;

            /* Comparison */
            assertEquals(jiComputed.rowCount(), jiExpected.rowCount());
            assertEquals(jiComputed.columnCount(), jiExpected.columnCount());
            for (int i = 0; i < jiComputed.rowCount(); i++)
            {
                for (int j = 0; j < jiComputed.columnCount(); j++)
                {
                    assertEquals(jiComputed.get(i, j), jiExpected.get(i, j), TOLERANCE_JAC);
                }
            }
        }
        //-------------------------------------------------------------------------
        public virtual void calibration_transition_coherence_par_rate()
        {
            RatesProvider provider = CalibrationEurStandard.calibrateEurStandard(VAL_DATE, DSC_MARKET_QUOTES, DSC_OIS_TENORS, FWD3_FIXING_QUOTE, FWD3_FRA_QUOTES, FWD3_IRS_QUOTES, FWD3_FRA_TENORS, FWD3_IRS_TENORS, FWD6_FIXING_QUOTE, FWD6_FRA_QUOTES, FWD6_IRS_QUOTES, FWD6_FRA_TENORS, FWD6_IRS_TENORS);

            /* Curve Discounting/EUR-EONIA */
            string[] dscIdValues = CalibrationEurStandard.dscIdValues(DSC_OIS_TENORS);
            /* Curve EUR-EURIBOR-3M */
            double[] fwd3MarketQuotes = CalibrationEurStandard.fwdMarketQuotes(FWD3_FIXING_QUOTE, FWD3_FRA_QUOTES, FWD3_IRS_QUOTES);
            string[] fwd3IdValue      = CalibrationEurStandard.fwdIdValue(3, FWD3_FIXING_QUOTE, FWD3_FRA_QUOTES, FWD3_IRS_QUOTES, FWD3_FRA_TENORS, FWD3_IRS_TENORS);
            /* Curve EUR-EURIBOR-6M */
            double[] fwd6MarketQuotes = CalibrationEurStandard.fwdMarketQuotes(FWD6_FIXING_QUOTE, FWD6_FRA_QUOTES, FWD6_IRS_QUOTES);
            string[] fwd6IdValue      = CalibrationEurStandard.fwdIdValue(6, FWD6_FIXING_QUOTE, FWD6_FRA_QUOTES, FWD6_IRS_QUOTES, FWD6_FRA_TENORS, FWD6_IRS_TENORS);
            /* All quotes for the curve calibration */
            MarketData allQuotes = CalibrationEurStandard.allQuotes(VAL_DATE, DSC_MARKET_QUOTES, dscIdValues, fwd3MarketQuotes, fwd3IdValue, fwd6MarketQuotes, fwd6IdValue);
            /* All nodes by groups. */
            RatesCurveGroupDefinition config = CalibrationEurStandard.config(DSC_OIS_TENORS, dscIdValues, FWD3_FRA_TENORS, FWD3_IRS_TENORS, fwd3IdValue, FWD6_FRA_TENORS, FWD6_IRS_TENORS, fwd6IdValue);

            ImmutableList <CurveDefinition> definitions = config.CurveDefinitions;
            // Test PV Dsc
            ImmutableList <CurveNode> dscNodes  = definitions.get(0).Nodes;
            IList <ResolvedTrade>     dscTrades = new List <ResolvedTrade>();

            for (int i = 0; i < dscNodes.size(); i++)
            {
                dscTrades.Add(dscNodes.get(i).resolvedTrade(1d, allQuotes, REF_DATA));
            }
            // OIS
            for (int loopnode = 0; loopnode < DSC_MARKET_QUOTES.Length; loopnode++)
            {
                PointSensitivities             pts = SWAP_PRICER.parRateSensitivity(((ResolvedSwapTrade)dscTrades[loopnode]).Product, provider).build();
                CurrencyParameterSensitivities ps  = provider.parameterSensitivity(pts);
                CurrencyParameterSensitivities mqs = MQC.sensitivity(ps, provider);
                assertEquals(mqs.size(), 3);   // Calibration of all curves simultaneously
                CurrencyParameterSensitivity mqsDsc = mqs.getSensitivity(CalibrationEurStandard.DSCON_CURVE_NAME, EUR);
                assertTrue(mqsDsc.MarketDataName.Equals(CalibrationEurStandard.DSCON_CURVE_NAME));
                assertTrue(mqsDsc.Currency.Equals(EUR));
                DoubleArray mqsData = mqsDsc.Sensitivity;
                assertEquals(mqsData.size(), DSC_MARKET_QUOTES.Length);
                for (int i = 0; i < mqsData.size(); i++)
                {
                    assertEquals(mqsData.get(i), (i == loopnode) ? 1.0 : 0.0, TOLERANCE_DELTA);
                }
            }
            // Test PV Fwd3
            ImmutableList <CurveNode> fwd3Nodes  = definitions.get(1).Nodes;
            IList <ResolvedTrade>     fwd3Trades = new List <ResolvedTrade>();

            for (int i = 0; i < fwd3Nodes.size(); i++)
            {
                fwd3Trades.Add(fwd3Nodes.get(i).resolvedTrade(1d, allQuotes, REF_DATA));
            }
            for (int loopnode = 0; loopnode < fwd3MarketQuotes.Length; loopnode++)
            {
                PointSensitivities pts = null;
                if (fwd3Trades[loopnode] is ResolvedIborFixingDepositTrade)
                {
                    pts = PRICER_FIXING.parSpreadSensitivity(((ResolvedIborFixingDepositTrade)fwd3Trades[loopnode]).Product, provider);
                }
                if (fwd3Trades[loopnode] is ResolvedFraTrade)
                {
                    pts = PRICER_FRA.parSpreadSensitivity(((ResolvedFraTrade)fwd3Trades[loopnode]).Product, provider);
                }
                if (fwd3Trades[loopnode] is ResolvedSwapTrade)
                {
                    pts = SWAP_PRICER.parSpreadSensitivity(((ResolvedSwapTrade)fwd3Trades[loopnode]).Product, provider).build();
                }
                CurrencyParameterSensitivities ps  = provider.parameterSensitivity(pts);
                CurrencyParameterSensitivities mqs = MQC.sensitivity(ps, provider);
                assertEquals(mqs.size(), 3);   // Calibration of all curves simultaneously
                CurrencyParameterSensitivity mqsDsc  = mqs.getSensitivity(CalibrationEurStandard.DSCON_CURVE_NAME, EUR);
                CurrencyParameterSensitivity mqsFwd3 = mqs.getSensitivity(CalibrationEurStandard.FWD3_CURVE_NAME, EUR);
                DoubleArray mqsDscData = mqsDsc.Sensitivity;
                assertEquals(mqsDscData.size(), DSC_MARKET_QUOTES.Length);
                for (int i = 0; i < mqsDscData.size(); i++)
                {
                    assertEquals(mqsDscData.get(i), 0.0, TOLERANCE_DELTA);
                }
                DoubleArray mqsFwd3Data = mqsFwd3.Sensitivity;
                assertEquals(mqsFwd3Data.size(), fwd3MarketQuotes.Length);
                for (int i = 0; i < mqsFwd3Data.size(); i++)
                {
                    assertEquals(mqsFwd3Data.get(i), (i == loopnode) ? 1.0 : 0.0, TOLERANCE_DELTA);
                }
            }
            // Test PV Fwd6
            ImmutableList <CurveNode> fwd6Nodes  = definitions.get(2).Nodes;
            IList <ResolvedTrade>     fwd6Trades = new List <ResolvedTrade>();

            for (int i = 0; i < fwd6Nodes.size(); i++)
            {
                fwd6Trades.Add(fwd6Nodes.get(i).resolvedTrade(1d, allQuotes, REF_DATA));
            }
            for (int loopnode = 0; loopnode < fwd6MarketQuotes.Length; loopnode++)
            {
                PointSensitivities pts = null;
                if (fwd6Trades[loopnode] is ResolvedIborFixingDepositTrade)
                {
                    pts = PRICER_FIXING.parSpreadSensitivity(((ResolvedIborFixingDepositTrade)fwd6Trades[loopnode]).Product, provider);
                }
                if (fwd6Trades[loopnode] is ResolvedFraTrade)
                {
                    pts = PRICER_FRA.parSpreadSensitivity(((ResolvedFraTrade)fwd6Trades[loopnode]).Product, provider);
                }
                if (fwd6Trades[loopnode] is ResolvedSwapTrade)
                {
                    pts = SWAP_PRICER.parSpreadSensitivity(((ResolvedSwapTrade)fwd6Trades[loopnode]).Product, provider).build();
                }
                CurrencyParameterSensitivities ps  = provider.parameterSensitivity(pts);
                CurrencyParameterSensitivities mqs = MQC.sensitivity(ps, provider);
                assertEquals(mqs.size(), 3);
                CurrencyParameterSensitivity mqsDsc  = mqs.getSensitivity(CalibrationEurStandard.DSCON_CURVE_NAME, EUR);
                CurrencyParameterSensitivity mqsFwd3 = mqs.getSensitivity(CalibrationEurStandard.FWD3_CURVE_NAME, EUR);
                CurrencyParameterSensitivity mqsFwd6 = mqs.getSensitivity(CalibrationEurStandard.FWD6_CURVE_NAME, EUR);
                DoubleArray mqsDscData = mqsDsc.Sensitivity;
                assertEquals(mqsDscData.size(), DSC_MARKET_QUOTES.Length);
                for (int i = 0; i < mqsDscData.size(); i++)
                {
                    assertEquals(mqsDscData.get(i), 0.0, TOLERANCE_DELTA);
                }
                DoubleArray mqsFwd3Data = mqsFwd3.Sensitivity;
                assertEquals(mqsFwd3Data.size(), fwd3MarketQuotes.Length);
                for (int i = 0; i < mqsFwd3Data.size(); i++)
                {
                    assertEquals(mqsFwd3Data.get(i), 0.0, TOLERANCE_DELTA);
                }
                DoubleArray mqsFwd6Data = mqsFwd6.Sensitivity;
                assertEquals(mqsFwd6Data.size(), fwd6MarketQuotes.Length);
                for (int i = 0; i < mqsFwd6Data.size(); i++)
                {
                    assertEquals(mqsFwd6Data.get(i), (i == loopnode) ? 1.0 : 0.0, TOLERANCE_DELTA);
                }
            }
        }