//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; } }
/// <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 theta of the FX barrier option product. /// <para> /// The present value theta is the negative of the first derivative of <seealso cref="#presentValue"/> with 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 present value theta of the product </returns> public virtual CurrencyAmount presentValueTheta(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { double theta = this.theta(option, ratesProvider, volatilities); ResolvedFxVanillaOption underlyingOption = option.UnderlyingOption; return(CurrencyAmount.of(underlyingOption.CounterCurrency, signedNotional(underlyingOption) * theta)); }
//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 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)); }
//------------------------------------------------------------------------- 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 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)); }
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))); }
//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); } }
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> /// 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 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)); }
/// <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> /// 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 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> /// 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 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); }