/// <summary>
        /// Computes the option price derivative with respect to the SABR parameters.
        /// <para>
        /// The price is SABR below the cut-off strike and extrapolated beyond.
        ///
        /// </para>
        /// </summary>
        /// <param name="strike">  the strike of the option </param>
        /// <param name="putCall">  whether the option is put or call </param>
        /// <returns> the option and its derivative </returns>
        public ValueDerivatives priceAdjointSabr(double strike, PutCall putCall)
        {
            double[] priceDerivativeSabr = new double[4];
            double   price;

            if (strike <= cutOffStrike)
            {     // Uses Hagan et al SABR function.
                ValueDerivatives volatilityA = sabrFunction.volatilityAdjoint(forward, strike, timeToExpiry, sabrData);
                ValueDerivatives pA          = BlackFormulaRepository.priceAdjoint(forward, strike, timeToExpiry, volatilityA.Value, putCall == PutCall.CALL);
                price = pA.Value;
                for (int loopparam = 0; loopparam < 4; loopparam++)
                {
                    priceDerivativeSabr[loopparam] = pA.getDerivative(3) * volatilityA.getDerivative(loopparam + 2);
                }
            }
            else
            {     // Uses extrapolation for call.
                if (parameterDerivativeSabr == null)
                {
                    parameterDerivativeSabr = computesParametersDerivativeSabr();
                    // Derivatives computed only once and only when required
                }
                double f   = extrapolation(strike);
                double fDa = f;
                double fDb = f / strike;
                double fDc = fDb / strike;
                price = putCall.Call ? f : f - forward + strike;   // Put by call/put parity
                for (int loopparam = 0; loopparam < 4; loopparam++)
                {
                    priceDerivativeSabr[loopparam] = fDa * parameterDerivativeSabr[loopparam][0] + fDb * parameterDerivativeSabr[loopparam][1] + fDc * parameterDerivativeSabr[loopparam][2];
                }
            }
            return(ValueDerivatives.of(price, DoubleArray.ofUnsafe(priceDerivativeSabr)));
        }
        //-------------------------------------------------------------------------
        private double[] computesFittingParameters()
        {
            double[] param = new double[3];     // Implementation note: called a,b,c in the note.
            // Computes derivatives at cut-off.
            double[] vD = new double[6];
//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[][] vD2 = new double[2][2];
            double[][] vD2 = RectangularArrays.ReturnRectangularDoubleArray(2, 2);
            volatilityK = sabrFunction.volatilityAdjoint2(forward, cutOffStrike, timeToExpiry, sabrData, vD, vD2);
            Pair <ValueDerivatives, double[][]> pa2 = BlackFormulaRepository.priceAdjoint2(forward, cutOffStrike, timeToExpiry, volatilityK, true);

            double[]   bsD  = pa2.First.Derivatives.toArrayUnsafe();
            double[][] bsD2 = pa2.Second;
            priceK[0] = pa2.First.Value;
            priceK[1] = bsD[1] + bsD[3] * vD[1];
            priceK[2] = bsD2[1][1] + bsD2[1][2] * vD[1] + (bsD2[2][1] + bsD2[2][2] * vD[1]) * vD[1] + bsD[3] * vD2[1][1];
            if (Math.Abs(priceK[0]) < SMALL_PRICE && Math.Abs(priceK[1]) < SMALL_PRICE && Math.Abs(priceK[2]) < SMALL_PRICE)
            {
                // Implementation note: If value and its derivatives is too small, then parameters are such that the extrapolated price is "very small".
                return(new double[] { -100.0, 0, 0 });
            }
            System.Func <double, double> toSolveC = getCFunction(priceK, cutOffStrike, mu);
            BracketRoot            bracketer      = new BracketRoot();
            double                 accuracy       = 1.0E-5;
            RidderSingleRootFinder rootFinder     = new RidderSingleRootFinder(accuracy);

            double[] range = bracketer.getBracketedPoints(toSolveC, -1.0, 1.0);
            param[2] = rootFinder.getRoot(toSolveC, range[0], range[1]).Value;
            param[1] = -2 * param[2] / cutOffStrike - (priceK[1] / priceK[0] * cutOffStrike + mu) * cutOffStrike;
            param[0] = Math.Log(priceK[0] / Math.Pow(cutOffStrike, -mu)) - param[1] / cutOffStrike - param[2] / (cutOffStrike * cutOffStrike);
            return(param);
        }
コード例 #3
0
//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes:
//ORIGINAL LINE: @Test(enabled = true) public void log_normal_atm()
        public virtual void log_normal_atm()
        {
            double  beta         = 0.50;
            Surface betaSurface  = ConstantSurface.of("Beta", beta).withMetadata(DefaultSurfaceMetadata.builder().xValueType(ValueType.YEAR_FRACTION).yValueType(ValueType.YEAR_FRACTION).zValueType(ValueType.SABR_BETA).surfaceName("Beta").build());
            double  shift        = 0.0000;
            Surface shiftSurface = ConstantSurface.of("Shift", shift).withMetadata(DefaultSurfaceMetadata.builder().xValueType(ValueType.YEAR_FRACTION).yValueType(ValueType.YEAR_FRACTION).surfaceName("Shift").build());
            SabrParametersSwaptionVolatilities calibratedSmile = SABR_CALIBRATION.calibrateWithFixedBetaAndShift(DEFINITION, CALIBRATION_TIME, DATA_SPARSE, MULTICURVE, betaSurface, shiftSurface);

            SabrParametersSwaptionVolatilities calibratedAtm = SABR_CALIBRATION.calibrateAlphaWithAtm(NAME_SABR, calibratedSmile, MULTICURVE, ATM_LOGNORMAL_SIMPLE, TENORS_SIMPLE, EXPIRIES_SIMPLE_2, INTERPOLATOR_2D);
            int nbExp   = EXPIRIES_SIMPLE_2.size();
            int nbTenor = TENORS_SIMPLE.size();

            for (int loopexpiry = 0; loopexpiry < nbExp; loopexpiry++)
            {
                for (int looptenor = 0; looptenor < nbTenor; looptenor++)
                {
                    double        tenor          = TENORS_SIMPLE.get(looptenor).get(ChronoUnit.YEARS);
                    LocalDate     expiry         = EUR_FIXED_1Y_EURIBOR_6M.FloatingLeg.StartDateBusinessDayAdjustment.adjust(CALIBRATION_DATE.plus(EXPIRIES_SIMPLE_2.get(loopexpiry)), REF_DATA);
                    LocalDate     effectiveDate  = EUR_FIXED_1Y_EURIBOR_6M.calculateSpotDateFromTradeDate(expiry, REF_DATA);
                    LocalDate     endDate        = effectiveDate.plus(TENORS_SIMPLE.get(looptenor));
                    SwapTrade     swap           = EUR_FIXED_1Y_EURIBOR_6M.toTrade(CALIBRATION_DATE, effectiveDate, endDate, BuySell.BUY, 1.0, 0.0);
                    double        parRate        = SWAP_PRICER.parRate(swap.resolve(REF_DATA).Product, MULTICURVE);
                    ZonedDateTime expiryDateTime = expiry.atTime(11, 0).atZone(ZoneId.of("Europe/Berlin"));
                    double        time           = calibratedAtm.relativeTime(expiryDateTime);
                    double        volBlack       = calibratedAtm.volatility(expiryDateTime, tenor, parRate, parRate);
                    double        priceComputed  = calibratedAtm.price(time, tenor, PutCall.CALL, parRate, parRate, volBlack);
                    double        priceBlack     = BlackFormulaRepository.price(parRate, parRate, time, DATA_LOGNORMAL_ATM_SIMPLE[looptenor + loopexpiry * nbTenor], true);
                    assertEquals(priceComputed, priceBlack, TOLERANCE_PRICE_CALIBRATION_ROOT);
                }
            }
        }
        /// <summary>
        /// Computes the option price derivative with respect to the forward.
        /// <para>
        /// The price is SABR below the cut-off strike and extrapolated beyond.
        ///
        /// </para>
        /// </summary>
        /// <param name="strike">  the strike of the option </param>
        /// <param name="putCall">  whether the option is put or call </param>
        /// <returns> the option price derivative </returns>
        public double priceDerivativeForward(double strike, PutCall putCall)
        {
            // Uses Hagan et al SABR function.
            if (strike <= cutOffStrike)
            {
                ValueDerivatives volatilityA = sabrFunction.volatilityAdjoint(forward, strike, timeToExpiry, sabrData);
                ValueDerivatives pA          = BlackFormulaRepository.priceAdjoint(forward, strike, timeToExpiry, volatilityA.Value, putCall == PutCall.CALL);
                return(pA.getDerivative(0) + pA.getDerivative(3) * volatilityA.getDerivative(0));
            }
            // Uses extrapolation for call.
            if (parameterDerivativeForward == null)
            {
                parameterDerivativeForward = computesParametersDerivativeForward();
            }
            double f               = extrapolation(strike);
            double fDa             = f;
            double fDb             = f / strike;
            double fDc             = fDb / strike;
            double priceDerivative = fDa * parameterDerivativeForward[0] + fDb * parameterDerivativeForward[1] + fDc * parameterDerivativeForward[2];

            if (putCall.Put)
            {     // Put by call/put parity
                priceDerivative -= 1;
            }
            return(priceDerivative);
        }
