// Test getDelta, getGamma and getVega
        public virtual void greeksTest()
        {
            double tol = 1.0e-12;
            double eps = 1.0e-5;

            EuropeanVanillaOption[] options = new EuropeanVanillaOption[] { ITM_CALL, ITM_PUT, OTM_CALL, OTM_PUT, ATM_CALL, ATM_PUT };
            foreach (EuropeanVanillaOption option in options)
            {
                // consistency with getPriceFunction for first order derivatives
                ValueDerivatives price = FUNCTION.getPriceAdjoint(option, VOL_DATA);
                double           delta = FUNCTION.getDelta(option, VOL_DATA);
                double           vega  = FUNCTION.getVega(option, VOL_DATA);
                assertEquals(price.getDerivative(0), delta, tol);
                assertEquals(price.getDerivative(1), vega, tol);

                // testing second order derivative against finite difference approximation
                NormalFunctionData dataUp  = NormalFunctionData.of(F + eps, DF, SIGMA);
                NormalFunctionData dataDw  = NormalFunctionData.of(F - eps, DF, SIGMA);
                double             deltaUp = FUNCTION.getDelta(option, dataUp);
                double             deltaDw = FUNCTION.getDelta(option, dataDw);
                double             @ref    = 0.5 * (deltaUp - deltaDw) / eps;
                double             gamma   = FUNCTION.getGamma(option, VOL_DATA);
                assertEquals(gamma, @ref, eps);

                EuropeanVanillaOption optionUp = EuropeanVanillaOption.of(option.Strike, T + eps, option.PutCall);
                EuropeanVanillaOption optionDw = EuropeanVanillaOption.of(option.Strike, T - eps, option.PutCall);
                double priceTimeUp             = FUNCTION.getPriceFunction(optionUp).apply(VOL_DATA);
                double priceTimeDw             = FUNCTION.getPriceFunction(optionDw).apply(VOL_DATA);
                @ref = -0.5 * (priceTimeUp - priceTimeDw) / eps;
                double theta = FUNCTION.getTheta(option, VOL_DATA);
                assertEquals(theta, @ref, eps);
            }
        }
        /// <summary>
        /// Tests the price derivative with respect to forward for options in SABR model with extrapolation.
        /// </summary>
        public virtual void priceDerivativeStrikePut()
        {
            double strikeIn  = 0.08;
            double strikeAt  = CUT_OFF_STRIKE;
            double strikeOut = 0.12;
            double shiftK    = 0.000001;
            EuropeanVanillaOption optionIn    = EuropeanVanillaOption.of(strikeIn, TIME_TO_EXPIRY, PutCall.PUT);
            EuropeanVanillaOption optionAt    = EuropeanVanillaOption.of(strikeAt, TIME_TO_EXPIRY, PutCall.PUT);
            EuropeanVanillaOption optionOut   = EuropeanVanillaOption.of(strikeOut, TIME_TO_EXPIRY, PutCall.PUT);
            EuropeanVanillaOption optionInKP  = EuropeanVanillaOption.of(strikeIn + shiftK, TIME_TO_EXPIRY, PutCall.PUT);
            EuropeanVanillaOption optionAtKP  = EuropeanVanillaOption.of(strikeAt + shiftK, TIME_TO_EXPIRY, PutCall.PUT);
            EuropeanVanillaOption optionOutKP = EuropeanVanillaOption.of(strikeOut + shiftK, TIME_TO_EXPIRY, PutCall.PUT);
            // Below cut-off strike
            double priceIn           = SABR_EXTRAPOLATION.price(optionIn.Strike, optionIn.PutCall);
            double priceInKP         = SABR_EXTRAPOLATION.price(optionInKP.Strike, optionInKP.PutCall);
            double priceInDK         = SABR_EXTRAPOLATION.priceDerivativeStrike(optionIn.Strike, optionIn.PutCall);
            double priceInDFExpected = (priceInKP - priceIn) / shiftK;

            assertEquals(priceInDFExpected, priceInDK, 1E-5);
            // At cut-off strike
            double priceAt           = SABR_EXTRAPOLATION.price(optionAt.Strike, optionAt.PutCall);
            double priceAtKP         = SABR_EXTRAPOLATION.price(optionAtKP.Strike, optionAtKP.PutCall);
            double priceAtDK         = SABR_EXTRAPOLATION.priceDerivativeStrike(optionAt.Strike, optionAt.PutCall);
            double priceAtDFExpected = (priceAtKP - priceAt) / shiftK;

            assertEquals(priceAtDFExpected, priceAtDK, 1E-5);
            // At cut-off strike
            double priceOut           = SABR_EXTRAPOLATION.price(optionOut.Strike, optionOut.PutCall);
            double priceOutKP         = SABR_EXTRAPOLATION.price(optionOutKP.Strike, optionOutKP.PutCall);
            double priceOutDK         = SABR_EXTRAPOLATION.priceDerivativeStrike(optionOut.Strike, optionOut.PutCall);
            double priceOutDFExpected = (priceOutKP - priceOut) / shiftK;

            assertEquals(priceOutDFExpected, priceOutDK, 1E-5);
        }
        /// <summary>
        /// Tests that the smile and its derivatives are smooth enough in SABR model with extrapolation.
        /// </summary>
        public virtual void smileSmooth()
        {
            int    nbPts       = 100;
            double rangeStrike = 0.02;

            double[] price  = new double[nbPts + 1];
            double[] strike = new double[nbPts + 1];
            for (int looppts = 0; looppts <= nbPts; looppts++)
            {
                strike[looppts] = CUT_OFF_STRIKE - rangeStrike + looppts * 2.0 * rangeStrike / nbPts;
                EuropeanVanillaOption option = EuropeanVanillaOption.of(strike[looppts], TIME_TO_EXPIRY, PutCall.CALL);
                price[looppts] = SABR_EXTRAPOLATION.price(option.Strike, option.PutCall);
            }
            double[] priceD  = new double[nbPts];
            double[] priceD2 = new double[nbPts];
            for (int looppts = 1; looppts < nbPts; looppts++)
            {
                priceD[looppts]  = (price[looppts + 1] - price[looppts - 1]) / (strike[looppts + 1] - strike[looppts - 1]);
                priceD2[looppts] = (price[looppts + 1] + price[looppts - 1] - 2 * price[looppts]) / ((strike[looppts + 1] - strike[looppts]) * (strike[looppts + 1] - strike[looppts]));
            }
            for (int looppts = 2; looppts < nbPts; looppts++)
            {
                assertEquals(priceD[looppts - 1], priceD[looppts], 1.5E-3);
                assertEquals(priceD2[looppts - 1], priceD2[looppts], 1.5E-1);
            }
        }
        public virtual void test_of()
        {
            EuropeanVanillaOption test = EuropeanVanillaOption.of(STRIKE, TIME, CALL);

            assertEquals(test.Strike, STRIKE, 0d);
            assertEquals(test.TimeToExpiry, TIME, 0d);
            assertEquals(test.PutCall, CALL);
            assertTrue(test.Call);
        }
        //-------------------------------------------------------------------------
        public virtual void coverage()
        {
            EuropeanVanillaOption test = EuropeanVanillaOption.of(STRIKE, TIME, CALL);

            coverImmutableBean(test);
            EuropeanVanillaOption test2 = EuropeanVanillaOption.of(110, 0.6, PUT);

            coverBeanEquals(test, test2);
        }
