예제 #1
0
 //-------------------------------------------------------------------------
 public virtual void test_price_presentValue()
 {
     for (int i = 0; i < NB_STRIKES; ++i)
     {
         ResolvedFxVanillaOption      call      = CALLS[i];
         ResolvedFxVanillaOptionTrade callTrade = ResolvedFxVanillaOptionTrade.builder().product(call).premium(Payment.of(EUR, 0, VAL_DATE)).build();
         double               computedPriceCall = PRICER.price(call, RATES_PROVIDER, VOLS);
         CurrencyAmount       computedCall      = PRICER.presentValue(call, RATES_PROVIDER, VOLS);
         double               timeToExpiry      = VOLS.relativeTime(EXPIRY);
         FxRate               forward           = FX_PRICER.forwardFxRate(UNDERLYING[i], RATES_PROVIDER);
         double               forwardRate       = forward.fxRate(CURRENCY_PAIR);
         double               strikeRate        = call.Strike;
         SmileDeltaParameters smileAtTime       = VOLS.Smile.smileForExpiry(timeToExpiry);
         double[]             strikes           = smileAtTime.strike(forwardRate).toArray();
         double[]             vols              = smileAtTime.Volatility.toArray();
         double               df                = RATES_PROVIDER.discountFactor(USD, PAY);
         double[]             weights           = this.weights(forwardRate, strikeRate, strikes, timeToExpiry, vols[1]);
         double               expectedPriceCall = BlackFormulaRepository.price(forwardRate, strikeRate, timeToExpiry, vols[1], true);
         for (int j = 0; j < 3; ++j)
         {
             expectedPriceCall += weights[j] * (BlackFormulaRepository.price(forwardRate, strikes[j], timeToExpiry, vols[j], true) - BlackFormulaRepository.price(forwardRate, strikes[j], timeToExpiry, vols[1], true));
         }
         expectedPriceCall *= df;
         assertEquals(computedPriceCall, expectedPriceCall, TOL);
         assertEquals(computedCall.Amount, expectedPriceCall * NOTIONAL, TOL * NOTIONAL);
         // test against trade pricer
         assertEquals(computedCall, TRADE_PRICER.presentValue(callTrade, RATES_PROVIDER, VOLS).getAmount(USD));
     }
 }
예제 #2
0
 //-------------------------------------------------------------------------
 public virtual void test_volatility()
 {
     for (int i = 0; i < NB_EXPIRY; i++)
     {
         double expiryTime = VOLS.relativeTime(TEST_EXPIRY[i]);
         for (int j = 0; j < NB_STRIKE; ++j)
         {
             double volExpected = SMILE_TERM.volatility(expiryTime, TEST_STRIKE[j], FORWARD[i]);
             double volComputed = VOLS.volatility(CURRENCY_PAIR, TEST_EXPIRY[i], TEST_STRIKE[j], FORWARD[i]);
             assertEquals(volComputed, volExpected, TOLERANCE);
         }
     }
 }
예제 #3
0
        //-------------------------------------------------------------------------
        /// <summary>
        /// Calculates the price of the foreign exchange vanilla option product.
        /// <para>
        /// The price of the product is the value on the valuation date for one unit of the base currency
        /// and is expressed in the counter currency. The price does not take into account the long/short flag.
        /// See <seealso cref="#presentValue"/> for scaling and currency.
        ///
        /// </para>
        /// </summary>
        /// <param name="option">  the option product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="volatilities">  the Black volatility provider </param>
        /// <returns> the price of the product </returns>
        public virtual double price(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionSmileVolatilities volatilities)
        {
            validate(ratesProvider, volatilities);
            double timeToExpiry = volatilities.relativeTime(option.Expiry);

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

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

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

            for (int i = 0; i < 3; i += 2)
            {
                double priceFwdAtm   = BlackFormulaRepository.price(forwardRate, strikes[i], timeToExpiry, volAtm, isCall);
                double priceFwdSmile = BlackFormulaRepository.price(forwardRate, strikes[i], timeToExpiry, vols[i], isCall);
                priceFwd += x[i] * (priceFwdSmile - priceFwdAtm);
            }
            return(df * priceFwd);
        }
예제 #4
0
        /// <summary>
        /// Calculates the currency exposure of the foreign exchange vanilla option product.
        /// </summary>
        /// <param name="option">  the option product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="volatilities">  the Black volatility provider </param>
        /// <returns> the currency exposure </returns>
        public virtual MultiCurrencyAmount currencyExposure(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionSmileVolatilities volatilities)
        {
            validate(ratesProvider, volatilities);
            double timeToExpiry = volatilities.relativeTime(option.Expiry);

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

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

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

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

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

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

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

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

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

            return(dfSensi.combinedWith(fwdSensi));
        }
