//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes:
//ORIGINAL LINE: @Test public void test_monotonicity()
        public virtual void test_monotonicity()
        {
            double priceDkoPrev = 100d;
            double priceDkiPrev = 0d;
            double priceUkoPrev = 0d;
            double priceUkiPrev = 100d;

            for (int i = 0; i < 50; ++i)
            {
                // up barrier
                double lowerBarrier = 1.1 + 0.006 * i;
                SimpleConstantContinuousBarrier dko       = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_OUT, lowerBarrier);
                ResolvedFxSingleBarrierOption   optionDko = ResolvedFxSingleBarrierOption.of(CALL, dko);
                double priceDko = PRICER_39.price(optionDko, RATE_PROVIDER, VOLS, DATA_39);
                SimpleConstantContinuousBarrier dki       = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_IN, lowerBarrier);
                ResolvedFxSingleBarrierOption   optionDki = ResolvedFxSingleBarrierOption.of(CALL, dki);
                double priceDki = PRICER_39.price(optionDki, RATE_PROVIDER, VOLS, DATA_39);
                // down barrier
                double higherBarrier = 1.4 + 0.006 * (i + 1);
                SimpleConstantContinuousBarrier uko       = SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_OUT, higherBarrier);
                ResolvedFxSingleBarrierOption   optionUko = ResolvedFxSingleBarrierOption.of(CALL, uko);
                double priceUko = PRICER_39.price(optionUko, RATE_PROVIDER, VOLS, DATA_39);
                SimpleConstantContinuousBarrier uki       = SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_IN, higherBarrier);
                ResolvedFxSingleBarrierOption   optionUki = ResolvedFxSingleBarrierOption.of(CALL, uki);
                double priceUki = PRICER_39.price(optionUki, RATE_PROVIDER, VOLS, DATA_39);
                assertTrue(priceDkoPrev > priceDko);
                assertTrue(priceDkiPrev < priceDki);
                assertTrue(priceUkoPrev < priceUko);
                assertTrue(priceUkiPrev > priceUki);
                priceDkoPrev = priceDko;
                priceDkiPrev = priceDki;
                priceUkoPrev = priceUko;
                priceUkiPrev = priceUki;
            }
        }
Exemple #2
0
        private void testDerivatives(double strike, bool isCall, SimpleConstantContinuousBarrier barrier)
        {
            ValueDerivatives computed = BARRIER_PRICER.priceAdjoint(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, isCall, barrier);
            double           spotUp   = BARRIER_PRICER.price(SPOT + EPS_FD, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, isCall, barrier);
            double           spotDw   = BARRIER_PRICER.price(SPOT - EPS_FD, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, isCall, barrier);
            double           strikeUp = BARRIER_PRICER.price(SPOT, strike + EPS_FD, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, isCall, barrier);
            double           strikeDw = BARRIER_PRICER.price(SPOT, strike - EPS_FD, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, isCall, barrier);
            double           rateUp   = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM + EPS_FD, VOLATILITY, isCall, barrier);
            double           rateDw   = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM - EPS_FD, VOLATILITY, isCall, barrier);
            double           costUp   = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY + EPS_FD, RATE_DOM, VOLATILITY, isCall, barrier);
            double           costDw   = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY - EPS_FD, RATE_DOM, VOLATILITY, isCall, barrier);
            double           volUp    = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY + EPS_FD, isCall, barrier);
            double           volDw    = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY - EPS_FD, isCall, barrier);
            double           timeUp   = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME + EPS_FD, COST_OF_CARRY, RATE_DOM, VOLATILITY, isCall, barrier);
            double           timeDw   = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME - EPS_FD, COST_OF_CARRY, RATE_DOM, VOLATILITY, isCall, barrier);
            ValueDerivatives spotUp1  = BARRIER_PRICER.priceAdjoint(SPOT + EPS_FD, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, isCall, barrier);
            ValueDerivatives spotDw1  = BARRIER_PRICER.priceAdjoint(SPOT - EPS_FD, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, isCall, barrier);

            assertEquals(computed.getDerivative(0), 0.5 * (spotUp - spotDw) / EPS_FD, EPS_FD);
            assertEquals(computed.getDerivative(1), 0.5 * (strikeUp - strikeDw) / EPS_FD, EPS_FD);
            assertEquals(computed.getDerivative(2), 0.5 * (rateUp - rateDw) / EPS_FD, EPS_FD);
            assertEquals(computed.getDerivative(3), 0.5 * (costUp - costDw) / EPS_FD, EPS_FD);
            assertEquals(computed.getDerivative(4), 0.5 * (volUp - volDw) / EPS_FD, EPS_FD);
            assertEquals(computed.getDerivative(5), 0.5 * (timeUp - timeDw) / EPS_FD, EPS_FD);
            assertEquals(computed.getDerivative(6), 0.5 * (spotUp1.getDerivative(0) - spotDw1.getDerivative(0)) / EPS_FD, EPS_FD);
        }
Exemple #3
0
        public virtual void test_trinomialTree_down()
        {
            int nSteps = 133;
            LatticeSpecification lattice = new CoxRossRubinsteinLatticeSpecification();
            DoubleArray          rebate  = DoubleArray.of(nSteps + 1, i => REBATE_AMOUNT);
            double barrierLevel          = 76d;
            double tol = 1.0e-2;

            foreach (bool isCall in new bool[] { true, false })
            {
                foreach (double strike in STRIKES)
                {
                    foreach (double interest in INTERESTS)
                    {
                        foreach (double vol in VOLS)
                        {
                            foreach (double dividend in DIVIDENDS)
                            {
                                OptionFunction function = ConstantContinuousSingleBarrierKnockoutFunction.of(strike, TIME, PutCall.ofPut(!isCall), nSteps, BarrierType.DOWN, barrierLevel, rebate);
                                SimpleConstantContinuousBarrier barrier = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_OUT, barrierLevel);
                                double exact    = REBATE_AMOUNT * REBATE_PRICER.price(SPOT, TIME, interest - dividend, interest, vol, barrier.inverseKnockType()) + BARRIER_PRICER.price(SPOT, strike, TIME, interest - dividend, interest, vol, isCall, barrier);
                                double computed = TRINOMIAL_TREE.optionPrice(function, lattice, SPOT, vol, interest, dividend);
                                assertEquals(computed, exact, Math.Max(exact, 1d) * tol);
                            }
                        }
                    }
                }
            }
        }