コード例 #5
0
        public virtual void test_price_formula()
        {
            double sampleVol = 0.2;

            for (int i = 0; i < NB_TEST; i++)
            {
                double expiryTime = VOLS.relativeTime(TEST_OPTION_EXPIRY[i]);
                for (int j = 0; j < NB_TEST; j++)
                {
                    foreach (PutCall putCall in new PutCall[] { PutCall.CALL, PutCall.PUT })
                    {
                        double price = VOLS.price(expiryTime, putCall, TEST_STRIKE[j], TEST_FORWARD, sampleVol);
                        double delta = VOLS.priceDelta(expiryTime, putCall, TEST_STRIKE[j], TEST_FORWARD, sampleVol);
                        double gamma = VOLS.priceGamma(expiryTime, putCall, TEST_STRIKE[j], TEST_FORWARD, sampleVol);
                        double theta = VOLS.priceTheta(expiryTime, putCall, TEST_STRIKE[j], TEST_FORWARD, sampleVol);
                        double vega  = VOLS.priceVega(expiryTime, putCall, TEST_STRIKE[j], TEST_FORWARD, sampleVol);
                        assertEquals(price, BlackFormulaRepository.price(TEST_FORWARD, TEST_STRIKE[j], expiryTime, sampleVol, putCall.Call));
                        assertEquals(delta, BlackFormulaRepository.delta(TEST_FORWARD, TEST_STRIKE[j], expiryTime, sampleVol, putCall.Call));
                        assertEquals(gamma, BlackFormulaRepository.gamma(TEST_FORWARD, TEST_STRIKE[j], expiryTime, sampleVol));
                        assertEquals(theta, BlackFormulaRepository.driftlessTheta(TEST_FORWARD, TEST_STRIKE[j], expiryTime, sampleVol));
                        assertEquals(vega, BlackFormulaRepository.vega(TEST_FORWARD, TEST_STRIKE[j], expiryTime, sampleVol));
                    }
                }
            }
        }
コード例 #6
0
        //-------------------------------------------------------------------------
        public virtual void test_presentValueSensitivityBlackVolatility()
        {
            SwaptionSensitivity sensiRec = PRICER.presentValueSensitivityModelParamsVolatility(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS);
            SwaptionSensitivity sensiPay = PRICER.presentValueSensitivityModelParamsVolatility(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS);
            double forward     = SWAP_PRICER.parRate(RSWAP_REC, RATE_PROVIDER);
            double annuityCash = SWAP_PRICER.LegPricer.annuityCash(RFIXED_LEG_REC, forward);
            double expiry      = VOLS.relativeTime(SWAPTION_REC_LONG.Expiry);
            double tenor       = VOLS.tenor(SETTLE, END);
            double volatility  = SURFACE.zValue(expiry, tenor);
            double settle      = ACT_ACT_ISDA.relativeYearFraction(VAL_DATE, SETTLE);
            double df          = Math.Exp(-DSC_CURVE.yValue(settle) * settle);
            double expectedRec = df * annuityCash * BlackFormulaRepository.vega(forward, RATE, expiry, volatility);
            double expectedPay = -df *annuityCash *BlackFormulaRepository.vega(forward, RATE, expiry, volatility);

            assertEquals(sensiRec.Currency, EUR);
            assertEquals(sensiRec.Sensitivity, expectedRec, NOTIONAL * TOL);
            assertEquals(sensiRec.VolatilitiesName, VOLS.Name);
            assertEquals(sensiRec.Expiry, expiry);
            assertEquals(sensiRec.Tenor, 5.0);
            assertEquals(sensiRec.Strike, RATE);
            assertEquals(sensiRec.Forward, forward, TOL);
            assertEquals(sensiPay.Currency, EUR);
            assertEquals(sensiPay.Sensitivity, expectedPay, NOTIONAL * TOL);
            assertEquals(sensiRec.VolatilitiesName, VOLS.Name);
            assertEquals(sensiPay.Expiry, expiry);
            assertEquals(sensiPay.Tenor, 5.0);
            assertEquals(sensiPay.Strike, RATE);
            assertEquals(sensiPay.Forward, forward, TOL);
        }