예제 #6
0
        /// <summary>
        /// Computes the present value sensitivity to the black volatilities used in the pricing.
        /// <para>
        /// The implied strikes and weights are fixed in this sensitivity computation.
        ///
        /// </para>
        /// </summary>
        /// <param name="option">  the option product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="volatilities">  the Black volatility provider </param>
        /// <returns> the present value sensitivity </returns>
        public virtual PointSensitivityBuilder presentValueSensitivityModelParamsVolatility(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionSmileVolatilities volatilities)
        {
            validate(ratesProvider, volatilities);
            double timeToExpiry = volatilities.relativeTime(option.Expiry);

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

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

            double[] x                         = vannaVolgaWeights(forwardRate, strikeRate, timeToExpiry, volAtm, strikes);
            double   vegaAtm                   = BlackFormulaRepository.vega(forwardRate, strikeRate, timeToExpiry, volAtm);
            double   signedNotional            = this.signedNotional(option);
            PointSensitivityBuilder sensiSmile = PointSensitivityBuilder.none();

            for (int i = 0; i < 3; i += 2)
            {
                double vegaFwdAtm = BlackFormulaRepository.vega(forwardRate, strikes[i], timeToExpiry, volAtm);
                vegaAtm -= x[i] * vegaFwdAtm;
                double vegaFwdSmile = BlackFormulaRepository.vega(forwardRate, strikes[i], timeToExpiry, vols[i]);
                sensiSmile = sensiSmile.combinedWith(FxOptionSensitivity.of(volatilities.Name, currencyPair, timeToExpiry, strikes[i], forwardRate, ccyCounter, df * signedNotional * x[i] * vegaFwdSmile));
            }
            FxOptionSensitivity sensiAtm = FxOptionSensitivity.of(volatilities.Name, currencyPair, timeToExpiry, strikes[1], forwardRate, ccyCounter, df * signedNotional * vegaAtm);

            return(sensiAtm.combinedWith(sensiSmile));
        }
        //-------------------------------------------------------------------------
        public virtual void test_price_presentValue()
        {
            double         priceCallOtm         = PRICER.price(CALL_OTM, RATES_PROVIDER, VOLS);
            CurrencyAmount pvCallOtm            = PRICER.presentValue(CALL_OTM, RATES_PROVIDER, VOLS);
            double         pricePutOtm          = PRICER.price(PUT_OTM, RATES_PROVIDER, VOLS);
            CurrencyAmount pvPutOtm             = PRICER.presentValue(PUT_OTM, RATES_PROVIDER, VOLS);
            double         timeToExpiry         = VOLS.relativeTime(EXPIRY);
            double         df                   = RATES_PROVIDER.discountFactor(USD, PAYMENT_DATE);
            double         forward              = PRICER.DiscountingFxSingleProductPricer.forwardFxRate(FX_PRODUCT_HIGH, RATES_PROVIDER).fxRate(CURRENCY_PAIR);
            double         volHigh              = SMILE_TERM.volatility(timeToExpiry, STRIKE_RATE_HIGH, forward);
            double         volLow               = SMILE_TERM.volatility(timeToExpiry, STRIKE_RATE_LOW, forward);
            double         expectedPriceCallOtm = df * BlackFormulaRepository.price(forward, STRIKE_RATE_HIGH, timeToExpiry, volHigh, true);
            double         expectedPricePutOtm  = df * BlackFormulaRepository.price(forward, STRIKE_RATE_LOW, timeToExpiry, volLow, false);
            double         expectedPvCallOtm    = -NOTIONAL *df *BlackFormulaRepository.price(forward, STRIKE_RATE_HIGH, timeToExpiry, volHigh, true);

            double expectedPvPutOtm = -NOTIONAL *df *BlackFormulaRepository.price(forward, STRIKE_RATE_LOW, timeToExpiry, volLow, false);

            assertEquals(priceCallOtm, expectedPriceCallOtm, TOL);
            assertEquals(pvCallOtm.Currency, USD);
            assertEquals(pvCallOtm.Amount, expectedPvCallOtm, NOTIONAL * TOL);
            assertEquals(pricePutOtm, expectedPricePutOtm, TOL);
            assertEquals(pvPutOtm.Currency, USD);
            assertEquals(pvPutOtm.Amount, expectedPvPutOtm, NOTIONAL * TOL);
        }