//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes:
//ORIGINAL LINE: @Test public void test_black()
        public virtual void test_black()
        {
            double tol = 1.0e-2;

            for (int i = 0; i < 11; ++i)
            {
                // up barrier
                double lowerBarrier = 1.1 + 0.025 * i;
                SimpleConstantContinuousBarrier dko       = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_OUT, lowerBarrier);
                ResolvedFxSingleBarrierOption   optionDko = ResolvedFxSingleBarrierOption.of(CALL, dko);
                double priceDkoBlack = BLACK_PRICER.price(optionDko, RATE_PROVIDER_FLAT, VOLS_FLAT);
                double priceDko      = PRICER_70.price(optionDko, RATE_PROVIDER_FLAT, VOLS_FLAT, DATA_70_FLAT);
                assertEqualsRelative(priceDko, priceDkoBlack, tol);
                SimpleConstantContinuousBarrier dki       = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_IN, lowerBarrier);
                ResolvedFxSingleBarrierOption   optionDki = ResolvedFxSingleBarrierOption.of(CALL, dki);
                double priceDkiBlack = BLACK_PRICER.price(optionDki, RATE_PROVIDER_FLAT, VOLS_FLAT);
                double priceDki      = PRICER_70.price(optionDki, RATE_PROVIDER_FLAT, VOLS_FLAT, DATA_70_FLAT);
                assertEqualsRelative(priceDki, priceDkiBlack, tol);
                // down barrier
                double higherBarrier = 1.45 + 0.025 * i;
                SimpleConstantContinuousBarrier uko       = SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_OUT, higherBarrier);
                ResolvedFxSingleBarrierOption   optionUko = ResolvedFxSingleBarrierOption.of(CALL, uko);
                double priceUkoBlack = BLACK_PRICER.price(optionUko, RATE_PROVIDER_FLAT, VOLS_FLAT);
                double priceUko      = PRICER_70.price(optionUko, RATE_PROVIDER_FLAT, VOLS_FLAT, DATA_70_FLAT);
                assertEqualsRelative(priceUko, priceUkoBlack, tol);
                SimpleConstantContinuousBarrier uki       = SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_IN, higherBarrier);
                ResolvedFxSingleBarrierOption   optionUki = ResolvedFxSingleBarrierOption.of(CALL, uki);
                double priceUkiBlack = BLACK_PRICER.price(optionUki, RATE_PROVIDER_FLAT, VOLS_FLAT);
                double priceUki      = PRICER_70.price(optionUki, RATE_PROVIDER_FLAT, VOLS_FLAT, DATA_70_FLAT);
                assertEqualsRelative(priceUki, priceUkiBlack, tol);
            }
        }
        //-------------------------------------------------------------------------
        private ValueDerivatives priceDerivatives(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities, RecombiningTrinomialTreeData data)
        {
            validate(option, ratesProvider, volatilities);
            validateData(option, ratesProvider, volatilities, data);
            int nSteps = data.NumberOfSteps;
            ResolvedFxVanillaOption underlyingOption = option.UnderlyingOption;
            double           timeToExpiry            = data.getTime(nSteps);
            ResolvedFxSingle underlyingFx            = underlyingOption.Underlying;
            Currency         ccyBase                  = underlyingFx.CounterCurrencyPayment.Currency;
            Currency         ccyCounter               = underlyingFx.CounterCurrencyPayment.Currency;
            DiscountFactors  baseDiscountFactors      = ratesProvider.discountFactors(ccyBase);
            DiscountFactors  counterDiscountFactors   = ratesProvider.discountFactors(ccyCounter);
            double           rebateAtExpiry           = 0d; // used to price knock-in option
            double           rebateAtExpiryDerivative = 0d; // used to price knock-in option
            double           notional                 = Math.Abs(underlyingFx.BaseCurrencyPayment.Amount);

            double[] rebateArray = new double[nSteps + 1];
            SimpleConstantContinuousBarrier barrier = (SimpleConstantContinuousBarrier)option.Barrier;

            if (option.Rebate.Present)
            {
                CurrencyAmount rebateCurrencyAmount = option.Rebate.get();
                double         rebatePerUnit        = rebateCurrencyAmount.Amount / notional;
                bool           isCounter            = rebateCurrencyAmount.Currency.Equals(ccyCounter);
                double         rebate = isCounter ? rebatePerUnit : rebatePerUnit * barrier.BarrierLevel;
                if (barrier.KnockType.KnockIn)
                {   // use in-out parity
                    double dfCounterAtExpiry = counterDiscountFactors.discountFactor(timeToExpiry);
                    double dfBaseAtExpiry    = baseDiscountFactors.discountFactor(timeToExpiry);
                    for (int i = 0; i < nSteps + 1; ++i)
                    {
                        rebateArray[i] = isCounter ? rebate * dfCounterAtExpiry / counterDiscountFactors.discountFactor(data.getTime(i)) : rebate * dfBaseAtExpiry / baseDiscountFactors.discountFactor(data.getTime(i));
                    }
                    if (isCounter)
                    {
                        rebateAtExpiry = rebatePerUnit * dfCounterAtExpiry;
                    }
                    else
                    {
                        rebateAtExpiry           = rebatePerUnit * data.Spot * dfBaseAtExpiry;
                        rebateAtExpiryDerivative = rebatePerUnit * dfBaseAtExpiry;
                    }
                }
                else
                {
                    Arrays.fill(rebateArray, rebate);
                }
            }
            ConstantContinuousSingleBarrierKnockoutFunction barrierFunction = ConstantContinuousSingleBarrierKnockoutFunction.of(underlyingOption.Strike, timeToExpiry, underlyingOption.PutCall, nSteps, barrier.BarrierType, barrier.BarrierLevel, DoubleArray.ofUnsafe(rebateArray));
            ValueDerivatives barrierPrice = TREE.optionPriceAdjoint(barrierFunction, data);

            if (barrier.KnockType.KnockIn)
            {     // use in-out parity
                EuropeanVanillaOptionFunction vanillaFunction = EuropeanVanillaOptionFunction.of(underlyingOption.Strike, timeToExpiry, underlyingOption.PutCall, nSteps);
                ValueDerivatives vanillaPrice = TREE.optionPriceAdjoint(vanillaFunction, data);
                return(ValueDerivatives.of(vanillaPrice.Value + rebateAtExpiry - barrierPrice.Value, DoubleArray.of(vanillaPrice.getDerivative(0) + rebateAtExpiryDerivative - barrierPrice.getDerivative(0))));
            }
            return(barrierPrice);
        }
Exemple #6
0
        /// <summary>
        /// Upper barrier level is very high.
        /// </summary>
        public virtual void largeBarrierTest()
        {
            SimpleConstantContinuousBarrier @in  = SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_IN, 1.0e4);
            SimpleConstantContinuousBarrier @out = SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_OUT, 1.0e4);
            double upIn  = PRICER.price(SPOT, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, @in);
            double upOut = PRICER.price(SPOT, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, @out);

            assertRelative(upIn, 0d);
            assertRelative(upOut, DF_DOM);
        }
