/**
* Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.pricer.fxopt;
import static com.opengamma.strata.basics.currency.Currency.EUR;
import static com.opengamma.strata.basics.currency.Currency.USD;
import static com.opengamma.strata.product.common.LongShort.LONG;
import static com.opengamma.strata.product.common.LongShort.SHORT;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import org.testng.annotations.Test;
import com.google.common.math.DoubleMath;
import com.opengamma.strata.basics.currency.CurrencyAmount;
import com.opengamma.strata.basics.currency.CurrencyPair;
import com.opengamma.strata.basics.currency.FxMatrix;
import com.opengamma.strata.basics.currency.MultiCurrencyAmount;
import com.opengamma.strata.collect.DoubleArrayMath;
import com.opengamma.strata.market.param.CurrencyParameterSensitivities;
import com.opengamma.strata.market.param.CurrencyParameterSensitivity;
import com.opengamma.strata.market.sensitivity.PointSensitivities;
import com.opengamma.strata.market.sensitivity.PointSensitivityBuilder;
import com.opengamma.strata.pricer.fx.RatesProviderFxDataSets;
import com.opengamma.strata.pricer.impl.option.BlackBarrierPriceFormulaRepository;
import com.opengamma.strata.pricer.impl.option.BlackOneTouchAssetPriceFormulaRepository;
import com.opengamma.strata.pricer.impl.option.BlackOneTouchCashPriceFormulaRepository;
import com.opengamma.strata.pricer.rate.ImmutableRatesProvider;
import com.opengamma.strata.pricer.sensitivity.RatesFiniteDifferenceSensitivityCalculator;
import com.opengamma.strata.product.fx.ResolvedFxSingle;
import com.opengamma.strata.product.fxopt.ResolvedFxSingleBarrierOption;
import com.opengamma.strata.product.fxopt.ResolvedFxVanillaOption;
import com.opengamma.strata.product.option.BarrierType;
import com.opengamma.strata.product.option.KnockType;
import com.opengamma.strata.product.option.SimpleConstantContinuousBarrier;
/**
* Test {@link BlackFxVanillaOptionProductPricer}.
*/
@Test
public class BlackFxSingleBarrierOptionProductPricerTest {
private static final ZoneId ZONE = ZoneId.of("Z");
private static final LocalDate VAL_DATE = LocalDate.of(2011, 6, 13);
private static final ZonedDateTime VAL_DATETIME = VAL_DATE.atStartOfDay(ZONE);
private static final LocalDate PAY_DATE = LocalDate.of(2014, 9, 15);
private static final LocalDate EXPIRY_DATE = LocalDate.of(2014, 9, 15);
private static final ZonedDateTime EXPIRY_DATETIME = EXPIRY_DATE.atStartOfDay(ZONE);
// providers
private static final BlackFxOptionSmileVolatilities VOLS =
FxVolatilitySmileDataSet.createVolatilitySmileProvider5(VAL_DATETIME);
private static final ImmutableRatesProvider RATE_PROVIDER =
RatesProviderFxDataSets.createProviderEurUsdActActIsda(VAL_DATE);
private static final BlackFxOptionSmileVolatilities VOLS_FLAT =
FxVolatilitySmileDataSet.createVolatilitySmileProvider5Flat(VAL_DATETIME);
// providers - valuation at expiry
private static final BlackFxOptionSmileVolatilities VOLS_EXPIRY =
FxVolatilitySmileDataSet.createVolatilitySmileProvider5(EXPIRY_DATETIME);
private static final ImmutableRatesProvider RATE_PROVIDER_EXPIRY =
RatesProviderFxDataSets.createProviderEurUsdActActIsda(EXPIRY_DATE);
// provider - valuation after expiry
private static final BlackFxOptionSmileVolatilities VOLS_AFTER =
FxVolatilitySmileDataSet.createVolatilitySmileProvider5(EXPIRY_DATETIME.plusDays(1));
private static final ImmutableRatesProvider RATE_PROVIDER_AFTER =
RatesProviderFxDataSets.createProviderEurUsdActActIsda(EXPIRY_DATE.plusDays(1));
private static final CurrencyPair CURRENCY_PAIR = CurrencyPair.of(EUR, USD);
private static final double SPOT = RATE_PROVIDER.fxRate(CURRENCY_PAIR);
private static final double NOTIONAL = 100_000_000d;
private static final double LEVEL_LOW = 1.35;
private static final double LEVEL_HIGH = 1.5;
private static final SimpleConstantContinuousBarrier BARRIER_DKI =
SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_IN, LEVEL_LOW);
private static final SimpleConstantContinuousBarrier BARRIER_DKO =
SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_OUT, LEVEL_LOW);
private static final SimpleConstantContinuousBarrier BARRIER_UKI =
SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_IN, LEVEL_HIGH);
private static final SimpleConstantContinuousBarrier BARRIER_UKO =
SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_OUT, LEVEL_HIGH);
private static final double REBATE_AMOUNT = 50_000d;
private static final CurrencyAmount REBATE = CurrencyAmount.of(USD, REBATE_AMOUNT);
private static final CurrencyAmount REBATE_BASE = CurrencyAmount.of(EUR, REBATE_AMOUNT);
private static final double STRIKE_RATE = 1.45;
// call
private static final CurrencyAmount EUR_AMOUNT_REC = CurrencyAmount.of(EUR, NOTIONAL);
private static final CurrencyAmount USD_AMOUNT_PAY = CurrencyAmount.of(USD, -NOTIONAL * STRIKE_RATE);
private static final ResolvedFxSingle FX_PRODUCT = ResolvedFxSingle.of(EUR_AMOUNT_REC, USD_AMOUNT_PAY, PAY_DATE);
private static final ResolvedFxVanillaOption CALL = ResolvedFxVanillaOption.builder()
.longShort(LONG)
.expiry(EXPIRY_DATETIME)
.underlying(FX_PRODUCT)
.build();
private static final ResolvedFxSingleBarrierOption CALL_DKI =
ResolvedFxSingleBarrierOption.of(CALL, BARRIER_DKI, REBATE);
private static final ResolvedFxSingleBarrierOption CALL_DKI_BASE =
ResolvedFxSingleBarrierOption.of(CALL, BARRIER_DKI, REBATE_BASE);
private static final ResolvedFxSingleBarrierOption CALL_DKO =
ResolvedFxSingleBarrierOption.of(CALL, BARRIER_DKO, REBATE);
private static final ResolvedFxSingleBarrierOption CALL_DKO_BASE =
ResolvedFxSingleBarrierOption.of(CALL, BARRIER_DKO, REBATE_BASE);
private static final ResolvedFxSingleBarrierOption CALL_UKI =
ResolvedFxSingleBarrierOption.of(CALL, BARRIER_UKI, REBATE);
private static final ResolvedFxSingleBarrierOption CALL_UKI_BASE =
ResolvedFxSingleBarrierOption.of(CALL, BARRIER_UKI, REBATE_BASE);
private static final ResolvedFxSingleBarrierOption CALL_UKO =
ResolvedFxSingleBarrierOption.of(CALL, BARRIER_UKO, REBATE);
private static final ResolvedFxSingleBarrierOption CALL_UKO_BASE =
ResolvedFxSingleBarrierOption.of(CALL, BARRIER_UKO, REBATE_BASE);
// put
private static final CurrencyAmount EUR_AMOUNT_PAY = CurrencyAmount.of(EUR, -NOTIONAL);
private static final CurrencyAmount USD_AMOUNT_REC = CurrencyAmount.of(USD, NOTIONAL * STRIKE_RATE);
private static final ResolvedFxSingle FX_PRODUCT_INV = ResolvedFxSingle.of(EUR_AMOUNT_PAY, USD_AMOUNT_REC, PAY_DATE);
private static final ResolvedFxVanillaOption PUT = ResolvedFxVanillaOption.builder()
.longShort(SHORT)
.expiry(EXPIRY_DATETIME)
.underlying(FX_PRODUCT_INV)
.build();
private static final ResolvedFxSingleBarrierOption PUT_DKI =
ResolvedFxSingleBarrierOption.of(PUT, BARRIER_DKI, REBATE);
private static final ResolvedFxSingleBarrierOption PUT_DKI_BASE =
ResolvedFxSingleBarrierOption.of(PUT, BARRIER_DKI, REBATE_BASE);
private static final ResolvedFxSingleBarrierOption PUT_DKO =
ResolvedFxSingleBarrierOption.of(PUT, BARRIER_DKO, REBATE);
private static final ResolvedFxSingleBarrierOption PUT_DKO_BASE =
ResolvedFxSingleBarrierOption.of(PUT, BARRIER_DKO, REBATE_BASE);
private static final ResolvedFxSingleBarrierOption PUT_UKI =
ResolvedFxSingleBarrierOption.of(PUT, BARRIER_UKI, REBATE);
private static final ResolvedFxSingleBarrierOption PUT_UKI_BASE =
ResolvedFxSingleBarrierOption.of(PUT, BARRIER_UKI, REBATE_BASE);
private static final ResolvedFxSingleBarrierOption PUT_UKO =
ResolvedFxSingleBarrierOption.of(PUT, BARRIER_UKO, REBATE);
private static final ResolvedFxSingleBarrierOption PUT_UKO_BASE =
ResolvedFxSingleBarrierOption.of(PUT, BARRIER_UKO, REBATE_BASE);
private static final ResolvedFxSingleBarrierOption[] OPTION_ALL = new ResolvedFxSingleBarrierOption[] {
CALL_DKI, CALL_DKI_BASE, CALL_DKO, CALL_DKO_BASE, CALL_UKI, CALL_UKI_BASE, CALL_UKO, CALL_UKO_BASE,
PUT_DKI, PUT_DKI_BASE, PUT_DKO, PUT_DKO_BASE, PUT_UKI, PUT_UKI_BASE, PUT_UKO, PUT_UKO_BASE };
private static final BlackFxSingleBarrierOptionProductPricer PRICER = BlackFxSingleBarrierOptionProductPricer.DEFAULT;
private static final BlackFxVanillaOptionProductPricer VANILLA_PRICER = BlackFxVanillaOptionProductPricer.DEFAULT;
private static final BlackBarrierPriceFormulaRepository BARRIER_PRICER = new BlackBarrierPriceFormulaRepository();
private static final BlackOneTouchAssetPriceFormulaRepository ASSET_REBATE_PRICER = new BlackOneTouchAssetPriceFormulaRepository();
private static final BlackOneTouchCashPriceFormulaRepository CASH_REBATE_PRICER = new BlackOneTouchCashPriceFormulaRepository();
private static final double TOL = 1.0e-12;
private static final double FD_EPS = 1.0e-6;
private static final RatesFiniteDifferenceSensitivityCalculator FD_CAL = new RatesFiniteDifferenceSensitivityCalculator(FD_EPS);
//-------------------------------------------------------------------------
public void test_price_presentValue() {
double computedPriceCall = PRICER.price(CALL_UKI, RATE_PROVIDER, VOLS);
double computedPricePut = PRICER.price(PUT_UKO_BASE, RATE_PROVIDER, VOLS);
CurrencyAmount computedPvCall = PRICER.presentValue(CALL_UKI, RATE_PROVIDER, VOLS);
CurrencyAmount computedPvPut = PRICER.presentValue(PUT_UKO_BASE, RATE_PROVIDER, VOLS);
double rateBase = RATE_PROVIDER.discountFactors(EUR).zeroRate(PAY_DATE);
double rateCounter = RATE_PROVIDER.discountFactors(USD).zeroRate(PAY_DATE);
double costOfCarry = rateCounter - rateBase;
double forward = RATE_PROVIDER.fxForwardRates(CURRENCY_PAIR).rate(EUR, PAY_DATE);
double volatility = VOLS.volatility(CURRENCY_PAIR, EXPIRY_DATETIME, STRIKE_RATE, forward);
double timeToExpiry = VOLS.relativeTime(EXPIRY_DATETIME);
double rebateRate = REBATE_AMOUNT / NOTIONAL;
double expectedCash =
CASH_REBATE_PRICER.price(SPOT, timeToExpiry, costOfCarry, rateCounter, volatility, BARRIER_UKO);
double expectedAsset =
ASSET_REBATE_PRICER.price(SPOT, timeToExpiry, costOfCarry, rateCounter, volatility, BARRIER_UKI);
double expectedPriceCall = BARRIER_PRICER.price(SPOT, STRIKE_RATE, timeToExpiry, costOfCarry, rateCounter,
volatility, true, BARRIER_UKI) + rebateRate * expectedCash;
double expectedPricePut = BARRIER_PRICER.price(SPOT, STRIKE_RATE, timeToExpiry, costOfCarry, rateCounter,
volatility, false, BARRIER_UKO) + rebateRate * expectedAsset;
assertEquals(computedPriceCall, expectedPriceCall, TOL);
assertEquals(computedPricePut, expectedPricePut, TOL);
assertEquals(computedPvCall.getCurrency(), USD);
assertEquals(computedPvPut.getCurrency(), USD);
assertEquals(computedPvCall.getAmount(), expectedPriceCall * NOTIONAL, TOL);
assertEquals(computedPvPut.getAmount(), -expectedPricePut * NOTIONAL, TOL);
}
public void test_price_presentValue_atExpiry() {
double computedPriceCall = PRICER.price(CALL_UKI, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
double computedPriceCallZero = PRICER.price(CALL_UKO, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
double computedPricePut = PRICER.price(PUT_UKO_BASE, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
CurrencyAmount computedPvCall = PRICER.presentValue(CALL_UKI, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
CurrencyAmount computedPvCallZero = PRICER.presentValue(CALL_UKO, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
CurrencyAmount computedPvPut = PRICER.presentValue(PUT_UKO_BASE, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
double expectedPriceCall = REBATE_AMOUNT / NOTIONAL;
double expectedPricePut = STRIKE_RATE - SPOT;
assertEquals(computedPriceCall, expectedPriceCall, TOL);
assertEquals(computedPriceCallZero, 0d, TOL);
assertEquals(computedPricePut, expectedPricePut, TOL);
assertEquals(computedPvCall.getAmount(), expectedPriceCall * NOTIONAL, TOL);
assertEquals(computedPvCallZero.getAmount(), 0d * NOTIONAL, TOL);
assertEquals(computedPvPut.getAmount(), -expectedPricePut * NOTIONAL, TOL);
}
public void test_price_presentValue_afterExpiry() {
double computedPriceCall = PRICER.price(CALL_UKI, RATE_PROVIDER_AFTER, VOLS_AFTER);
double computedPricePut = PRICER.price(PUT_UKO_BASE, RATE_PROVIDER_AFTER, VOLS_AFTER);
CurrencyAmount computedPvCall = PRICER.presentValue(CALL_UKI, RATE_PROVIDER_AFTER, VOLS_AFTER);
CurrencyAmount computedPvPut = PRICER.presentValue(PUT_UKO_BASE, RATE_PROVIDER_AFTER, VOLS_AFTER);
assertEquals(computedPriceCall, 0d, TOL);
assertEquals(computedPricePut, 0d, TOL);
assertEquals(computedPvCall.getAmount(), 0d, TOL);
assertEquals(computedPvPut.getAmount(), 0d, TOL);
}
//-------------------------------------------------------------------------
public void test_inOutParity() {
ResolvedFxSingleBarrierOption callDKI = ResolvedFxSingleBarrierOption.of(CALL, BARRIER_DKI);
ResolvedFxSingleBarrierOption callDKO = ResolvedFxSingleBarrierOption.of(CALL, BARRIER_DKO);
ResolvedFxSingleBarrierOption callUKI = ResolvedFxSingleBarrierOption.of(CALL, BARRIER_UKI);
ResolvedFxSingleBarrierOption callUKO = ResolvedFxSingleBarrierOption.of(CALL, BARRIER_UKO);
ResolvedFxSingleBarrierOption putDKI = ResolvedFxSingleBarrierOption.of(PUT, BARRIER_DKI);
ResolvedFxSingleBarrierOption putDKO = ResolvedFxSingleBarrierOption.of(PUT, BARRIER_DKO);
ResolvedFxSingleBarrierOption putUKI = ResolvedFxSingleBarrierOption.of(PUT, BARRIER_UKI);
ResolvedFxSingleBarrierOption putUKO = ResolvedFxSingleBarrierOption.of(PUT, BARRIER_UKO);
//pv
CurrencyAmount pvCall = VANILLA_PRICER.presentValue(CALL, RATE_PROVIDER, VOLS);
CurrencyAmount computedPvCallUp = PRICER.presentValue(callUKO, RATE_PROVIDER, VOLS)
.plus(PRICER.presentValue(callUKI, RATE_PROVIDER, VOLS));
CurrencyAmount computedPvCallDw = PRICER.presentValue(callDKO, RATE_PROVIDER, VOLS)
.plus(PRICER.presentValue(callDKI, RATE_PROVIDER, VOLS));
assertEquals(computedPvCallUp.getAmount(), pvCall.getAmount(), NOTIONAL * TOL);
assertEquals(computedPvCallDw.getAmount(), pvCall.getAmount(), NOTIONAL * TOL);
CurrencyAmount pvPut = VANILLA_PRICER.presentValue(PUT, RATE_PROVIDER, VOLS);
CurrencyAmount computedPvPutUp = PRICER.presentValue(putUKO, RATE_PROVIDER, VOLS)
.plus(PRICER.presentValue(putUKI, RATE_PROVIDER, VOLS));
CurrencyAmount computedPvPutDw = PRICER.presentValue(putDKO, RATE_PROVIDER, VOLS)
.plus(PRICER.presentValue(putDKI, RATE_PROVIDER, VOLS));
assertEquals(computedPvPutUp.getAmount(), pvPut.getAmount(), NOTIONAL * TOL);
assertEquals(computedPvPutDw.getAmount(), pvPut.getAmount(), NOTIONAL * TOL);
// curve sensitivity
PointSensitivities pvSensiCall = VANILLA_PRICER.presentValueSensitivityRatesStickyStrike(CALL, RATE_PROVIDER, VOLS);
PointSensitivities computedPvSensiCallUp = PRICER.presentValueSensitivityRatesStickyStrike(callUKO, RATE_PROVIDER, VOLS)
.combinedWith(PRICER.presentValueSensitivityRatesStickyStrike(callUKI, RATE_PROVIDER, VOLS)).build();
PointSensitivities computedPvSensiCallDw = PRICER.presentValueSensitivityRatesStickyStrike(callDKO, RATE_PROVIDER, VOLS)
.combinedWith(PRICER.presentValueSensitivityRatesStickyStrike(callDKI, RATE_PROVIDER, VOLS)).build();
assertTrue(RATE_PROVIDER.parameterSensitivity(pvSensiCall).equalWithTolerance(
RATE_PROVIDER.parameterSensitivity(computedPvSensiCallUp), TOL * NOTIONAL));
assertTrue(RATE_PROVIDER.parameterSensitivity(pvSensiCall).equalWithTolerance(
RATE_PROVIDER.parameterSensitivity(computedPvSensiCallDw), TOL * NOTIONAL));
PointSensitivities pvSensiPut = VANILLA_PRICER.presentValueSensitivityRatesStickyStrike(PUT, RATE_PROVIDER, VOLS);
PointSensitivities computedPvSensiPutUp = PRICER.presentValueSensitivityRatesStickyStrike(putUKO, RATE_PROVIDER, VOLS)
.combinedWith(PRICER.presentValueSensitivityRatesStickyStrike(putUKI, RATE_PROVIDER, VOLS)).build();
PointSensitivities computedPvSensiPutDw = PRICER.presentValueSensitivityRatesStickyStrike(putDKO, RATE_PROVIDER, VOLS)
.combinedWith(PRICER.presentValueSensitivityRatesStickyStrike(putDKI, RATE_PROVIDER, VOLS)).build();
assertTrue(RATE_PROVIDER.parameterSensitivity(pvSensiPut).equalWithTolerance(
RATE_PROVIDER.parameterSensitivity(computedPvSensiPutUp), TOL * NOTIONAL));
assertTrue(RATE_PROVIDER.parameterSensitivity(pvSensiPut).equalWithTolerance(
RATE_PROVIDER.parameterSensitivity(computedPvSensiPutDw), TOL * NOTIONAL));
}
public void farBarrierOutTest() {
double smallBarrier = 1.0e-6;
double largeBarrier = 1.0e6;
SimpleConstantContinuousBarrier dkoSmall =
SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_OUT, smallBarrier);
SimpleConstantContinuousBarrier uKoLarge =
SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_OUT, largeBarrier);
ResolvedFxSingleBarrierOption callDko = ResolvedFxSingleBarrierOption.of(CALL, dkoSmall, REBATE);
ResolvedFxSingleBarrierOption callUko = ResolvedFxSingleBarrierOption.of(CALL, uKoLarge, REBATE_BASE);
ResolvedFxSingleBarrierOption putDko = ResolvedFxSingleBarrierOption.of(PUT, dkoSmall, REBATE);
ResolvedFxSingleBarrierOption putUko = ResolvedFxSingleBarrierOption.of(PUT, uKoLarge, REBATE_BASE);
// pv
CurrencyAmount pvCallDko = PRICER.presentValue(callDko, RATE_PROVIDER, VOLS);
CurrencyAmount pvCallUko = PRICER.presentValue(callUko, RATE_PROVIDER, VOLS);
CurrencyAmount pvCall = VANILLA_PRICER.presentValue(CALL, RATE_PROVIDER, VOLS);
CurrencyAmount pvPutDko = PRICER.presentValue(putDko, RATE_PROVIDER, VOLS);
CurrencyAmount pvPutUko = PRICER.presentValue(putUko, RATE_PROVIDER, VOLS);
CurrencyAmount pvPut = VANILLA_PRICER.presentValue(PUT, RATE_PROVIDER, VOLS);
assertEquals(pvCallDko.getAmount(), pvCall.getAmount(), NOTIONAL * TOL);
assertEquals(pvCallUko.getAmount(), pvCall.getAmount(), NOTIONAL * TOL);
assertEquals(pvPutDko.getAmount(), pvPut.getAmount(), NOTIONAL * TOL);
assertEquals(pvPutUko.getAmount(), pvPut.getAmount(), NOTIONAL * TOL);
// currency exposure
MultiCurrencyAmount ceCallDko = PRICER.currencyExposure(callDko, RATE_PROVIDER, VOLS);
MultiCurrencyAmount ceCallUko = PRICER.currencyExposure(callUko, RATE_PROVIDER, VOLS);
MultiCurrencyAmount ceCall = VANILLA_PRICER.currencyExposure(CALL, RATE_PROVIDER, VOLS);
assertEquals(ceCallDko.getAmount(EUR).getAmount(), ceCall.getAmount(EUR).getAmount(), NOTIONAL * TOL);
assertEquals(ceCallDko.getAmount(USD).getAmount(), ceCall.getAmount(USD).getAmount(), NOTIONAL * TOL);
assertEquals(ceCallUko.getAmount(EUR).getAmount(), ceCall.getAmount(EUR).getAmount(), NOTIONAL * TOL);
assertEquals(ceCallUko.getAmount(USD).getAmount(), ceCall.getAmount(USD).getAmount(), NOTIONAL * TOL);
MultiCurrencyAmount cePutDko = PRICER.currencyExposure(putDko, RATE_PROVIDER, VOLS);
MultiCurrencyAmount cePutUko = PRICER.currencyExposure(putUko, RATE_PROVIDER, VOLS);
MultiCurrencyAmount cePut = VANILLA_PRICER.currencyExposure(PUT, RATE_PROVIDER, VOLS);
assertEquals(cePutDko.getAmount(EUR).getAmount(), cePut.getAmount(EUR).getAmount(), NOTIONAL * TOL);
assertEquals(cePutDko.getAmount(USD).getAmount(), cePut.getAmount(USD).getAmount(), NOTIONAL * TOL);
assertEquals(cePutUko.getAmount(EUR).getAmount(), cePut.getAmount(EUR).getAmount(), NOTIONAL * TOL);
assertEquals(cePutUko.getAmount(USD).getAmount(), cePut.getAmount(USD).getAmount(), NOTIONAL * TOL);
}
public void farBarrierInTest() {
double smallBarrier = 1.0e-6;
double largeBarrier = 1.0e6;
SimpleConstantContinuousBarrier dkiSmall =
SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_IN, smallBarrier);
SimpleConstantContinuousBarrier uKiLarge =
SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_IN, largeBarrier);
ResolvedFxSingleBarrierOption callDki = ResolvedFxSingleBarrierOption.of(CALL, dkiSmall, REBATE);
ResolvedFxSingleBarrierOption callUki = ResolvedFxSingleBarrierOption.of(CALL, uKiLarge, REBATE_BASE);
ResolvedFxSingleBarrierOption putDki = ResolvedFxSingleBarrierOption.of(PUT, dkiSmall, REBATE);
ResolvedFxSingleBarrierOption putUki = ResolvedFxSingleBarrierOption.of(PUT, uKiLarge, REBATE_BASE);
// pv
CurrencyAmount pvCallDki = PRICER.presentValue(callDki, RATE_PROVIDER, VOLS);
CurrencyAmount pvCallUki = PRICER.presentValue(callUki, RATE_PROVIDER, VOLS);
CurrencyAmount pvPutDki = PRICER.presentValue(putDki, RATE_PROVIDER, VOLS);
CurrencyAmount pvPutUki = PRICER.presentValue(putUki, RATE_PROVIDER, VOLS);
double dfUsd = RATE_PROVIDER.discountFactor(USD, PAY_DATE);
double dfEur = RATE_PROVIDER.discountFactor(EUR, PAY_DATE);
assertEquals(pvCallDki.getAmount(), REBATE_AMOUNT * dfUsd, NOTIONAL * TOL);
assertEquals(pvCallUki.getAmount(), REBATE_AMOUNT * SPOT * dfEur, NOTIONAL * TOL);
assertEquals(pvPutDki.getAmount(), -REBATE_AMOUNT * dfUsd, NOTIONAL * TOL);
assertEquals(pvPutUki.getAmount(), -REBATE_AMOUNT * SPOT * dfEur, NOTIONAL * TOL);
// currency exposure
MultiCurrencyAmount ceCallDki = PRICER.currencyExposure(callDki, RATE_PROVIDER, VOLS);
MultiCurrencyAmount ceCallUki = PRICER.currencyExposure(callUki, RATE_PROVIDER, VOLS);
assertEquals(ceCallDki.getAmount(EUR).getAmount(), 0d, NOTIONAL * TOL);
assertEquals(ceCallDki.getAmount(USD).getAmount(), REBATE_AMOUNT * dfUsd, NOTIONAL * TOL);
assertEquals(ceCallUki.getAmount(EUR).getAmount(), REBATE_AMOUNT * dfEur, NOTIONAL * TOL);
assertEquals(ceCallUki.getAmount(USD).getAmount(), 0d, NOTIONAL * TOL);
MultiCurrencyAmount cePutDki = PRICER.currencyExposure(putDki, RATE_PROVIDER, VOLS);
MultiCurrencyAmount cePutUki = PRICER.currencyExposure(putUki, RATE_PROVIDER, VOLS);
assertEquals(cePutDki.getAmount(EUR).getAmount(), 0d, NOTIONAL * TOL);
assertEquals(cePutDki.getAmount(USD).getAmount(), -REBATE_AMOUNT * dfUsd, NOTIONAL * TOL);
assertEquals(cePutUki.getAmount(EUR).getAmount(), -REBATE_AMOUNT * dfEur, NOTIONAL * TOL);
assertEquals(cePutUki.getAmount(USD).getAmount(), 0d, NOTIONAL * TOL);
}
//-------------------------------------------------------------------------
public void test_presentValueSensitivity() {
for (ResolvedFxSingleBarrierOption option : OPTION_ALL) {
PointSensitivityBuilder point = PRICER.presentValueSensitivityRatesStickyStrike(option, RATE_PROVIDER, VOLS);
CurrencyParameterSensitivities computed = RATE_PROVIDER.parameterSensitivity(point.build());
CurrencyParameterSensitivities expected = FD_CAL.sensitivity(RATE_PROVIDER,
p -> PRICER.presentValue(option, p, VOLS));
double pvVega = ((FxOptionSensitivity)
PRICER.presentValueSensitivityModelParamsVolatility(option, RATE_PROVIDER, VOLS)).getSensitivity();
CurrencyParameterSensitivities sensiViaFwd = FD_CAL.sensitivity(RATE_PROVIDER,
p -> CurrencyAmount.of(USD, VANILLA_PRICER.impliedVolatility(CALL, p, VOLS))).multipliedBy(-pvVega);
expected = expected.combinedWith(sensiViaFwd);
assertTrue(computed.equalWithTolerance(expected, FD_EPS * NOTIONAL * 10d));
}
}
public void test_presentValueSensitivity_atExpiry() {
for (ResolvedFxSingleBarrierOption option : OPTION_ALL) {
PointSensitivityBuilder point = PRICER.presentValueSensitivityRatesStickyStrike(option, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
CurrencyParameterSensitivities computed = RATE_PROVIDER_EXPIRY.parameterSensitivity(point.build());
CurrencyParameterSensitivities expected = FD_CAL.sensitivity(RATE_PROVIDER_EXPIRY,
p -> PRICER.presentValue(option, p, VOLS_EXPIRY));
assertTrue(computed.equalWithTolerance(expected, FD_EPS * NOTIONAL * 10d));
}
}
public void test_presentValueSensitivity_afterExpiry() {
for (ResolvedFxSingleBarrierOption option : OPTION_ALL) {
PointSensitivityBuilder point = PRICER.presentValueSensitivityRatesStickyStrike(option, RATE_PROVIDER_AFTER, VOLS_AFTER);
assertEquals(point, PointSensitivityBuilder.none());
}
}
//-------------------------------------------------------------------------
public void test_vega_presentValueSensitivityVolatility() {
double computedVegaCall = PRICER.vega(CALL_UKI, RATE_PROVIDER, VOLS);
FxOptionSensitivity computedCall =
(FxOptionSensitivity) PRICER.presentValueSensitivityModelParamsVolatility(CALL_UKI, RATE_PROVIDER, VOLS);
double computedVegaPut = PRICER.vega(PUT_UKO_BASE, RATE_PROVIDER, VOLS);
FxOptionSensitivity computedPut =
(FxOptionSensitivity) PRICER.presentValueSensitivityModelParamsVolatility(PUT_UKO_BASE, RATE_PROVIDER, VOLS);
double rateBase = RATE_PROVIDER.discountFactors(EUR).zeroRate(PAY_DATE);
double rateCounter = RATE_PROVIDER.discountFactors(USD).zeroRate(PAY_DATE);
double costOfCarry = rateCounter - rateBase;
double forward = RATE_PROVIDER.fxForwardRates(CURRENCY_PAIR).rate(EUR, PAY_DATE);
double volatility = VOLS.volatility(CURRENCY_PAIR, EXPIRY_DATETIME, STRIKE_RATE, forward);
double timeToExpiry = VOLS.relativeTime(EXPIRY_DATETIME);
double rebateRate = REBATE_AMOUNT / NOTIONAL;
double expectedCash = CASH_REBATE_PRICER
.priceAdjoint(SPOT, timeToExpiry, costOfCarry, rateCounter, volatility, BARRIER_UKO).getDerivative(3);
double expectedAsset = ASSET_REBATE_PRICER
.priceAdjoint(SPOT, timeToExpiry, costOfCarry, rateCounter, volatility, BARRIER_UKI).getDerivative(3);
double expectedCall = BARRIER_PRICER.priceAdjoint(SPOT, STRIKE_RATE, timeToExpiry, costOfCarry, rateCounter,
volatility, true, BARRIER_UKI).getDerivative(4) + rebateRate * expectedCash;
double expectedPut = BARRIER_PRICER.priceAdjoint(SPOT, STRIKE_RATE, timeToExpiry, costOfCarry, rateCounter,
volatility, false, BARRIER_UKO).getDerivative(4) + rebateRate * expectedAsset;
assertEquals(computedVegaCall, expectedCall, TOL);
assertEquals(computedCall.getSensitivity(), expectedCall * NOTIONAL, TOL * NOTIONAL);
assertEquals(computedCall.getCurrency(), USD);
assertEquals(computedCall.getCurrencyPair(), CURRENCY_PAIR);
assertEquals(computedCall.getStrike(), STRIKE_RATE);
assertEquals(computedCall.getForward(), forward, TOL);
assertEquals(computedCall.getExpiry(), timeToExpiry);
assertEquals(computedVegaPut, expectedPut, TOL);
assertEquals(computedPut.getSensitivity(), -expectedPut * NOTIONAL, TOL * NOTIONAL);
assertEquals(computedPut.getCurrency(), USD);
assertEquals(computedPut.getCurrencyPair(), CURRENCY_PAIR);
assertEquals(computedPut.getStrike(), STRIKE_RATE);
assertEquals(computedPut.getForward(), forward, TOL);
assertEquals(computedPut.getExpiry(), timeToExpiry);
}
public void test_vega_presentValueSensitivityVolatility_atExpiry() {
double computedVegaCall = PRICER.vega(CALL_UKI, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
PointSensitivityBuilder computedCall =
PRICER.presentValueSensitivityModelParamsVolatility(CALL_UKI, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
double computedVegaPut = PRICER.vega(PUT_UKO_BASE, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
PointSensitivityBuilder computedPut =
PRICER.presentValueSensitivityModelParamsVolatility(PUT_UKO_BASE, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
assertEquals(computedVegaCall, 0d);
assertEquals(computedCall, PointSensitivityBuilder.none());
assertEquals(computedVegaPut, 0d);
assertEquals(computedPut, PointSensitivityBuilder.none());
}
public void test_vega_presentValueSensitivityVolatility_afterExpiry() {
double computedVegaCall = PRICER.vega(CALL_UKI, RATE_PROVIDER_AFTER, VOLS_AFTER);
PointSensitivityBuilder computedCall =
PRICER.presentValueSensitivityModelParamsVolatility(CALL_UKI, RATE_PROVIDER_AFTER, VOLS_AFTER);
double computedVegaPut = PRICER.vega(PUT_UKO_BASE, RATE_PROVIDER_AFTER, VOLS_AFTER);
PointSensitivityBuilder computedPut =
PRICER.presentValueSensitivityModelParamsVolatility(PUT_UKO_BASE, RATE_PROVIDER_AFTER, VOLS_AFTER);
assertEquals(computedVegaCall, 0d);
assertEquals(computedCall, PointSensitivityBuilder.none());
assertEquals(computedVegaPut, 0d);
assertEquals(computedPut, PointSensitivityBuilder.none());
}
//-------------------------------------------------------------------------
public void test_currencyExposure() {
for (ResolvedFxSingleBarrierOption option : OPTION_ALL) {
CurrencyAmount pv = PRICER.presentValue(option, RATE_PROVIDER, VOLS_FLAT);
MultiCurrencyAmount computed = PRICER.currencyExposure(option, RATE_PROVIDER, VOLS_FLAT);
FxMatrix fxMatrix = FxMatrix.builder().addRate(EUR, USD, SPOT + FD_EPS).build();
ImmutableRatesProvider provBumped = RATE_PROVIDER.toBuilder().fxRateProvider(fxMatrix).build();
CurrencyAmount pvBumped = PRICER.presentValue(option, provBumped, VOLS_FLAT);
double ceCounterFD = pvBumped.getAmount() - pv.getAmount();
double ceBaseFD = pvBumped.getAmount() / (SPOT + FD_EPS) - pv.getAmount() / SPOT;
assertEquals(computed.getAmount(EUR).getAmount() * FD_EPS, ceCounterFD, NOTIONAL * TOL);
assertEquals(computed.getAmount(USD).getAmount() * (1.0d / (SPOT + FD_EPS) - 1.0d / SPOT), ceBaseFD, NOTIONAL * TOL);
}
}
public void test_currencyExposure_atExpiry() {
for (ResolvedFxSingleBarrierOption option : OPTION_ALL) {
CurrencyAmount pv = PRICER.presentValue(option, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
MultiCurrencyAmount computed = PRICER.currencyExposure(option, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
FxMatrix fxMatrix = FxMatrix.builder().addRate(EUR, USD, SPOT + FD_EPS).build();
ImmutableRatesProvider provBumped = RATE_PROVIDER_EXPIRY.toBuilder().fxRateProvider(fxMatrix).build();
CurrencyAmount pvBumped = PRICER.presentValue(option, provBumped, VOLS_EXPIRY);
double ceCounterFD = pvBumped.getAmount() - pv.getAmount();
double ceBaseFD = pvBumped.getAmount() / (SPOT + FD_EPS) - pv.getAmount() / SPOT;
assertEquals(computed.getAmount(EUR).getAmount() * FD_EPS, ceCounterFD, NOTIONAL * TOL);
assertEquals(computed.getAmount(USD).getAmount() * (1.0d / (SPOT + FD_EPS) - 1.0d / SPOT), ceBaseFD, NOTIONAL * TOL);
}
}
public void test_currencyExposure_afterExpiry() {
for (ResolvedFxSingleBarrierOption option : OPTION_ALL) {
MultiCurrencyAmount computed = PRICER.currencyExposure(option, RATE_PROVIDER_AFTER, VOLS_AFTER);
assertEquals(computed, MultiCurrencyAmount.empty());
}
}
//-------------------------------------------------------------------------
public void test_delta_presentValueDelta() {
double computedDeltaCall = PRICER.delta(CALL_UKI, RATE_PROVIDER, VOLS);
double computedDeltaPut = PRICER.delta(PUT_UKO_BASE, RATE_PROVIDER, VOLS);
CurrencyAmount computedPvDeltaCall = PRICER.presentValueDelta(CALL_UKI, RATE_PROVIDER, VOLS);
CurrencyAmount computedPvDeltaPut = PRICER.presentValueDelta(PUT_UKO_BASE, RATE_PROVIDER, VOLS);
double rateBase = RATE_PROVIDER.discountFactors(EUR).zeroRate(PAY_DATE);
double rateCounter = RATE_PROVIDER.discountFactors(USD).zeroRate(PAY_DATE);
double costOfCarry = rateCounter - rateBase;
double forward = RATE_PROVIDER.fxForwardRates(CURRENCY_PAIR).rate(EUR, PAY_DATE);
double volatility = VOLS.volatility(CURRENCY_PAIR, EXPIRY_DATETIME, STRIKE_RATE, forward);
double timeToExpiry = VOLS.relativeTime(EXPIRY_DATETIME);
double rebateRate = REBATE_AMOUNT / NOTIONAL;
double expectedCash = CASH_REBATE_PRICER
.priceAdjoint(SPOT, timeToExpiry, costOfCarry, rateCounter, volatility, BARRIER_UKO).getDerivative(0);
double expectedAsset = ASSET_REBATE_PRICER
.priceAdjoint(SPOT, timeToExpiry, costOfCarry, rateCounter, volatility, BARRIER_UKI).getDerivative(0);
double expectedDeltaCall = BARRIER_PRICER.priceAdjoint(SPOT, STRIKE_RATE, timeToExpiry, costOfCarry, rateCounter,
volatility, true, BARRIER_UKI).getDerivative(0) + rebateRate * expectedCash;
double expectedDeltaPut = BARRIER_PRICER.priceAdjoint(SPOT, STRIKE_RATE, timeToExpiry, costOfCarry, rateCounter,
volatility, false, BARRIER_UKO).getDerivative(0) + rebateRate * expectedAsset;
assertEquals(computedDeltaCall, expectedDeltaCall, TOL);
assertEquals(computedDeltaPut, expectedDeltaPut, TOL);
assertEquals(computedPvDeltaCall.getCurrency(), USD);
assertEquals(computedPvDeltaPut.getCurrency(), USD);
assertEquals(computedPvDeltaCall.getAmount(), expectedDeltaCall * NOTIONAL, TOL);
assertEquals(computedPvDeltaPut.getAmount(), -expectedDeltaPut * NOTIONAL, TOL);
}
public void test_delta_presentValueDelta_atExpiry() {
double computedDeltaCall = PRICER.delta(CALL_UKI, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
double computedDeltaPut = PRICER.delta(PUT_UKO_BASE, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
CurrencyAmount computedPvDeltaCall = PRICER.presentValueDelta(CALL_UKI, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
CurrencyAmount computedPvDeltaPut = PRICER.presentValueDelta(PUT_UKO_BASE, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
double expectedDeltaPut = -1d;
assertEquals(computedDeltaCall, 0d, TOL);
assertEquals(computedDeltaPut, expectedDeltaPut, TOL);
assertEquals(computedPvDeltaCall.getAmount(), 0d, TOL);
assertEquals(computedPvDeltaPut.getAmount(), -expectedDeltaPut * NOTIONAL, TOL);
}
public void test_delta_presentValueDelta_afterExpiry() {
double computedDeltaCall = PRICER.delta(CALL_UKI, RATE_PROVIDER_AFTER, VOLS_AFTER);
double computedDeltaPut = PRICER.delta(PUT_UKO_BASE, RATE_PROVIDER_AFTER, VOLS_AFTER);
CurrencyAmount computedPvDeltaCall = PRICER.presentValueDelta(CALL_UKI, RATE_PROVIDER_AFTER, VOLS_AFTER);
CurrencyAmount computedPvDeltaPut = PRICER.presentValueDelta(PUT_UKO_BASE, RATE_PROVIDER_AFTER, VOLS_AFTER);
assertEquals(computedDeltaCall, 0d, TOL);
assertEquals(computedDeltaPut, 0d, TOL);
assertEquals(computedPvDeltaCall.getAmount(), 0d, TOL);
assertEquals(computedPvDeltaPut.getAmount(), 0d, TOL);
}
//-------------------------------------------------------------------------
public void test_gamma_presentValueGamma() {
double computedGammaCall = PRICER.gamma(CALL_UKI, RATE_PROVIDER, VOLS);
double computedGammaPut = PRICER.gamma(PUT_UKO_BASE, RATE_PROVIDER, VOLS);
CurrencyAmount computedPvGammaCall = PRICER.presentValueGamma(CALL_UKI, RATE_PROVIDER, VOLS);
CurrencyAmount computedPvGammaPut = PRICER.presentValueGamma(PUT_UKO_BASE, RATE_PROVIDER, VOLS);
double rateBase = RATE_PROVIDER.discountFactors(EUR).zeroRate(PAY_DATE);
double rateCounter = RATE_PROVIDER.discountFactors(USD).zeroRate(PAY_DATE);
double costOfCarry = rateCounter - rateBase;
double forward = RATE_PROVIDER.fxForwardRates(CURRENCY_PAIR).rate(EUR, PAY_DATE);
double volatility = VOLS.volatility(CURRENCY_PAIR, EXPIRY_DATETIME, STRIKE_RATE, forward);
double timeToExpiry = VOLS.relativeTime(EXPIRY_DATETIME);
double rebateRate = REBATE_AMOUNT / NOTIONAL;
double expectedCash = CASH_REBATE_PRICER
.priceAdjoint(SPOT, timeToExpiry, costOfCarry, rateCounter, volatility, BARRIER_UKO).getDerivative(5);
double expectedAsset = ASSET_REBATE_PRICER
.priceAdjoint(SPOT, timeToExpiry, costOfCarry, rateCounter, volatility, BARRIER_UKI).getDerivative(5);
double expectedGammaCall = BARRIER_PRICER.priceAdjoint(SPOT, STRIKE_RATE, timeToExpiry, costOfCarry, rateCounter,
volatility, true, BARRIER_UKI).getDerivative(6) + rebateRate * expectedCash;
double expectedGammaPut = BARRIER_PRICER.priceAdjoint(SPOT, STRIKE_RATE, timeToExpiry, costOfCarry, rateCounter,
volatility, false, BARRIER_UKO).getDerivative(6) + rebateRate * expectedAsset;
assertEquals(computedGammaCall, expectedGammaCall, TOL);
assertEquals(computedGammaPut, expectedGammaPut, TOL);
assertEquals(computedPvGammaCall.getCurrency(), USD);
assertEquals(computedPvGammaPut.getCurrency(), USD);
assertEquals(computedPvGammaCall.getAmount(), expectedGammaCall * NOTIONAL, TOL);
assertEquals(computedPvGammaPut.getAmount(), -expectedGammaPut * NOTIONAL, TOL);
}
public void test_gamma_presentValueGamma_atExpiry() {
double computedGammaCall = PRICER.gamma(CALL_UKI, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
double computedGammaPut = PRICER.gamma(PUT_UKO_BASE, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
CurrencyAmount computedPvGammaCall = PRICER.presentValueGamma(CALL_UKI, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
CurrencyAmount computedPvGammaPut = PRICER.presentValueGamma(PUT_UKO_BASE, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
assertEquals(computedGammaCall, 0d, TOL);
assertEquals(computedGammaPut, 0d, TOL);
assertEquals(computedPvGammaCall.getAmount(), 0d, TOL);
assertEquals(computedPvGammaPut.getAmount(), 0d, TOL);
}
public void test_gamma_presentValueGamma_afterExpiry() {
double computedGammaCall = PRICER.gamma(CALL_UKI, RATE_PROVIDER_AFTER, VOLS_AFTER);
double computedGammaPut = PRICER.gamma(PUT_UKO_BASE, RATE_PROVIDER_AFTER, VOLS_AFTER);
CurrencyAmount computedPvGammaCall = PRICER.presentValueGamma(CALL_UKI, RATE_PROVIDER_AFTER, VOLS_AFTER);
CurrencyAmount computedPvGammaPut = PRICER.presentValueGamma(PUT_UKO_BASE, RATE_PROVIDER_AFTER, VOLS_AFTER);
assertEquals(computedGammaCall, 0d, TOL);
assertEquals(computedGammaPut, 0d, TOL);
assertEquals(computedPvGammaCall.getAmount(), 0d, TOL);
assertEquals(computedPvGammaPut.getAmount(), 0d, TOL);
}
//-------------------------------------------------------------------------
public void test_theta_presentValueTheta() {
double computedThetaCall = PRICER.theta(CALL_UKI, RATE_PROVIDER, VOLS);
double computedThetaPut = PRICER.theta(PUT_UKO_BASE, RATE_PROVIDER, VOLS);
CurrencyAmount computedPvThetaCall = PRICER.presentValueTheta(CALL_UKI, RATE_PROVIDER, VOLS);
CurrencyAmount computedPvThetaPut = PRICER.presentValueTheta(PUT_UKO_BASE, RATE_PROVIDER, VOLS);
double rateBase = RATE_PROVIDER.discountFactors(EUR).zeroRate(PAY_DATE);
double rateCounter = RATE_PROVIDER.discountFactors(USD).zeroRate(PAY_DATE);
double costOfCarry = rateCounter - rateBase;
double forward = RATE_PROVIDER.fxForwardRates(CURRENCY_PAIR).rate(EUR, PAY_DATE);
double volatility = VOLS.volatility(CURRENCY_PAIR, EXPIRY_DATETIME, STRIKE_RATE, forward);
double timeToExpiry = VOLS.relativeTime(EXPIRY_DATETIME);
double rebateRate = REBATE_AMOUNT / NOTIONAL;
double expectedCash = CASH_REBATE_PRICER
.priceAdjoint(SPOT, timeToExpiry, costOfCarry, rateCounter, volatility, BARRIER_UKO).getDerivative(4);
double expectedAsset = ASSET_REBATE_PRICER
.priceAdjoint(SPOT, timeToExpiry, costOfCarry, rateCounter, volatility, BARRIER_UKI).getDerivative(4);
double expectedThetaCall = BARRIER_PRICER.priceAdjoint(SPOT, STRIKE_RATE, timeToExpiry, costOfCarry, rateCounter,
volatility, true, BARRIER_UKI).getDerivative(5) + rebateRate * expectedCash;
double expectedThetaPut = BARRIER_PRICER.priceAdjoint(SPOT, STRIKE_RATE, timeToExpiry, costOfCarry, rateCounter,
volatility, false, BARRIER_UKO).getDerivative(5) + rebateRate * expectedAsset;
expectedThetaCall *= -1d;
expectedThetaPut *= -1d;
assertEquals(computedThetaCall, expectedThetaCall, TOL);
assertEquals(computedThetaPut, expectedThetaPut, TOL);
assertEquals(computedPvThetaCall.getCurrency(), USD);
assertEquals(computedPvThetaPut.getCurrency(), USD);
assertEquals(computedPvThetaCall.getAmount(), expectedThetaCall * NOTIONAL, TOL);
assertEquals(computedPvThetaPut.getAmount(), -expectedThetaPut * NOTIONAL, TOL);
}
public void test_theta_presentValueTheta_atExpiry() {
double computedThetaCall = PRICER.theta(CALL_UKI, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
double computedThetaPut = PRICER.theta(PUT_UKO_BASE, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
CurrencyAmount computedPvThetaCall = PRICER.presentValueTheta(CALL_UKI, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
CurrencyAmount computedPvThetaPut = PRICER.presentValueTheta(PUT_UKO_BASE, RATE_PROVIDER_EXPIRY, VOLS_EXPIRY);
double rateBase = RATE_PROVIDER_EXPIRY.discountFactors(EUR).zeroRate(PAY_DATE);
double rateCounter = RATE_PROVIDER_EXPIRY.discountFactors(USD).zeroRate(PAY_DATE);
double expectedThetaCall = -(REBATE_AMOUNT / NOTIONAL) * rateCounter;
double expectedThetaPut = -rateCounter * STRIKE_RATE + rateBase * SPOT;
expectedThetaCall *= -1d;
expectedThetaPut *= -1d;
assertEquals(computedThetaCall, expectedThetaCall, TOL);
assertEquals(computedThetaPut, expectedThetaPut, TOL);
assertEquals(computedPvThetaCall.getAmount(), expectedThetaCall * NOTIONAL, TOL * NOTIONAL);
assertEquals(computedPvThetaPut.getAmount(), -expectedThetaPut * NOTIONAL, TOL);
}
public void test_theta_presentValueTheta_afterExpiry() {
double computedThetaCall = PRICER.theta(CALL_UKI, RATE_PROVIDER_AFTER, VOLS_AFTER);
double computedThetaPut = PRICER.theta(PUT_UKO_BASE, RATE_PROVIDER_AFTER, VOLS_AFTER);
CurrencyAmount computedPvThetaCall = PRICER.presentValueTheta(CALL_UKI, RATE_PROVIDER_AFTER, VOLS_AFTER);
CurrencyAmount computedPvThetaPut = PRICER.presentValueTheta(PUT_UKO_BASE, RATE_PROVIDER_AFTER, VOLS_AFTER);
assertEquals(computedThetaCall, 0d, TOL);
assertEquals(computedThetaPut, 0d, TOL);
assertEquals(computedPvThetaCall.getAmount(), 0d, TOL);
assertEquals(computedPvThetaPut.getAmount(), 0d, TOL);
}
//-------------------------------------------------------------------------
public void regression_pv() {
CurrencyAmount pv = PRICER.presentValue(CALL_DKI, RATE_PROVIDER, VOLS);
assertEquals(pv.getAmount(), 9035006.129433425, NOTIONAL * TOL);
CurrencyAmount pvBase = PRICER.presentValue(CALL_DKI_BASE, RATE_PROVIDER, VOLS);
assertEquals(pvBase.getAmount(), 9038656.396419544, NOTIONAL * TOL); // UI put on USD/EUR rate with FX conversion in 2.x
CurrencyAmount pvPut = PRICER.presentValue(PUT_DKO, RATE_PROVIDER, VOLS);
assertEquals(pvPut.getAmount(), -55369.48871310125, NOTIONAL * TOL);
CurrencyAmount pvPutBase = PRICER.presentValue(PUT_DKO_BASE, RATE_PROVIDER, VOLS);
assertEquals(pvPutBase.getAmount(), -71369.96172030675, NOTIONAL * TOL); // UI call on USD/EUR rate with FX conversion in 2.x
}
public void regression_curveSensitivity() {
PointSensitivityBuilder point = PRICER.presentValueSensitivityRatesStickyStrike(CALL_DKI, RATE_PROVIDER, VOLS);
CurrencyParameterSensitivities pvSensi = RATE_PROVIDER.parameterSensitivity(point.build());
double[] eurSensi = new double[] {0.0, 0.0, 0.0, -8.23599758653779E7, -5.943903918586236E7 };
double[] usdSensi = new double[] {0.0, 0.0, 0.0, 6.526531701730868E7, 4.710185614928411E7 };
assertTrue(DoubleArrayMath.fuzzyEquals(
eurSensi,
pvSensi.getSensitivity(RatesProviderFxDataSets.getCurveName(EUR), USD).getSensitivity().toArray(),
NOTIONAL * TOL));
assertTrue(DoubleArrayMath.fuzzyEquals(
usdSensi,
pvSensi.getSensitivity(RatesProviderFxDataSets.getCurveName(USD), USD).getSensitivity().toArray(),
NOTIONAL * TOL));
PointSensitivityBuilder pointBase = PRICER.presentValueSensitivityRatesStickyStrike(CALL_DKI_BASE, RATE_PROVIDER,
VOLS);
CurrencyParameterSensitivities pvSensiBase =
RATE_PROVIDER.parameterSensitivity(pointBase.build()).convertedTo(EUR, RATE_PROVIDER);
double[] eurSensiBase = new double[] {0.0, 0.0, 0.0, -5.885393657463378E7, -4.247477498074986E7 };
double[] usdSensiBase = new double[] {0.0, 0.0, 0.0, 4.663853277047497E7, 3.365894110322015E7 };
assertTrue(DoubleArrayMath.fuzzyEquals(
eurSensiBase,
pvSensiBase.getSensitivity(RatesProviderFxDataSets.getCurveName(EUR), EUR).getSensitivity().toArray(),
NOTIONAL * TOL));
assertTrue(DoubleArrayMath.fuzzyEquals(
usdSensiBase,
pvSensiBase.getSensitivity(RatesProviderFxDataSets.getCurveName(USD), EUR).getSensitivity().toArray(),
NOTIONAL * TOL));
PointSensitivityBuilder pointPut =
PRICER.presentValueSensitivityRatesStickyStrike(PUT_DKO, RATE_PROVIDER, VOLS).multipliedBy(-1d);
CurrencyParameterSensitivities pvSensiPut = RATE_PROVIDER.parameterSensitivity(pointPut.build());
double[] eurSensiPut = new double[] {0.0, 0.0, 0.0, 22176.623866383557, 16004.827601682477 };
double[] usdSensiPut = new double[] {0.0, 0.0, 0.0, -48509.60688347871, -35009.29176024644 };
assertTrue(DoubleArrayMath.fuzzyEquals(
eurSensiPut,
pvSensiPut.getSensitivity(RatesProviderFxDataSets.getCurveName(EUR), USD).getSensitivity().toArray(),
NOTIONAL * TOL));
assertTrue(DoubleArrayMath.fuzzyEquals(
usdSensiPut,
pvSensiPut.getSensitivity(RatesProviderFxDataSets.getCurveName(USD), USD).getSensitivity().toArray(),
NOTIONAL * TOL));
PointSensitivityBuilder pointPutBase =
PRICER.presentValueSensitivityRatesStickyStrike(PUT_DKO_BASE, RATE_PROVIDER, VOLS).multipliedBy(-1d);
CurrencyParameterSensitivities pvSensiPutBase =
RATE_PROVIDER.parameterSensitivity(pointPutBase.build()).convertedTo(EUR, RATE_PROVIDER);
double[] eurSensiPutBase = new double[] {0.0, 0.0, 0.0, 24062.637495868825, 17365.96007956571 };
double[] usdSensiPutBase = new double[] {0.0, 0.0, 0.0, -44888.77092190999, -32396.141278548253 };
assertTrue(DoubleArrayMath.fuzzyEquals(
eurSensiPutBase,
pvSensiPutBase.getSensitivity(RatesProviderFxDataSets.getCurveName(EUR), EUR).getSensitivity().toArray(),
NOTIONAL * TOL));
assertTrue(DoubleArrayMath.fuzzyEquals(
usdSensiPutBase,
pvSensiPutBase.getSensitivity(RatesProviderFxDataSets.getCurveName(USD), EUR).getSensitivity().toArray(),
NOTIONAL * TOL));
}
public void regression_volSensitivity() {
PointSensitivityBuilder point = PRICER.presentValueSensitivityModelParamsVolatility(CALL_DKI, RATE_PROVIDER, VOLS);
CurrencyParameterSensitivity pvSensi =
VOLS.parameterSensitivity((FxOptionSensitivity) point).getSensitivities().get(0);
PointSensitivityBuilder pointBase =
PRICER.presentValueSensitivityModelParamsVolatility(CALL_DKI_BASE, RATE_PROVIDER, VOLS);
CurrencyParameterSensitivity pvSensiBase = VOLS
.parameterSensitivity((FxOptionSensitivity) pointBase).convertedTo(EUR, RATE_PROVIDER).getSensitivities().get(0);
PointSensitivityBuilder pointPut =
PRICER.presentValueSensitivityModelParamsVolatility(PUT_DKO, RATE_PROVIDER, VOLS).multipliedBy(-1d);
CurrencyParameterSensitivity pvSensiPut =
VOLS.parameterSensitivity((FxOptionSensitivity) pointPut).getSensitivities().get(0);
PointSensitivityBuilder pointPutBase =
PRICER.presentValueSensitivityModelParamsVolatility(PUT_DKO_BASE, RATE_PROVIDER, VOLS).multipliedBy(-1d);
CurrencyParameterSensitivity pvSensiPutBase = VOLS
.parameterSensitivity((FxOptionSensitivity) pointPutBase).convertedTo(EUR, RATE_PROVIDER).getSensitivities().get(0);
double[] computed = pvSensi.getSensitivity().toArray();
double[] computedBase = pvSensiBase.getSensitivity().toArray();
double[] computedPut = pvSensiPut.getSensitivity().toArray();
double[] computedPutBase = pvSensiPutBase.getSensitivity().toArray();
double[][] expected = new double[][] {
{0.0, 0.0, 0.0, 0.0, 0.0 }, {0.0, 0.0, 0.0, 0.0, 0.0 }, {0.0, 0.0, 0.0, 0.0, 0.0 },
{0.0, 0.0, 3.154862889936005E7, 186467.57005640838, 0.0 },
{0.0, 0.0, 5.688931113627187E7, 336243.18963600876, 0.0 } };
double[][] expectedBase = new double[][] {
{0.0, 0.0, 0.0, 0.0, 0.0 }, {0.0, 0.0, 0.0, 0.0, 0.0 }, {0.0, 0.0, 0.0, 0.0, 0.0 },
{0.0, 0.0, 2.2532363577178854E7, 133177.10564432456, 0.0 },
{0.0, 0.0, 4.063094615828866E7, 240148.4331822043, 0.0 } };
double[][] expectedPut = new double[][] {
{-0.0, -0.0, -0.0, -0.0, -0.0 }, {-0.0, -0.0, -0.0, -0.0, -0.0 },
{-0.0, -0.0, -0.0, -0.0, -0.0 }, {-0.0, -0.0, -53011.143048566446, -313.32135103910525, -0.0 },
{-0.0, -0.0, -95591.07688006328, -564.989238732409, -0.0 } };
double[][] expectedPutBase = new double[][] {
{-0.0, -0.0, -0.0, -0.0, -0.0 }, {-0.0, -0.0, -0.0, -0.0, -0.0 }, {-0.0, -0.0, -0.0, -0.0, -0.0 },
{-0.0, -0.0, -35148.33541137355, -207.743566815316, -0.0 },
{-0.0, -0.0, -63380.39588085656, -374.6086223530026, -0.0 } };
for (int i = 0; i < computed.length; ++i) {
int row = i / 5;
int col = i % 5;
assertTrue(DoubleMath.fuzzyEquals(computed[i], expected[row][col], NOTIONAL * TOL));
assertTrue(DoubleMath.fuzzyEquals(computedBase[i], expectedBase[row][col], NOTIONAL * TOL));
assertTrue(DoubleMath.fuzzyEquals(computedPut[i], expectedPut[row][col], NOTIONAL * TOL));
assertTrue(DoubleMath.fuzzyEquals(computedPutBase[i], expectedPutBase[row][col], NOTIONAL * TOL));
}
}
public void regression_currencyExposure() {
MultiCurrencyAmount pv = PRICER.currencyExposure(CALL_DKI, RATE_PROVIDER, VOLS);
assertEquals(pv.getAmount(EUR).getAmount(), -2.8939530642669797E7, NOTIONAL * TOL);
assertEquals(pv.getAmount(USD).getAmount(), 4.955034902917114E7, NOTIONAL * TOL);
MultiCurrencyAmount pvBase = PRICER.currencyExposure(CALL_DKI_BASE, RATE_PROVIDER, VOLS);
assertEquals(pvBase.getAmount(EUR).getAmount(), -2.8866459583853487E7, NOTIONAL * TOL);
assertEquals(pvBase.getAmount(USD).getAmount(), 4.9451699813814424E7, NOTIONAL * TOL);
MultiCurrencyAmount pvPut = PRICER.currencyExposure(PUT_DKO, RATE_PROVIDER, VOLS);
assertEquals(pvPut.getAmount(EUR).getAmount(), -105918.46956467835, NOTIONAL * TOL);
assertEquals(pvPut.getAmount(USD).getAmount(), 92916.36867744842, NOTIONAL * TOL);
MultiCurrencyAmount pvPutBase = PRICER.currencyExposure(PUT_DKO_BASE, RATE_PROVIDER, VOLS);
assertEquals(pvPutBase.getAmount(EUR).getAmount(), -76234.66256109312, NOTIONAL * TOL);
assertEquals(pvPutBase.getAmount(USD).getAmount(), 35358.56586522361, NOTIONAL * TOL);
}
}