コード例 #7
0
        //-------------------------------------------------------------------------
        /// <summary>
        /// Calculates the price of the foreign exchange vanilla option product.
        /// <para>
        /// The price of the product is the value on the valuation date for one unit of the base currency
        /// and is expressed in the counter currency. The price does not take into account the long/short flag.
        /// See <seealso cref="#presentValue"/> for scaling and currency.
        ///
        /// </para>
        /// </summary>
        /// <param name="option">  the option product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="volatilities">  the Black volatility provider </param>
        /// <returns> the price of the product </returns>
        public virtual double price(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionSmileVolatilities volatilities)
        {
            validate(ratesProvider, volatilities);
            double timeToExpiry = volatilities.relativeTime(option.Expiry);

            if (timeToExpiry <= 0d)
            {
                return(0d);
            }
            ResolvedFxSingle     underlyingFx = option.Underlying;
            Currency             ccyCounter   = option.CounterCurrency;
            double               df           = ratesProvider.discountFactor(ccyCounter, underlyingFx.PaymentDate);
            FxRate               forward      = fxPricer.forwardFxRate(underlyingFx, ratesProvider);
            CurrencyPair         currencyPair = underlyingFx.CurrencyPair;
            double               forwardRate  = forward.fxRate(currencyPair);
            double               strikeRate   = option.Strike;
            bool                 isCall       = option.PutCall.Call;
            SmileDeltaParameters smileAtTime  = volatilities.Smile.smileForExpiry(timeToExpiry);

            double[] strikes = smileAtTime.strike(forwardRate).toArray();
            double[] vols    = smileAtTime.Volatility.toArray();
            double   volAtm  = vols[1];

            double[] x        = vannaVolgaWeights(forwardRate, strikeRate, timeToExpiry, volAtm, strikes);
            double   priceFwd = BlackFormulaRepository.price(forwardRate, strikeRate, timeToExpiry, volAtm, isCall);

            for (int i = 0; i < 3; i += 2)
            {
                double priceFwdAtm   = BlackFormulaRepository.price(forwardRate, strikes[i], timeToExpiry, volAtm, isCall);
                double priceFwdSmile = BlackFormulaRepository.price(forwardRate, strikes[i], timeToExpiry, vols[i], isCall);
                priceFwd += x[i] * (priceFwdSmile - priceFwdAtm);
            }
            return(df * priceFwd);
        }
        //-------------------------------------------------------------------------
        /// <summary>
        /// Calculates the present value sensitivity to the SABR model parameters of the swaption product.
        /// <para>
        /// The sensitivity of the present value to the SABR model parameters, alpha, beta, rho and nu.
        ///
        /// </para>
        /// </summary>
        /// <param name="swaption">  the swaption product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="swaptionVolatilities">  the volatilities </param>
        /// <returns> the point sensitivity to the SABR model parameters </returns>
        public virtual PointSensitivityBuilder presentValueSensitivityModelParamsSabr(ResolvedSwaption swaption, RatesProvider ratesProvider, SabrSwaptionVolatilities 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          shift      = swaptionVolatilities.shift(expiry, tenor);
            double          pvbp       = SwapPricer.LegPricer.pvbp(fixedLeg, ratesProvider);
            double          strike     = SwapPricer.LegPricer.couponEquivalent(fixedLeg, ratesProvider, pvbp);

            if (expiry < 0d)
            {     // Option has expired already
                return(PointSensitivityBuilder.none());
            }
            double      forward    = SwapPricer.parRate(underlying, ratesProvider);
            double      volatility = swaptionVolatilities.volatility(expiry, tenor, strike, forward);
            DoubleArray derivative = swaptionVolatilities.volatilityAdjoint(expiry, tenor, strike, forward).Derivatives;
            // Backward sweep
            double vega = Math.Abs(pvbp) * BlackFormulaRepository.vega(forward + shift, strike + shift, expiry, volatility) * swaption.LongShort.sign();
            // sensitivities
            Currency ccy = fixedLeg.Currency;
            SwaptionVolatilitiesName name = swaptionVolatilities.Name;

            return(PointSensitivityBuilder.of(SwaptionSabrSensitivity.of(name, expiry, tenor, ALPHA, ccy, vega * derivative.get(2)), SwaptionSabrSensitivity.of(name, expiry, tenor, BETA, ccy, vega * derivative.get(3)), SwaptionSabrSensitivity.of(name, expiry, tenor, RHO, ccy, vega * derivative.get(4)), SwaptionSabrSensitivity.of(name, expiry, tenor, NU, ccy, vega * derivative.get(5))));
        }
        private const double TOLERANCE_PRICE_CALIBRATION_LS = 5.0E-4;   // Calibration Least Square; result not exact

//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes:
//ORIGINAL LINE: @Test public void normal_cube()
        public virtual void normal_cube()
        {
            double  beta         = 0.50;
            Surface betaSurface  = ConstantSurface.of("Beta", beta).withMetadata(DefaultSurfaceMetadata.builder().xValueType(ValueType.YEAR_FRACTION).yValueType(ValueType.YEAR_FRACTION).zValueType(ValueType.SABR_BETA).surfaceName("Beta").build());
            double  shift        = 0.0300;
            Surface shiftSurface = ConstantSurface.of("Shift", shift).withMetadata(DefaultSurfaceMetadata.builder().xValueType(ValueType.YEAR_FRACTION).yValueType(ValueType.YEAR_FRACTION).surfaceName("Shift").build());
            SabrParametersSwaptionVolatilities calibrated = SABR_CALIBRATION.calibrateWithFixedBetaAndShift(DEFINITION, CALIBRATION_TIME, DATA_SPARSE, MULTICURVE, betaSurface, shiftSurface);

            for (int looptenor = 0; looptenor < TENORS.size(); looptenor++)
            {
                double tenor = TENORS.get(looptenor).get(ChronoUnit.YEARS);
                for (int loopexpiry = 0; loopexpiry < EXPIRIES.size(); loopexpiry++)
                {
                    LocalDate     expiry         = EUR_FIXED_1Y_EURIBOR_6M.FloatingLeg.StartDateBusinessDayAdjustment.adjust(CALIBRATION_DATE.plus(EXPIRIES.get(loopexpiry)), REF_DATA);
                    LocalDate     effectiveDate  = EUR_FIXED_1Y_EURIBOR_6M.calculateSpotDateFromTradeDate(expiry, REF_DATA);
                    LocalDate     endDate        = effectiveDate.plus(TENORS.get(looptenor));
                    SwapTrade     swap           = EUR_FIXED_1Y_EURIBOR_6M.toTrade(CALIBRATION_DATE, effectiveDate, endDate, BuySell.BUY, 1.0, 0.0);
                    double        parRate        = SWAP_PRICER.parRate(swap.resolve(REF_DATA).Product, MULTICURVE);
                    ZonedDateTime expiryDateTime = expiry.atTime(11, 0).atZone(ZoneId.of("Europe/Berlin"));
                    double        time           = calibrated.relativeTime(expiryDateTime);
                    for (int loopmoney = 0; loopmoney < MONEYNESS.size(); loopmoney++)
                    {
                        if (!double.IsNaN(DATA_ARRAY_SPARSE[looptenor][loopexpiry][loopmoney]))
                        {
                            double strike        = parRate + MONEYNESS.get(loopmoney);
                            double volBlack      = calibrated.volatility(expiryDateTime, tenor, strike, parRate);
                            double priceComputed = BlackFormulaRepository.price(parRate + shift, parRate + MONEYNESS.get(loopmoney) + shift, time, volBlack, true);
                            double priceNormal   = NormalFormulaRepository.price(parRate, parRate + MONEYNESS.get(loopmoney), time, DATA_ARRAY_SPARSE[looptenor][loopexpiry][loopmoney], PutCall.CALL);
                            assertEquals(priceComputed, priceNormal, TOLERANCE_PRICE_CALIBRATION_LS);
                        }
                    }
                }
            }
        }
        //-------------------------------------------------------------------------
        /// <summary>
        /// Calculates the present value sensitivity of the swaption product to the rate curves.
        /// <para>
        /// The present value sensitivity is computed in a "sticky model parameter" style, i.e. the sensitivity to the
        /// curve nodes with the SABR model parameters unchanged. This sensitivity does not include a potential
        /// re-calibration of the model parameters to the raw market data.
        ///
        /// </para>
        /// </summary>
        /// <param name="swaption">  the swaption product </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 presentValueSensitivityRatesStickyModel(ResolvedSwaption swaption, RatesProvider ratesProvider, SabrSwaptionVolatilities swaptionVolatilities)
        {
            validate(swaption, ratesProvider, swaptionVolatilities);
            ZonedDateTime   expiryDateTime = swaption.Expiry;
            double          expiry         = swaptionVolatilities.relativeTime(expiryDateTime);
            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           shift         = swaptionVolatilities.shift(expiry, tenor);
            ValueDerivatives volatilityAdj = swaptionVolatilities.volatilityAdjoint(expiry, tenor, strike, forward);
            bool             isCall        = fixedLeg.PayReceive.Pay;
            // Payer at strike is exercise when rate > strike, i.e. call on rate
            // Backward sweep
            PointSensitivityBuilder pvbpDr    = SwapPricer.LegPricer.pvbpSensitivity(fixedLeg, ratesProvider);
            PointSensitivityBuilder forwardDr = SwapPricer.parRateSensitivity(underlying, ratesProvider);
            double shiftedForward             = forward + shift;
            double shiftedStrike = strike + shift;
            double price         = BlackFormulaRepository.price(shiftedForward, shiftedStrike, expiry, volatilityAdj.Value, isCall);
            double delta         = BlackFormulaRepository.delta(shiftedForward, shiftedStrike, expiry, volatilityAdj.Value, isCall);
            double vega          = BlackFormulaRepository.vega(shiftedForward, shiftedStrike, expiry, volatilityAdj.Value);
            double sign          = swaption.LongShort.sign();

            return(pvbpDr.multipliedBy(price * sign * Math.Sign(pvbp)).combinedWith(forwardDr.multipliedBy((delta + vega * volatilityAdj.getDerivative(0)) * Math.Abs(pvbp) * sign)));
        }
