//------------------------------------------------------------------------- // 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))); }
public virtual void test_trinomialTree_down() { int nSteps = 133; LatticeSpecification lattice = new CoxRossRubinsteinLatticeSpecification(); DoubleArray rebate = DoubleArray.of(nSteps + 1, i => REBATE_AMOUNT); double barrierLevel = 76d; double tol = 1.0e-2; foreach (bool isCall in new bool[] { true, false }) { foreach (double strike in STRIKES) { foreach (double interest in INTERESTS) { foreach (double vol in VOLS) { foreach (double dividend in DIVIDENDS) { OptionFunction function = ConstantContinuousSingleBarrierKnockoutFunction.of(strike, TIME, PutCall.ofPut(!isCall), nSteps, BarrierType.DOWN, barrierLevel, rebate); SimpleConstantContinuousBarrier barrier = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_OUT, barrierLevel); double exact = REBATE_AMOUNT * REBATE_PRICER.price(SPOT, TIME, interest - dividend, interest, vol, barrier.inverseKnockType()) + BARRIER_PRICER.price(SPOT, strike, TIME, interest - dividend, interest, vol, isCall, barrier); double computed = TRINOMIAL_TREE.optionPrice(function, lattice, SPOT, vol, interest, dividend); assertEquals(computed, exact, Math.Max(exact, 1d) * 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); }