Exemple #7
0
        /// <summary>
        /// Lower barrier level is very small.
        /// </summary>
        public virtual void smallBarrierTest()
        {
            SimpleConstantContinuousBarrier @in  = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_IN, 0.1d);
            SimpleConstantContinuousBarrier @out = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_OUT, 0.1d);
            double dwIn  = PRICER.price(SPOT, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, @in);
            double dwOut = PRICER.price(SPOT, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, @out);

            assertRelative(dwIn, 0d);
            assertRelative(dwOut, DF_DOM);
        }
        public virtual void test_tradePricer()
        {
            for (int i = 0; i < 11; ++i)
            {
                // up barrier
                double lowerBarrier = 1.1 + 0.025 * i;
                SimpleConstantContinuousBarrier    dko       = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_OUT, lowerBarrier);
                ResolvedFxSingleBarrierOption      callDko   = ResolvedFxSingleBarrierOption.of(CALL, dko);
                ResolvedFxSingleBarrierOptionTrade callTrade = ResolvedFxSingleBarrierOptionTrade.builder().product(callDko).premium(Payment.of(EUR, 0, VAL_DATE)).build();

                CurrencyAmount      pvProduct = PRICER_39.presentValue(callDko, RATE_PROVIDER, VOLS);
                MultiCurrencyAmount pvTrade   = TRADE_PRICER_39.presentValue(callTrade, RATE_PROVIDER, VOLS);
                assertEquals(pvTrade.getAmount(USD), pvProduct);
            }
        }
        //-------------------------------------------------------------------------
        //  The derivatives are [0] spot, [1] strike, [2] rate, [3] cost-of-carry, [4] volatility, [5] timeToExpiry, [6] spot twice
        private ValueDerivatives priceDerivatives(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities)
        {
            validate(option, ratesProvider, volatilities);
            SimpleConstantContinuousBarrier barrier          = (SimpleConstantContinuousBarrier)option.Barrier;
            ResolvedFxVanillaOption         underlyingOption = option.UnderlyingOption;

            double[] derivatives = new double[7];
            if (volatilities.relativeTime(underlyingOption.Expiry) < 0d)
            {
                return(ValueDerivatives.of(0d, DoubleArray.ofUnsafe(derivatives)));
            }
            ResolvedFxSingle underlyingFx           = underlyingOption.Underlying;
            CurrencyPair     currencyPair           = underlyingFx.CurrencyPair;
            Currency         ccyBase                = currencyPair.Base;
            Currency         ccyCounter             = currencyPair.Counter;
            DiscountFactors  baseDiscountFactors    = ratesProvider.discountFactors(ccyBase);
            DiscountFactors  counterDiscountFactors = ratesProvider.discountFactors(ccyCounter);

            double           rateBase         = baseDiscountFactors.zeroRate(underlyingFx.PaymentDate);
            double           rateCounter      = counterDiscountFactors.zeroRate(underlyingFx.PaymentDate);
            double           costOfCarry      = rateCounter - rateBase;
            double           dfBase           = baseDiscountFactors.discountFactor(underlyingFx.PaymentDate);
            double           dfCounter        = counterDiscountFactors.discountFactor(underlyingFx.PaymentDate);
            double           todayFx          = ratesProvider.fxRate(currencyPair);
            double           strike           = underlyingOption.Strike;
            double           forward          = todayFx * dfBase / dfCounter;
            double           volatility       = volatilities.volatility(currencyPair, underlyingOption.Expiry, strike, forward);
            double           timeToExpiry     = volatilities.relativeTime(underlyingOption.Expiry);
            ValueDerivatives valueDerivatives = BARRIER_PRICER.priceAdjoint(todayFx, strike, timeToExpiry, costOfCarry, rateCounter, volatility, underlyingOption.PutCall.Call, barrier);

            if (!option.Rebate.Present)
            {
                return(valueDerivatives);
            }
            CurrencyAmount   rebate = option.Rebate.get();
            ValueDerivatives valueDerivativesRebate = rebate.Currency.Equals(ccyCounter) ? CASH_REBATE_PRICER.priceAdjoint(todayFx, timeToExpiry, costOfCarry, rateCounter, volatility, barrier.inverseKnockType()) : ASSET_REBATE_PRICER.priceAdjoint(todayFx, timeToExpiry, costOfCarry, rateCounter, volatility, barrier.inverseKnockType());
            double           rebateRate             = rebate.Amount / Math.Abs(underlyingFx.BaseCurrencyPayment.Amount);
            double           price = valueDerivatives.Value + rebateRate * valueDerivativesRebate.Value;

            derivatives[0] = valueDerivatives.getDerivative(0) + rebateRate * valueDerivativesRebate.getDerivative(0);
            derivatives[1] = valueDerivatives.getDerivative(1);
            for (int i = 2; i < 7; ++i)
            {
                derivatives[i] = valueDerivatives.getDerivative(i) + rebateRate * valueDerivativesRebate.getDerivative(i - 1);
            }
            return(ValueDerivatives.of(price, DoubleArray.ofUnsafe(derivatives)));
        }
Exemple #10
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);
            }
        }
Exemple #11
0
        private void testSmallValues(double strike, bool isCall, SimpleConstantContinuousBarrier barrier)
        {
            // small parameters
            double volUp = 2.0e-3;
            double volDw = 1.0e-3;
            double time  = 1.0e-2;
            // price
            double optUp = BARRIER_PRICER.price(SPOT, strike, time, COST_OF_CARRY, RATE_DOM, volUp, isCall, barrier);
            double optDw = BARRIER_PRICER.price(SPOT, strike, time, COST_OF_CARRY, RATE_DOM, volDw, isCall, barrier);

            assertRelative(optUp, optDw);
            // price adjoint
            ValueDerivatives optUpAdj = BARRIER_PRICER.priceAdjoint(SPOT, strike, time, COST_OF_CARRY, RATE_DOM, volUp, isCall, barrier);
            ValueDerivatives optDwAdj = BARRIER_PRICER.priceAdjoint(SPOT, strike, time, COST_OF_CARRY, RATE_DOM, volDw, isCall, barrier);

            assertRelative(optUpAdj.Value, optDwAdj.Value);
            for (int i = 0; i < 6; ++i)
            {
                assertRelative(optUpAdj.getDerivative(i), optDwAdj.getDerivative(i));
            }
        }