コード例 #11
0
        public virtual InterpolatedNodalSurface localVolatilityFromPrice(Surface callPriceSurface, double spot, System.Func <double, double> interestRate, System.Func <double, double> dividendRate)
        {
            double[][]           stateValue  = new double[nSteps + 1][];
            double[]             df          = new double[nSteps];
            IList <DoubleMatrix> probability = new List <DoubleMatrix>(nSteps);
            int nTotal = (nSteps - 1) * (nSteps - 1) + 1;

            double[] timeRes = new double[nTotal];
            double[] spotRes = new double[nTotal];
            double[] volRes  = new double[nTotal];
            // uniform grid based on TrigeorgisLatticeSpecification, using reference values
            double refPrice      = callPriceSurface.zValue(maxTime, spot) * Math.Exp(interestRate(maxTime) * maxTime);
            double refForward    = spot * Math.Exp((interestRate(maxTime) - dividendRate(maxTime)) * maxTime);
            double refVolatility = BlackFormulaRepository.impliedVolatility(refPrice, refForward, spot, maxTime, true);
            double dt            = maxTime / nSteps;
            double dx            = refVolatility * Math.Sqrt(3d * dt);
            double upFactor      = Math.Exp(dx);
            double downFactor    = Math.Exp(-dx);

            double[] adSec      = new double[2 * nSteps + 1];
            double[] assetPrice = new double[2 * nSteps + 1];
            for (int i = nSteps; i > -1; --i)
            {
                if (i == 0)
                {
                    resolveFirstLayer(interestRate, dividendRate, nTotal, dt, spot, adSec, assetPrice, timeRes, spotRes, volRes, df, stateValue, probability);
                }
                else
                {
                    double   time             = dt * i;
                    double   zeroRate         = interestRate(time);
                    double   zeroDividendRate = dividendRate(time);
                    int      nNodes           = 2 * i + 1;
                    double[] assetPriceLocal  = new double[nNodes];
                    double[] callOptionPrice  = new double[nNodes];
                    double[] putOptionPrice   = new double[nNodes];
                    int      position         = i - 1;
                    double   assetTmp         = spot * Math.Pow(upFactor, i);
                    // call options for upper half nodes
                    for (int j = nNodes - 1; j > position - 1; --j)
                    {
                        assetPriceLocal[j] = assetTmp;
                        callOptionPrice[j] = callPriceSurface.zValue(time, assetPriceLocal[j]);
                        assetTmp          *= downFactor;
                    }
                    // put options for lower half nodes
                    assetTmp = spot * Math.Pow(downFactor, i);
                    for (int j = 0; j < position + 2; ++j)
                    {
                        assetPriceLocal[j] = assetTmp;
                        putOptionPrice[j]  = callPriceSurface.zValue(time, assetPriceLocal[j]) - spot * Math.Exp(-zeroDividendRate * time) + Math.Exp(-zeroRate * time) * assetPriceLocal[j];
                        assetTmp          *= upFactor;
                    }
                    resolveLayer(interestRate, dividendRate, i, nTotal, position, dt, zeroRate, zeroDividendRate, callOptionPrice, putOptionPrice, adSec, assetPrice, assetPriceLocal, timeRes, spotRes, volRes, df, stateValue, probability);
                }
            }
            SurfaceMetadata metadata = DefaultSurfaceMetadata.builder().xValueType(ValueType.YEAR_FRACTION).yValueType(ValueType.STRIKE).zValueType(ValueType.LOCAL_VOLATILITY).surfaceName(SurfaceName.of("localVol_" + callPriceSurface.Name)).build();

            return(InterpolatedNodalSurface.ofUnsorted(metadata, DoubleArray.ofUnsafe(timeRes), DoubleArray.ofUnsafe(spotRes), DoubleArray.ofUnsafe(volRes), interpolator));
        }
コード例 #12
0
 private void priceCheck(double[] strikes)
 {
     for (int i = 0; i < N; i++)
     {
         double ivNormalComputed    = NormalFormulaRepository.impliedVolatilityFromBlackApproximated(FORWARD, strikes[i], T, SIGMA_BLACK[i]);
         double priceNormalComputed = NormalFormulaRepository.price(FORWARD, strikes[i], T, ivNormalComputed, PutCall.CALL) * DF;
         double priceBlack          = BlackFormulaRepository.price(FORWARD, strikes[i], T, SIGMA_BLACK[i], true) * DF;
         assertEquals(priceNormalComputed, priceBlack, TOLERANCE_PRICE);
     }
 }