Esempio n. 6
0
        public virtual void intrinsic_price()
        {
            NormalFunctionData    data    = NormalFunctionData.of(1.0, 1.0, 0.01);
            EuropeanVanillaOption option1 = EuropeanVanillaOption.of(0.5, 1.0, PutCall.CALL);

            assertThrowsIllegalArg(() => impliedVolatility(data, option1, 1e-6));
            EuropeanVanillaOption option2 = EuropeanVanillaOption.of(1.5, 1.0, PutCall.PUT);

            assertThrowsIllegalArg(() => impliedVolatility(data, option2, 1e-6));
        }
        /// <summary>
        /// Tests that the smile and its derivatives are smooth enough in SABR model with extrapolation
        /// for different time to maturity (in particular close to maturity).
        /// </summary>
        public virtual void smileSmoothMaturity()
        {
            int nbPts = 100;

            double[] timeToExpiry = new double[] { 2.0, 1.0, 0.50, 0.25, 1.0d / 12.0d, 1.0d / 52.0d, 1.0d / 365d };
            int      nbTTM        = timeToExpiry.Length;
            double   rangeStrike  = 0.02;

            double[] strike = new double[nbPts + 1];
            for (int looppts = 0; looppts <= nbPts; looppts++)
            {
                strike[looppts] = CUT_OFF_STRIKE - rangeStrike + looppts * 2.0 * rangeStrike / nbPts;
            }
            SabrExtrapolationRightFunction[] sabrExtrapolation = new SabrExtrapolationRightFunction[nbTTM];
            for (int loopmat = 0; loopmat < nbTTM; loopmat++)
            {
                sabrExtrapolation[loopmat] = SabrExtrapolationRightFunction.of(FORWARD, timeToExpiry[loopmat], SABR_DATA, CUT_OFF_STRIKE, MU);
            }
//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[][] price = new double[nbTTM][nbPts + 1];
            double[][] price = RectangularArrays.ReturnRectangularDoubleArray(nbTTM, nbPts + 1);
            for (int loopmat = 0; loopmat < nbTTM; loopmat++)
            {
                for (int looppts = 0; looppts <= nbPts; looppts++)
                {
                    EuropeanVanillaOption option = EuropeanVanillaOption.of(strike[looppts], timeToExpiry[loopmat], PutCall.CALL);
                    price[loopmat][looppts] = sabrExtrapolation[loopmat].price(option.Strike, option.PutCall);
                }
            }
//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[][] priceD = new double[nbTTM][nbPts - 1];
            double[][] priceD = RectangularArrays.ReturnRectangularDoubleArray(nbTTM, nbPts - 1);
//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[][] priceD2 = new double[nbTTM][nbPts - 1];
            double[][] priceD2 = RectangularArrays.ReturnRectangularDoubleArray(nbTTM, nbPts - 1);
            for (int loopmat = 0; loopmat < nbTTM; loopmat++)
            {
                for (int looppts = 1; looppts < nbPts; looppts++)
                {
                    priceD[loopmat][looppts - 1]  = (price[loopmat][looppts + 1] - price[loopmat][looppts - 1]) / (strike[looppts + 1] - strike[looppts - 1]);
                    priceD2[loopmat][looppts - 1] = (price[loopmat][looppts + 1] + price[loopmat][looppts - 1] - 2 * price[loopmat][looppts]) / ((strike[looppts + 1] - strike[looppts]) * (strike[looppts + 1] - strike[looppts]));
                }
            }
            double epsDensity = 1.0E-20;     // Conditions are not checked when the density is very small.

            for (int loopmat = 0; loopmat < nbTTM; loopmat++)
            {
                for (int looppts = 1; looppts < nbPts - 1; looppts++)
                {
                    assertTrue(((priceD[loopmat][looppts] / priceD[loopmat][looppts - 1] < 1) && (priceD[loopmat][looppts] / priceD[loopmat][looppts - 1] > 0.50)) || Math.Abs(priceD2[loopmat][looppts]) < epsDensity);
                    assertTrue(priceD2[loopmat][looppts] > 0 || Math.Abs(priceD2[loopmat][looppts]) < epsDensity);
                    assertTrue((priceD2[loopmat][looppts] / priceD2[loopmat][looppts - 1] < 1 && priceD2[loopmat][looppts] / priceD2[loopmat][looppts - 1] > 0.50) || Math.Abs(priceD2[loopmat][looppts]) < epsDensity);
                }
            }
        }