//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes:
//ORIGINAL LINE: @Test public void test_inOutParity()
        public virtual void test_inOutParity()
        {
            double tol       = 1.0e-2;
            double callPrice = VANILLA_PRICER.price(CALL, RATE_PROVIDER, VOLS);
            double putPrice  = VANILLA_PRICER.price(PUT, RATE_PROVIDER, VOLS);

            for (int i = 0; i < 11; ++i)
            {
                // up barrier
                double lowerBarrier = 1.1 + 0.025 * i;
                SimpleConstantContinuousBarrier dko     = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_OUT, lowerBarrier);
                ResolvedFxSingleBarrierOption   callDko = ResolvedFxSingleBarrierOption.of(CALL, dko);
                double priceCallDko = PRICER_39.price(callDko, RATE_PROVIDER, VOLS, DATA_39);
                ResolvedFxSingleBarrierOption putDko = ResolvedFxSingleBarrierOption.of(PUT, dko);
                double pricePutDko = PRICER_39.price(putDko, RATE_PROVIDER, VOLS, DATA_39);
                SimpleConstantContinuousBarrier dki     = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_IN, lowerBarrier);
                ResolvedFxSingleBarrierOption   callDki = ResolvedFxSingleBarrierOption.of(CALL, dki);
                double priceCallDki = PRICER_39.price(callDki, RATE_PROVIDER, VOLS, DATA_39);
                ResolvedFxSingleBarrierOption putDki = ResolvedFxSingleBarrierOption.of(PUT, dki);
                double pricePutDki = PRICER_39.price(putDki, RATE_PROVIDER, VOLS, DATA_39);
                assertEqualsRelative(priceCallDko + priceCallDki, callPrice, tol);
                assertEqualsRelative(pricePutDko + pricePutDki, putPrice, tol);
                // down barrier
                double higherBarrier = 1.45 + 0.025 * i;
                SimpleConstantContinuousBarrier uko     = SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_OUT, higherBarrier);
                ResolvedFxSingleBarrierOption   callUko = ResolvedFxSingleBarrierOption.of(CALL, uko);
                double priceCallUko = PRICER_39.price(callUko, RATE_PROVIDER, VOLS, DATA_39);
                ResolvedFxSingleBarrierOption putUko = ResolvedFxSingleBarrierOption.of(PUT, uko);
                double pricePutUko = PRICER_39.price(putUko, RATE_PROVIDER, VOLS, DATA_39);
                SimpleConstantContinuousBarrier uki     = SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_IN, higherBarrier);
                ResolvedFxSingleBarrierOption   callUki = ResolvedFxSingleBarrierOption.of(CALL, uki);
                double priceCallUki = PRICER_39.price(callUki, RATE_PROVIDER, VOLS, DATA_39);
                ResolvedFxSingleBarrierOption putUki = ResolvedFxSingleBarrierOption.of(PUT, uki);
                double pricePutUki = PRICER_39.price(putUki, RATE_PROVIDER, VOLS, DATA_39);
                assertEqualsRelative(priceCallUko + priceCallUki, callPrice, tol);
                assertEqualsRelative(pricePutUko + pricePutUki, putPrice, tol);
            }
        }
        /// <summary>
        /// Calculates the price of the FX barrier 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>
        /// <para>
        /// The volatility used in this computation is the Black implied volatility at expiry time and strike.
        ///
        /// </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(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities)
        {
            validate(option, ratesProvider, volatilities);
            SimpleConstantContinuousBarrier barrier          = (SimpleConstantContinuousBarrier)option.Barrier;
            ResolvedFxVanillaOption         underlyingOption = option.UnderlyingOption;

            if (volatilities.relativeTime(underlyingOption.Expiry) < 0d)
            {
                return(0d);
            }
            ResolvedFxSingle underlyingFx           = underlyingOption.Underlying;
            Currency         ccyBase                = underlyingFx.BaseCurrencyPayment.Currency;
            Currency         ccyCounter             = underlyingFx.CounterCurrencyPayment.Currency;
            CurrencyPair     currencyPair           = underlyingFx.CurrencyPair;
            DiscountFactors  baseDiscountFactors    = ratesProvider.discountFactors(ccyBase);
            DiscountFactors  counterDiscountFactors = ratesProvider.discountFactors(ccyCounter);

            double rateBase     = baseDiscountFactors.zeroRate(underlyingFx.PaymentDate);
            double rateCounter  = counterDiscountFactors.zeroRate(underlyingFx.PaymentDate);
            double costOfCarry  = rateCounter - rateBase;
            double dfBase       = baseDiscountFactors.discountFactor(underlyingFx.PaymentDate);
            double dfCounter    = counterDiscountFactors.discountFactor(underlyingFx.PaymentDate);
            double todayFx      = ratesProvider.fxRate(currencyPair);
            double strike       = underlyingOption.Strike;
            double forward      = todayFx * dfBase / dfCounter;
            double volatility   = volatilities.volatility(currencyPair, underlyingOption.Expiry, strike, forward);
            double timeToExpiry = volatilities.relativeTime(underlyingOption.Expiry);
            double price        = BARRIER_PRICER.price(todayFx, strike, timeToExpiry, costOfCarry, rateCounter, volatility, underlyingOption.PutCall.Call, barrier);

            if (option.Rebate.Present)
            {
                CurrencyAmount rebate      = option.Rebate.get();
                double         priceRebate = rebate.Currency.Equals(ccyCounter) ? CASH_REBATE_PRICER.price(todayFx, timeToExpiry, costOfCarry, rateCounter, volatility, barrier.inverseKnockType()) : ASSET_REBATE_PRICER.price(todayFx, timeToExpiry, costOfCarry, rateCounter, volatility, barrier.inverseKnockType());
                price += priceRebate * rebate.Amount / Math.Abs(underlyingFx.BaseCurrencyPayment.Amount);
            }
            return(price);
        }
