//-------------------------------------------------------------------------
        /// <summary>
        /// Calculates the present value sensitivity of the swaption to the rate curves.
        /// <para>
        /// The present value sensitivity is computed in a "sticky strike" style, i.e. the sensitivity to the
        /// curve nodes with the volatility at the swaption strike unchanged. This sensitivity does not include a potential
        /// change of volatility due to the implicit change of forward rate or moneyness.
        ///
        /// </para>
        /// </summary>
        /// <param name="swaption">  the swaption </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="swaptionVolatilities">  the volatilities </param>
        /// <returns> the point sensitivity to the rate curves </returns>
        public virtual PointSensitivityBuilder presentValueSensitivityRatesStickyStrike(ResolvedSwaption swaption, RatesProvider ratesProvider, SwaptionVolatilities swaptionVolatilities)
        {
            validate(swaption, ratesProvider, swaptionVolatilities);
            double          expiry     = swaptionVolatilities.relativeTime(swaption.Expiry);
            ResolvedSwap    underlying = swaption.Underlying;
            ResolvedSwapLeg fixedLeg   = this.fixedLeg(underlying);

            if (expiry < 0d)
            {     // Option has expired already
                return(PointSensitivityBuilder.none());
            }
            double  forward    = SwapPricer.parRate(underlying, ratesProvider);
            double  pvbp       = SwapPricer.LegPricer.pvbp(fixedLeg, ratesProvider);
            double  strike     = SwapPricer.LegPricer.couponEquivalent(fixedLeg, ratesProvider, pvbp);
            double  tenor      = swaptionVolatilities.tenor(fixedLeg.StartDate, fixedLeg.EndDate);
            double  volatility = swaptionVolatilities.volatility(expiry, tenor, strike, forward);
            PutCall putCall    = PutCall.ofPut(fixedLeg.PayReceive.Receive);
            double  price      = swaptionVolatilities.price(expiry, tenor, putCall, strike, forward, volatility);
            double  delta      = swaptionVolatilities.priceDelta(expiry, tenor, putCall, strike, forward, volatility);
            // Backward sweep
            PointSensitivityBuilder pvbpDr    = SwapPricer.LegPricer.pvbpSensitivity(fixedLeg, ratesProvider);
            PointSensitivityBuilder forwardDr = SwapPricer.parRateSensitivity(underlying, ratesProvider);
            double sign = swaption.LongShort.sign();

            return(pvbpDr.multipliedBy(price * sign * Math.Sign(pvbp)).combinedWith(forwardDr.multipliedBy(delta * Math.Abs(pvbp) * sign)));
        }
        //-------------------------------------------------------------------------
        /// <summary>
        /// Calculates the present value sensitivity of the swaption to the rate curves.
        /// <para>
        /// The present value sensitivity is computed in a "sticky strike" style, i.e. the sensitivity to the
        /// curve nodes with the volatility at the swaption strike unchanged. This sensitivity does not include a potential
        /// change of volatility due to the implicit change of forward rate or moneyness.
        ///
        /// </para>
        /// </summary>
        /// <param name="swaption">  the swaption </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="swaptionVolatilities">  the volatilities </param>
        /// <returns> the point sensitivity to the rate curves </returns>
        public virtual PointSensitivityBuilder presentValueSensitivityRatesStickyStrike(ResolvedSwaption swaption, RatesProvider ratesProvider, SwaptionVolatilities swaptionVolatilities)
        {
            validate(swaption, ratesProvider, swaptionVolatilities);
            double          expiry     = swaptionVolatilities.relativeTime(swaption.Expiry);
            ResolvedSwap    underlying = swaption.Underlying;
            ResolvedSwapLeg fixedLeg   = this.fixedLeg(underlying);

            if (expiry < 0d)
            {     // Option has expired already
                return(PointSensitivityBuilder.none());
            }
            double           forward           = SwapPricer.parRate(underlying, ratesProvider);
            ValueDerivatives annuityDerivative = SwapPricer.LegPricer.annuityCashDerivative(fixedLeg, forward);
            double           annuityCash       = annuityDerivative.Value;
            double           annuityCashDr     = annuityDerivative.getDerivative(0);
            LocalDate        settlementDate    = ((CashSwaptionSettlement)swaption.SwaptionSettlement).SettlementDate;
            double           discountSettle    = ratesProvider.discountFactor(fixedLeg.Currency, settlementDate);
            double           strike            = calculateStrike(fixedLeg);
            double           tenor             = swaptionVolatilities.tenor(fixedLeg.StartDate, fixedLeg.EndDate);
            double           volatility        = swaptionVolatilities.volatility(expiry, tenor, strike, forward);
            PutCall          putCall           = PutCall.ofPut(fixedLeg.PayReceive.Receive);
            double           price             = swaptionVolatilities.price(expiry, tenor, putCall, strike, forward, volatility);
            double           delta             = swaptionVolatilities.priceDelta(expiry, tenor, putCall, strike, forward, volatility);
            // Backward sweep
            PointSensitivityBuilder forwardSensi        = SwapPricer.parRateSensitivity(underlying, ratesProvider);
            PointSensitivityBuilder discountSettleSensi = ratesProvider.discountFactors(fixedLeg.Currency).zeroRatePointSensitivity(settlementDate);
            double sign = swaption.LongShort.sign();

            return(forwardSensi.multipliedBy(sign * discountSettle * (annuityCash * delta + annuityCashDr * price)).combinedWith(discountSettleSensi.multipliedBy(sign * annuityCash * price)));
        }