Esempio n. 8
0
 static NormalFormulaRepositoryImpliedVolatilityTest()
 {
     PRICES = new double[N];
     SIGMA  = new double[N];
     DATA   = new NormalFunctionData[N];
     for (int i = 0; i < N; i++)
     {
         STRIKES[i]     = FORWARD + (-N / 2 + i) * 10;
         STRIKES_ATM[i] = FORWARD + (-0.5d * N + i) / 100.0d;
         SIGMA[i]       = FORWARD * (0.05 + 4.0 * i / 100.0);
         SIGMA_BLACK[i] = 0.20 + i / 100.0d;
         DATA[i]        = NormalFunctionData.of(FORWARD, DF, SIGMA[i]);
         OPTIONS[i]     = EuropeanVanillaOption.of(STRIKES[i], T, PutCall.CALL);
         PRICES[i]      = FUNCTION.getPriceFunction(OPTIONS[i]).apply(DATA[i]);
     }
 }
        // Testing the branch for sigmaRootT < 1e-16
        public virtual void smallParameterGreeksTest()
        {
            double             eps       = 1.0e-5;
            NormalFunctionData dataVolUp = NormalFunctionData.of(F, DF, eps);
            NormalFunctionData dataFwUp  = NormalFunctionData.of(F + eps, DF, 0.0);
            NormalFunctionData dataFwDw  = NormalFunctionData.of(F - eps, DF, 0.0);

            EuropeanVanillaOption[] options = new EuropeanVanillaOption[] { ITM_CALL, ITM_PUT, OTM_CALL, OTM_PUT, ATM_CALL, ATM_PUT };
            foreach (EuropeanVanillaOption option in options)
            {
                double delta    = FUNCTION.getDelta(option, ZERO_VOL_DATA);
                double priceUp  = FUNCTION.getPriceFunction(option).apply(dataFwUp);
                double priceDw  = FUNCTION.getPriceFunction(option).apply(dataFwDw);
                double refDelta = 0.5 * (priceUp - priceDw) / eps;
                assertEquals(delta, refDelta, eps);

                double vega       = FUNCTION.getVega(option, ZERO_VOL_DATA);
                double priceVolUp = FUNCTION.getPriceFunction(option).apply(dataVolUp);
                double price      = FUNCTION.getPriceFunction(option).apply(ZERO_VOL_DATA);
                double refVega    = (priceVolUp - price) / eps;
                assertEquals(vega, refVega, eps);

                double gamma    = FUNCTION.getGamma(option, ZERO_VOL_DATA);
                double deltaUp  = FUNCTION.getDelta(option, dataFwUp);
                double deltaDw  = FUNCTION.getDelta(option, dataFwDw);
                double refGamma = 0.5 * (deltaUp - deltaDw) / eps;
                if (Math.Abs(refGamma) > 0.1 / eps)
                {   // infinity handled
                    assertTrue(double.IsInfinity(gamma));
                }
                else
                {
                    assertEquals(gamma, refGamma, eps);
                }

                EuropeanVanillaOption optionUp = EuropeanVanillaOption.of(option.Strike, T + eps, option.PutCall);
                EuropeanVanillaOption optionDw = EuropeanVanillaOption.of(option.Strike, T - eps, option.PutCall);
                double priceTimeUp             = FUNCTION.getPriceFunction(optionUp).apply(ZERO_VOL_DATA);
                double priceTimeDw             = FUNCTION.getPriceFunction(optionDw).apply(ZERO_VOL_DATA);
                double refTheta = -0.5 * (priceTimeUp - priceTimeDw) / eps;
                double theta    = FUNCTION.getTheta(option, ZERO_VOL_DATA);
                assertEquals(theta, refTheta, eps);
            }
        }
        /// <summary>
        /// Tests the price derivative with respect to forward for options in SABR model with extrapolation.
        /// </summary>
        public virtual void priceDerivativeForwardPut()
        {
            SabrExtrapolationRightFunction func = SabrExtrapolationRightFunction.of(FORWARD, SABR_DATA, CUT_OFF_STRIKE, TIME_TO_EXPIRY, MU, SabrHaganVolatilityFunctionProvider.DEFAULT);
            double strikeIn  = 0.08;
            double strikeAt  = CUT_OFF_STRIKE;
            double strikeOut = 0.12;
            EuropeanVanillaOption optionIn  = EuropeanVanillaOption.of(strikeIn, TIME_TO_EXPIRY, PutCall.PUT);
            EuropeanVanillaOption optionAt  = EuropeanVanillaOption.of(strikeAt, TIME_TO_EXPIRY, PutCall.PUT);
            EuropeanVanillaOption optionOut = EuropeanVanillaOption.of(strikeOut, TIME_TO_EXPIRY, PutCall.PUT);
            double          shiftF          = 0.000001;
            SabrFormulaData sabrDataFP      = SabrFormulaData.of(ALPHA, BETA, RHO, NU);
            SabrExtrapolationRightFunction sabrExtrapolationFP = SabrExtrapolationRightFunction.of(FORWARD + shiftF, TIME_TO_EXPIRY, sabrDataFP, CUT_OFF_STRIKE, MU);
            // Below cut-off strike
            double priceIn           = func.price(optionIn.Strike, optionIn.PutCall);
            double priceInFP         = sabrExtrapolationFP.price(optionIn.Strike, optionIn.PutCall);
            double priceInDF         = func.priceDerivativeForward(optionIn.Strike, optionIn.PutCall);
            double priceInDFExpected = (priceInFP - priceIn) / shiftF;

            assertEquals(priceInDFExpected, priceInDF, 1E-5);
            // At cut-off strike
            double priceAt           = func.price(optionAt.Strike, optionAt.PutCall);
            double priceAtFP         = sabrExtrapolationFP.price(optionAt.Strike, optionAt.PutCall);
            double priceAtDF         = func.priceDerivativeForward(optionAt.Strike, optionAt.PutCall);
            double priceAtDFExpected = (priceAtFP - priceAt) / shiftF;

            assertEquals(priceAtDFExpected, priceAtDF, 1E-6);
            // Above cut-off strike
            double priceOut           = func.price(optionOut.Strike, optionOut.PutCall);
            double priceOutFP         = sabrExtrapolationFP.price(optionOut.Strike, optionOut.PutCall);
            double priceOutDF         = func.priceDerivativeForward(optionOut.Strike, optionOut.PutCall);
            double priceOutDFExpected = (priceOutFP - priceOut) / shiftF;

            assertEquals(priceOutDFExpected, priceOutDF, 1E-5);
            double[] abc           = func.Parameter;
            double[] abcDF         = func.ParameterDerivativeForward;
            double[] abcFP         = sabrExtrapolationFP.Parameter;
            double[] abcDFExpected = new double[3];
            for (int loopparam = 0; loopparam < 3; loopparam++)
            {
                abcDFExpected[loopparam] = (abcFP[loopparam] - abc[loopparam]) / shiftF;
                assertEquals(1.0, abcDFExpected[loopparam] / abcDF[loopparam], 5E-2);
            }
        }
        public virtual void testPriceAdjoint()
        {
            // Price
            double           price        = FUNCTION.getPriceFunction(ITM_CALL).apply(VOL_DATA);
            ValueDerivatives priceAdjoint = FUNCTION.getPriceAdjoint(ITM_CALL, VOL_DATA);

            assertEquals(priceAdjoint.Value, price, 1E-10);
            // Price with 0 volatility
            double           price0        = FUNCTION.getPriceFunction(ITM_CALL).apply(ZERO_VOL_DATA);
            ValueDerivatives price0Adjoint = FUNCTION.getPriceAdjoint(ITM_CALL, ZERO_VOL_DATA);

            assertEquals(price0Adjoint.Value, price0, 1E-10);
            // Derivative forward.
            double             deltaF         = 0.01;
            NormalFunctionData dataFP         = NormalFunctionData.of(F + deltaF, DF, SIGMA);
            NormalFunctionData dataFM         = NormalFunctionData.of(F - deltaF, DF, SIGMA);
            double             priceFP        = FUNCTION.getPriceFunction(ITM_CALL).apply(dataFP);
            double             priceFM        = FUNCTION.getPriceFunction(ITM_CALL).apply(dataFM);
            double             derivativeF_FD = (priceFP - priceFM) / (2 * deltaF);

            assertEquals(priceAdjoint.getDerivative(0), derivativeF_FD, 1E-7);
            // Derivative strike.
            double deltaK = 0.01;
            EuropeanVanillaOption optionKP = EuropeanVanillaOption.of(F - DELTA + deltaK, T, CALL);
            EuropeanVanillaOption optionKM = EuropeanVanillaOption.of(F - DELTA - deltaK, T, CALL);
            double priceKP        = FUNCTION.getPriceFunction(optionKP).apply(VOL_DATA);
            double priceKM        = FUNCTION.getPriceFunction(optionKM).apply(VOL_DATA);
            double derivativeK_FD = (priceKP - priceKM) / (2 * deltaK);

            assertEquals(priceAdjoint.getDerivative(2), derivativeK_FD, 1E-7);
            // Derivative volatility.
            double             deltaV         = 0.0001;
            NormalFunctionData dataVP         = NormalFunctionData.of(F, DF, SIGMA + deltaV);
            NormalFunctionData dataVM         = NormalFunctionData.of(F, DF, SIGMA - deltaV);
            double             priceVP        = FUNCTION.getPriceFunction(ITM_CALL).apply(dataVP);
            double             priceVM        = FUNCTION.getPriceFunction(ITM_CALL).apply(dataVM);
            double             derivativeV_FD = (priceVP - priceVM) / (2 * deltaV);

            assertEquals(priceAdjoint.getDerivative(1), derivativeV_FD, 1E-6);
        }
        /// <summary>
        /// Tests the price put/call parity for options in SABR model with extrapolation.
        /// </summary>
        public virtual void pricePutCallParity()
        {
            double strikeIn               = 0.08;
            double strikeAt               = CUT_OFF_STRIKE;
            double strikeOut              = 0.12;
            EuropeanVanillaOption callIn  = EuropeanVanillaOption.of(strikeIn, TIME_TO_EXPIRY, PutCall.CALL);
            EuropeanVanillaOption putIn   = EuropeanVanillaOption.of(strikeIn, TIME_TO_EXPIRY, PutCall.PUT);
            EuropeanVanillaOption callAt  = EuropeanVanillaOption.of(strikeAt, TIME_TO_EXPIRY, PutCall.CALL);
            EuropeanVanillaOption putAt   = EuropeanVanillaOption.of(strikeAt, TIME_TO_EXPIRY, PutCall.PUT);
            EuropeanVanillaOption callOut = EuropeanVanillaOption.of(strikeOut, TIME_TO_EXPIRY, PutCall.CALL);
            EuropeanVanillaOption putOut  = EuropeanVanillaOption.of(strikeOut, TIME_TO_EXPIRY, PutCall.PUT);
            double priceCallIn            = SABR_EXTRAPOLATION.price(callIn.Strike, callIn.PutCall);
            double pricePutIn             = SABR_EXTRAPOLATION.price(putIn.Strike, putIn.PutCall);

            assertEquals(FORWARD - strikeIn, priceCallIn - pricePutIn, TOLERANCE_PRICE);
            double priceCallAt = SABR_EXTRAPOLATION.price(callAt.Strike, callAt.PutCall);
            double pricePutAt  = SABR_EXTRAPOLATION.price(putAt.Strike, putAt.PutCall);

            assertEquals(FORWARD - strikeAt, priceCallAt - pricePutAt, TOLERANCE_PRICE);
            double priceCallOut = SABR_EXTRAPOLATION.price(callOut.Strike, callOut.PutCall);
            double pricePutOut  = SABR_EXTRAPOLATION.price(putOut.Strike, putOut.PutCall);

            assertEquals(FORWARD - strikeOut, priceCallOut - pricePutOut, TOLERANCE_PRICE);
        }
        public virtual void test_serialization()
        {
            EuropeanVanillaOption test = EuropeanVanillaOption.of(STRIKE, TIME, CALL);

            assertSerialization(test);
        }
        /// <summary>
        /// Tests the price derivative with respect to forward for options in SABR model with extrapolation.
        /// </summary>
        public virtual void priceDerivativeSabrPut()
        {
            SabrExtrapolationRightFunction func = SabrExtrapolationRightFunction.of(FORWARD, SABR_DATA, CUT_OFF_STRIKE, TIME_TO_EXPIRY, MU, SabrHaganVolatilityFunctionProvider.DEFAULT);
            double strikeIn  = 0.08;
            double strikeAt  = CUT_OFF_STRIKE;
            double strikeOut = 0.12;
            EuropeanVanillaOption optionIn  = EuropeanVanillaOption.of(strikeIn, TIME_TO_EXPIRY, PutCall.PUT);
            EuropeanVanillaOption optionAt  = EuropeanVanillaOption.of(strikeAt, TIME_TO_EXPIRY, PutCall.PUT);
            EuropeanVanillaOption optionOut = EuropeanVanillaOption.of(strikeOut, TIME_TO_EXPIRY, PutCall.PUT);
            double          shift           = 0.000001;
            SabrFormulaData sabrDataAP      = SabrFormulaData.of(ALPHA + shift, BETA, RHO, NU);
            SabrFormulaData sabrDataBP      = SabrFormulaData.of(ALPHA, BETA + shift, RHO, NU);
            SabrFormulaData sabrDataRP      = SabrFormulaData.of(ALPHA, BETA, RHO + shift, NU);
            SabrFormulaData sabrDataNP      = SabrFormulaData.of(ALPHA, BETA, RHO, NU + shift);
            SabrExtrapolationRightFunction sabrExtrapolationAP = SabrExtrapolationRightFunction.of(FORWARD, TIME_TO_EXPIRY, sabrDataAP, CUT_OFF_STRIKE, MU);
            SabrExtrapolationRightFunction sabrExtrapolationBP = SabrExtrapolationRightFunction.of(FORWARD, TIME_TO_EXPIRY, sabrDataBP, CUT_OFF_STRIKE, MU);
            SabrExtrapolationRightFunction sabrExtrapolationRP = SabrExtrapolationRightFunction.of(FORWARD, TIME_TO_EXPIRY, sabrDataRP, CUT_OFF_STRIKE, MU);
            SabrExtrapolationRightFunction sabrExtrapolationNP = SabrExtrapolationRightFunction.of(FORWARD, TIME_TO_EXPIRY, sabrDataNP, CUT_OFF_STRIKE, MU);
            // Below cut-off strike
            double priceInExpected = func.price(optionIn.Strike, optionIn.PutCall);

            double[] priceInPP = new double[4];
            priceInPP[0] = sabrExtrapolationAP.price(optionIn.Strike, optionIn.PutCall);
            priceInPP[1] = sabrExtrapolationBP.price(optionIn.Strike, optionIn.PutCall);
            priceInPP[2] = sabrExtrapolationRP.price(optionIn.Strike, optionIn.PutCall);
            priceInPP[3] = sabrExtrapolationNP.price(optionIn.Strike, optionIn.PutCall);
            ValueDerivatives resIn   = func.priceAdjointSabr(optionIn.Strike, optionIn.PutCall);
            double           priceIn = resIn.Value;

            double[] priceInDsabr = resIn.Derivatives.toArray();
            assertEquals(priceInExpected, priceIn, TOLERANCE_PRICE);
            double[] priceInDsabrExpected = new double[4];
            for (int loopparam = 0; loopparam < 3; loopparam++)
            {
                priceInDsabrExpected[loopparam] = (priceInPP[loopparam] - priceIn) / shift;
                assertEquals(priceInDsabrExpected[loopparam], priceInDsabr[loopparam], 1E-5);
            }
            // At cut-off strike
            double priceAtExpected = func.price(optionAt.Strike, optionAt.PutCall);

            double[] priceAtPP = new double[4];
            priceAtPP[0] = sabrExtrapolationAP.price(optionAt.Strike, optionAt.PutCall);
            priceAtPP[1] = sabrExtrapolationBP.price(optionAt.Strike, optionAt.PutCall);
            priceAtPP[2] = sabrExtrapolationRP.price(optionAt.Strike, optionAt.PutCall);
            priceAtPP[3] = sabrExtrapolationNP.price(optionAt.Strike, optionAt.PutCall);
            ValueDerivatives resAt   = func.priceAdjointSabr(optionAt.Strike, optionAt.PutCall);
            double           priceAt = resAt.Value;

            double[] priceAtDsabr = resAt.Derivatives.toArray();
            assertEquals(priceAtExpected, priceAt, TOLERANCE_PRICE);
            double[] priceAtDsabrExpected = new double[4];
            for (int loopparam = 0; loopparam < 3; loopparam++)
            {
                priceAtDsabrExpected[loopparam] = (priceAtPP[loopparam] - priceAt) / shift;
                assertEquals(priceAtDsabrExpected[loopparam], priceAtDsabr[loopparam], 1E-5);
            }
            // Above cut-off strike
            double priceOutExpected = func.price(optionOut.Strike, optionOut.PutCall);

            double[] priceOutPP = new double[4];
            priceOutPP[0] = sabrExtrapolationAP.price(optionOut.Strike, optionOut.PutCall);
            priceOutPP[1] = sabrExtrapolationBP.price(optionOut.Strike, optionOut.PutCall);
            priceOutPP[2] = sabrExtrapolationRP.price(optionOut.Strike, optionOut.PutCall);
            priceOutPP[3] = sabrExtrapolationNP.price(optionOut.Strike, optionOut.PutCall);
            ValueDerivatives resOut   = func.priceAdjointSabr(optionOut.Strike, optionOut.PutCall);
            double           priceOut = resOut.Value;

            double[] priceOutDsabr = resOut.Derivatives.toArray();
            assertEquals(priceOutExpected, priceOut, TOLERANCE_PRICE);
            double[]   abc   = func.Parameter;
            double[][] abcDP = func.ParameterDerivativeSabr;
//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[][] abcPP = new double[4][3];
            double[][] abcPP = RectangularArrays.ReturnRectangularDoubleArray(4, 3);
            abcPP[0] = sabrExtrapolationAP.Parameter;
            abcPP[1] = sabrExtrapolationBP.Parameter;
            abcPP[2] = sabrExtrapolationRP.Parameter;
            abcPP[3] = sabrExtrapolationNP.Parameter;
//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[][] abcDPExpected = new double[4][3];
            double[][] abcDPExpected = RectangularArrays.ReturnRectangularDoubleArray(4, 3);
            for (int loopparam = 0; loopparam < 4; loopparam++)
            {
                for (int loopabc = 0; loopabc < 3; loopabc++)
                {
                    abcDPExpected[loopparam][loopabc] = (abcPP[loopparam][loopabc] - abc[loopabc]) / shift;
                    assertEquals(1.0, abcDPExpected[loopparam][loopabc] / abcDP[loopparam][loopabc], 5.0E-2);
                }
            }
            double[] priceOutDsabrExpected = new double[4];
            for (int loopparam = 0; loopparam < 4; loopparam++)
            {
                priceOutDsabrExpected[loopparam] = (priceOutPP[loopparam] - priceOut) / shift;
                assertEquals(1.0, priceOutDsabrExpected[loopparam] / priceOutDsabr[loopparam], 4.0E-4);
            }
        }
 public virtual void testNegativeTime()
 {
     assertThrowsIllegalArg(() => EuropeanVanillaOption.of(STRIKE, -TIME, CALL));
 }