//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes:
//ORIGINAL LINE: @Test public void test_black_currencyExposure()
        public virtual void test_black_currencyExposure()
        {
            double tol = 7.0e-2;     // large tol due to approximated delta

            for (int i = 0; i < 8; ++i)
            {
                // up barrier
                double lowerBarrier = 1.1 + 0.025 * i;
                SimpleConstantContinuousBarrier dko        = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_OUT, lowerBarrier);
                ResolvedFxSingleBarrierOption   optionDko  = ResolvedFxSingleBarrierOption.of(CALL, dko, REBATE_BASE);
                MultiCurrencyAmount             ceDkoBlack = BLACK_PRICER.currencyExposure(optionDko, RATE_PROVIDER_FLAT, VOLS_FLAT);
                MultiCurrencyAmount             ceDko      = PRICER_70.currencyExposure(optionDko, RATE_PROVIDER_FLAT, VOLS_FLAT, DATA_70_FLAT);
                assertEquals(ceDko.getAmount(EUR).Amount, ceDkoBlack.getAmount(EUR).Amount, NOTIONAL * tol);
                assertEquals(ceDko.getAmount(USD).Amount, ceDkoBlack.getAmount(USD).Amount, NOTIONAL * tol);
                SimpleConstantContinuousBarrier dki        = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_IN, lowerBarrier);
                ResolvedFxSingleBarrierOption   optionDki  = ResolvedFxSingleBarrierOption.of(CALL, dki, REBATE);
                MultiCurrencyAmount             ceDkiBlack = BLACK_PRICER.currencyExposure(optionDki, RATE_PROVIDER_FLAT, VOLS_FLAT);
                MultiCurrencyAmount             ceDki      = PRICER_70.currencyExposure(optionDki, RATE_PROVIDER_FLAT, VOLS_FLAT, DATA_70_FLAT);
                assertEquals(ceDki.getAmount(EUR).Amount, ceDkiBlack.getAmount(EUR).Amount, NOTIONAL * tol);
                assertEquals(ceDki.getAmount(USD).Amount, ceDkiBlack.getAmount(USD).Amount, NOTIONAL * tol);
                // down barrier
                double higherBarrier = 1.45 + 0.025 * i;
                SimpleConstantContinuousBarrier uko        = SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_OUT, higherBarrier);
                ResolvedFxSingleBarrierOption   optionUko  = ResolvedFxSingleBarrierOption.of(CALL, uko, REBATE);
                MultiCurrencyAmount             ceUkoBlack = BLACK_PRICER.currencyExposure(optionUko, RATE_PROVIDER_FLAT, VOLS_FLAT);
                MultiCurrencyAmount             ceUko      = PRICER_70.currencyExposure(optionUko, RATE_PROVIDER_FLAT, VOLS_FLAT, DATA_70_FLAT);
                assertEquals(ceUko.getAmount(EUR).Amount, ceUkoBlack.getAmount(EUR).Amount, NOTIONAL * tol);
                assertEquals(ceUko.getAmount(USD).Amount, ceUkoBlack.getAmount(USD).Amount, NOTIONAL * tol);
                SimpleConstantContinuousBarrier uki        = SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_IN, higherBarrier);
                ResolvedFxSingleBarrierOption   optionUki  = ResolvedFxSingleBarrierOption.of(CALL, uki, REBATE_BASE);
                MultiCurrencyAmount             ceUkiBlack = BLACK_PRICER.currencyExposure(optionUki, RATE_PROVIDER_FLAT, VOLS_FLAT);
                MultiCurrencyAmount             ceUki      = PRICER_70.currencyExposure(optionUki, RATE_PROVIDER_FLAT, VOLS_FLAT, DATA_70_FLAT);
                assertEquals(ceUki.getAmount(EUR).Amount, ceUkiBlack.getAmount(EUR).Amount, NOTIONAL * tol);
                assertEquals(ceUki.getAmount(USD).Amount, ceUkiBlack.getAmount(USD).Amount, NOTIONAL * tol);
            }
        }
Exemple #15
0
        /// <summary>
        /// Computes the price of a barrier option.
        /// </summary>
        /// <param name="spot">  the spot </param>
        /// <param name="strike">  the strike </param>
        /// <param name="timeToExpiry">  the time to expiry </param>
        /// <param name="costOfCarry">  the cost of carry </param>
        /// <param name="rate">  the interest rate </param>
        /// <param name="lognormalVol">  the lognormal volatility </param>
        /// <param name="isCall">  true if call, false otherwise </param>
        /// <param name="barrier">  the barrier </param>
        /// <returns> the price </returns>
        public virtual double price(double spot, double strike, double timeToExpiry, double costOfCarry, double rate, double lognormalVol, bool isCall, SimpleConstantContinuousBarrier barrier)
        {
            ArgChecker.notNull(barrier, "barrier");
            bool   isKnockIn = barrier.KnockType.KnockIn;
            bool   isDown    = barrier.BarrierType.Down;
            double h         = barrier.BarrierLevel;

            ArgChecker.isFalse(isDown && spot <= barrier.BarrierLevel, "The Data is not consistent with an alive barrier (DOWN and spot<=barrier).");
            ArgChecker.isFalse(!isDown && spot >= barrier.BarrierLevel, "The Data is not consistent with an alive barrier (UP and spot>=barrier).");
            int    phi     = isCall ? 1 : -1;
            double eta     = isDown ? 1 : -1;
            double df1     = Math.Exp(timeToExpiry * (costOfCarry - rate));
            double df2     = Math.Exp(-rate * timeToExpiry);
            double sigmaSq = lognormalVol * lognormalVol;
            double sigmaT  = lognormalVol * Math.Sqrt(timeToExpiry);

            if (DoubleMath.fuzzyEquals(Math.Min(timeToExpiry, sigmaSq), 0d, SMALL))
            {
                if (isKnockIn)
                {
                    return(0d);
                }
                double dscFwd = df1 * spot;
                double dscStr = df2 * strike;
                return(isCall ? (dscFwd >= dscStr ? dscFwd - dscStr : 0d) : (dscStr >= dscFwd ? dscStr - dscFwd : 0d));
            }
            double mu = (costOfCarry - 0.5 * sigmaSq) / sigmaSq;
            double m1 = sigmaT * (1 + mu);
            double x1 = Math.Log(spot / strike) / sigmaT + m1;
            double x2 = Math.Log(spot / h) / sigmaT + m1;
            double y1 = Math.Log(h * h / spot / strike) / sigmaT + m1;
            double y2 = Math.Log(h / spot) / sigmaT + m1;
            double xA = getA(spot, strike, df1, df2, x1, sigmaT, phi);
            double xB = getA(spot, strike, df1, df2, x2, sigmaT, phi);
            double xC = getC(spot, strike, df1, df2, y1, sigmaT, h, mu, phi, eta);
            double xD = getC(spot, strike, df1, df2, y2, sigmaT, h, mu, phi, eta);

            if (isKnockIn)
            {     // KnockIn
                if (isDown)
                {
                    if (isCall)
                    {
                        return(strike > h ? xC : xA - xB + xD);
                    }
                    return(strike > h ? xB - xC + xD : xA);
                }
                if (isCall)
                {
                    return(strike > h ? xA : xB - xC + xD);
                }
                return(strike > h ? xA - xB + xD : xC);
            }
            // KnockOut
            if (isDown)
            {
                if (isCall)
                {
                    return(strike > h ? xA - xC : xB - xD);
                }
                return(strike > h ? xA - xB + xC - xD : 0d);
            }
            if (isCall)
            {
                return(strike > h ? 0d : xA - xB + xC - xD);
            }
            return(strike > h ? xB - xD : xA - xC);
        }