コード例 #13
0
        public virtual void test_theta_from_future_price()
        {
            double futurePrice  = 1.1d;
            double computed     = OPTION_PRICER.theta(FUTURE_OPTION_PRODUCT, RATE_PROVIDER, VOLS, futurePrice);
            double strike       = FUTURE_OPTION_PRODUCT.StrikePrice;
            double expiryTime   = ACT_365F.relativeYearFraction(VAL_DATE, FUTURE_OPTION_PRODUCT.ExpiryDate);
            double logMoneyness = Math.Log(strike / futurePrice);
            double vol          = SURFACE.zValue(expiryTime, logMoneyness);
            double expected     = BlackFormulaRepository.driftlessTheta(futurePrice, strike, expiryTime, vol);

            assertEquals(computed, expected, TOL);
        }
コード例 #14
0
        public virtual void test_price()
        {
            double computed     = OPTION_PRICER.price(FUTURE_OPTION_PRODUCT, RATE_PROVIDER, VOLS);
            double futurePrice  = FUTURE_PRICER.price(FUTURE_OPTION_PRODUCT.UnderlyingFuture, RATE_PROVIDER);
            double strike       = FUTURE_OPTION_PRODUCT.StrikePrice;
            double expiryTime   = ACT_365F.relativeYearFraction(VAL_DATE, FUTURE_OPTION_PRODUCT.ExpiryDate);
            double logMoneyness = Math.Log(strike / futurePrice);
            double vol          = SURFACE.zValue(expiryTime, logMoneyness);
            double expected     = BlackFormulaRepository.price(futurePrice, strike, expiryTime, vol, true);

            assertEquals(computed, expected, TOL);
        }
コード例 #15
0
        //-------------------------------------------------------------------------
        public virtual void present_value_theta_formula()
        {
            double         forward         = PRICER_SWAP.parRate(RSWAP_REC, MULTI_USD);
            double         pvbp            = PRICER_SWAP.LegPricer.pvbp(RSWAP_REC.getLegs(SwapLegType.FIXED).get(0), MULTI_USD);
            double         volatility      = BLACK_VOLS_USD_STD.volatility(SWAPTION_LONG_REC.Expiry, SWAP_TENOR_YEAR, STRIKE, forward);
            double         expiry          = BLACK_VOLS_USD_STD.relativeTime(SWAPTION_LONG_REC.Expiry);
            double         pvThetaExpected = BlackFormulaRepository.driftlessTheta(forward, STRIKE, expiry, volatility) * Math.Abs(pvbp);
            CurrencyAmount pvThetaComputed = PRICER_SWAPTION_BLACK.presentValueTheta(SWAPTION_LONG_REC, MULTI_USD, BLACK_VOLS_USD_STD);

            assertEquals(pvThetaComputed.Currency, USD);
            assertEquals(pvThetaComputed.Amount, pvThetaExpected, TOLERANCE_PV);
        }
コード例 #16
0
        //-------------------------------------------------------------------------
        /// <summary>
        /// Calculates the strikes in ascending order.
        /// <para>
        /// The result has twice the number of values plus one as the delta/volatility.
        /// The put with lower delta (in absolute value) first, at-the-money and call with larger delta first.
        ///
        /// </para>
        /// </summary>
        /// <param name="forward">  the forward </param>
        /// <returns> the strikes </returns>
        public DoubleArray strike(double forward)
        {
            int nbDelta = delta.size();

            double[] strike = new double[2 * nbDelta + 1];
            strike[nbDelta] = forward * Math.Exp(volatility.get(nbDelta) * volatility.get(nbDelta) * expiry / 2.0);
            for (int loopdelta = 0; loopdelta < nbDelta; loopdelta++)
            {
                strike[loopdelta] = BlackFormulaRepository.impliedStrike(-delta.get(loopdelta), false, forward, expiry, volatility.get(loopdelta));                           // Put
                strike[2 * nbDelta - loopdelta] = BlackFormulaRepository.impliedStrike(delta.get(loopdelta), true, forward, expiry, volatility.get(2 * nbDelta - loopdelta)); // Call
            }
            return(DoubleArray.ofUnsafe(strike));
        }
        private void checkCalibrationNormal(DoubleArray moneyness, DoubleArray normalVol, DoubleArray startParameters, BitArray @fixed, double shift, double tolerance)
        {
            Pair <LeastSquareResultsWithTransform, DoubleArray> rComputed = SABR_CALIBRATION.calibrateLsShiftedFromNormalVolatilities(BDA, CALIBRATION_TIME, ACT_365F, EXPIRY_PERIOD, FORWARD, moneyness, ValueType.SIMPLE_MONEYNESS, normalVol, startParameters, @fixed, shift);
            SabrFormulaData sabrComputed = SabrFormulaData.of(rComputed.First.ModelParameters.toArrayUnsafe());

            for (int i = 0; i < moneyness.size(); i++)
            {
                double ivComputed    = SABR_FORMULA.volatility(FORWARD + shift, FORWARD + moneyness.get(i) + shift, TIME_EXPIRY, sabrComputed.Alpha, sabrComputed.Beta, sabrComputed.Rho, sabrComputed.Nu);
                double priceComputed = BlackFormulaRepository.price(FORWARD + shift, FORWARD + moneyness.get(i) + shift, TIME_EXPIRY, ivComputed, true);
                double priceNormal   = NormalFormulaRepository.price(FORWARD, FORWARD + moneyness.get(i), TIME_EXPIRY, normalVol.get(i), PutCall.CALL);
                assertEquals(priceComputed, priceNormal, tolerance);
            }
        }
        //-------------------------------------------------------------------------
        public virtual void test_presentValue()
        {
            CurrencyAmount computedRec = SWAPTION_PRICER.presentValue(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS);
            CurrencyAmount computedPay = SWAPTION_PRICER.presentValue(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS);
            double         forward     = SWAP_PRICER.parRate(RSWAP_REC, RATE_PROVIDER);
            double         pvbp        = SWAP_PRICER.LegPricer.pvbp(RSWAP_REC.getLegs(SwapLegType.FIXED).get(0), RATE_PROVIDER);
            double         volatility  = VOLS.volatility(SWAPTION_REC_LONG.Expiry, TENOR_YEAR, RATE, forward);
            double         maturity    = VOLS.relativeTime(SWAPTION_REC_LONG.Expiry);
            double         expectedRec = pvbp * BlackFormulaRepository.price(forward + SwaptionSabrRateVolatilityDataSet.SHIFT, RATE + SwaptionSabrRateVolatilityDataSet.SHIFT, maturity, volatility, false);
            double         expectedPay = -pvbp *BlackFormulaRepository.price(forward + SwaptionSabrRateVolatilityDataSet.SHIFT, RATE + SwaptionSabrRateVolatilityDataSet.SHIFT, maturity, volatility, true);

            assertEquals(computedRec.Currency, USD);
            assertEquals(computedRec.Amount, expectedRec, NOTIONAL * TOL);
            assertEquals(computedPay.Currency, USD);
            assertEquals(computedPay.Amount, expectedPay, NOTIONAL * TOL);
        }