Esempio n. 3
0
        //-------------------------------------------------------------------------
        /// <summary>
        /// Computes the implied normal volatility from the present value of a swaption.
        /// <para>
        /// The guess volatility for the start of the root-finding process is 1%.
        ///
        /// </para>
        /// </summary>
        /// <param name="swaption">  the product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="dayCount">  the day-count used to estimate the time between valuation date and swaption expiry </param>
        /// <param name="presentValue">  the present value of the swaption product </param>
        /// <returns> the implied volatility associated with the present value </returns>
        public virtual double impliedVolatilityFromPresentValue(ResolvedSwaption swaption, RatesProvider ratesProvider, DayCount dayCount, double presentValue)
        {
            double sign = swaption.LongShort.sign();

            ArgChecker.isTrue(presentValue * sign > 0, "Present value sign must be in line with the option Long/Short flag ");
            validateSwaption(swaption);
            LocalDate valuationDate = ratesProvider.ValuationDate;
            LocalDate expiryDate    = swaption.ExpiryDate;

            ArgChecker.isTrue(expiryDate.isAfter(valuationDate), "Expiry must be after valuation date to compute an implied volatility");
            double          expiry     = dayCount.yearFraction(valuationDate, expiryDate);
            ResolvedSwap    underlying = swaption.Underlying;
            ResolvedSwapLeg fixedLeg   = this.fixedLeg(underlying);
            double          forward    = SwapPricer.parRate(underlying, ratesProvider);
            double          numeraire  = calculateNumeraire(swaption, fixedLeg, forward, ratesProvider);
            double          strike     = calculateStrike(fixedLeg);
            PutCall         putCall    = PutCall.ofPut(fixedLeg.PayReceive.Receive);

            return(NormalFormulaRepository.impliedVolatility(Math.Abs(presentValue), forward, strike, expiry, 0.01, numeraire, putCall));
        }
        //-------------------------------------------------------------------------
        /// <summary>
        /// Calculates the present value of the swaption.
        /// <para>
        /// The result is expressed using the currency of the swaption.
        ///
        /// </para>
        /// </summary>
        /// <param name="swaption">  the swaption </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="swaptionVolatilities">  the volatilities </param>
        /// <returns> the present value </returns>
        public virtual CurrencyAmount presentValue(ResolvedSwaption swaption, RatesProvider ratesProvider, SwaptionVolatilities swaptionVolatilities)
        {
            validate(swaption, ratesProvider, swaptionVolatilities);
            double          expiry     = swaptionVolatilities.relativeTime(swaption.Expiry);
            ResolvedSwap    underlying = swaption.Underlying;
            ResolvedSwapLeg fixedLeg   = this.fixedLeg(underlying);

            if (expiry < 0d)
            {     // Option has expired already
                return(CurrencyAmount.of(fixedLeg.Currency, 0d));
            }
            double  forward    = swapPricer.parRate(underlying, ratesProvider);
            double  numeraire  = calculateNumeraire(swaption, fixedLeg, forward, ratesProvider);
            double  strike     = calculateStrike(fixedLeg);
            double  tenor      = swaptionVolatilities.tenor(fixedLeg.StartDate, fixedLeg.EndDate);
            double  volatility = swaptionVolatilities.volatility(expiry, tenor, strike, forward);
            PutCall putCall    = PutCall.ofPut(fixedLeg.PayReceive.Receive);
            double  price      = numeraire * swaptionVolatilities.price(expiry, tenor, putCall, strike, forward, volatility);

            return(CurrencyAmount.of(fixedLeg.Currency, price * swaption.LongShort.sign()));
        }
        //-------------------------------------------------------------------------
        /// <summary>
        /// Calculates the present value sensitivity to the implied volatility of the swaption.
        /// <para>
        /// The sensitivity to the implied volatility is also called vega.
        ///
        /// </para>
        /// </summary>
        /// <param name="swaption">  the swaption </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="swaptionVolatilities">  the volatilities </param>
        /// <returns> the point sensitivity to the volatility </returns>
        public virtual SwaptionSensitivity presentValueSensitivityModelParamsVolatility(ResolvedSwaption swaption, RatesProvider ratesProvider, SwaptionVolatilities swaptionVolatilities)
        {
            validate(swaption, ratesProvider, swaptionVolatilities);
            double          expiry     = swaptionVolatilities.relativeTime(swaption.Expiry);
            ResolvedSwap    underlying = swaption.Underlying;
            ResolvedSwapLeg fixedLeg   = this.fixedLeg(underlying);
            double          tenor      = swaptionVolatilities.tenor(fixedLeg.StartDate, fixedLeg.EndDate);
            double          pvbp       = SwapPricer.LegPricer.pvbp(fixedLeg, ratesProvider);
            double          strike     = SwapPricer.LegPricer.couponEquivalent(fixedLeg, ratesProvider, pvbp);

            if (expiry < 0d)
            {     // Option has expired already
                return(SwaptionSensitivity.of(swaptionVolatilities.Name, expiry, tenor, strike, 0d, fixedLeg.Currency, 0d));
            }
            double  forward    = SwapPricer.parRate(underlying, ratesProvider);
            double  numeraire  = Math.Abs(pvbp);
            double  volatility = swaptionVolatilities.volatility(expiry, tenor, strike, forward);
            PutCall putCall    = PutCall.ofPut(fixedLeg.PayReceive.Receive);
            double  vega       = numeraire * swaptionVolatilities.priceVega(expiry, tenor, putCall, strike, forward, volatility);

            return(SwaptionSensitivity.of(swaptionVolatilities.Name, expiry, tenor, strike, forward, fixedLeg.Currency, vega * swaption.LongShort.sign()));
        }