Exemple #16
0
        /// <summary>
        /// Computes the price of a one-touch/no-touch option.
        /// </summary>
        /// <param name="spot">  the spot </param>
        /// <param name="timeToExpiry">  the time to expiry </param>
        /// <param name="costOfCarry">  the cost of carry </param>
        /// <param name="rate">  the interest rate </param>
        /// <param name="lognormalVol">  the lognormal volatility </param>
        /// <param name="barrier">  the barrier </param>
        /// <returns> the price </returns>
        public virtual double price(double spot, double timeToExpiry, double costOfCarry, double rate, double lognormalVol, SimpleConstantContinuousBarrier barrier)
        {
            ArgChecker.notNull(barrier, "barrier");
            bool   isKnockIn = barrier.KnockType.KnockIn;
            bool   isDown    = barrier.BarrierType.Down;
            double h         = barrier.BarrierLevel;

            ArgChecker.isFalse(isDown && spot <= barrier.BarrierLevel, "The Data is not consistent with an alive barrier (DOWN and spot<=barrier).");
            ArgChecker.isFalse(!isDown && spot >= barrier.BarrierLevel, "The Data is not consistent with an alive barrier (UP and spot>=barrier).");
            double eta            = isDown ? 1 : -1;
            double df2            = Math.Exp(-rate * timeToExpiry);
            double lognormalVolSq = lognormalVol * lognormalVol;
            double lognormalVolT  = lognormalVol * Math.Sqrt(timeToExpiry);

            if (DoubleMath.fuzzyEquals(Math.Min(timeToExpiry, lognormalVolSq), 0d, SMALL))
            {
                return(isKnockIn ? 0d : df2);
            }
            double mu     = (costOfCarry - 0.5 * lognormalVolSq) / lognormalVolSq;
            double lambda = Math.Sqrt(mu * mu + 2 * rate / lognormalVolSq);
            double m1     = lognormalVolT * (1 + mu);
            double x2     = Math.Log(spot / h) / lognormalVolT + m1;
            double y2     = Math.Log(h / spot) / lognormalVolT + m1;
            double z      = Math.Log(h / spot) / lognormalVolT + lambda * lognormalVolT;
            double xE     = isKnockIn ? getF(spot, z, lognormalVolT, h, mu, lambda, eta) : getE(spot, df2, x2, y2, lognormalVolT, h, mu, eta);

            return(xE);
        }
