//------------------------------------------------------------------------- /// <summary> /// Calibrate trinomial tree to Black volatilities by using a vanilla option. /// <para> /// {@code ResolvedFxVanillaOption} is typically the underlying option of an exotic instrument to price using the /// calibrated tree, and is used to ensure that the grid points properly cover the lifetime of the target option. /// /// </para> /// </summary> /// <param name="option"> the vanilla option </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="volatilities"> the Black volatility provider </param> /// <returns> the trinomial tree data </returns> public virtual RecombiningTrinomialTreeData calibrateTrinomialTree(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { double timeToExpiry = volatilities.relativeTime(option.Expiry); CurrencyPair currencyPair = option.Underlying.CurrencyPair; return(calibrateTrinomialTree(timeToExpiry, currencyPair, ratesProvider, volatilities)); }
//------------------------------------------------------------------------- public virtual void test_presentValueSensitivity() { for (int i = 0; i < NB_STRIKES; ++i) { ResolvedFxVanillaOption option = CALLS[i]; PointSensitivityBuilder point = PRICER.presentValueSensitivityRatesStickyStrike(option, RATES_PROVIDER, VOLS); CurrencyParameterSensitivities sensiComputed = RATES_PROVIDER.parameterSensitivity(point.build()); double timeToExpiry = VOLS.relativeTime(EXPIRY); double forwardRate = FX_PRICER.forwardFxRate(UNDERLYING[i], RATES_PROVIDER).fxRate(CURRENCY_PAIR); double strikeRate = option.Strike; SmileDeltaParameters smileAtTime = VOLS.Smile.smileForExpiry(timeToExpiry); double[] vols = smileAtTime.Volatility.toArray(); double df = RATES_PROVIDER.discountFactor(USD, PAY); CurrencyParameterSensitivities sensiExpected = FD_CAL.sensitivity(RATES_PROVIDER, p => PRICER.presentValue(option, p, VOLS)); CurrencyParameterSensitivities sensiRes = FD_CAL.sensitivity(RATES_PROVIDER, (ImmutableRatesProvider p) => { double fwd = FX_PRICER.forwardFxRate(option.Underlying, p).fxRate(CURRENCY_PAIR); double[] strs = smileAtTime.strike(fwd).toArray(); double[] wghts = weights(fwd, strikeRate, strs, timeToExpiry, vols[1]); double res = 0d; for (int j = 0; j < 3; ++j) { res += wghts[j] * (BlackFormulaRepository.price(forwardRate, strs[j], timeToExpiry, vols[j], true) - BlackFormulaRepository.price(forwardRate, strs[j], timeToExpiry, vols[1], true)); } return(CurrencyAmount.of(USD, -res * df * NOTIONAL)); }); assertTrue(sensiComputed.equalWithTolerance(sensiExpected.combinedWith(sensiRes), FD_EPS * NOTIONAL * 10d)); } }
//------------------------------------------------------------------------- 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)); } }
//------------------------------------------------------------------------- /// <summary> /// Calculates the currency exposure of the FX vanilla option trade. /// </summary> /// <param name="trade"> the option trade </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(ResolvedFxVanillaOptionTrade trade, RatesProvider ratesProvider, BlackFxOptionSmileVolatilities volatilities) { Payment premium = trade.Premium; CurrencyAmount pvPremium = paymentPricer.presentValue(premium, ratesProvider); ResolvedFxVanillaOption product = trade.Product; return(productPricer.currencyExposure(product, ratesProvider, volatilities).plus(pvPremium)); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value sensitivity of the FX vanilla option trade. /// <para> /// The present value sensitivity of the trade is the sensitivity of the present value to /// the underlying curves. /// </para> /// <para> /// The volatility is fixed in this sensitivity computation. /// /// </para> /// </summary> /// <param name="trade"> the option trade </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="volatilities"> the Black volatility provider </param> /// <returns> the present value curve sensitivity of the trade </returns> public virtual PointSensitivities presentValueSensitivityRatesStickyStrike(ResolvedFxVanillaOptionTrade trade, RatesProvider ratesProvider, BlackFxOptionSmileVolatilities volatilities) { ResolvedFxVanillaOption product = trade.Product; PointSensitivities pvcsProduct = productPricer.presentValueSensitivityRatesStickyStrike(product, ratesProvider, volatilities).build(); Payment premium = trade.Premium; PointSensitivities pvcsPremium = paymentPricer.presentValueSensitivity(premium, ratesProvider).build(); return(pvcsProduct.combinedWith(pvcsPremium)); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value of the FX vanilla option trade. /// <para> /// The present value of the trade is the value on the valuation date. /// /// </para> /// </summary> /// <param name="trade"> the option trade </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="volatilities"> the Black volatility provider </param> /// <returns> the present value of the trade </returns> public virtual MultiCurrencyAmount presentValue(ResolvedFxVanillaOptionTrade trade, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { ResolvedFxVanillaOption product = trade.Product; CurrencyAmount pvProduct = productPricer.presentValue(product, ratesProvider, volatilities); Payment premium = trade.Premium; CurrencyAmount pvPremium = paymentPricer.presentValue(premium, ratesProvider); return(MultiCurrencyAmount.of(pvProduct).plus(pvPremium)); }
static VannaVolgaFxVanillaOptionProductPricerTest() { for (int i = 0; i < NB_STRIKES; ++i) { double strike = STRIKE_MIN + i * STRIKE_RANGE / (NB_STRIKES - 1d); CurrencyAmount eurAmount = CurrencyAmount.of(EUR, NOTIONAL); CurrencyAmount usdAmount = CurrencyAmount.of(USD, -NOTIONAL * strike); UNDERLYING[i] = ResolvedFxSingle.of(eurAmount, usdAmount, PAY); CALLS[i] = ResolvedFxVanillaOption.builder().longShort(LONG).expiry(EXPIRY).underlying(UNDERLYING[i]).build(); PUTS[i] = ResolvedFxVanillaOption.builder().longShort(SHORT).expiry(EXPIRY).underlying(UNDERLYING[i].inverse()).build(); } }
public virtual void test_price_presentValue_afterExpiry() { for (int i = 0; i < NB_STRIKES; ++i) { ResolvedFxVanillaOption call = CALLS[i]; ResolvedFxVanillaOptionTrade callTrade = ResolvedFxVanillaOptionTrade.builder().product(call).premium(Payment.of(EUR, 0, VOLS_AFTER.ValuationDate)).build(); double computedPriceCall = PRICER.price(call, RATES_PROVIDER_AFTER, VOLS_AFTER); CurrencyAmount computedCall = PRICER.presentValue(call, RATES_PROVIDER_AFTER, VOLS_AFTER); assertEquals(computedPriceCall, 0d, TOL); assertEquals(computedCall.Amount, 0d, TOL); ResolvedFxVanillaOption put = PUTS[i]; ResolvedFxVanillaOptionTrade putTrade = ResolvedFxVanillaOptionTrade.builder().product(put).premium(Payment.of(EUR, 0, VOLS_AFTER.ValuationDate)).build(); double computedPricePut = PRICER.price(put, RATES_PROVIDER_AFTER, VOLS_AFTER); CurrencyAmount computedPut = PRICER.presentValue(put, RATES_PROVIDER_AFTER, VOLS_AFTER); assertEquals(computedPricePut, 0d, TOL); assertEquals(computedPut.Amount, 0d, TOL); // test against trade pricer assertEquals(computedCall, TRADE_PRICER.presentValue(callTrade, RATES_PROVIDER_AFTER, VOLS_AFTER).getAmount(USD)); assertEquals(computedPut, TRADE_PRICER.presentValue(putTrade, RATES_PROVIDER_AFTER, VOLS_AFTER).getAmount(USD)); } }
//------------------------------------------------------------------------- /// <summary> /// Computes the present value sensitivity to the black volatility used in the pricing. /// <para> /// The result is a single sensitivity to the volatility used. /// /// </para> /// </summary> /// <param name="trade"> the option trade </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="volatilities"> the Black volatility provider </param> /// <returns> the present value sensitivity </returns> public virtual PointSensitivities presentValueSensitivityModelParamsVolatility(ResolvedFxVanillaOptionTrade trade, RatesProvider ratesProvider, BlackFxOptionSmileVolatilities volatilities) { ResolvedFxVanillaOption product = trade.Product; return(productPricer.presentValueSensitivityModelParamsVolatility(product, ratesProvider, volatilities).build()); }