Esempio n. 6
0
        public virtual void test_trinomialTree_down()
        {
            int nSteps = 133;
            LatticeSpecification lattice = new CoxRossRubinsteinLatticeSpecification();
            DoubleArray          rebate  = DoubleArray.of(nSteps + 1, i => REBATE_AMOUNT);
            double barrierLevel          = 76d;
            double tol = 1.0e-2;

            foreach (bool isCall in new bool[] { true, false })
            {
                foreach (double strike in STRIKES)
                {
                    foreach (double interest in INTERESTS)
                    {
                        foreach (double vol in VOLS)
                        {
                            foreach (double dividend in DIVIDENDS)
                            {
                                OptionFunction function = ConstantContinuousSingleBarrierKnockoutFunction.of(strike, TIME, PutCall.ofPut(!isCall), nSteps, BarrierType.DOWN, barrierLevel, rebate);
                                SimpleConstantContinuousBarrier barrier = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_OUT, barrierLevel);
                                double exact    = REBATE_AMOUNT * REBATE_PRICER.price(SPOT, TIME, interest - dividend, interest, vol, barrier.inverseKnockType()) + BARRIER_PRICER.price(SPOT, strike, TIME, interest - dividend, interest, vol, isCall, barrier);
                                double computed = TRINOMIAL_TREE.optionPrice(function, lattice, SPOT, vol, interest, dividend);
                                assertEquals(computed, exact, Math.Max(exact, 1d) * tol);
                            }
                        }
                    }
                }
            }
        }