Exemple #17
0
        /// <summary>
        /// Computes the price and derivatives of a barrier option.
        ///
        /// The derivatives are [0] spot, [1] strike, [2] rate, [3] cost-of-carry, [4] volatility, [5] timeToExpiry, [6] spot twice
        /// </summary>
        /// <param name="spot">  the spot </param>
        /// <param name="strike">  the strike </param>
        /// <param name="timeToExpiry">  the time to expiry </param>
        /// <param name="costOfCarry">  the cost of carry </param>
        /// <param name="rate">  the interest rate </param>
        /// <param name="lognormalVol">  the lognormal volatility </param>
        /// <param name="isCall">  true if call, false otherwise </param>
        /// <param name="barrier">  the barrier </param>
        /// <returns> the price and derivatives </returns>
        public virtual ValueDerivatives priceAdjoint(double spot, double strike, double timeToExpiry, double costOfCarry, double rate, double lognormalVol, bool isCall, SimpleConstantContinuousBarrier barrier)
        {
            ArgChecker.notNull(barrier, "barrier");
            double[] derivatives = new double[7];
            bool     isKnockIn   = barrier.KnockType.KnockIn;
            bool     isDown      = barrier.BarrierType.Down;
            double   h           = barrier.BarrierLevel;

            ArgChecker.isFalse(isDown && spot <= barrier.BarrierLevel, "The Data is not consistent with an alive barrier (DOWN and spot<=barrier).");
            ArgChecker.isFalse(!isDown && spot >= barrier.BarrierLevel, "The Data is not consistent with an alive barrier (UP and spot>=barrier).");
            int    phi            = isCall ? 1 : -1;
            double eta            = isDown ? 1 : -1;
            double df1            = Math.Exp(timeToExpiry * (costOfCarry - rate));
            double df2            = Math.Exp(-rate * timeToExpiry);
            double lognormalVolSq = lognormalVol * lognormalVol;
            double lognormalVolT  = lognormalVol * Math.Sqrt(timeToExpiry);

            if (DoubleMath.fuzzyEquals(Math.Min(timeToExpiry, lognormalVolSq), 0d, SMALL))
            {
                if (isKnockIn)
                {
                    return(ValueDerivatives.of(0d, DoubleArray.filled(7)));
                }
                double dscFwd = df1 * spot;
                double dscStr = df2 * strike;
                double price  = 0d;
                if (isCall)
                {
                    if (dscFwd >= dscStr)
                    {
                        price          = dscFwd - dscStr;
                        derivatives[0] = df1;
                        derivatives[1] = -df2;
                        derivatives[2] = -timeToExpiry * price;
                        derivatives[3] = timeToExpiry * dscFwd;
                        derivatives[5] = (costOfCarry - rate) * dscFwd + rate * dscStr;
                    }
                }
                else
                {
                    if (dscStr >= dscFwd)
                    {
                        price          = dscStr - dscFwd;
                        derivatives[0] = -df1;
                        derivatives[1] = df2;
                        derivatives[2] = -timeToExpiry * price;
                        derivatives[3] = -timeToExpiry * dscFwd;
                        derivatives[5] = -rate * dscStr - (costOfCarry - rate) * dscFwd;
                    }
                }
                return(ValueDerivatives.of(price, DoubleArray.ofUnsafe(derivatives)));
            }
            double mu = (costOfCarry - 0.5 * lognormalVolSq) / lognormalVolSq;
            double m1 = lognormalVolT * (1 + mu);
            double x1 = Math.Log(spot / strike) / lognormalVolT + m1;
            double x2 = Math.Log(spot / h) / lognormalVolT + m1;
            double y1 = Math.Log(h * h / spot / strike) / lognormalVolT + m1;
            double y2 = Math.Log(h / spot) / lognormalVolT + m1;

            double[] aDerivFirst = 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[][] aDerivSecond = new double[2][2];
            double[][] aDerivSecond = RectangularArrays.ReturnRectangularDoubleArray(2, 2);
            double     xA           = getAAdjoint(spot, strike, df1, df2, x1, lognormalVolT, phi, aDerivFirst, aDerivSecond);

            double[] bDerivFirst = 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[][] bDerivSecond = new double[2][2];
            double[][] bDerivSecond = RectangularArrays.ReturnRectangularDoubleArray(2, 2);
            double     xB           = getAAdjoint(spot, strike, df1, df2, x2, lognormalVolT, phi, bDerivFirst, bDerivSecond);

            double[] cDerivFirst = new double[7];
//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[][] cDerivSecond = new double[2][2];
            double[][] cDerivSecond = RectangularArrays.ReturnRectangularDoubleArray(2, 2);
            double     xC           = getCAdjoint(spot, strike, df1, df2, y1, lognormalVolT, h, mu, phi, eta, cDerivFirst, cDerivSecond);

            double[] dDerivFirst = new double[7];
//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[][] dDerivSecond = new double[2][2];
            double[][] dDerivSecond = RectangularArrays.ReturnRectangularDoubleArray(2, 2);
            double     xD           = getCAdjoint(spot, strike, df1, df2, y2, lognormalVolT, h, mu, phi, eta, dDerivFirst, dDerivSecond);
            double     xDBar        = 0d;
            double     xCBar        = 0d;
            double     xBBar        = 0d;
            double     xABar        = 0d;
            double     price;

            if (isKnockIn)
            {         // IN start
                if (isDown)
                {     // DOWN start
                    if (isCall)
                    { // Call start
                        if (strike > h)
                        {
                            xCBar = 1d;
                            price = xC;
                        }
                        else
                        {
                            xABar = 1d;
                            xBBar = -1d;
                            xDBar = 1d;
                            price = xA - xB + xD;
                        }
                    }
                    else
                    {     // Put start
                        if (strike > h)
                        {
                            xBBar = 1d;
                            xCBar = -1d;
                            xDBar = 1d;
                            price = xB - xC + xD;
                        }
                        else
                        {
                            xABar = 1d;
                            price = xA;
                        }
                    }     // DOWN end
                }
                else
                {   // UP start
                    if (isCall)
                    {
                        if (strike > h)
                        {
                            xABar = 1d;
                            price = xA;
                        }
                        else
                        {
                            xBBar = 1d;
                            xCBar = -1d;
                            xDBar = 1d;
                            price = xB - xC + xD;
                        }
                    }
                    else
                    {
                        if (strike > h)
                        {
                            xABar = 1d;
                            xBBar = -1d;
                            xDBar = 1d;
                            price = xA - xB + xD;
                        }
                        else
                        {
                            xCBar = 1d;
                            price = xC;
                        }
                    } // UP end
                }     // IN end
            }
            else
            {         // OUT start
                if (isDown)
                {     // DOWN start
                    if (isCall)
                    { // CALL start
                        if (strike > h)
                        {
                            xABar = 1d;
                            xCBar = -1d;
                            price = xA - xC;
                        }
                        else
                        {
                            xBBar = 1d;
                            xDBar = -1d;
                            price = xB - xD;
                        }
                    }
                    else
                    {     // PUT start
                        if (strike > h)
                        {
                            xABar = 1d;
                            xBBar = -1d;
                            xCBar = 1d;
                            xDBar = -1d;
                            price = xA - xB + xC - xD;
                        }
                        else
                        {
                            price = 0d;
                        } // PUT end
                    }     // DOWN end
                }
                else
                {   // UP start
                    if (isCall)
                    {
                        if (strike > h)
                        {
                            price = 0d;
                        }
                        else
                        {
                            xABar = 1d;
                            xBBar = -1d;
                            xCBar = 1d;
                            xDBar = -1d;
                            price = xA - xB + xC - xD;
                        }
                    }
                    else
                    {
                        if (strike > h)
                        {
                            xBBar = 1d;
                            xDBar = -1d;
                            price = xB - xD;
                        }
                        else
                        {
                            xABar = 1d;
                            xCBar = -1d;
                            price = xA - xC;
                        } // PUT end
                    }     // UP end
                }         // OUT end
            }
            double dxyds             = 1d / spot / lognormalVolT;
            double x1Bar             = aDerivFirst[4] * xABar;
            double x2Bar             = bDerivFirst[4] * xBBar;
            double y1Bar             = cDerivFirst[4] * xCBar;
            double y2Bar             = dDerivFirst[4] * xDBar;
            double m1Bar             = x1Bar + x2Bar + y1Bar + y2Bar;
            double muBar             = cDerivFirst[6] * xCBar + dDerivFirst[6] * xDBar + lognormalVolT * m1Bar;
            double lognormalVolTBar  = aDerivFirst[5] * xABar + bDerivFirst[5] * xBBar + cDerivFirst[5] * xCBar + dDerivFirst[5] * xDBar - Math.Log(h / spot) / (lognormalVolT * lognormalVolT) * y2Bar - Math.Log(h * h / spot / strike) / (lognormalVolT * lognormalVolT) * y1Bar - Math.Log(spot / h) / (lognormalVolT * lognormalVolT) * x2Bar - Math.Log(spot / strike) / (lognormalVolT * lognormalVolT) * x1Bar + (1d + mu) * m1Bar;
            double lognormalVolSqBar = -costOfCarry / (lognormalVolSq * lognormalVolSq) * muBar;
            double df2Bar            = aDerivFirst[3] * xABar + bDerivFirst[3] * xBBar + cDerivFirst[3] * xCBar + dDerivFirst[3] * xDBar;
            double df1Bar            = aDerivFirst[2] * xABar + bDerivFirst[2] * xBBar + cDerivFirst[2] * xCBar + dDerivFirst[2] * xDBar;

            derivatives[0] = aDerivFirst[0] * xABar + bDerivFirst[0] * xBBar + cDerivFirst[0] * xCBar + dDerivFirst[0] * xDBar + x1Bar * dxyds + x2Bar * dxyds - y1Bar * dxyds - y2Bar * dxyds;
            derivatives[1] = aDerivFirst[1] * xABar + bDerivFirst[1] * xBBar + cDerivFirst[1] * xCBar + dDerivFirst[1] * xDBar - (x1Bar + y1Bar) / strike / lognormalVolT;
            derivatives[2] = -timeToExpiry * (df1 * df1Bar + df2 * df2Bar);
            derivatives[3] = timeToExpiry * df1 * df1Bar + muBar / lognormalVolSq;
            derivatives[4] = 2d * lognormalVol * lognormalVolSqBar + Math.Sqrt(timeToExpiry) * lognormalVolTBar;
            derivatives[5] = (costOfCarry - rate) * df1 * df1Bar - rate * df2 * df2Bar + lognormalVolTBar * lognormalVolT * 0.5 / timeToExpiry;
            derivatives[6] = aDerivSecond[0][0] * xABar + bDerivSecond[0][0] * xBBar + cDerivSecond[0][0] * xCBar + dDerivSecond[0][0] * xDBar + 2d * xABar * aDerivSecond[0][1] * dxyds + 2d * xBBar * bDerivSecond[0][1] * dxyds - 2d * xCBar * cDerivSecond[0][1] * dxyds - 2d * xDBar * dDerivSecond[0][1] * dxyds + xABar * aDerivSecond[1][1] * dxyds * dxyds + xBBar * bDerivSecond[1][1] * dxyds * dxyds + xCBar * cDerivSecond[1][1] * dxyds * dxyds + xDBar * dDerivSecond[1][1] * dxyds * dxyds - x1Bar * dxyds / spot - x2Bar * dxyds / spot + y1Bar * dxyds / spot + y2Bar * dxyds / spot;
            return(ValueDerivatives.of(price, DoubleArray.ofUnsafe(derivatives)));
        }