コード例 #19
0
        /// <summary>
        /// Calculates the currency exposure of the foreign exchange vanilla option product.
        /// </summary>
        /// <param name="option">  the option product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="volatilities">  the Black volatility provider </param>
        /// <returns> the currency exposure </returns>
        public virtual MultiCurrencyAmount currencyExposure(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionSmileVolatilities volatilities)
        {
            validate(ratesProvider, volatilities);
            double timeToExpiry = volatilities.relativeTime(option.Expiry);

            if (timeToExpiry <= 0d)
            {
                return(MultiCurrencyAmount.empty());
            }
            ResolvedFxSingle     underlyingFx           = option.Underlying;
            Currency             ccyCounter             = option.CounterCurrency;
            double               df                     = ratesProvider.discountFactor(ccyCounter, underlyingFx.PaymentDate);
            FxRate               forward                = fxPricer.forwardFxRate(underlyingFx, ratesProvider);
            CurrencyPair         currencyPair           = underlyingFx.CurrencyPair;
            double               spot                   = ratesProvider.fxRate(currencyPair);
            double               forwardRate            = forward.fxRate(currencyPair);
            double               fwdRateSpotSensitivity = fxPricer.forwardFxRateSpotSensitivity(option.PutCall.Call ? underlyingFx : underlyingFx.inverse(), ratesProvider);
            double               strikeRate             = option.Strike;
            bool                 isCall                 = option.PutCall.Call;
            SmileDeltaParameters smileAtTime            = volatilities.Smile.smileForExpiry(timeToExpiry);

            double[] strikes = smileAtTime.strike(forwardRate).toArray();
            double[] vols    = smileAtTime.Volatility.toArray();
            double   volAtm  = vols[1];

            double[] x        = vannaVolgaWeights(forwardRate, strikeRate, timeToExpiry, volAtm, strikes);
            double   priceFwd = BlackFormulaRepository.price(forwardRate, strikeRate, timeToExpiry, volAtm, isCall);
            double   deltaFwd = BlackFormulaRepository.delta(forwardRate, strikeRate, timeToExpiry, volAtm, isCall);

            for (int i = 0; i < 3; i += 2)
            {
                double priceFwdAtm   = BlackFormulaRepository.price(forwardRate, strikes[i], timeToExpiry, volAtm, isCall);
                double priceFwdSmile = BlackFormulaRepository.price(forwardRate, strikes[i], timeToExpiry, vols[i], isCall);
                priceFwd += x[i] * (priceFwdSmile - priceFwdAtm);
                double deltaFwdAtm   = BlackFormulaRepository.delta(forwardRate, strikes[i], timeToExpiry, volAtm, isCall);
                double deltaFwdSmile = BlackFormulaRepository.delta(forwardRate, strikes[i], timeToExpiry, vols[i], isCall);
                deltaFwd += x[i] * (deltaFwdSmile - deltaFwdAtm);
            }
            double         price          = df * priceFwd;
            double         delta          = df * deltaFwd * fwdRateSpotSensitivity;
            double         signedNotional = this.signedNotional(option);
            CurrencyAmount domestic       = CurrencyAmount.of(currencyPair.Counter, (price - delta * spot) * signedNotional);
            CurrencyAmount foreign        = CurrencyAmount.of(currencyPair.Base, delta * signedNotional);

            return(MultiCurrencyAmount.of(domestic, foreign));
        }
コード例 #20
0
        //-------------------------------------------------------------------------
        /// <summary>
        /// Computes the option price with numeraire=1.
        /// <para>
        /// The price is SABR below the cut-off strike and extrapolated beyond.
        ///
        /// </para>
        /// </summary>
        /// <param name="strike">  the strike of the option </param>
        /// <param name="putCall">  whether the option is put or call </param>
        /// <returns> the option price </returns>
        public double price(double strike, PutCall putCall)
        {
            // Uses Hagan et al SABR function.
            if (strike <= cutOffStrike)
            {
                double volatility = sabrFunction.volatility(forward, strike, timeToExpiry, sabrData);
                return(BlackFormulaRepository.price(forward, strike, timeToExpiry, volatility, putCall.Call));
            }
            // Uses extrapolation for call.
            double price = extrapolation(strike);

            if (putCall.Put)
            {     // Put by call/put parity
                price -= (forward - strike);
            }
            return(price);
        }
        /// <summary>
        /// Calculate the true SABR delta and gamma and compare with that found by finite difference
        /// </summary>
//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes:
//ORIGINAL LINE: @Test(enabled = false) public void testGreeks()
        public virtual void testGreeks()
        {
            double           eps             = 1e-3;
            double           f               = 1.2;
            double           k               = 1.4;
            double           t               = 5.0;
            double           alpha           = 0.3;
            double           beta            = 0.6;
            double           rho             = -0.4;
            double           nu              = 0.4;
            SabrFormulaData  sabrData        = SabrFormulaData.of(alpha, beta, rho, nu);
            ValueDerivatives adj             = FUNCTION.volatilityAdjoint(f, k, t, sabrData);
            double           bsDelta         = BlackFormulaRepository.delta(f, k, t, adj.Value, true);
            double           bsVega          = BlackFormulaRepository.vega(f, k, t, adj.Value);
            double           volForwardSense = adj.getDerivative(1);
            double           delta           = bsDelta + bsVega * volForwardSense;

            SabrFormulaData data      = SabrFormulaData.of(alpha, beta, rho, nu);
            double          volUp     = FUNCTION.volatility(f + eps, k, t, data);
            double          volDown   = FUNCTION.volatility(f - eps, k, t, data);
            double          priceUp   = BlackFormulaRepository.price(f + eps, k, t, volUp, true);
            double          price     = BlackFormulaRepository.price(f, k, t, adj.Value, true);
            double          priceDown = BlackFormulaRepository.price(f - eps, k, t, volDown, true);
            double          fdDelta   = (priceUp - priceDown) / 2 / eps;

            assertEquals(fdDelta, delta, 1e-6);

            double bsVanna = BlackFormulaRepository.vanna(f, k, t, adj.Value);
            double bsGamma = BlackFormulaRepository.gamma(f, k, t, adj.Value);

            double[] volD1 = new double[5];
//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[][] volD2 = new double[2][2];
            double[][] volD2 = RectangularArrays.ReturnRectangularDoubleArray(2, 2);
            FUNCTION.volatilityAdjoint2(f, k, t, sabrData, volD1, volD2);
            double d2Sigmad2Fwd = volD2[0][0];
            double gamma        = bsGamma + 2 * bsVanna * adj.getDerivative(1) + bsVega * d2Sigmad2Fwd;
            double fdGamma      = (priceUp + priceDown - 2 * price) / eps / eps;

            double d2Sigmad2FwdFD = (volUp + volDown - 2 * adj.Value) / eps / eps;

            assertEquals(d2Sigmad2FwdFD, d2Sigmad2Fwd, 1e-4);

            assertEquals(fdGamma, gamma, 1e-2);
        }