Esempio n. 7
0
        /// <summary>
        /// Test consistency between price methods, and Greek via finite difference.
        /// </summary>
        public virtual void test_trinomialTree()
        {
            int    nSteps = 135;
            double dt     = TIME / nSteps;
            LatticeSpecification lattice = new CoxRossRubinsteinLatticeSpecification();
            double fdEps = 1.0e-4;

            foreach (bool isCall in new bool[] { true, false })
            {
                foreach (double strike in STRIKES)
                {
                    foreach (double interest in INTERESTS)
                    {
                        foreach (double vol in VOLS)
                        {
                            foreach (double dividend in DIVIDENDS)
                            {
                                OptionFunction function   = EuropeanVanillaOptionFunction.of(strike, TIME, PutCall.ofPut(!isCall), nSteps);
                                double[]       @params    = lattice.getParametersTrinomial(vol, interest - dividend, dt).toArray();
                                DoubleArray    time       = DoubleArray.of(nSteps + 1, i => dt * i);
                                DoubleArray    df         = DoubleArray.of(nSteps, i => Math.Exp(-interest * dt));
                                double[][]     stateValue = new double[nSteps + 1][];
                                stateValue[0] = new double[] { SPOT };
                                IList <DoubleMatrix> prob  = new List <DoubleMatrix>();
                                double[]             probs = new double[] { @params[5], @params[4], @params[3] };
                                for (int i = 0; i < nSteps; ++i)
                                {
                                    int index = i;
                                    stateValue[i + 1] = DoubleArray.of(2 * i + 3, j => SPOT * Math.Pow(@params[2], index + 1 - j) * Math.Pow(@params[1], j)).toArray();
                                    double[][] probMatrix = new double[2 * i + 1][];
                                    Arrays.fill(probMatrix, probs);
                                    prob.Add(DoubleMatrix.ofUnsafe(probMatrix));
                                }
                                RecombiningTrinomialTreeData treeData = RecombiningTrinomialTreeData.of(DoubleMatrix.ofUnsafe(stateValue), prob, df, time);
                                double priceData   = TRINOMIAL_TREE.optionPrice(function, treeData);
                                double priceParams = TRINOMIAL_TREE.optionPrice(function, lattice, SPOT, vol, interest, dividend);
                                assertEquals(priceData, priceParams);
                                ValueDerivatives priceDeriv = TRINOMIAL_TREE.optionPriceAdjoint(function, treeData);
                                assertEquals(priceDeriv.Value, priceData);
                                double priceUp = TRINOMIAL_TREE.optionPrice(function, lattice, SPOT + fdEps, vol, interest, dividend);
                                double priceDw = TRINOMIAL_TREE.optionPrice(function, lattice, SPOT - fdEps, vol, interest, dividend);
                                double fdDelta = 0.5 * (priceUp - priceDw) / fdEps;
                                assertEquals(priceDeriv.getDerivative(0), fdDelta, 3.0e-2);
                            }
                        }
                    }
                }
            }
        }
        public virtual void test_trinomialTree()
        {
            int nSteps = 135;

            LatticeSpecification[] lattices = new LatticeSpecification[]
            {
                new CoxRossRubinsteinLatticeSpecification(),
                new TrigeorgisLatticeSpecification()
            };
            double tol = 5.0e-3;

            foreach (bool isCall in new bool[] { true, false })
            {
                foreach (double strike in STRIKES)
                {
                    foreach (double interest in INTERESTS)
                    {
                        foreach (double vol in VOLS)
                        {
                            foreach (double dividend in DIVIDENDS)
                            {
                                OptionFunction function = EuropeanVanillaOptionFunction.of(strike, TIME, PutCall.ofPut(!isCall), nSteps);
                                double         exact    = BlackScholesFormulaRepository.price(SPOT, strike, TIME, vol, interest, interest - dividend, isCall);
                                foreach (LatticeSpecification lattice in lattices)
                                {
                                    double computed = TRINOMIAL_TREE.optionPrice(function, lattice, SPOT, vol, interest, dividend);
                                    assertEquals(computed, exact, Math.Max(exact, 1d) * tol);
                                }
                            }
                        }
                    }
                }
            }
        }