Exemple #18
0
        /// <summary>
        /// Computes the price and derivatives of a one-touch/no-touch option.
        /// <para>
        /// The derivatives are [0] spot, [1] rate, [2] cost-of-carry, [3] volatility, [4] timeToExpiry, [5] spot twice.
        ///
        /// </para>
        /// </summary>
        /// <param name="spot">  the spot </param>
        /// <param name="timeToExpiry">  the time to expiry </param>
        /// <param name="costOfCarry">  the cost of carry </param>
        /// <param name="rate">  the interest rate </param>
        /// <param name="lognormalVol">  the lognormal volatility </param>
        /// <param name="barrier">  the barrier </param>
        /// <returns> the price and derivatives </returns>
        public virtual ValueDerivatives priceAdjoint(double spot, double timeToExpiry, double costOfCarry, double rate, double lognormalVol, SimpleConstantContinuousBarrier barrier)
        {
            ArgChecker.notNull(barrier, "barrier");
            double[] derivatives = new double[6];
            bool     isKnockIn   = barrier.KnockType.KnockIn;
            bool     isDown      = barrier.BarrierType.Down;
            double   h           = barrier.BarrierLevel;

            ArgChecker.isFalse(isDown && spot <= barrier.BarrierLevel, "The Data is not consistent with an alive barrier (DOWN and spot<=barrier).");
            ArgChecker.isFalse(!isDown && spot >= barrier.BarrierLevel, "The Data is not consistent with an alive barrier (UP and spot>=barrier).");

            double eta            = isDown ? 1 : -1;
            double df2            = Math.Exp(-rate * timeToExpiry);
            double lognormalVolSq = lognormalVol * lognormalVol;
            double lognormalVolT  = lognormalVol * Math.Sqrt(timeToExpiry);

            if (DoubleMath.fuzzyEquals(Math.Min(timeToExpiry, lognormalVolSq), 0d, SMALL))
            {
                if (isKnockIn)
                {
                    return(ValueDerivatives.of(0d, DoubleArray.filled(6)));
                }
                double price = df2;
                derivatives[1] = -timeToExpiry * price;
                derivatives[4] = -rate * price;
                return(ValueDerivatives.of(price, DoubleArray.ofUnsafe(derivatives)));
            }
            double mu     = (costOfCarry - 0.5 * lognormalVolSq) / lognormalVolSq;
            double lambda = Math.Sqrt(mu * mu + 2d * rate / lognormalVolSq);
            double m1     = lognormalVolT * (1d + mu);
            double x2     = Math.Log(spot / h) / lognormalVolT + m1;
            double y2     = Math.Log(h / spot) / lognormalVolT + m1;
            double z      = Math.Log(h / spot) / lognormalVolT + lambda * lognormalVolT;

            double[] eDerivFirst      = new double[6];
            double[] eDerivSecond     = new double[6];
            double[] fDerivFirst      = new double[5];
            double[] fDerivSecond     = new double[5];
            double   price            = isKnockIn ? getFAdjoint(spot, z, lognormalVolT, h, mu, lambda, eta, fDerivFirst, fDerivSecond) : getEAdjoint(spot, df2, x2, y2, lognormalVolT, h, mu, eta, eDerivFirst, eDerivSecond);
            double   zBar             = 0.0;
            double   y2Bar            = 0.0;
            double   x2Bar            = 0.0;
            double   zSqBar           = 0.0;
            double   y2SqBar          = 0.0;
            double   x2SqBar          = 0.0;
            double   zsBar            = 0.0;
            double   y2sBar           = 0.0;
            double   lambdaBar        = 0.0;
            double   muBar            = 0.0;
            double   lognormalVolTBar = 0.0;
            double   df2Bar           = 0.0;

            if (isKnockIn)
            {
                zBar             = fDerivFirst[1];
                lambdaBar        = fDerivFirst[4]; // only F has lambda dependence, which in turn is a function of mu, see muBar+= below
                muBar            = fDerivFirst[3];
                lognormalVolTBar = fDerivFirst[2];
                derivatives[0]   = fDerivFirst[0];
                zSqBar           = fDerivSecond[1];
                zsBar            = fDerivSecond[2];
                derivatives[5]   = fDerivSecond[0];
            }
            else
            {
                y2Bar            = eDerivFirst[3];
                x2Bar            = eDerivFirst[2];
                muBar            = eDerivFirst[5];
                lognormalVolTBar = eDerivFirst[4];
                df2Bar           = eDerivFirst[1];
                derivatives[0]   = eDerivFirst[0];
                y2SqBar          = eDerivSecond[2];
                x2SqBar          = eDerivSecond[1];
                y2sBar           = eDerivSecond[3];
                derivatives[5]   = eDerivSecond[0];
            }
            double dxyds = 1d / spot / lognormalVolT;
            double m1Bar = x2Bar + y2Bar;

            muBar            += +lognormalVolT * m1Bar + mu / lambda * lambdaBar;
            lognormalVolTBar += +(lambda - Math.Log(h / spot) / (lognormalVolT * lognormalVolT)) * zBar - Math.Log(h / spot) / (lognormalVolT * lognormalVolT) * y2Bar - Math.Log(spot / h) / (lognormalVolT * lognormalVolT) * x2Bar + (1 + mu) * m1Bar;
            double lognormalVolSqBar = -costOfCarry / (lognormalVolSq * lognormalVolSq) * muBar - rate / (lognormalVolSq * lognormalVolSq) / lambda * lambdaBar;

            derivatives[0] += dxyds * x2Bar - dxyds * y2Bar - dxyds * zBar;
            derivatives[1]  = -timeToExpiry * df2 * df2Bar + lambdaBar / lambda / lognormalVolSq;
            derivatives[2]  = muBar / lognormalVolSq;
            derivatives[3]  = 2d * lognormalVol * lognormalVolSqBar + Math.Sqrt(timeToExpiry) * lognormalVolTBar;
            derivatives[4]  = -rate * df2 * df2Bar + lognormalVolTBar * lognormalVolT * 0.5 / timeToExpiry;
            derivatives[5] += -dxyds * x2Bar / spot + dxyds * y2Bar / spot + dxyds * zBar / spot + dxyds * dxyds * x2SqBar + dxyds * dxyds * y2SqBar - 2d * dxyds * y2sBar + dxyds * dxyds * zSqBar - 2d * dxyds * zsBar;
            return(ValueDerivatives.of(price, DoubleArray.ofUnsafe(derivatives)));
        }