Esempio n. 16
0
        /// <summary>
        /// Regression to 2.x, including rebate.
        /// </summary>
        public virtual void adjointPriceRegression()
        {
            BlackOneTouchCashPriceFormulaRepository rebate = new BlackOneTouchCashPriceFormulaRepository();

            double[]   priceDIExp       = new double[] { 6.625939880275156, 8.17524397035564, 3.51889794875554, 16.046696834562567, 10.70082805329517, 4.016261046580751 };
            double[]   priceDOExp       = new double[] { 16.801234633074746, 1.2809481492685348, 11.695029389570358, 1.9796398042263066, 21.122005303422565, 1.2480461457697478 };
            double[]   priceUIExp       = new double[] { 21.738904060619003, 5.660922675994705, 13.534230659666587, 12.751249399664466, 30.003917380997216, 2.454685902906281 };
            double[]   priceUOExp       = new double[] { 1.8022701280119453, 3.909269118910516, 1.7936963539403596, 5.389086914405454, 1.9329156510015661, 2.9236209647252656 };
            double[][] derivativesDIExp = new double[][]
            {
                new double[] { -0.256723218835973, -0.21326378136855229, -23.23617273082793, 53.887600866294676, 58.707263782832555 },
                new double[] { -0.22502757956883834, 0.3356609584177749, -28.669348717959508, -89.71793740640288, 57.79705127007245 },
                new double[] { -0.13240455064001755, -0.10777900727966121, -12.34024486138928, 37.506678277403935, 41.63892946136302 },
                new double[] { -0.42819609658445323, 0.441145732506666, -56.273347803397485, -151.04122279937647, 78.46755307304 },
                new double[] { -0.38528948548712183, -0.33466444779640325, -37.52619152936393, 62.57455743118484, 68.36140924884158 },
                new double[] { -0.10797845731130963, 0.21426029198992397, -14.08442230033797, -47.32420873845068, 39.147069642753685 }
            };
            double[][] derivativesDOExp = new double[][]
            {
                new double[] { 0.925317598744783, -0.2806575880039709, -55.697543854725964, 194.462195344832, 3.192368381065041 },
                new double[] { -0.03864414399539151, 0.009587256919136517, -1.270237829323396, -5.21052475720073, 4.102580893825152 },
                new double[] { 0.6324628371075294, -0.22479677856150546, -37.79085149394349, 148.7848961295844, 31.79584488974962 },
                new double[] { -0.004011720421074989, 0.06544806636160204, -3.7204441809560977, -5.9454611683655045, -5.032778721927358 },
                new double[] { 1.1693201681318741, -0.29024484492310754, -70.84983552060324, 228.28109929421754, -24.681781274058867 },
                new double[] { -0.04025696351697804, 0.0, -1.1548554608892951, -5.098392910877228, 4.53255833202904 }
            };
            double[][] derivativesUIExp = new double[][]
            {
                new double[] { 0.6472001227436213, -0.49131423321491496, -76.23506081532145, 247.30828672024398, 60.930906232993976 },
                new double[] { 0.15101969748879138, 0.2734357730161942, -19.852002808967725, -65.3919684893132, 53.213862714176926 },
                new double[] { 0.4769152573039112, -0.33257578584116665, -47.46250751883076, 185.24241099218733, 72.3408333224538 },
                new double[] { 0.28724757364329634, 0.43217422038994247, -44.716710223480845, -110.92464376467034, 67.97645289437169 },
                new double[] { 0.7893004079366213, -0.6080809040345517, -105.21921711692173, 290.19622455207696, 44.461552265540746 },
                new double[] { 0.06323542648613031, 0.15666910219655739, -8.608213577315155, -34.903930997004814, 34.230011428672505 }
            };
            double[][] derivativesUOExp = new double[][]
            {
                new double[] { 0.03976906121488867, -0.0026071361576082536, -0.590128468837802, 1.9384002530437727, 0.40226173936432547 },
                new double[] { -0.3963166170033215, 0.07181244232071722, -7.979056436920486, -28.639602912129345, 8.119305258181384 },
                new double[] { 0.041517833213300284, 0.0, -0.5600615351073366, 1.946054176962064, 0.5274768371195269 },
                new double[] { -0.7010805865991248, 0.07441957847832553, -13.168554459478084, -45.16514944091054, 4.891857265201665 },
                new double[] { 0.013105078757830808, -0.016828388684959006, -1.0482826316507563, 1.5563229354864467, -1.3483884822973111 },
                new double[] { -0.19309604326471816, 0.05759118979336658, -4.522536882517435, -16.621779890162028, 8.88315235457093 }
            };

            EuropeanVanillaOption[] options = new EuropeanVanillaOption[] { EuropeanVanillaOption.of(STRIKE_MID, EXPIRY_TIME, PutCall.CALL), EuropeanVanillaOption.of(STRIKE_MID, EXPIRY_TIME, PutCall.PUT), EuropeanVanillaOption.of(STRIKE_HIGH, EXPIRY_TIME, PutCall.CALL), EuropeanVanillaOption.of(STRIKE_HIGH, EXPIRY_TIME, PutCall.PUT), EuropeanVanillaOption.of(STRIKE_LOW, EXPIRY_TIME, PutCall.CALL), EuropeanVanillaOption.of(STRIKE_LOW, EXPIRY_TIME, PutCall.PUT) };
            int n = options.Length;

            for (int j = 0; j < n; ++j)
            {
                // down-in
                double           priceDINew        = BARRIER_PRICER.price(SPOT, options[j].Strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, options[j].Call, BARRIER_DOWN_IN);
                ValueDerivatives priceDIAdjointNew = BARRIER_PRICER.priceAdjoint(SPOT, options[j].Strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, options[j].Call, BARRIER_DOWN_IN);
                double           priceDIRb         = rebate.price(SPOT, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, BARRIER_DOWN_OUT);
                ValueDerivatives priceDIAdjointRb  = rebate.priceAdjoint(SPOT, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, BARRIER_DOWN_OUT);
                assertRelative(priceDIExp[j], priceDINew + priceDIRb * REBATE);
                assertRelative(priceDIExp[j], priceDIAdjointNew.Value + priceDIAdjointRb.Value * REBATE);
                // down-out
                double           priceDONew        = BARRIER_PRICER.price(SPOT, options[j].Strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, options[j].Call, BARRIER_DOWN_OUT);
                ValueDerivatives priceDOAdjointNew = BARRIER_PRICER.priceAdjoint(SPOT, options[j].Strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, options[j].Call, BARRIER_DOWN_OUT);
                double           priceDORb         = rebate.price(SPOT, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, BARRIER_DOWN_IN);
                ValueDerivatives priceDOAdjointRb  = rebate.priceAdjoint(SPOT, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, BARRIER_DOWN_IN);
                assertRelative(priceDOExp[j], priceDONew + priceDORb * REBATE);
                assertRelative(priceDOExp[j], priceDOAdjointNew.Value + priceDOAdjointRb.Value * REBATE);
                // up-in
                double           priceUINew        = BARRIER_PRICER.price(SPOT, options[j].Strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, options[j].Call, BARRIER_UP_IN);
                ValueDerivatives priceUIAdjointNew = BARRIER_PRICER.priceAdjoint(SPOT, options[j].Strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, options[j].Call, BARRIER_UP_IN);
                double           priceUIRb         = rebate.price(SPOT, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, BARRIER_UP_OUT);
                ValueDerivatives priceUIAdjointRb  = rebate.priceAdjoint(SPOT, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, BARRIER_UP_OUT);
                assertRelative(priceUIExp[j], priceUINew + priceUIRb * REBATE);
                assertRelative(priceUIExp[j], priceUIAdjointNew.Value + priceUIAdjointRb.Value * REBATE);
                // up-out
                double           priceUONew        = BARRIER_PRICER.price(SPOT, options[j].Strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, options[j].Call, BARRIER_UP_OUT);
                ValueDerivatives priceUOAdjointNew = BARRIER_PRICER.priceAdjoint(SPOT, options[j].Strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, options[j].Call, BARRIER_UP_OUT);
                double           priceUORb         = rebate.price(SPOT, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, BARRIER_UP_IN);
                ValueDerivatives priceUOAdjointRb  = rebate.priceAdjoint(SPOT, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, BARRIER_UP_IN);
                assertRelative(priceUOExp[j], priceUONew + priceUORb * REBATE);
                assertRelative(priceUOExp[j], priceUOAdjointNew.Value + priceUOAdjointRb.Value * REBATE);
                // derivatives
                for (int i = 0; i < 5; ++i)
                {
                    int    k        = i == 0 ? i : i - 1;
                    double rebateDI = i == 1 ? 0d : priceDIAdjointRb.getDerivative(k);
                    double rebateDO = i == 1 ? 0d : priceDOAdjointRb.getDerivative(k);
                    double rebateUI = i == 1 ? 0d : priceUIAdjointRb.getDerivative(k);
                    double rebateUO = i == 1 ? 0d : priceUOAdjointRb.getDerivative(k);
                    assertRelative(derivativesDIExp[j][i], priceDIAdjointNew.getDerivative(i) + REBATE * rebateDI);
                    assertRelative(derivativesDOExp[j][i], priceDOAdjointNew.getDerivative(i) + REBATE * rebateDO);
                    assertRelative(derivativesUIExp[j][i], priceUIAdjointNew.getDerivative(i) + REBATE * rebateUI);
                    assertRelative(derivativesUOExp[j][i], priceUOAdjointNew.getDerivative(i) + REBATE * rebateUO);
                }
            }
        }
        /// <summary>
        /// Tests the price derivative with respect to forward for options in SABR model with extrapolation. Other data.
        /// </summary>
        public virtual void priceDerivativeSABR2()
        {
            double alpha  = 0.06;
            double beta   = 0.5;
            double rho    = 0.0;
            double nu     = 0.3;
            double cutOff = 0.10;
            double mu     = 2.5;
            double strike = 0.15;
            double t      = 2.366105247;
            EuropeanVanillaOption option   = EuropeanVanillaOption.of(strike, t, PutCall.CALL);
            SabrFormulaData       sabrData = SabrFormulaData.of(alpha, beta, rho, nu);
            double forward = 0.0404500579038675;
            SabrExtrapolationRightFunction sabrExtrapolation = SabrExtrapolationRightFunction.of(forward, t, sabrData, cutOff, mu);
            double          shift      = 0.000001;
            SabrFormulaData sabrDataAP = SabrFormulaData.of(alpha + shift, beta, rho, nu);
            SabrFormulaData sabrDataBP = SabrFormulaData.of(alpha, beta + shift, rho, nu);
            SabrFormulaData sabrDataRP = SabrFormulaData.of(alpha, beta, rho + shift, nu);
            SabrFormulaData sabrDataNP = SabrFormulaData.of(alpha, beta, rho, nu + shift);
            SabrExtrapolationRightFunction sabrExtrapolationAP = SabrExtrapolationRightFunction.of(forward, t, sabrDataAP, cutOff, mu);
            SabrExtrapolationRightFunction sabrExtrapolationBP = SabrExtrapolationRightFunction.of(forward, t, sabrDataBP, cutOff, mu);
            SabrExtrapolationRightFunction sabrExtrapolationRP = SabrExtrapolationRightFunction.of(forward, t, sabrDataRP, cutOff, mu);
            SabrExtrapolationRightFunction sabrExtrapolationNP = SabrExtrapolationRightFunction.of(forward, t, sabrDataNP, cutOff, mu);

            // Above cut-off strike
            double[]   abc   = sabrExtrapolation.Parameter;
            double[][] abcDP = sabrExtrapolation.ParameterDerivativeSabr;
//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[][] abcPP = new double[4][3];
            double[][] abcPP = RectangularArrays.ReturnRectangularDoubleArray(4, 3);
            abcPP[0] = sabrExtrapolationAP.Parameter;
            abcPP[1] = sabrExtrapolationBP.Parameter;
            abcPP[2] = sabrExtrapolationRP.Parameter;
            abcPP[3] = sabrExtrapolationNP.Parameter;
//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[][] abcDPExpected = new double[4][3];
            double[][] abcDPExpected = RectangularArrays.ReturnRectangularDoubleArray(4, 3);
            for (int loopparam = 0; loopparam < 4; loopparam++)
            {
                for (int loopabc = 0; loopabc < 3; loopabc++)
                {
                    abcDPExpected[loopparam][loopabc] = (abcPP[loopparam][loopabc] - abc[loopabc]) / shift;
                    assertEquals(1.0, abcDPExpected[loopparam][loopabc] / abcDP[loopparam][loopabc], 5.0E-2);
                }
            }
            double priceOutExpected = sabrExtrapolation.price(option.Strike, option.PutCall);

            double[] priceOutPP = new double[4];
            priceOutPP[0] = sabrExtrapolationAP.price(option.Strike, option.PutCall);
            priceOutPP[1] = sabrExtrapolationBP.price(option.Strike, option.PutCall);
            priceOutPP[2] = sabrExtrapolationRP.price(option.Strike, option.PutCall);
            priceOutPP[3] = sabrExtrapolationNP.price(option.Strike, option.PutCall);
            ValueDerivatives resOut   = sabrExtrapolation.priceAdjointSabr(option.Strike, option.PutCall);
            double           priceOut = resOut.Value;

            double[] priceOutDsabr = resOut.Derivatives.toArray();
            assertEquals(priceOutExpected, priceOut, 1E-5);
            double[] priceOutDsabrExpected = new double[4];
            for (int loopparam = 0; loopparam < 4; loopparam++)
            {
                priceOutDsabrExpected[loopparam] = (priceOutPP[loopparam] - priceOut) / shift;
                assertEquals(1.0, priceOutDsabrExpected[loopparam] / priceOutDsabr[loopparam], 4.0E-4);
            }
        }