/** * Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.pricer.swaption; import static com.opengamma.strata.basics.currency.Currency.USD; import static com.opengamma.strata.basics.date.DayCounts.ACT_365F; import static com.opengamma.strata.basics.index.IborIndices.USD_LIBOR_3M; import static com.opengamma.strata.collect.TestHelper.assertThrowsIllegalArg; import static com.opengamma.strata.product.common.BuySell.BUY; import static com.opengamma.strata.product.common.BuySell.SELL; import static com.opengamma.strata.product.swap.type.FixedIborSwapConventions.USD_FIXED_6M_LIBOR_3M; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; import java.time.LocalDate; import java.time.LocalTime; import java.time.Period; import java.time.ZoneId; import org.testng.annotations.Test; import com.opengamma.strata.basics.ReferenceData; import com.opengamma.strata.basics.currency.CurrencyAmount; import com.opengamma.strata.basics.currency.MultiCurrencyAmount; import com.opengamma.strata.basics.date.AdjustableDate; import com.opengamma.strata.market.param.CurrencyParameterSensitivities; import com.opengamma.strata.market.sensitivity.PointSensitivities; import com.opengamma.strata.market.sensitivity.PointSensitivity; import com.opengamma.strata.market.sensitivity.PointSensitivityBuilder; import com.opengamma.strata.pricer.datasets.RatesProviderDataSets; import com.opengamma.strata.pricer.impl.option.EuropeanVanillaOption; import com.opengamma.strata.pricer.impl.option.NormalFunctionData; import com.opengamma.strata.pricer.impl.option.NormalPriceFunction; import com.opengamma.strata.pricer.rate.ImmutableRatesProvider; import com.opengamma.strata.pricer.sensitivity.RatesFiniteDifferenceSensitivityCalculator; import com.opengamma.strata.pricer.swap.DiscountingSwapProductPricer; import com.opengamma.strata.product.common.LongShort; import com.opengamma.strata.product.common.PutCall; import com.opengamma.strata.product.swap.ResolvedSwap; import com.opengamma.strata.product.swap.Swap; import com.opengamma.strata.product.swap.SwapLegType; import com.opengamma.strata.product.swaption.CashSwaptionSettlement; import com.opengamma.strata.product.swaption.CashSwaptionSettlementMethod; import com.opengamma.strata.product.swaption.PhysicalSwaptionSettlement; import com.opengamma.strata.product.swaption.ResolvedSwaption; import com.opengamma.strata.product.swaption.Swaption; /** * Test {@link NormalSwaptionCashParYieldProductPricer}. */ @Test public class NormalSwaptionCashParYieldProductPricerTest { private static final ReferenceData REF_DATA = ReferenceData.standard(); private static final LocalDate VAL_DATE = RatesProviderDataSets.VAL_DATE_2014_01_22; private static final LocalDate SWAPTION_EXERCISE_DATE = VAL_DATE.plusYears(5); private static final LocalDate SWAPTION_PAST_EXERCISE_DATE = VAL_DATE.minusYears(1); private static final LocalTime SWAPTION_EXPIRY_TIME = LocalTime.of(11, 0); private static final ZoneId SWAPTION_EXPIRY_ZONE = ZoneId.of("America/New_York"); private static final LocalDate SWAP_EFFECTIVE_DATE = USD_LIBOR_3M.calculateEffectiveFromFixing(SWAPTION_EXERCISE_DATE, REF_DATA); private static final int SWAP_TENOR_YEAR = 5; private static final Period SWAP_TENOR = Period.ofYears(SWAP_TENOR_YEAR); private static final LocalDate SWAP_MATURITY_DATE = SWAP_EFFECTIVE_DATE.plus(SWAP_TENOR); private static final double STRIKE = 0.01; private static final double NOTIONAL = 100_000_000; private static final Swap SWAP_REC = USD_FIXED_6M_LIBOR_3M .toTrade(VAL_DATE, SWAP_EFFECTIVE_DATE, SWAP_MATURITY_DATE, SELL, NOTIONAL, STRIKE).getProduct(); private static final ResolvedSwap RSWAP_REC = SWAP_REC.resolve(REF_DATA); private static final Swap SWAP_PAY = USD_FIXED_6M_LIBOR_3M .toTrade(VAL_DATE, SWAP_EFFECTIVE_DATE, SWAP_MATURITY_DATE, BUY, NOTIONAL, STRIKE).getProduct(); private static final ResolvedSwap RSWAP_PAY = SWAP_PAY.resolve(REF_DATA); private static final Swap SWAP_REC_PAST = USD_FIXED_6M_LIBOR_3M // Only for checks; no actual computation on that swap .toTrade(SWAPTION_PAST_EXERCISE_DATE, SWAPTION_PAST_EXERCISE_DATE, SWAPTION_PAST_EXERCISE_DATE.plusYears(10), SELL, NOTIONAL, STRIKE).getProduct(); private static final Swap SWAP_PAY_PAST = USD_FIXED_6M_LIBOR_3M // Only for checks; no actual computation on that swap .toTrade(SWAPTION_PAST_EXERCISE_DATE, SWAPTION_PAST_EXERCISE_DATE, SWAPTION_PAST_EXERCISE_DATE.plusYears(10), BUY, NOTIONAL, STRIKE).getProduct(); private static final LocalDate SETTLE_DATE = USD_LIBOR_3M.getEffectiveDateOffset().adjust(SWAPTION_EXERCISE_DATE, REF_DATA); private static final CashSwaptionSettlement PAR_YIELD = CashSwaptionSettlement.of(SETTLE_DATE, CashSwaptionSettlementMethod.PAR_YIELD); private static final ResolvedSwaption SWAPTION_REC_LONG = Swaption.builder() .swaptionSettlement(PAR_YIELD) .expiryDate(AdjustableDate.of(SWAPTION_EXERCISE_DATE)) .expiryTime(SWAPTION_EXPIRY_TIME) .expiryZone(SWAPTION_EXPIRY_ZONE) .longShort(LongShort.LONG) .underlying(SWAP_REC) .build() .resolve(REF_DATA); private static final ResolvedSwaption SWAPTION_REC_SHORT = Swaption.builder() .swaptionSettlement(PAR_YIELD) .expiryDate(AdjustableDate.of(SWAPTION_EXERCISE_DATE)) .expiryTime(SWAPTION_EXPIRY_TIME) .expiryZone(SWAPTION_EXPIRY_ZONE) .longShort(LongShort.SHORT) .underlying(SWAP_REC) .build() .resolve(REF_DATA); private static final ResolvedSwaption SWAPTION_PAY_LONG = Swaption.builder() .swaptionSettlement(PAR_YIELD) .expiryDate(AdjustableDate.of(SWAPTION_EXERCISE_DATE)) .expiryTime(SWAPTION_EXPIRY_TIME) .expiryZone(SWAPTION_EXPIRY_ZONE) .longShort(LongShort.LONG) .underlying(SWAP_PAY) .build() .resolve(REF_DATA); private static final ResolvedSwaption SWAPTION_PAY_SHORT = Swaption.builder() .swaptionSettlement(PAR_YIELD) .expiryDate(AdjustableDate.of(SWAPTION_EXERCISE_DATE)) .expiryTime(SWAPTION_EXPIRY_TIME) .expiryZone(SWAPTION_EXPIRY_ZONE) .longShort(LongShort.SHORT) .underlying(SWAP_PAY) .build() .resolve(REF_DATA); private static final ResolvedSwaption SWAPTION_REC_LONG_AT_EXPIRY = Swaption.builder() .swaptionSettlement(PAR_YIELD) .expiryDate(AdjustableDate.of(VAL_DATE)) .expiryTime(SWAPTION_EXPIRY_TIME) .expiryZone(SWAPTION_EXPIRY_ZONE) .longShort(LongShort.LONG) .underlying(SWAP_REC) .build() .resolve(REF_DATA); private static final ResolvedSwaption SWAPTION_PAY_SHORT_AT_EXPIRY = Swaption.builder() .swaptionSettlement(PAR_YIELD) .expiryDate(AdjustableDate.of(VAL_DATE)) .expiryTime(SWAPTION_EXPIRY_TIME) .expiryZone(SWAPTION_EXPIRY_ZONE) .longShort(LongShort.SHORT) .underlying(SWAP_PAY) .build() .resolve(REF_DATA); private static final ResolvedSwaption SWAPTION_REC_LONG_PAST = Swaption.builder() .swaptionSettlement(PAR_YIELD) .expiryDate(AdjustableDate.of(SWAPTION_PAST_EXERCISE_DATE)) .expiryTime(SWAPTION_EXPIRY_TIME) .expiryZone(SWAPTION_EXPIRY_ZONE) .longShort(LongShort.LONG) .underlying(SWAP_REC_PAST) .build() .resolve(REF_DATA); private static final ResolvedSwaption SWAPTION_PAY_SHORT_PAST = Swaption.builder() .swaptionSettlement(PAR_YIELD) .expiryDate(AdjustableDate.of(SWAPTION_PAST_EXERCISE_DATE)) .expiryTime(SWAPTION_EXPIRY_TIME) .expiryZone(SWAPTION_EXPIRY_ZONE) .longShort(LongShort.LONG) .underlying(SWAP_PAY_PAST) .build() .resolve(REF_DATA); // volatility and rate providers private static final ImmutableRatesProvider RATE_PROVIDER = RatesProviderDataSets.multiUsd(VAL_DATE); private static final NormalSwaptionExpiryTenorVolatilities VOLS = SwaptionNormalVolatilityDataSets.NORMAL_SWAPTION_VOLS_USD_STD; private static final NormalSwaptionVolatilities VOLS_FLAT = SwaptionNormalVolatilityDataSets.NORMAL_SWAPTION_VOLS_USD_FLAT; // test parameters private static final double FD_EPS = 1.0E-7; private static final double TOL = 1.0e-12; // pricers private static final NormalPriceFunction NORMAL = new NormalPriceFunction(); private static final NormalSwaptionCashParYieldProductPricer PRICER_SWAPTION = NormalSwaptionCashParYieldProductPricer.DEFAULT; private static final DiscountingSwapProductPricer PRICER_SWAP = DiscountingSwapProductPricer.DEFAULT; private static final RatesFiniteDifferenceSensitivityCalculator FINITE_DIFFERENCE_CALCULATOR = new RatesFiniteDifferenceSensitivityCalculator(FD_EPS); //------------------------------------------------------------------------- public void test_presentValue() { CurrencyAmount pvRecComputed = PRICER_SWAPTION.presentValue(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS); CurrencyAmount pvPayComputed = PRICER_SWAPTION.presentValue(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS); double forward = PRICER_SWAP.parRate(RSWAP_REC, RATE_PROVIDER); double annuityCash = PRICER_SWAP.getLegPricer().annuityCash(RSWAP_REC.getLegs(SwapLegType.FIXED).get(0), forward); double volatility = VOLS.volatility(SWAPTION_REC_LONG.getExpiry(), SWAP_TENOR_YEAR, STRIKE, forward); double discount = RATE_PROVIDER.discountFactor(USD, SETTLE_DATE); NormalFunctionData normalData = NormalFunctionData.of(forward, annuityCash * discount, volatility); double expiry = VOLS.relativeTime(SWAPTION_REC_LONG.getExpiry()); EuropeanVanillaOption optionRec = EuropeanVanillaOption.of(STRIKE, expiry, PutCall.PUT); EuropeanVanillaOption optionPay = EuropeanVanillaOption.of(STRIKE, expiry, PutCall.CALL); double pvRecExpected = NORMAL.getPriceFunction(optionRec).apply(normalData); double pvPayExpected = -NORMAL.getPriceFunction(optionPay).apply(normalData); assertEquals(pvRecComputed.getCurrency(), USD); assertEquals(pvRecComputed.getAmount(), pvRecExpected, NOTIONAL * TOL); assertEquals(pvPayComputed.getCurrency(), USD); assertEquals(pvPayComputed.getAmount(), pvPayExpected, NOTIONAL * TOL); } public void test_presentValue_at_expiry() { CurrencyAmount pvRec = PRICER_SWAPTION.presentValue(SWAPTION_REC_LONG_AT_EXPIRY, RATE_PROVIDER, VOLS); CurrencyAmount pvPay = PRICER_SWAPTION.presentValue(SWAPTION_PAY_SHORT_AT_EXPIRY, RATE_PROVIDER, VOLS); double forward = PRICER_SWAP.parRate(RSWAP_REC, RATE_PROVIDER); double annuityCash = PRICER_SWAP.getLegPricer().annuityCash(RSWAP_REC.getLegs(SwapLegType.FIXED).get(0), forward); double discount = RATE_PROVIDER.discountFactor(USD, SETTLE_DATE); assertEquals(pvRec.getAmount(), 0d, NOTIONAL * TOL); assertEquals(pvPay.getAmount(), discount * annuityCash * (STRIKE - forward), NOTIONAL * TOL); } public void test_presentValue_after_expiry() { CurrencyAmount pvRec = PRICER_SWAPTION.presentValue(SWAPTION_REC_LONG_PAST, RATE_PROVIDER, VOLS); CurrencyAmount pvPay = PRICER_SWAPTION.presentValue(SWAPTION_PAY_SHORT_PAST, RATE_PROVIDER, VOLS); assertEquals(pvRec.getAmount(), 0d, NOTIONAL * TOL); assertEquals(pvPay.getAmount(), 0d, NOTIONAL * TOL); } public void test_presentValue_parity() { CurrencyAmount pvRecLong = PRICER_SWAPTION.presentValue(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS); CurrencyAmount pvRecShort = PRICER_SWAPTION.presentValue(SWAPTION_REC_SHORT, RATE_PROVIDER, VOLS); CurrencyAmount pvPayLong = PRICER_SWAPTION.presentValue(SWAPTION_PAY_LONG, RATE_PROVIDER, VOLS); CurrencyAmount pvPayShort = PRICER_SWAPTION.presentValue(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS); assertEquals(pvRecLong.getAmount(), -pvRecShort.getAmount(), NOTIONAL * TOL); assertEquals(pvPayLong.getAmount(), -pvPayShort.getAmount(), NOTIONAL * TOL); double forward = PRICER_SWAP.parRate(RSWAP_REC, RATE_PROVIDER); double annuityCash = PRICER_SWAP.getLegPricer().annuityCash(RSWAP_REC.getLegs(SwapLegType.FIXED).get(0), forward); double discount = RATE_PROVIDER.discountFactor(USD, SETTLE_DATE); double expected = discount * annuityCash * (forward - STRIKE); assertEquals(pvPayLong.getAmount() - pvRecLong.getAmount(), expected, NOTIONAL * TOL); assertEquals(pvPayShort.getAmount() - pvRecShort.getAmount(), -expected, NOTIONAL * TOL); } public void test_physicalSettlement() { Swaption swaption = Swaption.builder() .swaptionSettlement(PhysicalSwaptionSettlement.DEFAULT) .expiryDate(AdjustableDate.of(SWAPTION_EXERCISE_DATE)) .expiryTime(SWAPTION_EXPIRY_TIME) .expiryZone(SWAPTION_EXPIRY_ZONE) .longShort(LongShort.LONG) .underlying(SWAP_REC) .build(); assertThrowsIllegalArg(() -> PRICER_SWAPTION.presentValue(swaption.resolve(REF_DATA), RATE_PROVIDER, VOLS)); } //------------------------------------------------------------------------- public void test_presentValueDelta() { CurrencyAmount pvDeltaRecComputed = PRICER_SWAPTION.presentValueDelta(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS); CurrencyAmount pvDeltaPayComputed = PRICER_SWAPTION.presentValueDelta(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS); double forward = PRICER_SWAP.parRate(RSWAP_REC, RATE_PROVIDER); double annuityCash = PRICER_SWAP.getLegPricer().annuityCash(RSWAP_REC.getLegs(SwapLegType.FIXED).get(0), forward); double volatility = VOLS.volatility(SWAPTION_REC_LONG.getExpiry(), SWAP_TENOR_YEAR, STRIKE, forward); double discount = RATE_PROVIDER.discountFactor(USD, SETTLE_DATE); NormalFunctionData normalData = NormalFunctionData.of(forward, annuityCash * discount, volatility); double expiry = VOLS.relativeTime(SWAPTION_REC_LONG.getExpiry()); EuropeanVanillaOption optionRec = EuropeanVanillaOption.of(STRIKE, expiry, PutCall.PUT); EuropeanVanillaOption optionPay = EuropeanVanillaOption.of(STRIKE, expiry, PutCall.CALL); double pvDeltaRecExpected = NORMAL.getDelta(optionRec, normalData); double pvDeltaPayExpected = -NORMAL.getDelta(optionPay, normalData); assertEquals(pvDeltaRecComputed.getCurrency(), USD); assertEquals(pvDeltaRecComputed.getAmount(), pvDeltaRecExpected, NOTIONAL * TOL); assertEquals(pvDeltaPayComputed.getCurrency(), USD); assertEquals(pvDeltaPayComputed.getAmount(), pvDeltaPayExpected, NOTIONAL * TOL); } public void test_presentValueDelta_at_expiry() { CurrencyAmount pvDeltaRec = PRICER_SWAPTION.presentValueDelta(SWAPTION_REC_LONG_AT_EXPIRY, RATE_PROVIDER, VOLS); CurrencyAmount pvDeltaPay = PRICER_SWAPTION.presentValueDelta(SWAPTION_PAY_SHORT_AT_EXPIRY, RATE_PROVIDER, VOLS); double forward = PRICER_SWAP.parRate(RSWAP_REC, RATE_PROVIDER); double annuityCash = PRICER_SWAP.getLegPricer().annuityCash(RSWAP_REC.getLegs(SwapLegType.FIXED).get(0), forward); double discount = RATE_PROVIDER.discountFactor(USD, SETTLE_DATE); assertEquals(pvDeltaRec.getAmount(), 0d, NOTIONAL * TOL); assertEquals(pvDeltaPay.getAmount(), -discount * annuityCash, NOTIONAL * TOL); } public void test_presentValueDelta_after_expiry() { CurrencyAmount pvDeltaRec = PRICER_SWAPTION.presentValueDelta(SWAPTION_REC_LONG_PAST, RATE_PROVIDER, VOLS); CurrencyAmount pvDeltaPay = PRICER_SWAPTION.presentValueDelta(SWAPTION_PAY_SHORT_PAST, RATE_PROVIDER, VOLS); assertEquals(pvDeltaRec.getAmount(), 0d, NOTIONAL * TOL); assertEquals(pvDeltaPay.getAmount(), 0d, NOTIONAL * TOL); } public void test_presentValueDelta_parity() { CurrencyAmount pvDeltaRecLong = PRICER_SWAPTION.presentValueDelta(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS); CurrencyAmount pvDeltaRecShort = PRICER_SWAPTION.presentValueDelta(SWAPTION_REC_SHORT, RATE_PROVIDER, VOLS); CurrencyAmount pvDeltaPayLong = PRICER_SWAPTION.presentValueDelta(SWAPTION_PAY_LONG, RATE_PROVIDER, VOLS); CurrencyAmount pvDeltaPayShort = PRICER_SWAPTION.presentValueDelta(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS); assertEquals(pvDeltaRecLong.getAmount(), -pvDeltaRecShort.getAmount(), NOTIONAL * TOL); assertEquals(pvDeltaPayLong.getAmount(), -pvDeltaPayShort.getAmount(), NOTIONAL * TOL); double forward = PRICER_SWAP.parRate(RSWAP_REC, RATE_PROVIDER); double annuityCash = PRICER_SWAP.getLegPricer().annuityCash(RSWAP_REC.getLegs(SwapLegType.FIXED).get(0), forward); double discount = RATE_PROVIDER.discountFactor(USD, SETTLE_DATE); double expected = discount * annuityCash; assertEquals(pvDeltaPayLong.getAmount() - pvDeltaRecLong.getAmount(), expected, NOTIONAL * TOL); assertEquals(pvDeltaPayShort.getAmount() - pvDeltaRecShort.getAmount(), -expected, NOTIONAL * TOL); } //------------------------------------------------------------------------- public void test_presentValueGamma() { CurrencyAmount pvGammaRecComputed = PRICER_SWAPTION.presentValueGamma(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS); CurrencyAmount pvGammaPayComputed = PRICER_SWAPTION.presentValueGamma(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS); double forward = PRICER_SWAP.parRate(RSWAP_REC, RATE_PROVIDER); double annuityCash = PRICER_SWAP.getLegPricer().annuityCash(RSWAP_REC.getLegs(SwapLegType.FIXED).get(0), forward); double volatility = VOLS.volatility(SWAPTION_REC_LONG.getExpiry(), SWAP_TENOR_YEAR, STRIKE, forward); double discount = RATE_PROVIDER.discountFactor(USD, SETTLE_DATE); NormalFunctionData normalData = NormalFunctionData.of(forward, annuityCash * discount, volatility); double expiry = VOLS.relativeTime(SWAPTION_REC_LONG.getExpiry()); EuropeanVanillaOption optionRec = EuropeanVanillaOption.of(STRIKE, expiry, PutCall.PUT); EuropeanVanillaOption optionPay = EuropeanVanillaOption.of(STRIKE, expiry, PutCall.CALL); double pvGammaRecExpected = NORMAL.getGamma(optionRec, normalData); double pvGammaPayExpected = -NORMAL.getGamma(optionPay, normalData); assertEquals(pvGammaRecComputed.getCurrency(), USD); assertEquals(pvGammaRecComputed.getAmount(), pvGammaRecExpected, NOTIONAL * TOL); assertEquals(pvGammaPayComputed.getCurrency(), USD); assertEquals(pvGammaPayComputed.getAmount(), pvGammaPayExpected, NOTIONAL * TOL); } public void test_presentValueGamma_at_expiry() { CurrencyAmount pvGammaRec = PRICER_SWAPTION.presentValueGamma(SWAPTION_REC_LONG_AT_EXPIRY, RATE_PROVIDER, VOLS); CurrencyAmount pvGammaPay = PRICER_SWAPTION.presentValueGamma(SWAPTION_PAY_SHORT_AT_EXPIRY, RATE_PROVIDER, VOLS); assertEquals(pvGammaRec.getAmount(), 0d, NOTIONAL * TOL); assertEquals(pvGammaPay.getAmount(), 0d, NOTIONAL * TOL); } public void test_presentValueGamma_after_expiry() { CurrencyAmount pvGammaRec = PRICER_SWAPTION.presentValueGamma(SWAPTION_REC_LONG_PAST, RATE_PROVIDER, VOLS); CurrencyAmount pvGammaPay = PRICER_SWAPTION.presentValueGamma(SWAPTION_PAY_SHORT_PAST, RATE_PROVIDER, VOLS); assertEquals(pvGammaRec.getAmount(), 0d, NOTIONAL * TOL); assertEquals(pvGammaPay.getAmount(), 0d, NOTIONAL * TOL); } public void test_presentValueGamma_parity() { CurrencyAmount pvGammaRecLong = PRICER_SWAPTION.presentValueGamma(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS); CurrencyAmount pvGammaRecShort = PRICER_SWAPTION.presentValueGamma(SWAPTION_REC_SHORT, RATE_PROVIDER, VOLS); CurrencyAmount pvGammaPayLong = PRICER_SWAPTION.presentValueGamma(SWAPTION_PAY_LONG, RATE_PROVIDER, VOLS); CurrencyAmount pvGammaPayShort = PRICER_SWAPTION.presentValueGamma(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS); assertEquals(pvGammaRecLong.getAmount(), -pvGammaRecShort.getAmount(), NOTIONAL * TOL); assertEquals(pvGammaPayLong.getAmount(), -pvGammaPayShort.getAmount(), NOTIONAL * TOL); assertEquals(pvGammaPayLong.getAmount(), pvGammaRecLong.getAmount(), NOTIONAL * TOL); assertEquals(pvGammaPayShort.getAmount(), pvGammaRecShort.getAmount(), NOTIONAL * TOL); } //------------------------------------------------------------------------- public void test_presentValueTheta() { CurrencyAmount pvThetaRecComputed = PRICER_SWAPTION.presentValueTheta(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS); CurrencyAmount pvThetaPayComputed = PRICER_SWAPTION.presentValueTheta(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS); double forward = PRICER_SWAP.parRate(RSWAP_REC, RATE_PROVIDER); double annuityCash = PRICER_SWAP.getLegPricer().annuityCash(RSWAP_REC.getLegs(SwapLegType.FIXED).get(0), forward); double volatility = VOLS.volatility(SWAPTION_REC_LONG.getExpiry(), SWAP_TENOR_YEAR, STRIKE, forward); double discount = RATE_PROVIDER.discountFactor(USD, SETTLE_DATE); NormalFunctionData normalData = NormalFunctionData.of(forward, annuityCash * discount, volatility); double expiry = VOLS.relativeTime(SWAPTION_REC_LONG.getExpiry()); EuropeanVanillaOption optionRec = EuropeanVanillaOption.of(STRIKE, expiry, PutCall.PUT); EuropeanVanillaOption optionPay = EuropeanVanillaOption.of(STRIKE, expiry, PutCall.CALL); double pvThetaRecExpected = NORMAL.getTheta(optionRec, normalData); double pvThetaPayExpected = -NORMAL.getTheta(optionPay, normalData); assertEquals(pvThetaRecComputed.getCurrency(), USD); assertEquals(pvThetaRecComputed.getAmount(), pvThetaRecExpected, NOTIONAL * TOL); assertEquals(pvThetaPayComputed.getCurrency(), USD); assertEquals(pvThetaPayComputed.getAmount(), pvThetaPayExpected, NOTIONAL * TOL); } public void test_presentValueTheta_at_expiry() { CurrencyAmount pvThetaRec = PRICER_SWAPTION.presentValueTheta(SWAPTION_REC_LONG_AT_EXPIRY, RATE_PROVIDER, VOLS); CurrencyAmount pvThetaPay = PRICER_SWAPTION.presentValueTheta(SWAPTION_PAY_SHORT_AT_EXPIRY, RATE_PROVIDER, VOLS); assertEquals(pvThetaRec.getAmount(), 0d, NOTIONAL * TOL); assertEquals(pvThetaPay.getAmount(), 0d, NOTIONAL * TOL); } public void test_presentValueTheta_after_expiry() { CurrencyAmount pvThetaRec = PRICER_SWAPTION.presentValueTheta(SWAPTION_REC_LONG_PAST, RATE_PROVIDER, VOLS); CurrencyAmount pvThetaPay = PRICER_SWAPTION.presentValueTheta(SWAPTION_PAY_SHORT_PAST, RATE_PROVIDER, VOLS); assertEquals(pvThetaRec.getAmount(), 0d, NOTIONAL * TOL); assertEquals(pvThetaPay.getAmount(), 0d, NOTIONAL * TOL); } public void test_presentValueTheta_parity() { CurrencyAmount pvThetaRecLong = PRICER_SWAPTION.presentValueTheta(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS); CurrencyAmount pvThetaRecShort = PRICER_SWAPTION.presentValueTheta(SWAPTION_REC_SHORT, RATE_PROVIDER, VOLS); CurrencyAmount pvThetaPayLong = PRICER_SWAPTION.presentValueTheta(SWAPTION_PAY_LONG, RATE_PROVIDER, VOLS); CurrencyAmount pvThetaPayShort = PRICER_SWAPTION.presentValueTheta(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS); assertEquals(pvThetaRecLong.getAmount(), -pvThetaRecShort.getAmount(), NOTIONAL * TOL); assertEquals(pvThetaPayLong.getAmount(), -pvThetaPayShort.getAmount(), NOTIONAL * TOL); assertEquals(pvThetaPayLong.getAmount(), pvThetaRecLong.getAmount(), NOTIONAL * TOL); assertEquals(pvThetaPayShort.getAmount(), pvThetaRecShort.getAmount(), NOTIONAL * TOL); } //------------------------------------------------------------------------- public void test_currencyExposure() { MultiCurrencyAmount computedRec = PRICER_SWAPTION.currencyExposure(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS); MultiCurrencyAmount computedPay = PRICER_SWAPTION.currencyExposure(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS); PointSensitivityBuilder pointRec = PRICER_SWAPTION.presentValueSensitivityRatesStickyStrike(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS); MultiCurrencyAmount expectedRec = RATE_PROVIDER.currencyExposure(pointRec.build()) .plus(PRICER_SWAPTION.presentValue(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS)); assertEquals(computedRec.size(), 1); assertEquals(computedRec.getAmount(USD).getAmount(), expectedRec.getAmount(USD).getAmount(), NOTIONAL * TOL); PointSensitivityBuilder pointPay = PRICER_SWAPTION.presentValueSensitivityRatesStickyStrike(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS); MultiCurrencyAmount expectedPay = RATE_PROVIDER.currencyExposure(pointPay.build()) .plus(PRICER_SWAPTION.presentValue(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS)); assertEquals(computedPay.size(), 1); assertEquals(computedPay.getAmount(USD).getAmount(), expectedPay.getAmount(USD).getAmount(), NOTIONAL * TOL); } public void test_currencyExposure_at_expiry() { MultiCurrencyAmount computedRec = PRICER_SWAPTION.currencyExposure(SWAPTION_REC_LONG_AT_EXPIRY, RATE_PROVIDER, VOLS); MultiCurrencyAmount computedPay = PRICER_SWAPTION.currencyExposure(SWAPTION_PAY_SHORT_AT_EXPIRY, RATE_PROVIDER, VOLS); PointSensitivityBuilder pointRec = PRICER_SWAPTION.presentValueSensitivityRatesStickyStrike(SWAPTION_REC_LONG_AT_EXPIRY, RATE_PROVIDER, VOLS); MultiCurrencyAmount expectedRec = RATE_PROVIDER.currencyExposure(pointRec.build()) .plus(PRICER_SWAPTION.presentValue(SWAPTION_REC_LONG_AT_EXPIRY, RATE_PROVIDER, VOLS)); assertEquals(computedRec.size(), 1); assertEquals(computedRec.getAmount(USD).getAmount(), expectedRec.getAmount(USD).getAmount(), NOTIONAL * TOL); PointSensitivityBuilder pointPay = PRICER_SWAPTION.presentValueSensitivityRatesStickyStrike(SWAPTION_PAY_SHORT_AT_EXPIRY, RATE_PROVIDER, VOLS); MultiCurrencyAmount expectedPay = RATE_PROVIDER.currencyExposure(pointPay.build()) .plus(PRICER_SWAPTION.presentValue(SWAPTION_PAY_SHORT_AT_EXPIRY, RATE_PROVIDER, VOLS)); assertEquals(computedPay.size(), 1); assertEquals(computedPay.getAmount(USD).getAmount(), expectedPay.getAmount(USD).getAmount(), NOTIONAL * TOL); } public void test_currencyExposure_after_expiry() { MultiCurrencyAmount computedRec = PRICER_SWAPTION.currencyExposure(SWAPTION_REC_LONG_PAST, RATE_PROVIDER, VOLS); MultiCurrencyAmount computedPay = PRICER_SWAPTION.currencyExposure(SWAPTION_PAY_SHORT_PAST, RATE_PROVIDER, VOLS); assertEquals(computedRec.size(), 1); assertEquals(computedRec.getAmount(USD).getAmount(), 0d, NOTIONAL * TOL); assertEquals(computedPay.size(), 1); assertEquals(computedPay.getAmount(USD).getAmount(), 0d, NOTIONAL * TOL); } //------------------------------------------------------------------------- public void test_impliedVolatility() { double forward = PRICER_SWAP.parRate(RSWAP_REC, RATE_PROVIDER); double expected = VOLS.volatility(SWAPTION_REC_LONG.getExpiry(), SWAP_TENOR_YEAR, STRIKE, forward); double computedRec = PRICER_SWAPTION.impliedVolatility(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS); double computedPay = PRICER_SWAPTION.impliedVolatility(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS); assertEquals(computedRec, expected, TOL); assertEquals(computedPay, expected, TOL); } public void test_impliedVolatility_at_expiry() { double forward = PRICER_SWAP.parRate(RSWAP_REC, RATE_PROVIDER); double expected = VOLS.volatility( VAL_DATE.atTime(SWAPTION_EXPIRY_TIME).atZone(SWAPTION_EXPIRY_ZONE), SWAP_TENOR_YEAR, STRIKE, forward); double computedRec = PRICER_SWAPTION.impliedVolatility(SWAPTION_REC_LONG_AT_EXPIRY, RATE_PROVIDER, VOLS); double computedPay = PRICER_SWAPTION.impliedVolatility(SWAPTION_PAY_SHORT_AT_EXPIRY, RATE_PROVIDER, VOLS); assertEquals(computedRec, expected, TOL); assertEquals(computedPay, expected, TOL); } public void test_impliedVolatility_after_expiry() { assertThrowsIllegalArg(() -> PRICER_SWAPTION.impliedVolatility(SWAPTION_REC_LONG_PAST, RATE_PROVIDER, VOLS)); assertThrowsIllegalArg(() -> PRICER_SWAPTION.impliedVolatility(SWAPTION_PAY_SHORT_PAST, RATE_PROVIDER, VOLS)); } //------------------------------------------------------------------------- public void implied_volatility_round_trip() { // Compute pv and then implied vol from PV and compare with direct implied vol CurrencyAmount pvLongRec = PRICER_SWAPTION.presentValue(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS); double impliedLongRecComputed = PRICER_SWAPTION.impliedVolatilityFromPresentValue( SWAPTION_REC_LONG, RATE_PROVIDER, ACT_365F, pvLongRec.getAmount()); double impliedLongRecInterpolated = PRICER_SWAPTION.impliedVolatility(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS); assertEquals(impliedLongRecComputed, impliedLongRecInterpolated, TOL); CurrencyAmount pvLongPay = PRICER_SWAPTION.presentValue(SWAPTION_PAY_LONG, RATE_PROVIDER, VOLS); double impliedLongPayComputed = PRICER_SWAPTION.impliedVolatilityFromPresentValue( SWAPTION_PAY_LONG, RATE_PROVIDER, ACT_365F, pvLongPay.getAmount()); double impliedLongPayInterpolated = PRICER_SWAPTION.impliedVolatility(SWAPTION_PAY_LONG, RATE_PROVIDER, VOLS); assertEquals(impliedLongPayComputed, impliedLongPayInterpolated, TOL); CurrencyAmount pvShortRec = PRICER_SWAPTION.presentValue(SWAPTION_REC_SHORT, RATE_PROVIDER, VOLS); double impliedShortRecComputed = PRICER_SWAPTION.impliedVolatilityFromPresentValue( SWAPTION_REC_SHORT, RATE_PROVIDER, ACT_365F, pvShortRec.getAmount()); double impliedShortRecInterpolated = PRICER_SWAPTION.impliedVolatility(SWAPTION_REC_SHORT, RATE_PROVIDER, VOLS); assertEquals(impliedShortRecComputed, impliedShortRecInterpolated, TOL); } public void implied_volatility_wrong_sign() { CurrencyAmount pvLongRec = PRICER_SWAPTION.presentValue(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS); assertThrowsIllegalArg(() -> PRICER_SWAPTION.impliedVolatilityFromPresentValue( SWAPTION_REC_LONG, RATE_PROVIDER, ACT_365F, -pvLongRec.getAmount())); } //------------------------------------------------------------------------- public void test_presentValueSensitivityRatesStickyStrike() { PointSensitivities pointRec = PRICER_SWAPTION .presentValueSensitivityRatesStickyStrike(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS_FLAT).build(); CurrencyParameterSensitivities computedRec = RATE_PROVIDER.parameterSensitivity(pointRec); CurrencyParameterSensitivities expectedRec = FINITE_DIFFERENCE_CALCULATOR.sensitivity( RATE_PROVIDER, (p) -> PRICER_SWAPTION.presentValue(SWAPTION_REC_LONG, p, VOLS_FLAT)); assertTrue(computedRec.equalWithTolerance(expectedRec, NOTIONAL * FD_EPS * 200d)); PointSensitivities pointPay = PRICER_SWAPTION .presentValueSensitivityRatesStickyStrike(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS_FLAT).build(); CurrencyParameterSensitivities computedPay = RATE_PROVIDER.parameterSensitivity(pointPay); CurrencyParameterSensitivities expectedPay = FINITE_DIFFERENCE_CALCULATOR.sensitivity( RATE_PROVIDER, (p) -> PRICER_SWAPTION.presentValue(SWAPTION_PAY_SHORT, p, VOLS_FLAT)); assertTrue(computedPay.equalWithTolerance(expectedPay, NOTIONAL * FD_EPS * 200d)); } public void test_presentValueSensitivityRatesStickyStrike_at_expiry() { PointSensitivities pointRec = PRICER_SWAPTION.presentValueSensitivityRatesStickyStrike( SWAPTION_REC_LONG_AT_EXPIRY, RATE_PROVIDER, VOLS).build(); for (PointSensitivity sensi : pointRec.getSensitivities()) { assertEquals(Math.abs(sensi.getSensitivity()), 0d); } PointSensitivities pointPay = PRICER_SWAPTION.presentValueSensitivityRatesStickyStrike( SWAPTION_PAY_SHORT_AT_EXPIRY, RATE_PROVIDER, VOLS).build(); CurrencyParameterSensitivities computedPay = RATE_PROVIDER.parameterSensitivity(pointPay); CurrencyParameterSensitivities expectedPay = FINITE_DIFFERENCE_CALCULATOR.sensitivity( RATE_PROVIDER, (p) -> PRICER_SWAPTION.presentValue(SWAPTION_PAY_SHORT_AT_EXPIRY, p, VOLS_FLAT)); assertTrue(computedPay.equalWithTolerance(expectedPay, NOTIONAL * FD_EPS * 100d)); } public void test_presentValueSensitivityRatesStickyStrike_after_expiry() { PointSensitivityBuilder pointRec = PRICER_SWAPTION .presentValueSensitivityRatesStickyStrike(SWAPTION_REC_LONG_PAST, RATE_PROVIDER, VOLS); PointSensitivityBuilder pointPay = PRICER_SWAPTION .presentValueSensitivityRatesStickyStrike(SWAPTION_PAY_SHORT_PAST, RATE_PROVIDER, VOLS); assertEquals(pointRec, PointSensitivityBuilder.none()); assertEquals(pointPay, PointSensitivityBuilder.none()); } public void test_presentValueSensitivityRatesStickyStrike_parity() { CurrencyParameterSensitivities pvSensiRecLong = RATE_PROVIDER.parameterSensitivity( PRICER_SWAPTION.presentValueSensitivityRatesStickyStrike(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS).build()); CurrencyParameterSensitivities pvSensiRecShort = RATE_PROVIDER.parameterSensitivity( PRICER_SWAPTION.presentValueSensitivityRatesStickyStrike(SWAPTION_REC_SHORT, RATE_PROVIDER, VOLS).build()); CurrencyParameterSensitivities pvSensiPayLong = RATE_PROVIDER.parameterSensitivity( PRICER_SWAPTION.presentValueSensitivityRatesStickyStrike(SWAPTION_PAY_LONG, RATE_PROVIDER, VOLS).build()); CurrencyParameterSensitivities pvSensiPayShort = RATE_PROVIDER.parameterSensitivity( PRICER_SWAPTION.presentValueSensitivityRatesStickyStrike(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS).build()); assertTrue(pvSensiRecLong.equalWithTolerance(pvSensiRecShort.multipliedBy(-1d), NOTIONAL * TOL)); assertTrue(pvSensiPayLong.equalWithTolerance(pvSensiPayShort.multipliedBy(-1d), NOTIONAL * TOL)); double forward = PRICER_SWAP.parRate(RSWAP_REC, RATE_PROVIDER); PointSensitivityBuilder forwardSensi = PRICER_SWAP.parRateSensitivity(RSWAP_REC, RATE_PROVIDER); double annuityCash = PRICER_SWAP.getLegPricer().annuityCash(RSWAP_REC.getLegs(SwapLegType.FIXED).get(0), forward); double annuityCashDeriv = PRICER_SWAP.getLegPricer() .annuityCashDerivative(RSWAP_REC.getLegs(SwapLegType.FIXED).get(0), forward).getDerivative(0); double discount = RATE_PROVIDER.discountFactor(USD, SETTLE_DATE); PointSensitivityBuilder discountSensi = RATE_PROVIDER.discountFactors(USD).zeroRatePointSensitivity(SETTLE_DATE); PointSensitivities expecedPoint = discountSensi.multipliedBy(annuityCash * (forward - STRIKE)).combinedWith( forwardSensi.multipliedBy(discount * annuityCash + discount * annuityCashDeriv * (forward - STRIKE))).build(); CurrencyParameterSensitivities expected = RATE_PROVIDER.parameterSensitivity(expecedPoint); assertTrue(expected.equalWithTolerance(pvSensiPayLong.combinedWith(pvSensiRecLong.multipliedBy(-1d)), NOTIONAL * TOL)); assertTrue(expected.equalWithTolerance(pvSensiRecShort.combinedWith(pvSensiPayShort.multipliedBy(-1d)), NOTIONAL * TOL)); } //------------------------------------------------------------------------- public void test_presentValueSensitivityNormalVolatility() { SwaptionSensitivity computedRec = PRICER_SWAPTION .presentValueSensitivityModelParamsVolatility(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS); CurrencyAmount pvRecUp = PRICER_SWAPTION.presentValue(SWAPTION_REC_LONG, RATE_PROVIDER, SwaptionNormalVolatilityDataSets.normalVolSwaptionProviderUsdStsShifted(FD_EPS)); CurrencyAmount pvRecDw = PRICER_SWAPTION.presentValue(SWAPTION_REC_LONG, RATE_PROVIDER, SwaptionNormalVolatilityDataSets.normalVolSwaptionProviderUsdStsShifted(-FD_EPS)); double expectedRec = 0.5 * (pvRecUp.getAmount() - pvRecDw.getAmount()) / FD_EPS; assertEquals(computedRec.getCurrency(), USD); assertEquals(computedRec.getSensitivity(), expectedRec, FD_EPS * NOTIONAL); assertEquals(computedRec.getVolatilitiesName(), VOLS.getName()); assertEquals(computedRec.getExpiry(), VOLS.relativeTime(SWAPTION_REC_LONG.getExpiry())); assertEquals(computedRec.getTenor(), SWAP_TENOR_YEAR, TOL); assertEquals(computedRec.getStrike(), STRIKE, TOL); assertEquals(computedRec.getForward(), PRICER_SWAP.parRate(RSWAP_REC, RATE_PROVIDER), TOL); SwaptionSensitivity computedPay = PRICER_SWAPTION .presentValueSensitivityModelParamsVolatility(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS); CurrencyAmount pvUpPay = PRICER_SWAPTION.presentValue(SWAPTION_PAY_SHORT, RATE_PROVIDER, SwaptionNormalVolatilityDataSets.normalVolSwaptionProviderUsdStsShifted(FD_EPS)); CurrencyAmount pvDwPay = PRICER_SWAPTION.presentValue(SWAPTION_PAY_SHORT, RATE_PROVIDER, SwaptionNormalVolatilityDataSets.normalVolSwaptionProviderUsdStsShifted(-FD_EPS)); double expectedPay = 0.5 * (pvUpPay.getAmount() - pvDwPay.getAmount()) / FD_EPS; assertEquals(computedPay.getCurrency(), USD); assertEquals(computedPay.getSensitivity(), expectedPay, FD_EPS * NOTIONAL); assertEquals(computedPay.getVolatilitiesName(), VOLS.getName()); assertEquals(computedPay.getExpiry(), VOLS.relativeTime(SWAPTION_PAY_SHORT.getExpiry())); assertEquals(computedPay.getTenor(), SWAP_TENOR_YEAR, TOL); assertEquals(computedPay.getStrike(), STRIKE, TOL); assertEquals(computedPay.getForward(), PRICER_SWAP.parRate(RSWAP_PAY, RATE_PROVIDER), TOL); } public void test_presentValueSensitivityNormalVolatility_at_expiry() { SwaptionSensitivity sensiRec = PRICER_SWAPTION.presentValueSensitivityModelParamsVolatility(SWAPTION_REC_LONG_AT_EXPIRY, RATE_PROVIDER, VOLS); assertEquals(sensiRec.getSensitivity(), 0d, NOTIONAL * TOL); SwaptionSensitivity sensiPay = PRICER_SWAPTION.presentValueSensitivityModelParamsVolatility(SWAPTION_PAY_SHORT_AT_EXPIRY, RATE_PROVIDER, VOLS); assertEquals(sensiPay.getSensitivity(), 0d, NOTIONAL * TOL); } public void test_presentValueSensitivityNormalVolatility_after_expiry() { SwaptionSensitivity sensiRec = PRICER_SWAPTION.presentValueSensitivityModelParamsVolatility(SWAPTION_REC_LONG_PAST, RATE_PROVIDER, VOLS); SwaptionSensitivity sensiPay = PRICER_SWAPTION.presentValueSensitivityModelParamsVolatility(SWAPTION_PAY_SHORT_PAST, RATE_PROVIDER, VOLS); assertEquals(sensiRec.getSensitivity(), 0.0d, NOTIONAL * TOL); assertEquals(sensiPay.getSensitivity(), 0.0d, NOTIONAL * TOL); } public void test_presentValueSensitivityNormalVolatility_parity() { SwaptionSensitivity pvSensiRecLong = PRICER_SWAPTION.presentValueSensitivityModelParamsVolatility(SWAPTION_REC_LONG, RATE_PROVIDER, VOLS); SwaptionSensitivity pvSensiRecShort = PRICER_SWAPTION.presentValueSensitivityModelParamsVolatility(SWAPTION_REC_SHORT, RATE_PROVIDER, VOLS); SwaptionSensitivity pvSensiPayLong = PRICER_SWAPTION.presentValueSensitivityModelParamsVolatility(SWAPTION_PAY_LONG, RATE_PROVIDER, VOLS); SwaptionSensitivity pvSensiPayShort = PRICER_SWAPTION.presentValueSensitivityModelParamsVolatility(SWAPTION_PAY_SHORT, RATE_PROVIDER, VOLS); assertEquals(pvSensiRecLong.getSensitivity(), -pvSensiRecShort.getSensitivity(), NOTIONAL * TOL); assertEquals(pvSensiPayLong.getSensitivity(), -pvSensiPayShort.getSensitivity(), NOTIONAL * TOL); assertEquals(pvSensiRecLong.getSensitivity(), pvSensiPayLong.getSensitivity(), NOTIONAL * TOL); assertEquals(pvSensiPayShort.getSensitivity(), pvSensiPayShort.getSensitivity(), NOTIONAL * TOL); } }