コード例 #22
0
        /// <summary>
        /// Computes the option price derivative with respect to the strike.
        /// <para>
        /// The price is SABR below the cut-off strike and extrapolated beyond.
        ///
        /// </para>
        /// </summary>
        /// <param name="strike">  the strike of the option </param>
        /// <param name="putCall">  whether the option is put or call </param>
        /// <returns> the option price derivative </returns>
        public double priceDerivativeStrike(double strike, PutCall putCall)
        {
            // Uses Hagan et al SABR function.
            if (strike <= cutOffStrike)
            {
                ValueDerivatives volatilityAdjoint = sabrFunction.volatilityAdjoint(forward, strike, timeToExpiry, sabrData);
                ValueDerivatives bsAdjoint         = BlackFormulaRepository.priceAdjoint(forward, strike, timeToExpiry, volatilityAdjoint.Value, putCall.Equals(PutCall.CALL));
                return(bsAdjoint.getDerivative(1) + bsAdjoint.getDerivative(3) * volatilityAdjoint.getDerivative(1));
            }
            // Uses extrapolation for call.
            double pDK = extrapolationDerivative(strike);

            if (putCall.Put)
            {     // Put by call/put parity
                pDK += 1.0;
            }
            return(pDK);
        }
コード例 #23
0
        //-------------------------------------------------------------------------
        public virtual void test_presentValueTheta()
        {
            CurrencyAmount computedRec = PRICER.presentValueTheta(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS);
            CurrencyAmount computedPay = PRICER.presentValueTheta(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS);
            double         forward     = SWAP_PRICER.parRate(RSWAP_REC, RATE_PROVIDER);
            double         annuityCash = SWAP_PRICER.LegPricer.annuityCash(RFIXED_LEG_REC, forward);
            double         expiry      = VOLS.relativeTime(SWAPTION_REC_LONG.Expiry);
            double         tenor       = VOLS.tenor(SETTLE, END);
            double         volatility  = SURFACE.zValue(expiry, tenor);
            double         settle      = ACT_ACT_ISDA.relativeYearFraction(VAL_DATE, SETTLE);
            double         df          = Math.Exp(-DSC_CURVE.yValue(settle) * settle);
            double         expectedRec = df * annuityCash * BlackFormulaRepository.driftlessTheta(forward, RATE, expiry, volatility);
            double         expectedPay = -df *annuityCash *BlackFormulaRepository.driftlessTheta(forward, RATE, expiry, volatility);

            assertEquals(computedRec.Currency, EUR);
            assertEquals(computedRec.Amount, expectedRec, NOTIONAL * TOL);
            assertEquals(computedPay.Currency, EUR);
            assertEquals(computedPay.Amount, expectedPay, NOTIONAL * TOL);
        }
        //-------------------------------------------------------------------------
        /// <summary>
        /// Calculates the Black theta of the foreign exchange vanilla option product.
        /// <para>
        /// The theta is the negative of the first derivative of <seealso cref="#price"/> with respect to time parameter
        /// in Black formula (the discounted driftless theta).
        ///
        /// </para>
        /// </summary>
        /// <param name="option">  the option product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="volatilities">  the Black volatility provider </param>
        /// <returns> the theta of the product </returns>
        public virtual double theta(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities)
        {
            double timeToExpiry = volatilities.relativeTime(option.Expiry);

            if (timeToExpiry <= 0d)
            {
                return(0d);
            }
            ResolvedFxSingle underlying     = option.Underlying;
            FxRate           forward        = fxPricer.forwardFxRate(underlying, ratesProvider);
            CurrencyPair     strikePair     = underlying.CurrencyPair;
            double           forwardRate    = forward.fxRate(strikePair);
            double           strikeRate     = option.Strike;
            double           volatility     = volatilities.volatility(strikePair, option.Expiry, strikeRate, forwardRate);
            double           fwdTheta       = BlackFormulaRepository.driftlessTheta(forwardRate, strikeRate, timeToExpiry, volatility);
            double           discountFactor = ratesProvider.discountFactor(option.CounterCurrency, underlying.PaymentDate);

            return(discountFactor * fwdTheta);
        }
コード例 #25
0
        //-------------------------------------------------------------------------
        /// <summary>
        /// Calculates the present value sensitivity of the foreign exchange vanilla option product.
        /// <para>
        /// The present value sensitivity of the product is the sensitivity of <seealso cref="#presentValue"/> to
        /// the underlying curves.
        /// </para>
        /// <para>
        /// The implied strikes and weights are fixed in this sensitivity computation.
        ///
        /// </para>
        /// </summary>
        /// <param name="option">  the option product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="volatilities">  the Black volatility provider </param>
        /// <returns> the present value curve sensitivity of the product </returns>
        public virtual PointSensitivityBuilder presentValueSensitivityRatesStickyStrike(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionSmileVolatilities volatilities)
        {
            validate(ratesProvider, volatilities);
            double timeToExpiry = volatilities.relativeTime(option.Expiry);

            if (timeToExpiry <= 0d)
            {
                return(PointSensitivityBuilder.none());
            }
            ResolvedFxSingle     underlyingFx = option.Underlying;
            Currency             ccyCounter   = option.CounterCurrency;
            double               df           = ratesProvider.discountFactor(ccyCounter, underlyingFx.PaymentDate);
            FxRate               forward      = fxPricer.forwardFxRate(underlyingFx, ratesProvider);
            CurrencyPair         currencyPair = underlyingFx.CurrencyPair;
            double               forwardRate  = forward.fxRate(currencyPair);
            double               strikeRate   = option.Strike;
            bool                 isCall       = option.PutCall.Call;
            SmileDeltaParameters smileAtTime  = volatilities.Smile.smileForExpiry(timeToExpiry);

            double[] strikes = smileAtTime.strike(forwardRate).toArray();
            double[] vols    = smileAtTime.Volatility.toArray();
            double   volAtm  = vols[1];

            double[] x        = vannaVolgaWeights(forwardRate, strikeRate, timeToExpiry, volAtm, strikes);
            double   priceFwd = BlackFormulaRepository.price(forwardRate, strikeRate, timeToExpiry, volAtm, isCall);
            double   deltaFwd = BlackFormulaRepository.delta(forwardRate, strikeRate, timeToExpiry, volAtm, isCall);

            for (int i = 0; i < 3; i += 2)
            {
                double priceFwdAtm   = BlackFormulaRepository.price(forwardRate, strikes[i], timeToExpiry, volAtm, isCall);
                double priceFwdSmile = BlackFormulaRepository.price(forwardRate, strikes[i], timeToExpiry, vols[i], isCall);
                priceFwd += x[i] * (priceFwdSmile - priceFwdAtm);
                double deltaFwdAtm   = BlackFormulaRepository.delta(forwardRate, strikes[i], timeToExpiry, volAtm, isCall);
                double deltaFwdSmile = BlackFormulaRepository.delta(forwardRate, strikes[i], timeToExpiry, vols[i], isCall);
                deltaFwd += x[i] * (deltaFwdSmile - deltaFwdAtm);
            }
            double signedNotional            = this.signedNotional(option);
            PointSensitivityBuilder dfSensi  = ratesProvider.discountFactors(ccyCounter).zeroRatePointSensitivity(underlyingFx.PaymentDate).multipliedBy(priceFwd * signedNotional);
            PointSensitivityBuilder fwdSensi = fxPricer.forwardFxRatePointSensitivity(option.PutCall.Call ? underlyingFx : underlyingFx.inverse(), ratesProvider).multipliedBy(df * deltaFwd * signedNotional);

            return(dfSensi.combinedWith(fwdSensi));
        }
