//------------------------------------------------------------------------- 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); }
//------------------------------------------------------------------------- public virtual void test_presentValue() { CurrencyAmount pvComputed = PRICER.presentValue(BILL, PROVIDER); double pvExpected = DSC_FACTORS_ISSUER.discountFactor(MATURITY_DATE) * NOTIONAL.Amount; assertEquals(pvComputed.Currency, EUR); assertEquals(pvComputed.Amount, pvExpected, TOLERANCE_PV); }
//------------------------------------------------------------------------- public virtual void test_dirtyPriceFromCurves() { double computed = PRICER.dirtyPriceFromCurves(PRODUCT, PROVIDER, REF_DATA); CurrencyAmount pv = PRICER.presentValue(PRODUCT, PROVIDER); LocalDate settlement = DATE_OFFSET.adjust(VAL_DATE, REF_DATA); double df = DSC_FACTORS_REPO.discountFactor(settlement); assertEquals(computed, pv.Amount / df / NOTIONAL); }
public virtual void test_discountFactorTimeDerivative() { DiscountFactors test = DiscountFactors.of(GBP, DATE_VAL, CURVE); double relativeYearFraction = ACT_365F.relativeYearFraction(DATE_VAL, DATE_AFTER); double expectedP = test.discountFactor(relativeYearFraction + EPS); double expectedM = test.discountFactor(relativeYearFraction - EPS); assertEquals(test.discountFactorTimeDerivative(relativeYearFraction), (expectedP - expectedM) / (2 * EPS), TOL_FD); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the deposit fair rate given the start and end time and the accrual factor. /// <para> /// When the deposit has already started the number may not be meaningful as the remaining period /// is not in line with the accrual factor. /// /// </para> /// </summary> /// <param name="deposit"> the product </param> /// <param name="provider"> the rates provider </param> /// <returns> the par rate </returns> public virtual double parRate(ResolvedTermDeposit deposit, RatesProvider provider) { Currency currency = deposit.Currency; DiscountFactors discountFactors = provider.discountFactors(currency); double dfStart = discountFactors.discountFactor(deposit.StartDate); double dfEnd = discountFactors.discountFactor(deposit.EndDate); double accrualFactor = deposit.YearFraction; return((dfStart / dfEnd - 1d) / accrualFactor); }
private SimpleRatesProvider provider(LocalDate valuationDate, double dfStart, double dfEnd) { DiscountFactors mockDf = mock(typeof(DiscountFactors)); when(mockDf.discountFactor(START_DATE)).thenReturn(dfStart); when(mockDf.discountFactor(END_DATE)).thenReturn(dfEnd); SimpleRatesProvider prov = new SimpleRatesProvider(valuationDate, mockDf); return(prov); }
//------------------------------------------------------------------------- public virtual void priceFromCurves() { LocalDate settlementDate = VAL_DATE.plusDays(1); double priceComputed = PRICER.priceFromCurves(BILL, PROVIDER, settlementDate); double dfMaturity = DSC_FACTORS_ISSUER.discountFactor(MATURITY_DATE); double dfSettle = DSC_FACTORS_REPO.discountFactor(settlementDate); double priceExpected = dfMaturity / dfSettle; assertEquals(priceComputed, priceExpected, TOLERANCE_PRICE); }
/// <summary> /// Test present value sensitivity for AFMA FRA discounting method. /// </summary> public virtual void test_presentValueSensitivity_AFMA() { RateComputationFn<RateComputation> mockObs = mock(typeof(RateComputationFn)); DiscountFactors mockDf = mock(typeof(DiscountFactors)); SimpleRatesProvider simpleProv = new SimpleRatesProvider(VAL_DATE, mockDf); ResolvedFra fraExp = RFRA_AFMA; double forwardRate = 0.05; double discountRate = 0.025; double paymentTime = 0.3; double discountFactor = Math.Exp(-discountRate * paymentTime); LocalDate fixingDate = FRA_AFMA.StartDate; IborIndexObservation obs = IborIndexObservation.of(FRA.Index, fixingDate, REF_DATA); PointSensitivityBuilder sens = IborRateSensitivity.of(obs, 1d); when(mockDf.discountFactor(fraExp.PaymentDate)).thenReturn(discountFactor); when(mockDf.zeroRatePointSensitivity(fraExp.PaymentDate)).thenReturn(ZeroRateSensitivity.of(fraExp.Currency, paymentTime, -discountFactor * paymentTime)); when(mockObs.rateSensitivity(fraExp.FloatingRate, fraExp.StartDate, fraExp.EndDate, simpleProv)).thenReturn(sens); when(mockObs.rate(fraExp.FloatingRate, FRA_AFMA.StartDate, FRA_AFMA.EndDate, simpleProv)).thenReturn(forwardRate); DiscountingFraProductPricer test = new DiscountingFraProductPricer(mockObs); PointSensitivities sensitivity = test.presentValueSensitivity(fraExp, simpleProv); double eps = 1.e-7; double fdDscSense = dscSensitivity(RFRA_AFMA, forwardRate, discountFactor, paymentTime, eps); double fdSense = presentValueFwdSensitivity(RFRA_AFMA, forwardRate, discountFactor, eps); ImmutableList<PointSensitivity> sensitivities = sensitivity.Sensitivities; assertEquals(sensitivities.size(), 2); IborRateSensitivity sensitivity0 = (IborRateSensitivity) sensitivities.get(0); assertEquals(sensitivity0.Index, FRA_AFMA.Index); assertEquals(sensitivity0.Observation.FixingDate, fixingDate); assertEquals(sensitivity0.Sensitivity, fdSense, FRA_AFMA.Notional * eps); ZeroRateSensitivity sensitivity1 = (ZeroRateSensitivity) sensitivities.get(1); assertEquals(sensitivity1.Currency, FRA_AFMA.Currency); assertEquals(sensitivity1.YearFraction, paymentTime); assertEquals(sensitivity1.Sensitivity, fdDscSense, FRA_AFMA.Notional * eps); }
/// <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)); }
//------------------------------------------------------------------------- public virtual void calibration_test() { RatesProvider result2 = CALIBRATOR.calibrate(CURVE_GROUP_DEFN, ALL_QUOTES, REF_DATA); // pv test CurveNode[] fwd3Nodes = CURVES_NODES[0][0]; IList <ResolvedTrade> fwd3Trades = new List <ResolvedTrade>(); for (int i = 0; i < fwd3Nodes.Length; i++) { fwd3Trades.Add(fwd3Nodes[i].resolvedTrade(1d, ALL_QUOTES, REF_DATA)); } for (int i = 0; i < FWD6_NB_NODES; i++) { MultiCurrencyAmount pvIrs2 = SWAP_PRICER.presentValue(((ResolvedSwapTrade)fwd3Trades[i]).Product, result2); assertEquals(pvIrs2.getAmount(GBP).Amount, 0.0, TOLERANCE_PV); } // regression test for curve DiscountFactors dsc = result2.discountFactors(GBP); double prevDsc = 0d; for (int i = 0; i < 121; ++i) { double time = ((double)i); double curDsc = dsc.discountFactor(time); if (i > 59) { double fwd = prevDsc / curDsc - 1d; assertEquals(fwd, 0.042, 2d * ONE_BP); } assertEquals(curDsc, DSC_EXP.get(i), ONE_PC); prevDsc = curDsc; } }
/// <summary> /// Computes the present value of the payment by discounting. /// <para> /// The present value is zero if the payment date is before the valuation date. /// </para> /// <para> /// The specified discount factors should be for the payment currency, however this is not validated. /// /// </para> /// </summary> /// <param name="payment"> the payment </param> /// <param name="discountFactors"> the discount factors to price against </param> /// <returns> the present value </returns> public virtual CurrencyAmount presentValue(Payment payment, DiscountFactors discountFactors) { if (discountFactors.ValuationDate.isAfter(payment.Date)) { return(CurrencyAmount.zero(payment.Currency)); } return(payment.Value.multipliedBy(discountFactors.discountFactor(payment.Date))); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value by discounting the final cash flow (nominal + interest) /// and the initial payment (initial amount). /// <para> /// The present value of the product is the value on the valuation date. /// /// </para> /// </summary> /// <param name="deposit"> the product </param> /// <param name="provider"> the rates provider </param> /// <returns> the present value of the product </returns> public virtual CurrencyAmount presentValue(ResolvedTermDeposit deposit, RatesProvider provider) { Currency currency = deposit.Currency; if (provider.ValuationDate.isAfter(deposit.EndDate)) { return(CurrencyAmount.of(currency, 0.0d)); } DiscountFactors discountFactors = provider.discountFactors(currency); double dfStart = discountFactors.discountFactor(deposit.StartDate); double dfEnd = discountFactors.discountFactor(deposit.EndDate); double pvStart = initialAmount(deposit, provider) * dfStart; double pvEnd = (deposit.Notional + deposit.Interest) * dfEnd; double pv = pvEnd - pvStart; return(CurrencyAmount.of(currency, pv)); }
public virtual void test_of() { RepoCurveDiscountFactors test = RepoCurveDiscountFactors.of(DSC_FACTORS, GROUP); assertEquals(test.RepoGroup, GROUP); assertEquals(test.Currency, GBP); assertEquals(test.ValuationDate, DATE); assertEquals(test.discountFactor(DATE_AFTER), DSC_FACTORS.discountFactor(DATE_AFTER)); }
/// <summary> /// Calculates the present value sensitivity of the Ibor fixing product. /// <para> /// The present value sensitivity of the product is the sensitivity of the present value to /// the underlying curves. /// /// </para> /// </summary> /// <param name="deposit"> the product </param> /// <param name="provider"> the rates provider </param> /// <returns> the point sensitivity of the present value </returns> public virtual PointSensitivities presentValueSensitivity(ResolvedIborFixingDeposit deposit, RatesProvider provider) { double forwardRate = this.forwardRate(deposit, provider); DiscountFactors discountFactors = provider.discountFactors(deposit.Currency); double discountFactor = discountFactors.discountFactor(deposit.EndDate); // sensitivity PointSensitivityBuilder sensiFwd = forwardRateSensitivity(deposit, provider).multipliedBy(-discountFactor * deposit.Notional * deposit.YearFraction); PointSensitivityBuilder sensiDsc = discountFactors.zeroRatePointSensitivity(deposit.EndDate).multipliedBy(deposit.Notional * deposit.YearFraction * (deposit.FixedRate - forwardRate)); return(sensiFwd.combinedWith(sensiDsc).build()); }
public virtual PointSensitivityBuilder presentValueSensitivity(FxResetNotionalExchange @event, RatesProvider provider) { DiscountFactors discountFactors = provider.discountFactors(@event.Currency); PointSensitivityBuilder sensiDsc = discountFactors.zeroRatePointSensitivity(@event.PaymentDate); sensiDsc = sensiDsc.multipliedBy(forecastValue(@event, provider)); PointSensitivityBuilder sensiFx = forecastValueSensitivity(@event, provider); sensiFx = sensiFx.multipliedBy(discountFactors.discountFactor(@event.PaymentDate)); return(sensiDsc.combinedWith(sensiFx)); }
/// <summary> /// Calculates the present value sensitivity of the FRA product. /// <para> /// The present value sensitivity of the product is the sensitivity of the present value to /// the underlying curves. /// /// </para> /// </summary> /// <param name="fra"> the product </param> /// <param name="provider"> the rates provider </param> /// <returns> the point sensitivity of the present value </returns> public virtual PointSensitivities presentValueSensitivity(ResolvedFra fra, RatesProvider provider) { DiscountFactors discountFactors = provider.discountFactors(fra.Currency); double df = discountFactors.discountFactor(fra.PaymentDate); double notional = fra.Notional; double unitAmount = this.unitAmount(fra, provider); double derivative = this.derivative(fra, provider); PointSensitivityBuilder iborSens = forwardRateSensitivity(fra, provider).multipliedBy(derivative * df * notional); PointSensitivityBuilder discSens = discountFactors.zeroRatePointSensitivity(fra.PaymentDate).multipliedBy(unitAmount * notional); return(iborSens.withCurrency(fra.Currency).combinedWith(discSens).build()); }
static DiscountingFraTradePricerTest() { DiscountFactors mockDf = mock(typeof(DiscountFactors)); IborIndexRates mockIbor = mock(typeof(IborIndexRates)); RATES_PROVIDER = new SimpleRatesProvider(VAL_DATE, mockDf); RATES_PROVIDER.IborRates = mockIbor; IborIndexObservation obs = ((IborRateComputation)RFRA.FloatingRate).Observation; IborRateSensitivity sens = IborRateSensitivity.of(obs, 1d); when(mockIbor.ratePointSensitivity(obs)).thenReturn(sens); when(mockIbor.rate(obs)).thenReturn(FORWARD_RATE); when(mockDf.discountFactor(RFRA.PaymentDate)).thenReturn(DISCOUNT_FACTOR); }
//------------------------------------------------------------------------- public virtual PointSensitivityBuilder presentValueSensitivity(RatePaymentPeriod period, RatesProvider provider) { Currency ccy = period.Currency; DiscountFactors discountFactors = provider.discountFactors(ccy); LocalDate paymentDate = period.PaymentDate; double df = discountFactors.discountFactor(paymentDate); PointSensitivityBuilder forecastSensitivity = forecastValueSensitivity(period, provider); forecastSensitivity = forecastSensitivity.multipliedBy(df); double forecastValue = this.forecastValue(period, provider); PointSensitivityBuilder dscSensitivity = discountFactors.zeroRatePointSensitivity(paymentDate); dscSensitivity = dscSensitivity.multipliedBy(forecastValue); return(forecastSensitivity.combinedWith(dscSensitivity)); }
//------------------------------------------------------------------------- // 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))); }
//------------------------------------------------------------------------- // creates a simple provider private SimpleRatesProvider createProvider(NotionalExchange ne) { LocalDate paymentDate = ne.PaymentDate; double paymentTime = DAY_COUNT.relativeYearFraction(VAL_DATE, paymentDate); Currency currency = ne.Currency; DiscountFactors mockDf = mock(typeof(DiscountFactors)); when(mockDf.discountFactor(paymentDate)).thenReturn(DISCOUNT_FACTOR); ZeroRateSensitivity sens = ZeroRateSensitivity.of(currency, paymentTime, -DISCOUNT_FACTOR * paymentTime); when(mockDf.zeroRatePointSensitivity(paymentDate)).thenReturn(sens); SimpleRatesProvider prov = new SimpleRatesProvider(VAL_DATE, mockDf); prov.DayCount = DAY_COUNT; return(prov); }
//------------------------------------------------------------------------- // creates a simple provider private SimpleRatesProvider createProvider(FxResetNotionalExchange ne) { LocalDate paymentDate = ne.PaymentDate; double paymentTime = ACT_360.relativeYearFraction(VAL_DATE, paymentDate); Currency currency = ne.Currency; DiscountFactors mockDf = mock(typeof(DiscountFactors)); when(mockDf.discountFactor(paymentDate)).thenReturn(DISCOUNT_FACTOR); ZeroRateSensitivity sens = ZeroRateSensitivity.of(currency, paymentTime, -DISCOUNT_FACTOR * paymentTime); when(mockDf.zeroRatePointSensitivity(paymentDate)).thenReturn(sens); FxIndexRates mockFxRates = mock(typeof(FxIndexRates)); when(mockFxRates.rate(ne.Observation, ne.ReferenceCurrency)).thenReturn(FX_RATE); SimpleRatesProvider prov = new SimpleRatesProvider(VAL_DATE); prov.DiscountFactors = mockDf; prov.FxIndexRates = mockFxRates; prov.DayCount = ACT_360; return(prov); }
//------------------------------------------------------------------------- public CurrencyParameterSensitivities parameterSensitivity(FxForwardSensitivity pointSensitivity) { // use the specified base currency to determine the desired currency pair // then derive sensitivity from discount factors based off desired currency pair, not that of the index CurrencyPair currencyPair = pointSensitivity.CurrencyPair; Currency refBaseCurrency = pointSensitivity.ReferenceCurrency; Currency refCounterCurrency = pointSensitivity.ReferenceCounterCurrency; Currency sensitivityCurrency = pointSensitivity.Currency; LocalDate referenceDate = pointSensitivity.ReferenceDate; bool inverse = refBaseCurrency.Equals(currencyPair.Counter); DiscountFactors discountFactorsRefBase = (inverse ? counterCurrencyDiscountFactors : baseCurrencyDiscountFactors); DiscountFactors discountFactorsRefCounter = (inverse ? baseCurrencyDiscountFactors : counterCurrencyDiscountFactors); double dfCcyBaseAtMaturity = discountFactorsRefBase.discountFactor(referenceDate); double dfCcyCounterAtMaturityInv = 1d / discountFactorsRefCounter.discountFactor(referenceDate); double fxRate = fxRateProvider.fxRate(refBaseCurrency, refCounterCurrency); ZeroRateSensitivity dfCcyBaseAtMaturitySensitivity = discountFactorsRefBase.zeroRatePointSensitivity(referenceDate, sensitivityCurrency).multipliedBy(fxRate * dfCcyCounterAtMaturityInv * pointSensitivity.Sensitivity); ZeroRateSensitivity dfCcyCounterAtMaturitySensitivity = discountFactorsRefCounter.zeroRatePointSensitivity(referenceDate, sensitivityCurrency).multipliedBy(-fxRate * dfCcyBaseAtMaturity * dfCcyCounterAtMaturityInv * dfCcyCounterAtMaturityInv * pointSensitivity.Sensitivity); return(discountFactorsRefBase.parameterSensitivity(dfCcyBaseAtMaturitySensitivity).combinedWith(discountFactorsRefCounter.parameterSensitivity(dfCcyCounterAtMaturitySensitivity))); }
/// <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); }