예제 #8
0
        //-------------------------------------------------------------------------
        // bumping a node point at (nodeExpiry, nodeDelta)
        private double nodeSensitivity(BlackFxOptionSmileVolatilities provider, CurrencyPair pair, ZonedDateTime expiry, double strike, double forward, double nodeExpiry, double nodeDelta)
        {
            double strikeMod  = provider.CurrencyPair.Equals(pair) ? strike : 1.0 / strike;
            double forwardMod = provider.CurrencyPair.Equals(pair) ? forward : 1.0 / forward;

            InterpolatedStrikeSmileDeltaTermStructure smileTerm = (InterpolatedStrikeSmileDeltaTermStructure)provider.Smile;

            double[] times  = smileTerm.Expiries.toArray();
            int      nTimes = times.Length;

            SmileDeltaParameters[] volTermUp = new SmileDeltaParameters[nTimes];
            SmileDeltaParameters[] volTermDw = new SmileDeltaParameters[nTimes];
            int deltaIndex = -1;

            for (int i = 0; i < nTimes; ++i)
            {
                DoubleArray deltas       = smileTerm.VolatilityTerm.get(i).Delta;
                int         nDeltas      = deltas.size();
                int         nDeltasTotal = 2 * nDeltas + 1;
                double[]    deltasTotal  = new double[nDeltasTotal];
                deltasTotal[nDeltas] = 0.5d;
                for (int j = 0; j < nDeltas; ++j)
                {
                    deltasTotal[j] = 1d - deltas.get(j);
                    deltasTotal[2 * nDeltas - j] = deltas.get(j);
                }
                double[] volsUp = smileTerm.VolatilityTerm.get(i).Volatility.toArray();
                double[] volsDw = smileTerm.VolatilityTerm.get(i).Volatility.toArray();
                if (Math.Abs(times[i] - nodeExpiry) < TOLERANCE)
                {
                    for (int j = 0; j < nDeltasTotal; ++j)
                    {
                        if (Math.Abs(deltasTotal[j] - nodeDelta) < TOLERANCE)
                        {
                            deltaIndex = j;
                            volsUp[j] += EPS;
                            volsDw[j] -= EPS;
                        }
                    }
                }
                volTermUp[i] = SmileDeltaParameters.of(times[i], deltas, DoubleArray.copyOf(volsUp));
                volTermDw[i] = SmileDeltaParameters.of(times[i], deltas, DoubleArray.copyOf(volsDw));
            }
            InterpolatedStrikeSmileDeltaTermStructure smileTermUp = InterpolatedStrikeSmileDeltaTermStructure.of(ImmutableList.copyOf(volTermUp), ACT_365F);
            InterpolatedStrikeSmileDeltaTermStructure smileTermDw = InterpolatedStrikeSmileDeltaTermStructure.of(ImmutableList.copyOf(volTermDw), ACT_365F);
            BlackFxOptionSmileVolatilities            provUp      = BlackFxOptionSmileVolatilities.of(NAME, CURRENCY_PAIR, VAL_DATE_TIME, smileTermUp);
            BlackFxOptionSmileVolatilities            provDw      = BlackFxOptionSmileVolatilities.of(NAME, CURRENCY_PAIR, VAL_DATE_TIME, smileTermDw);
            double volUp      = provUp.volatility(pair, expiry, strike, forward);
            double volDw      = provDw.volatility(pair, expiry, strike, forward);
            double totalSensi = 0.5 * (volUp - volDw) / EPS;

            double expiryTime = provider.relativeTime(expiry);
            SmileDeltaParameters singleSmile = smileTerm.smileForExpiry(expiryTime);

            double[] strikesUp = singleSmile.strike(forwardMod).toArray();
            double[] strikesDw = strikesUp.Clone();
            double[] vols      = singleSmile.Volatility.toArray();
            strikesUp[deltaIndex] += EPS;
            strikesDw[deltaIndex] -= EPS;
            double volStrikeUp = LINEAR.bind(DoubleArray.ofUnsafe(strikesUp), DoubleArray.ofUnsafe(vols), FLAT, FLAT).interpolate(strikeMod);
            double volStrikeDw = LINEAR.bind(DoubleArray.ofUnsafe(strikesDw), DoubleArray.ofUnsafe(vols), FLAT, FLAT).interpolate(strikeMod);
            double sensiStrike = 0.5 * (volStrikeUp - volStrikeDw) / EPS;
            SmileDeltaParameters singleSmileUp = smileTermUp.smileForExpiry(expiryTime);
            double strikeUp = singleSmileUp.strike(forwardMod).get(deltaIndex);
            SmileDeltaParameters singleSmileDw = smileTermDw.smileForExpiry(expiryTime);
            double strikeDw = singleSmileDw.strike(forwardMod).get(deltaIndex);
            double sensiVol = 0.5 * (strikeUp - strikeDw) / EPS;

            return(totalSensi - sensiStrike * sensiVol);
        }