コード例 #26
0
        private double[] vannaVolgaWeights(double forward, double strike, double timeToExpiry, double volATM, double[] strikesReference)
        {
            double lnk21 = Math.Log(strikesReference[1] / strikesReference[0]);
            double lnk31 = Math.Log(strikesReference[2] / strikesReference[0]);
            double lnk32 = Math.Log(strikesReference[2] / strikesReference[1]);

            double[] lnk = new double[3];
            for (int loopvv = 0; loopvv < 3; loopvv++)
            {
                lnk[loopvv] = Math.Log(strikesReference[loopvv] / strike);
            }
            double[] x        = new double[3];
            double   vega0    = BlackFormulaRepository.vega(forward, strikesReference[0], timeToExpiry, volATM);
            double   vegaFlat = BlackFormulaRepository.vega(forward, strike, timeToExpiry, volATM);
            double   vega2    = BlackFormulaRepository.vega(forward, strikesReference[2], timeToExpiry, volATM);

            x[0] = vegaFlat * lnk[1] * lnk[2] / (vega0 * lnk21 * lnk31);
            x[2] = vegaFlat * lnk[0] * lnk[1] / (vega2 * lnk31 * lnk32);
            return(x);
        }
        /// <summary>
        /// Tests the price for options in SABR model with extrapolation.
        /// </summary>
        public virtual void price()
        {
            double strikeIn        = 0.08;
            double strikeAt        = CUT_OFF_STRIKE;
            double strikeOut       = 0.12;
            double volatilityIn    = SABR_FUNCTION.volatility(FORWARD, strikeIn, TIME_TO_EXPIRY, SABR_DATA);
            double priceExpectedIn = BlackFormulaRepository.price(FORWARD, strikeIn, TIME_TO_EXPIRY, volatilityIn, true);
            double priceIn         = SABR_EXTRAPOLATION.price(strikeIn, PutCall.CALL);

            assertEquals(priceExpectedIn, priceIn, TOLERANCE_PRICE);
            double volatilityAt    = SABR_FUNCTION.volatility(FORWARD, strikeAt, TIME_TO_EXPIRY, SABR_DATA);
            double priceExpectedAt = BlackFormulaRepository.price(FORWARD, strikeAt, TIME_TO_EXPIRY, volatilityAt, true);
            double priceAt         = SABR_EXTRAPOLATION.price(strikeAt, PutCall.CALL);

            assertEquals(priceExpectedAt, priceAt, TOLERANCE_PRICE);
            double priceOut         = SABR_EXTRAPOLATION.price(strikeOut, PutCall.CALL);
            double priceExpectedOut = 5.427104E-5;     // From previous run

            assertEquals(priceExpectedOut, priceOut, TOLERANCE_PRICE);
        }
        //-------------------------------------------------------------------------
        public virtual void test_presentValueSensitivity()
        {
            PointSensitivities             point    = OPTION_TRADE_PRICER.presentValueSensitivityRates(OPTION_TRADE, RATE_PROVIDER, VOLS);
            CurrencyParameterSensitivities computed = RATE_PROVIDER.parameterSensitivity(point);
            double futurePrice    = FUTURE_PRICER.price(OPTION_PRODUCT.UnderlyingFuture, RATE_PROVIDER);
            double strike         = OPTION_PRODUCT.StrikePrice;
            double expiryTime     = ACT_365F.relativeYearFraction(VAL_DATE, OPTION_PRODUCT.ExpiryDate);
            double logMoneyness   = Math.Log(strike / futurePrice);
            double logMoneynessUp = Math.Log(strike / (futurePrice + EPS));
            double logMoneynessDw = Math.Log(strike / (futurePrice - EPS));
            double vol            = SURFACE.zValue(expiryTime, logMoneyness);
            double volUp          = SURFACE.zValue(expiryTime, logMoneynessUp);
            double volDw          = SURFACE.zValue(expiryTime, logMoneynessDw);
            double volSensi       = 0.5 * (volUp - volDw) / EPS;
            double vega           = BlackFormulaRepository.vega(futurePrice, strike, expiryTime, vol);
            CurrencyParameterSensitivities sensiVol = RATE_PROVIDER.parameterSensitivity(FUTURE_PRICER.priceSensitivity(OPTION_PRODUCT.UnderlyingFuture, RATE_PROVIDER)).multipliedBy(-vega * volSensi * NOTIONAL * QUANTITY);
            CurrencyParameterSensitivities expected = FD_CAL.sensitivity(RATE_PROVIDER, (p) => OPTION_TRADE_PRICER.presentValue(OPTION_TRADE, (p), VOLS, REFERENCE_PRICE));

            assertTrue(computed.equalWithTolerance(expected.combinedWith(sensiVol), 30d * EPS * NOTIONAL * QUANTITY));
        }
コード例 #29
0
        /// <summary>
        /// Upper barrier level is very high: knock-in is close to 0, knock-out is close to vanilla.
        /// </summary>
        public virtual void largeBarrierTest()
        {
            SimpleConstantContinuousBarrier upIn  = SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_IN, 1.0e4);
            SimpleConstantContinuousBarrier upOut = SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_OUT, 1.0e4);

            foreach (double strike in STRIKES)
            {
                // call
                double callVanilla = BlackFormulaRepository.price(FWD_FX, strike, EXPIRY_TIME, VOLATILITY, true) * DF_DOM;
                double callUpIn    = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, true, upIn);
                double callUpOut   = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, true, upOut);
                assertRelative(callUpIn, 0d);
                assertRelative(callUpOut, callVanilla);
                // put
                double putVanilla = BlackFormulaRepository.price(FWD_FX, strike, EXPIRY_TIME, VOLATILITY, false) * DF_DOM;
                double putUpIn    = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, false, upIn);
                double putUpOut   = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, false, upOut);
                assertRelative(putUpIn, 0d);
                assertRelative(putUpOut, putVanilla);
            }
        }
コード例 #30
0
 /// <summary>
 /// Check "in + out = vanilla" is satisfied.
 /// </summary>
 public virtual void inOutParity()
 {
     foreach (double strike in STRIKES)
     {
         // call
         double callVanilla = BlackFormulaRepository.price(FWD_FX, strike, EXPIRY_TIME, VOLATILITY, true) * DF_DOM;
         double callUpIn    = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, true, BARRIER_UP_IN);
         double callUpOut   = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, true, BARRIER_UP_OUT);
         double callDownIn  = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, true, BARRIER_DOWN_IN);
         double callDownOut = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, true, BARRIER_DOWN_OUT);
         assertRelative(callUpIn + callUpOut, callVanilla);
         assertRelative(callDownIn + callDownOut, callVanilla);
         // put
         double putVanilla = BlackFormulaRepository.price(FWD_FX, strike, EXPIRY_TIME, VOLATILITY, false) * DF_DOM;
         double putUpIn    = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, false, BARRIER_UP_IN);
         double putUpOut   = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, false, BARRIER_UP_OUT);
         double putDownIn  = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, false, BARRIER_DOWN_IN);
         double putDownOut = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, false, BARRIER_DOWN_OUT);
         assertRelative(putUpIn + putUpOut, putVanilla);
         assertRelative(putDownIn + putDownOut, putVanilla);
     }
 }