/// <summary> /// Calculates the delta of the FX barrier option product. /// <para> /// The delta is the first derivative of <seealso cref="#price"/> with respect to spot. /// /// </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 delta of the product </returns> public virtual double delta(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { if (volatilities.relativeTime(option.UnderlyingOption.Expiry) < 0d) { return(0d); } ValueDerivatives priceDerivatives = this.priceDerivatives(option, ratesProvider, volatilities); return(priceDerivatives.getDerivative(0)); }
/// <summary> /// Calculates the currency exposure of the FX barrier option product. /// <para> /// This assumes the tree is already calibrated and the tree data is stored as {@code RecombiningTrinomialTreeData}. /// The tree data should be consistent with the pricer and other inputs, see <seealso cref="#validateData"/>. /// /// </para> /// </summary> /// <param name="option"> the option product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="volatilities"> the Black volatility provider </param> /// <param name="treeData"> the trinomial tree data </param> /// <returns> the currency exposure </returns> public virtual MultiCurrencyAmount currencyExposure(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities, RecombiningTrinomialTreeData treeData) { ResolvedFxVanillaOption underlyingOption = option.UnderlyingOption; ValueDerivatives priceDerivatives = this.priceDerivatives(option, ratesProvider, volatilities, treeData); double price = priceDerivatives.Value; double delta = priceDerivatives.getDerivative(0); CurrencyPair currencyPair = underlyingOption.Underlying.CurrencyPair; double todayFx = ratesProvider.fxRate(currencyPair); double signedNotional = this.signedNotional(underlyingOption); CurrencyAmount domestic = CurrencyAmount.of(currencyPair.Counter, (price - delta * todayFx) * signedNotional); CurrencyAmount foreign = CurrencyAmount.of(currencyPair.Base, delta * signedNotional); return(MultiCurrencyAmount.of(domestic, foreign)); }
//------------------------------------------------------------------------- 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"); }
/// <summary> /// Calculates the present value of the FX barrier option product. /// <para> /// The present value of the product is the value on the valuation date. /// It is expressed in the counter currency. /// </para> /// <para> /// This assumes the tree is already calibrated and the tree data is stored as {@code RecombiningTrinomialTreeData}. /// The tree data should be consistent with the pricer and other inputs, see <seealso cref="#validateData"/>. /// /// </para> /// </summary> /// <param name="option"> the option product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="volatilities"> the Black volatility provider </param> /// <param name="treeData"> the trinomial tree data </param> /// <returns> the present value of the product </returns> public virtual CurrencyAmount presentValue(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities, RecombiningTrinomialTreeData treeData) { double price = this.price(option, ratesProvider, volatilities, treeData); ResolvedFxVanillaOption underlyingOption = option.UnderlyingOption; return(CurrencyAmount.of(underlyingOption.CounterCurrency, signedNotional(underlyingOption) * price)); }
/// <summary> /// Calculates the present value sensitivity of the FX barrier option product. /// <para> /// The present value sensitivity of the product is the sensitivity of <seealso cref="#presentValue"/> to /// the underlying curve parameters. /// </para> /// <para> /// The sensitivity is computed by bump and re-price. /// /// </para> /// </summary> /// <param name="option"> the option product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="volatilities"> the Black volatility provider </param> /// <param name="baseTreeData"> the trinomial tree data </param> /// <returns> the present value of the product </returns> public virtual CurrencyParameterSensitivities presentValueSensitivityRates(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities, RecombiningTrinomialTreeData baseTreeData) { ArgChecker.isTrue(baseTreeData.NumberOfSteps == calibrator.NumberOfSteps, "the number of steps mismatch between pricer and trinomial tree data"); double shift = 1.0e-5; CurrencyAmount pvBase = presentValue(option, ratesProvider, volatilities, baseTreeData); ResolvedFxVanillaOption underlyingOption = option.UnderlyingOption; ResolvedFxSingle underlyingFx = underlyingOption.Underlying; CurrencyPair currencyPair = underlyingFx.CurrencyPair; ImmutableRatesProvider immRatesProvider = ratesProvider.toImmutableRatesProvider(); ImmutableMap <Currency, Curve> baseCurves = immRatesProvider.DiscountCurves; CurrencyParameterSensitivities result = CurrencyParameterSensitivities.empty(); foreach (KeyValuePair <Currency, Curve> entry in baseCurves.entrySet()) { if (currencyPair.contains(entry.Key)) { Curve curve = entry.Value; int nParams = curve.ParameterCount; DoubleArray sensitivity = DoubleArray.of(nParams, i => { Curve dscBumped = curve.withParameter(i, curve.getParameter(i) + shift); IDictionary <Currency, Curve> mapBumped = new Dictionary <Currency, Curve>(baseCurves); mapBumped[entry.Key] = dscBumped; ImmutableRatesProvider providerDscBumped = immRatesProvider.toBuilder().discountCurves(mapBumped).build(); double pvBumped = presentValue(option, providerDscBumped, volatilities).Amount; return((pvBumped - pvBase.Amount) / shift); }); result = result.combinedWith(curve.createParameterSensitivity(pvBase.Currency, sensitivity)); } } return(result); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value of the FX barrier option trade. /// <para> /// The present value of the trade is the value on the valuation date. /// </para> /// <para> /// The trinomial tree is first calibrated to Black volatilities, /// then the price is computed based on the calibrated tree. /// /// </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(ResolvedFxSingleBarrierOptionTrade trade, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { ResolvedFxSingleBarrierOption product = trade.Product; CurrencyAmount pvProduct = productPricer.presentValue(product, ratesProvider, volatilities); Payment premium = trade.Premium; CurrencyAmount pvPremium = paymentPricer.presentValue(premium, ratesProvider); return(MultiCurrencyAmount.of(pvProduct, pvPremium)); }
//------------------------------------------------------------------------- /// <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(ResolvedFxSingleBarrierOption, RatesProvider, BlackFxOptionVolatilities) presentValue"/> /// for scaling and currency. /// </para> /// <para> /// The trinomial tree is first calibrated to Black volatilities, /// then the price is computed based on the calibrated tree. /// /// </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) { RecombiningTrinomialTreeData treeData = calibrator.calibrateTrinomialTree(option.UnderlyingOption, ratesProvider, volatilities); return(price(option, ratesProvider, volatilities, treeData)); }
//------------------------------------------------------------------------- /// <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)); }
/// <summary> /// Calibrate trinomial tree to Black volatilities. /// <para> /// {@code timeToExpiry} determines the coverage of the resulting trinomial tree. /// Thus this should match the time to expiry of the target instrument to price using the calibrated tree. /// /// </para> /// </summary> /// <param name="timeToExpiry"> the time to expiry </param> /// <param name="currencyPair"> the currency pair </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(double timeToExpiry, CurrencyPair currencyPair, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { validate(ratesProvider, volatilities); if (timeToExpiry <= 0d) { throw new System.ArgumentException("option expired"); } Currency ccyBase = currencyPair.Base; Currency ccyCounter = currencyPair.Counter; double todayFx = ratesProvider.fxRate(currencyPair); DiscountFactors baseDiscountFactors = ratesProvider.discountFactors(ccyBase); DiscountFactors counterDiscountFactors = ratesProvider.discountFactors(ccyCounter); System.Func <double, double> interestRate = (double?t) => { return(counterDiscountFactors.zeroRate(t.Value)); }; System.Func <double, double> dividendRate = (double?t) => { return(baseDiscountFactors.zeroRate(t.Value)); }; System.Func <DoublesPair, double> impliedVolSurface = (DoublesPair tk) => { double dfBase = baseDiscountFactors.discountFactor(tk.First); double dfCounter = counterDiscountFactors.discountFactor(tk.First); double forward = todayFx * dfBase / dfCounter; return(volatilities.volatility(currencyPair, tk.First, tk.Second, forward)); }; ImpliedTrinomialTreeLocalVolatilityCalculator localVol = new ImpliedTrinomialTreeLocalVolatilityCalculator(nSteps, timeToExpiry); return(localVol.calibrateImpliedVolatility(impliedVolSurface, todayFx, interestRate, dividendRate)); }
//------------------------------------------------------------------------- // 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))); }
//------------------------------------------------------------------------- private void validate(RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { ArgChecker.isTrue(ratesProvider.ValuationDate.isEqual(volatilities.ValuationDateTime.toLocalDate()), "Volatility and rate data must be for the same date"); }
/// <summary> /// Calculates the theta of the FX barrier option product. /// <para> /// The theta is the negative of the first derivative of <seealso cref="#price"/> with respect to time parameter. /// /// </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 theta of the product </returns> public virtual double theta(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { ValueDerivatives priceDerivatives = this.priceDerivatives(option, ratesProvider, volatilities); return(-priceDerivatives.getDerivative(5)); }
//------------------------------------------------------------------------- /// <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. This is also called Black vega. /// /// </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(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { ResolvedFxVanillaOption underlyingOption = option.UnderlyingOption; if (volatilities.relativeTime(underlyingOption.Expiry) <= 0d) { return(PointSensitivityBuilder.none()); } ValueDerivatives priceDerivatives = this.priceDerivatives(option, ratesProvider, volatilities); ResolvedFxSingle underlyingFx = underlyingOption.Underlying; CurrencyPair currencyPair = underlyingFx.CurrencyPair; Currency ccyBase = currencyPair.Base; Currency ccyCounter = currencyPair.Counter; double dfBase = ratesProvider.discountFactor(ccyBase, underlyingFx.PaymentDate); double dfCounter = ratesProvider.discountFactor(ccyCounter, underlyingFx.PaymentDate); double todayFx = ratesProvider.fxRate(currencyPair); double forward = todayFx * dfBase / dfCounter; return(FxOptionSensitivity.of(volatilities.Name, currencyPair, volatilities.relativeTime(underlyingOption.Expiry), underlyingOption.Strike, forward, ccyCounter, priceDerivatives.getDerivative(4) * signedNotional(underlyingOption))); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value gamma of the FX barrier option product. /// <para> /// The present value gamma is the second derivative of <seealso cref="#presentValue"/> with respect to spot. /// /// </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 gamma of the product </returns> public virtual CurrencyAmount presentValueGamma(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { double gamma = this.gamma(option, ratesProvider, volatilities); ResolvedFxVanillaOption underlyingOption = option.UnderlyingOption; return(CurrencyAmount.of(underlyingOption.CounterCurrency, signedNotional(underlyingOption) * gamma)); }
//------------------------------------------------------------------------- /// <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, BlackFxOptionVolatilities volatilities) { CurrencyPair strikePair = option.Underlying.CurrencyPair; double price = this.price(option, ratesProvider, volatilities); double delta = this.delta(option, ratesProvider, volatilities); double spot = ratesProvider.fxRate(strikePair); double signedNotional = this.signedNotional(option); CurrencyAmount domestic = CurrencyAmount.of(strikePair.Counter, (price - delta * spot) * signedNotional); CurrencyAmount foreign = CurrencyAmount.of(strikePair.Base, delta * signedNotional); return(MultiCurrencyAmount.of(domestic, foreign)); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the delta of the foreign exchange vanilla option product. /// <para> /// The delta is the first derivative of <seealso cref="#price"/> with respect to spot. /// /// </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 delta of the product </returns> public virtual double delta(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { ResolvedFxSingle underlying = option.Underlying; double fwdDelta = undiscountedDelta(option, ratesProvider, volatilities); double discountFactor = ratesProvider.discountFactor(option.CounterCurrency, underlying.PaymentDate); double fwdRateSpotSensitivity = fxPricer.forwardFxRateSpotSensitivity(option.PutCall.Call ? underlying : underlying.inverse(), ratesProvider); return(fwdDelta * discountFactor * fwdRateSpotSensitivity); }
//------------------------------------------------------------------------- /// <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, BlackFxOptionVolatilities volatilities) { ResolvedFxSingle underlying = option.Underlying; double forwardPrice = undiscountedPrice(option, ratesProvider, volatilities); double discountFactor = ratesProvider.discountFactor(option.CounterCurrency, underlying.PaymentDate); return(discountFactor * forwardPrice); }
/// <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 volatility is 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 PointSensitivities presentValueSensitivityRatesStickyStrike(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { if (volatilities.relativeTime(option.Expiry) < 0d) { return(PointSensitivities.empty()); } ResolvedFxSingle underlying = option.Underlying; double fwdDelta = undiscountedDelta(option, ratesProvider, volatilities); double discountFactor = ratesProvider.discountFactor(option.CounterCurrency, underlying.PaymentDate); double notional = signedNotional(option); PointSensitivityBuilder fwdSensi = fxPricer.forwardFxRatePointSensitivity(option.PutCall.Call ? underlying : underlying.inverse(), ratesProvider).multipliedBy(notional * discountFactor * fwdDelta); double fwdPrice = undiscountedPrice(option, ratesProvider, volatilities); PointSensitivityBuilder dscSensi = ratesProvider.discountFactors(option.CounterCurrency).zeroRatePointSensitivity(underlying.PaymentDate).multipliedBy(notional * fwdPrice); return(fwdSensi.combinedWith(dscSensi).build().convertedTo(option.CounterCurrency, ratesProvider)); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value sensitivity of the FX barrier option trade. /// <para> /// The present value sensitivity of the trade is the sensitivity of the present value to /// the underlying curves. /// </para> /// <para> /// The sensitivity is computed by bump and re-price, returning <seealso cref="CurrencyParameterSensitivities"/>, /// not <seealso cref="PointSensitivities"/>. /// </para> /// <para> /// The trinomial tree is first calibrated to Black volatilities, /// then the price is computed based on the calibrated tree. /// /// </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 CurrencyParameterSensitivities presentValueSensitivityRates(ResolvedFxSingleBarrierOptionTrade trade, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { ResolvedFxSingleBarrierOption product = trade.Product; CurrencyParameterSensitivities sensProduct = productPricer.presentValueSensitivityRates(product, ratesProvider, volatilities); Payment premium = trade.Premium; PointSensitivityBuilder pvcsPremium = paymentPricer.presentValueSensitivity(premium, ratesProvider); CurrencyParameterSensitivities sensPremium = ratesProvider.parameterSensitivity(pvcsPremium.build()); return(sensProduct.combinedWith(sensPremium)); }
// the delta without discounting private double undiscountedDelta(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { double timeToExpiry = volatilities.relativeTime(option.Expiry); if (timeToExpiry < 0d) { return(0d); } ResolvedFxSingle underlying = option.Underlying; FxRate forward = fxPricer.forwardFxRate(underlying, ratesProvider); CurrencyPair strikePair = underlying.CurrencyPair; double forwardRate = forward.fxRate(strikePair); double strikeRate = option.Strike; bool isCall = option.PutCall.Call; if (timeToExpiry == 0d) { return(isCall ? (forwardRate > strikeRate ? 1d : 0d) : (strikeRate > forwardRate ? -1d : 0d)); } double volatility = volatilities.volatility(strikePair, option.Expiry, strikeRate, forwardRate); return(BlackFormulaRepository.delta(forwardRate, strikeRate, timeToExpiry, volatility, isCall)); }
/// <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(ResolvedFxSingleBarrierOption, RatesProvider, BlackFxOptionVolatilities, RecombiningTrinomialTreeData) presnetValue"/> /// for scaling and currency. /// </para> /// <para> /// This assumes the tree is already calibrated and the tree data is stored as {@code RecombiningTrinomialTreeData}. /// The tree data should be consistent with the pricer and other inputs, see <seealso cref="#validateData"/>. /// /// </para> /// </summary> /// <param name="option"> the option product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="volatilities"> the Black volatility provider </param> /// <param name="treeData"> the trinomial tree data </param> /// <returns> the price of the product </returns> public virtual double price(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities, RecombiningTrinomialTreeData treeData) { return(priceDerivatives(option, ratesProvider, volatilities, treeData).Value); }
/// <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="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, BlackFxOptionVolatilities volatilities) { if (volatilities.relativeTime(option.Expiry) <= 0d) { return(PointSensitivityBuilder.none()); } ResolvedFxSingle underlying = option.Underlying; FxRate forward = fxPricer.forwardFxRate(underlying, ratesProvider); CurrencyPair strikePair = underlying.CurrencyPair; CurrencyAmount valueVega = presentValueVega(option, ratesProvider, volatilities); return(FxOptionSensitivity.of(volatilities.Name, strikePair, volatilities.relativeTime(option.Expiry), option.Strike, forward.fxRate(strikePair), valueVega.Currency, valueVega.Amount)); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value sensitivity of the FX barrier option product. /// <para> /// The present value sensitivity of the product is the sensitivity of <seealso cref="#presentValue"/> to /// the underlying curve parameters. /// </para> /// <para> /// The sensitivity is computed by bump and re-price. /// /// </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 of the product </returns> public virtual CurrencyParameterSensitivities presentValueSensitivityRates(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { RecombiningTrinomialTreeData baseTreeData = calibrator.calibrateTrinomialTree(option.UnderlyingOption, ratesProvider, volatilities); return(presentValueSensitivityRates(option, ratesProvider, volatilities, baseTreeData)); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the Black theta of the foreign exchange vanilla option product. /// <para> /// The theta is the negative of the first derivative of <seealso cref="#price"/> with respect to time parameter /// in Black formula (the discounted driftless theta). /// /// </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 theta of the product </returns> public virtual double theta(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { double timeToExpiry = volatilities.relativeTime(option.Expiry); if (timeToExpiry <= 0d) { return(0d); } ResolvedFxSingle underlying = option.Underlying; FxRate forward = fxPricer.forwardFxRate(underlying, ratesProvider); CurrencyPair strikePair = underlying.CurrencyPair; double forwardRate = forward.fxRate(strikePair); double strikeRate = option.Strike; double volatility = volatilities.volatility(strikePair, option.Expiry, strikeRate, forwardRate); double fwdTheta = BlackFormulaRepository.driftlessTheta(forwardRate, strikeRate, timeToExpiry, volatility); double discountFactor = ratesProvider.discountFactor(option.CounterCurrency, underlying.PaymentDate); return(discountFactor * fwdTheta); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the currency exposure of the FX barrier option product. /// <para> /// The trinomial tree is first calibrated to Black volatilities, /// then the price is computed based on the calibrated tree. /// /// </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 currency exposure </returns> public virtual MultiCurrencyAmount currencyExposure(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { RecombiningTrinomialTreeData treeData = calibrator.calibrateTrinomialTree(option.UnderlyingOption, ratesProvider, volatilities); return(currencyExposure(option, ratesProvider, volatilities, treeData)); }
/// <summary> /// Calculates the present value theta of the foreign exchange vanilla option product. /// <para> /// The present value theta is the negative of the first derivative of <seealso cref="#presentValue"/> with time parameter /// in Black formula, i.e., the driftless theta of the present value. /// /// </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 vega of the product </returns> public virtual CurrencyAmount presentValueTheta(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { double theta = this.theta(option, ratesProvider, volatilities); return(CurrencyAmount.of(option.CounterCurrency, signedNotional(option) * theta)); }
//------------------------------------------------------------------------- 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); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the implied Black volatility 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 implied volatility of the product </returns> /// <exception cref="IllegalArgumentException"> if the option has expired </exception> public virtual double impliedVolatility(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { double timeToExpiry = volatilities.relativeTime(option.Expiry); if (timeToExpiry <= 0d) { throw new System.ArgumentException("valuation is after option's expiry."); } FxRate forward = fxPricer.forwardFxRate(option.Underlying, ratesProvider); CurrencyPair strikePair = option.Underlying.CurrencyPair; return(volatilities.volatility(strikePair, option.Expiry, option.Strike, forward.fxRate(strikePair))); }
private void validate(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { ArgChecker.isTrue(option.Barrier is SimpleConstantContinuousBarrier, "barrier should be SimpleConstantContinuousBarrier"); ArgChecker.isTrue(ratesProvider.ValuationDate.isEqual(volatilities.ValuationDateTime.toLocalDate()), "Volatility and rate data must be for the same date"); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value sensitivity of the FX barrier option product. /// <para> /// The present value sensitivity of the product is the sensitivity of <seealso cref="#presentValue"/> to /// the underlying curves. /// </para> /// <para> /// The volatility is fixed in this sensitivity computation, i.e., sticky-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 present value curve sensitivity of the product </returns> public virtual PointSensitivityBuilder presentValueSensitivityRatesStickyStrike(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { ResolvedFxVanillaOption underlyingOption = option.UnderlyingOption; if (volatilities.relativeTime(underlyingOption.Expiry) <= 0d) { return(PointSensitivityBuilder.none()); } ValueDerivatives priceDerivatives = this.priceDerivatives(option, ratesProvider, volatilities); ResolvedFxSingle underlyingFx = underlyingOption.Underlying; CurrencyPair currencyPair = underlyingFx.CurrencyPair; double signedNotional = this.signedNotional(underlyingOption); double counterYearFraction = ratesProvider.discountFactors(currencyPair.Counter).relativeYearFraction(underlyingFx.PaymentDate); ZeroRateSensitivity counterSensi = ZeroRateSensitivity.of(currencyPair.Counter, counterYearFraction, signedNotional * (priceDerivatives.getDerivative(2) + priceDerivatives.getDerivative(3))); double baseYearFraction = ratesProvider.discountFactors(currencyPair.Base).relativeYearFraction(underlyingFx.PaymentDate); ZeroRateSensitivity baseSensi = ZeroRateSensitivity.of(currencyPair.Base, baseYearFraction, currencyPair.Counter, -priceDerivatives.getDerivative(3) * signedNotional); return(counterSensi.combinedWith(baseSensi)); }