//-------------------------------------------------------------------------
        private void validateData(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities, RecombiningTrinomialTreeData data)
        {
            ResolvedFxVanillaOption underlyingOption = option.UnderlyingOption;

            ArgChecker.isTrue(DoubleMath.fuzzyEquals(data.getTime(data.NumberOfSteps), volatilities.relativeTime(underlyingOption.Expiry), SMALL), "time to expiry mismatch between pricing option and trinomial tree data");
            ArgChecker.isTrue(DoubleMath.fuzzyEquals(data.Spot, ratesProvider.fxRate(underlyingOption.Underlying.CurrencyPair), SMALL), "today's FX rate mismatch between rates provider and trinomial tree data");
        }
        //-------------------------------------------------------------------------
        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 #3
0
        public virtual void test_recoverVolatility()
        {
            int    nSteps       = TREE_DATA.NumberOfSteps;
            double spot         = TREE_DATA.Spot;
            double timeToExpiry = TREE_DATA.getTime(nSteps);
            double dfDom        = RATE_PROVIDER.discountFactors(USD).discountFactor(timeToExpiry);
            double dfFor        = RATE_PROVIDER.discountFactors(EUR).discountFactor(timeToExpiry);
            double forward      = spot * dfFor / dfDom;

            for (int i = 0; i < 100; ++i)
            {
                double         strike     = spot * (0.8 + 0.004 * i);
                OptionFunction func       = EuropeanVanillaOptionFunction.of(strike, timeToExpiry, PutCall.CALL, nSteps);
                double         price      = TREE.optionPrice(func, TREE_DATA);
                double         impliedVol = BlackFormulaRepository.impliedVolatility(price / dfDom, forward, strike, timeToExpiry, true);
                double         orgVol     = VOLS.volatility(FX_PRODUCT.CurrencyPair, timeToExpiry, strike, forward);
                assertEquals(impliedVol, orgVol, orgVol * 0.1);   // large tol
                double priceMrkt      = TREE.optionPrice(func, TREE_DATA_MRKT);
                double impliedVolMrkt = BlackFormulaRepository.impliedVolatility(priceMrkt / dfDom, forward, strike, timeToExpiry, true);
                double orgVolMrkt     = VOLS_MRKT.volatility(FX_PRODUCT.CurrencyPair, timeToExpiry, strike, forward);
                assertEquals(impliedVolMrkt, orgVolMrkt, orgVolMrkt * 0.1);   // large tol
            }
        }