@Test(enabled = false) /** Analyzes the shape of the forward curve. */ public void forwardAnalysis() { final MulticurveProviderInterface marketDsc = CURVES_PAR_SPREAD_MQ_WITHOUT_TODAY_BLOCK.get(0).getFirst(); final int jump = 1; final int startIndex = 0; final int nbDate = 2750; ZonedDateTime startDate = ScheduleCalculator.getAdjustedDate(NOW, AUDBB3M.getSpotLag() + startIndex * jump, SYD); final double[] rateDsc = new double[nbDate]; final double[] startTime = new double[nbDate]; try { final FileWriter writer = new FileWriter("fwd-dsc.csv"); for (int loopdate = 0; loopdate < nbDate; loopdate++) { startTime[loopdate] = TimeCalculator.getTimeBetween(NOW, startDate); final ZonedDateTime endDate = ScheduleCalculator.getAdjustedDate(startDate, AUDBB3M, SYD); final double endTime = TimeCalculator.getTimeBetween(NOW, endDate); final double accrualFactor = AUDBB3M.getDayCount().getDayCountFraction(startDate, endDate); rateDsc[loopdate] = marketDsc.getForwardRate(AUDBB3M, startTime[loopdate], endTime, accrualFactor); startDate = ScheduleCalculator.getAdjustedDate(startDate, jump, SYD); writer.append(0.0 + "," + startTime[loopdate] + "," + rateDsc[loopdate] + "\n"); } writer.flush(); writer.close(); } catch (final IOException e) { e.printStackTrace(); } }
/** * Builder of Ibor-like coupon from the fixing date and the index. The payment and accrual dates * are the one of the fixing period. * * @param notional Coupon notional. * @param fixingDate The coupon fixing date. * @param index The coupon Ibor index. * @param calendar The holiday calendar for the ibor index. * @return The Ibor coupon. */ public static CouponIborDefinition from( final double notional, final ZonedDateTime fixingDate, final IborIndex index, final Calendar calendar) { ArgumentChecker.notNull(fixingDate, "fixing date"); ArgumentChecker.notNull(index, "index"); final ZonedDateTime fixingPeriodStartDate = ScheduleCalculator.getAdjustedDate(fixingDate, index.getSpotLag(), calendar); final ZonedDateTime fixingPeriodEndDate = ScheduleCalculator.getAdjustedDate( fixingPeriodStartDate, index.getTenor(), index.getBusinessDayConvention(), calendar, index.isEndOfMonth()); final double fixingPeriodAccrualFactor = index .getDayCount() .getDayCountFraction(fixingPeriodStartDate, fixingPeriodEndDate, calendar); return new CouponIborDefinition( index.getCurrency(), fixingPeriodEndDate, fixingPeriodStartDate, fixingPeriodEndDate, fixingPeriodAccrualFactor, notional, fixingDate, index, calendar); }
/** * Builder from the financial details. The accrual and fixing dates (start and end) are the same. * The day count for the payment is the same as the one for the index. The payment date is * computed from the endFixingPeriodDate by moving backward by one day (overnight), then forward * by the index publication lag and finally by the settlementDays days. * * @param index The OIS index. * @param settlementDate The coupon settlement date. * @param endFixingPeriodDate The end date of the fixing period (also used for the end accrual * date). * @param notional The notional. * @param settlementDays The number of days between last fixing date and the payment date (also * called payment lag). * @param calendar The holiday calendar for the overnight index. * @return The OIS coupon. */ public static CouponONSimplifiedDefinition from( final IndexON index, final ZonedDateTime settlementDate, final ZonedDateTime endFixingPeriodDate, final double notional, final int settlementDays, final Calendar calendar) { ZonedDateTime lastFixingDate = ScheduleCalculator.getAdjustedDate(endFixingPeriodDate, -1, calendar); // Overnight lastFixingDate = ScheduleCalculator.getAdjustedDate( lastFixingDate, index.getPublicationLag(), calendar); // Lag final ZonedDateTime paymentDate = ScheduleCalculator.getAdjustedDate(lastFixingDate, settlementDays, calendar); final double payementAccrualFactor = index.getDayCount().getDayCountFraction(settlementDate, endFixingPeriodDate, calendar); return new CouponONSimplifiedDefinition( index.getCurrency(), paymentDate, settlementDate, endFixingPeriodDate, payementAccrualFactor, notional, index, settlementDate, endFixingPeriodDate, payementAccrualFactor); }
@Test /** Tests put call parity. */ public void presentValuePutCallParity() { final double strike = 1.45; final boolean isCall = true; final boolean isLong = true; final double notional = 100000000; final ZonedDateTime payDate = ScheduleCalculator.getAdjustedDate( REFERENCE_DATE, Period.ofMonths(9), BUSINESS_DAY, CALENDAR); final ZonedDateTime expDate = ScheduleCalculator.getAdjustedDate(payDate, -SETTLEMENT_DAYS, CALENDAR); final ForexDefinition forexUnderlyingDefinition = new ForexDefinition(EUR, USD, payDate, notional, strike); final ForexOptionDigitalDefinition callDefinition = new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, isCall, isLong); final ForexOptionDigitalDefinition putDefinition = new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, !isCall, isLong); final ForexOptionDigital call = callDefinition.toDerivative(REFERENCE_DATE, CURVES_NAME); final ForexOptionDigital put = putDefinition.toDerivative(REFERENCE_DATE, CURVES_NAME); final MultipleCurrencyAmount pvCall = METHOD_DIGITAL_SPREAD.presentValue(call, SMILE_BUNDLE); final MultipleCurrencyAmount pvPut = METHOD_DIGITAL_SPREAD.presentValue(put, SMILE_BUNDLE); final Double pvCash = PVC.visit(put.getUnderlyingForex().getPaymentCurrency2(), CURVES); assertEquals( "Forex Digital option: call spread method - present value", pvCall.getAmount(USD) + pvPut.getAmount(USD), Math.abs(pvCash), TOLERANCE_PRICE_FLAT); }
@Override public BondFixedTransaction toDerivative(final ZonedDateTime date) { ArgumentChecker.notNull(date, "date"); final ZonedDateTime spot = ScheduleCalculator.getAdjustedDate( date, getUnderlyingBond().getSettlementDays(), getUnderlyingBond().getCalendar()); final BondFixedSecurity bondPurchase = getUnderlyingBond().toDerivative(date, getSettlementDate()); final BondFixedSecurity bondStandard = getUnderlyingBond().toDerivative(date); final int nbCoupon = getUnderlyingBond().getCoupons().getNumberOfPayments(); int couponIndex = 0; // The index of the coupon of the spot date. for (int loopcpn = 0; loopcpn < nbCoupon; loopcpn++) { if (getUnderlyingBond() .getCoupons() .getNthPayment(loopcpn) .getAccrualEndDate() .isAfter(spot)) { couponIndex = loopcpn; break; } } final double notionalStandard = getUnderlyingBond().getCoupons().getNthPayment(couponIndex).getNotional(); double price; if (getSettlementDate() .isBefore(date)) { // If settlement already took place, the price is set to 0. price = 0.0; } else { price = getPrice(); } final BondFixedTransaction result = new BondFixedTransaction( bondPurchase, getQuantity(), price, bondStandard, notionalStandard); return result; }
@Override public BondInterestIndexedSecurity<PaymentFixed, Coupon> toDerivative(final ZonedDateTime date) { ArgumentChecker.notNull(date, "date"); final ZonedDateTime spot = ScheduleCalculator.getAdjustedDate(date, getSettlementDays(), getCalendar()); return toDerivative(date, spot, ImmutableZonedDateTimeDoubleTimeSeries.ofEmpty(ZoneOffset.UTC)); }
@Override public BondIborSecurity toDerivative( final ZonedDateTime date, final DoubleTimeSeries<ZonedDateTime> indexFixingTS) { ArgumentChecker.notNull(date, "date"); final ZonedDateTime spot = ScheduleCalculator.getAdjustedDate(date, getSettlementDays(), getCalendar()); return toDerivative(date, indexFixingTS, spot); }
@Override public BondInterestIndexedSecurity<PaymentFixed, Coupon> toDerivative( final ZonedDateTime date, final DoubleTimeSeries<ZonedDateTime> data) { ArgumentChecker.notNull(date, "date"); ArgumentChecker.notNull(data, "data"); final ZonedDateTime spot = ScheduleCalculator.getAdjustedDate(date, getSettlementDays(), getCalendar()); return toDerivative(date, spot, data); }
static { for (int loopexp = 0; loopexp < NB_EXPIRY; loopexp++) { EXPIRY_DATE[loopexp] = ScheduleCalculator.getAdjustedDate( TOTAL_SWAP_DEFINITION.getFixedLeg().getNthPayment(loopexp).getAccrualStartDate(), -IBOR_SETTLEMENT_DAYS, CALENDAR); EXPIRY_SWAP_DEFINITION[loopexp] = TOTAL_SWAP_DEFINITION.trimStart(EXPIRY_DATE[loopexp]); } }
@Override public BondIborSecurity toDerivative(final ZonedDateTime date) { ArgumentChecker.notNull(date, "date"); final ZonedDateTime spot = ScheduleCalculator.getAdjustedDate(date, getSettlementDays(), getCalendar()); return toDerivative( date, ImmutableZonedDateTimeDoubleTimeSeries.of(DateUtils.getUTCDate(1800, 1, 1), 0.0), spot); }
@Override public BillSecurity toDerivative(final ZonedDateTime date, final String... yieldCurveNames) { ArgumentChecker.notNull(date, "Reference date"); ArgumentChecker.notNull(yieldCurveNames, "Yield curve names"); ArgumentChecker.isTrue( !date.isAfter(_endDate), "Reference date {} is after end date {}", date, _endDate); ZonedDateTime settlementDate = ScheduleCalculator.getAdjustedDate(date, _settlementDays, _calendar); settlementDate = (settlementDate.isAfter(_endDate)) ? _endDate : settlementDate; return toDerivative(date, settlementDate, yieldCurveNames); }
@Test /** Tests the present value for curves with seasonal adjustment. */ public void presentValueSeasonality() { MarketBundle marketSeason = MarketDataSets.createMarket2(PRICING_DATE); int tenorYear = 5; double notional = 100000000; ZonedDateTime settleDate = ScheduleCalculator.getAdjustedDate(PRICING_DATE, USDLIBOR3M.getSpotLag(), CALENDAR_USD); ZonedDateTime paymentDate = ScheduleCalculator.getAdjustedDate( settleDate, Period.ofYears(tenorYear), BUSINESS_DAY, CALENDAR_USD, USDLIBOR3M.isEndOfMonth()); double weightSettle = 1.0 - (settleDate.getDayOfMonth() - 1.0) / settleDate.getMonthOfYear().getLastDayOfMonth(settleDate.isLeapYear()); double indexStart = weightSettle * 225.964 + (1 - weightSettle) * 225.722; CouponInflationZeroCouponInterpolationDefinition zeroCouponUsdDefinition = CouponInflationZeroCouponInterpolationDefinition.from( settleDate, paymentDate, notional, PRICE_INDEX_US, indexStart, MONTH_LAG, false); CouponInflationZeroCouponInterpolation zeroCouponUsd = zeroCouponUsdDefinition.toDerivative(PRICING_DATE, "not used"); CurrencyAmount pvInflation = METHOD.presentValue(zeroCouponUsd, marketSeason); double df = MARKET .getCurve(zeroCouponUsd.getCurrency()) .getDiscountFactor(zeroCouponUsd.getPaymentTime()); double indexMonth0 = marketSeason.getCurve(PRICE_INDEX_US).getPriceIndex(zeroCouponUsd.getReferenceEndTime()[0]); double indexMonth1 = marketSeason.getCurve(PRICE_INDEX_US).getPriceIndex(zeroCouponUsd.getReferenceEndTime()[1]); double finalIndex = zeroCouponUsdDefinition.getWeight() * indexMonth0 + (1 - zeroCouponUsdDefinition.getWeight()) * indexMonth1; double pvExpected = (finalIndex / indexStart - 1) * df * notional; assertEquals( "PV in market with seasonal adjustment", pvExpected, pvInflation.getAmount(), 1E-2); }
@Test /** Tests the put/call parity currency exposure. */ public void currencyExposurePutCallParity() { final double strike = 1.45; final boolean isCall = true; final boolean isLong = true; final double notional = 100000000; final ZonedDateTime payDate = ScheduleCalculator.getAdjustedDate( REFERENCE_DATE, Period.ofMonths(9), BUSINESS_DAY, CALENDAR); final ZonedDateTime expDate = ScheduleCalculator.getAdjustedDate(payDate, -SETTLEMENT_DAYS, CALENDAR); final ForexDefinition forexUnderlyingDefinition = new ForexDefinition(EUR, USD, payDate, notional, strike); final ForexOptionDigitalDefinition forexOptionDefinitionCall = new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, isCall, isLong); final ForexOptionDigitalDefinition forexOptionDefinitionPut = new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, !isCall, isLong); final ForexOptionDigital forexOptionCall = forexOptionDefinitionCall.toDerivative(REFERENCE_DATE, CURVES_NAME); final ForexOptionDigital forexOptionPut = forexOptionDefinitionPut.toDerivative(REFERENCE_DATE, CURVES_NAME); final MultipleCurrencyAmount currencyExposureCall = METHOD_DIGITAL_SPREAD.currencyExposure(forexOptionCall, SMILE_BUNDLE); final MultipleCurrencyAmount currencyExposurePut = METHOD_DIGITAL_SPREAD.currencyExposure(forexOptionPut, SMILE_BUNDLE); final Double pvCash = PVC.visit(forexOptionPut.getUnderlyingForex().getPaymentCurrency2(), CURVES); assertEquals( "Forex Digital option: currency exposure put/call parity foreign", 0, currencyExposureCall.getAmount(EUR) + currencyExposurePut.getAmount(EUR), TOLERANCE_PRICE); assertEquals( "Forex Digital option: currency exposure put/call parity domestic", Math.abs(pvCash), currencyExposureCall.getAmount(USD) + currencyExposurePut.getAmount(USD), TOLERANCE_PRICE); }
/** * Constructor of a Ibor-like floating coupon from the coupon details and the Ibor index. The * payment currency is the index currency. The fixing dates and accrual factors are inferred from * the index. * * @param currency The coupon currency. * @param paymentDate The coupon payment date. * @param accrualStartDate The start date of the accrual period. * @param accrualEndDate The end date of the accrual period. * @param paymentAccrualFactor The accrual factor of the accrual period. * @param notional The coupon notional. * @param fixingDate The coupon fixing date. * @param index The coupon Ibor index. Should of the same currency as the payment. * @param calendar The holiday calendar for the ibor index. */ public CouponIborDefinition( final Currency currency, final ZonedDateTime paymentDate, final ZonedDateTime accrualStartDate, final ZonedDateTime accrualEndDate, final double paymentAccrualFactor, final double notional, final ZonedDateTime fixingDate, final IborIndex index, final Calendar calendar) { super( currency, paymentDate, accrualStartDate, accrualEndDate, paymentAccrualFactor, notional, fixingDate); ArgumentChecker.notNull(index, "index"); ArgumentChecker.notNull(calendar, "calendar"); ArgumentChecker.isTrue( currency.equals(index.getCurrency()), "index currency different from payment currency"); _index = index; _fixingPeriodStartDate = ScheduleCalculator.getAdjustedDate(fixingDate, _index.getSpotLag(), calendar); _fixingPeriodEndDate = ScheduleCalculator.getAdjustedDate( _fixingPeriodStartDate, index.getTenor(), index.getBusinessDayConvention(), calendar, index.isEndOfMonth()); _fixingPeriodAccrualFactor = index .getDayCount() .getDayCountFraction(_fixingPeriodStartDate, _fixingPeriodEndDate, calendar); _calendar = calendar; }
/** * Builder from the financial details. * * @param effectiveDate The underlying swap effective date (delivery date). * @param generator The swap generator. * @param tenor The underlying swap tenor. * @param notional The futures notional. * @param rate The underlying swap rate. * @return The futures. */ public static SwapFuturesPriceDeliverableSecurityDefinition from( final ZonedDateTime effectiveDate, final GeneratorSwapFixedIbor generator, final Period tenor, final double notional, final double rate) { ArgumentChecker.notNull(effectiveDate, "Effective date"); ArgumentChecker.notNull(generator, "Generator"); final ZonedDateTime lastTradingDate = ScheduleCalculator.getAdjustedDate( effectiveDate, -generator.getSpotLag(), generator.getCalendar()); final SwapFixedIborDefinition swap = SwapFixedIborDefinition.from(effectiveDate, tenor, generator, 1.0, rate, false); return new SwapFuturesPriceDeliverableSecurityDefinition(lastTradingDate, swap, notional); }
@Test public void generateInstrument() { final double marketQuote = -0.0001; final double notional = 123000; final double quantity = 123; final GeneratorAttributeET attribute = new GeneratorAttributeET(false); final BillTransactionDefinition billGenerated = GENERATOR_BILL.generateInstrument(REFERENCE_DATE, marketQuote, notional, attribute); final ZonedDateTime dettleDate = ScheduleCalculator.getAdjustedDate(REFERENCE_DATE, SETTLEMENT_DAYS, CALENDAR); final BillTransactionDefinition billExpected = BillTransactionDefinition.fromYield( BILL_BEL_SEC_DEFINITION, quantity, dettleDate, marketQuote, CALENDAR); assertEquals("Bill Generator: generate instrument", billExpected, billGenerated); }
/** * Builder from financial details. The accrual and fixing dates (start and end) are the same. The * day count for the payment is the same as the one for the index. * * @param index The OIS index. * @param settlementDate The coupon settlement date. * @param tenor The coupon tenor. * @param notional The notional. * @param settlementDays The number of days between last fixing and the payment (also called spot * lag). * @param businessDayConvention The business day convention to compute the end date of the coupon. * @param isEOM The end-of-month convention to compute the end date of the coupon. * @param calendar The holiday calendar for the overnight index. * @return The OIS coupon. */ public static CouponONSimplifiedDefinition from( final IndexON index, final ZonedDateTime settlementDate, final Period tenor, final double notional, final int settlementDays, final BusinessDayConvention businessDayConvention, final boolean isEOM, final Calendar calendar) { final ZonedDateTime endFixingPeriodDate = ScheduleCalculator.getAdjustedDate( settlementDate, tenor, businessDayConvention, calendar, isEOM); return CouponONSimplifiedDefinition.from( index, settlementDate, endFixingPeriodDate, notional, settlementDays, calendar); }
@Test /** Tests the toDerivative method. */ public void toDerivativeFixingBeforeStart() { final ZonedDateTime referenceDate = ScheduleCalculator.getAdjustedDate(TRADE_DATE, 1, EUR_CALENDAR); final DoubleTimeSeries<ZonedDateTime> fixingTS = ImmutableZonedDateTimeDoubleTimeSeries.ofUTC( new ZonedDateTime[] {DateUtils.getUTCDate(2011, 9, 7)}, new double[] {0.01}); final Payment cpnConverted = ON_COMPOUNDED_COUPON_DEFINITION.toDerivative(referenceDate, fixingTS); final double paymentTime = EUR_DAY_COUNT.getDayCountFraction(referenceDate, EUR_PAYMENT_DATE, EUR_CALENDAR); final double[] FIXING_PERIOD_START_TIMES = new double[ON_COMPOUNDED_COUPON_DEFINITION.getFixingPeriodDates().length - 1]; final double[] FIXING_PERIOD_END_TIMES = new double[ON_COMPOUNDED_COUPON_DEFINITION.getFixingPeriodDates().length - 1]; final double[] FIXING_PERIOD_ACCRUAL_FACTOR = new double[ON_COMPOUNDED_COUPON_DEFINITION.getFixingPeriodDates().length - 1]; for (int i = 0; i < ON_COMPOUNDED_COUPON_DEFINITION.getFixingPeriodDates().length - 1; i++) { FIXING_PERIOD_START_TIMES[i] = EUR_DAY_COUNT.getDayCountFraction( referenceDate, ON_COMPOUNDED_COUPON_DEFINITION.getFixingPeriodDates()[i], EUR_CALENDAR); FIXING_PERIOD_END_TIMES[i] = EUR_DAY_COUNT.getDayCountFraction( referenceDate, ON_COMPOUNDED_COUPON_DEFINITION.getFixingPeriodDates()[i + 1], EUR_CALENDAR); FIXING_PERIOD_ACCRUAL_FACTOR[i] = EUR_DAY_COUNT.getDayCountFraction( ON_COMPOUNDED_COUPON_DEFINITION.getFixingPeriodDates()[i], ON_COMPOUNDED_COUPON_DEFINITION.getFixingPeriodDates()[i + 1], EUR_CALENDAR); } final CouponONCompounded cpnExpected = new CouponONCompounded( EUR_CUR, paymentTime, EUR_PAYMENT_YEAR_FRACTION, NOTIONAL, EUR_OIS, FIXING_PERIOD_START_TIMES, FIXING_PERIOD_END_TIMES, FIXING_PERIOD_ACCRUAL_FACTOR, NOTIONAL); assertEquals("CouponONCompounded definition: toDerivative", cpnExpected, cpnConverted); }
/** * Builder of a coupon from the accrual dates and the index. The fixing dates are calculated using * the index. The payment date is the end accrual date. * * @param accrualStartDate Start date of the accrual period. * @param accrualEndDate End date of the accrual period. * @param accrualFactor The accrual factor of the accrual period. * @param notional The coupon notional. * @param index The coupon Ibor index. Should of the same currency as the payment. * @param calendar The holiday calendar for the ibor index. * @return The coupon. */ public static CouponIborDefinition from( final ZonedDateTime accrualStartDate, final ZonedDateTime accrualEndDate, final double accrualFactor, final double notional, final IborIndex index, final Calendar calendar) { final ZonedDateTime fixingDate = ScheduleCalculator.getAdjustedDate(accrualStartDate, -index.getSpotLag(), calendar); return new CouponIborDefinition( index.getCurrency(), accrualEndDate, accrualStartDate, accrualEndDate, accrualFactor, notional, fixingDate, index, calendar); }
/** * {@inheritDoc} * * @deprecated Use the method that does not take yield curve names */ @Deprecated @Override public BondFixedTransaction toDerivative( final ZonedDateTime date, final String... yieldCurveNames) { // Implementation note: First yield curve used for coupon and notional (credit), the second for // risk free settlement. ArgumentChecker.notNull(date, "date"); ArgumentChecker.notNull(yieldCurveNames, "yield curve names"); ArgumentChecker.isTrue(yieldCurveNames.length > 0, "at least one curve required"); final ZonedDateTime spot = ScheduleCalculator.getAdjustedDate( date, getUnderlyingBond().getSettlementDays(), getUnderlyingBond().getCalendar()); final BondFixedSecurity bondPurchase = getUnderlyingBond().toDerivative(date, getSettlementDate(), yieldCurveNames); final BondFixedSecurity bondStandard = getUnderlyingBond().toDerivative(date, yieldCurveNames); final int nbCoupon = getUnderlyingBond().getCoupons().getNumberOfPayments(); int couponIndex = 0; // The index of the coupon of the spot date. for (int loopcpn = 0; loopcpn < nbCoupon; loopcpn++) { if (getUnderlyingBond() .getCoupons() .getNthPayment(loopcpn) .getAccrualEndDate() .isAfter(spot)) { couponIndex = loopcpn; break; } } final double notionalStandard = getUnderlyingBond().getCoupons().getNthPayment(couponIndex).getNotional(); double price; if (getSettlementDate() .isBefore(date)) { // If settlement already took place, the price is set to 0. price = 0.0; } else { price = getPrice(); } final BondFixedTransaction result = new BondFixedTransaction( bondPurchase, getQuantity(), price, bondStandard, notionalStandard); return result; }
/** * Swap with spread builder from the settlement date, a tenor, a swap generator and other details. * * @param settlementDate The settlement date. * @param tenor The swap total tenor. The Ibor index conventions are used to compute the maturity * date. * @param generator The swap generator. * @param notional The swap notional. the same notional is used for both legs. * @param fixedRate The swap fixed rate. * @param spread The Ibor leg spread. * @param isPayer The payer flag of the fixed leg. * @param calendar The holiday calendar of the ibor leg. * @return The swap. */ public static SwapFixedIborSpreadDefinition from( final ZonedDateTime settlementDate, final Period tenor, final GeneratorSwapFixedIbor generator, final double notional, final double fixedRate, final double spread, final boolean isPayer, final Calendar calendar) { ArgumentChecker.notNull(settlementDate, "Settlement date"); ArgumentChecker.notNull(tenor, "Tenor"); ArgumentChecker.notNull(generator, "Swap generator"); final ZonedDateTime maturityDate = ScheduleCalculator.getAdjustedDate( settlementDate, tenor, generator.getIborIndex(), calendar); final AnnuityCouponFixedDefinition fixedLeg = AnnuityCouponFixedDefinition.from( generator.getCurrency(), settlementDate, maturityDate, generator.getFixedLegPeriod(), generator.getCalendar(), generator.getFixedLegDayCount(), generator.getIborIndex().getBusinessDayConvention(), generator.getIborIndex().isEndOfMonth(), notional, fixedRate, isPayer); final AnnuityCouponIborSpreadDefinition iborLeg = AnnuityCouponIborSpreadDefinition.from( settlementDate, maturityDate, notional, generator.getIborIndex(), spread, !isPayer, calendar); return new SwapFixedIborSpreadDefinition(fixedLeg, iborLeg); }
/** * Tests related to the pricing of CMS coupons with Hull-White (extended Vasicek) model and * different numerical methods. */ public class CouponCMSHullWhiteMethodsTest { private static final Calendar TARGET = new MondayToFridayCalendar("TARGET"); private static final GeneratorSwapFixedIborMaster GENERATOR_SWAP_MASTER = GeneratorSwapFixedIborMaster.getInstance(); private static final GeneratorSwapFixedIbor GENERATOR_EUR1YEURIBOR6M = GENERATOR_SWAP_MASTER.getGenerator("EUR1YEURIBOR6M", TARGET); private static final Period TENOR_SWAP = Period.ofYears(10); private static final IndexSwap SWAP_EUR10Y = new IndexSwap(GENERATOR_EUR1YEURIBOR6M, TENOR_SWAP); private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2012, 1, 17); // Coupon CMS: 6m fixing in advance (payment in arrears); ACT/360 private static final Period TENOR_COUPON = Period.ofMonths(6); private static final Period TENOR_FIXING = Period.ofMonths(60); private static final DayCount ACT360 = DayCountFactory.INSTANCE.getDayCount("Actual/360"); private static final ZonedDateTime FIXING_DATE = ScheduleCalculator.getAdjustedDate( REFERENCE_DATE, TENOR_FIXING, GENERATOR_EUR1YEURIBOR6M.getBusinessDayConvention(), TARGET, GENERATOR_EUR1YEURIBOR6M.isEndOfMonth()); private static final ZonedDateTime START_DATE = ScheduleCalculator.getAdjustedDate( FIXING_DATE, GENERATOR_EUR1YEURIBOR6M.getSpotLag(), TARGET); private static final ZonedDateTime PAYMENT_DATE = ScheduleCalculator.getAdjustedDate( START_DATE, TENOR_COUPON, GENERATOR_EUR1YEURIBOR6M.getBusinessDayConvention(), TARGET, GENERATOR_EUR1YEURIBOR6M.isEndOfMonth()); private static final double NOTIONAL = 100000000; // 100m private static final double ACCRUAL_FACTOR = ACT360.getDayCountFraction(START_DATE, PAYMENT_DATE); private static final CouponCMSDefinition CPN_CMS_DEFINITION = CouponCMSDefinition.from( PAYMENT_DATE, START_DATE, PAYMENT_DATE, ACCRUAL_FACTOR, NOTIONAL, SWAP_EUR10Y); private static final YieldCurveBundle CURVES = TestsDataSetsSABR.createCurves2(); private static final String[] CURVE_NAMES = TestsDataSetsSABR.curves2Names(); private static final HullWhiteOneFactorPiecewiseConstantParameters PARAMETERS_HW = TestsDataSetHullWhite.createHullWhiteParameters(); private static final HullWhiteOneFactorPiecewiseConstantDataBundle BUNDLE_HW = new HullWhiteOneFactorPiecewiseConstantDataBundle(PARAMETERS_HW, CURVES); private static final CouponCMS CPN_CMS = (CouponCMS) CPN_CMS_DEFINITION.toDerivative( REFERENCE_DATE, new String[] {CURVE_NAMES[0], CURVE_NAMES[2]}); private static final CouponCMSHullWhiteNumericalIntegrationMethod METHOD_NI = CouponCMSHullWhiteNumericalIntegrationMethod.getInstance(); private static final CouponCMSHullWhiteApproximationMethod METHOD_APP = CouponCMSHullWhiteApproximationMethod.getInstance(); private static final CouponCMSDiscountingMethod METHOD_DSC = CouponCMSDiscountingMethod.getInstance(); private static final double TOLERANCE_PRICE = 1.0E-2; private static final double TOLERANCE_PRICE_APP = 5.0E+0; @Test public void presentValueNumericalIntegration() { CurrencyAmount pvNumericalIntegration = METHOD_NI.presentValue(CPN_CMS, BUNDLE_HW); double pvPrevious = 1124760.482; // From previous run assertEquals( "Coupon CMS - Hull-White - present value - numerical integration", pvPrevious, pvNumericalIntegration.getAmount(), TOLERANCE_PRICE); // Comparison with non-adjusted figures: to have the right order of magnitude CurrencyAmount pvDiscounting = METHOD_DSC.presentValue(CPN_CMS, BUNDLE_HW); assertEquals( "Coupon CMS - Hull-White - present value - numerical integration", 1.0, pvDiscounting.getAmount() / pvNumericalIntegration.getAmount(), 0.20); } @Test public void presentValueApproximation() { CurrencyAmount pvNumericalIntegration = METHOD_NI.presentValue(CPN_CMS, BUNDLE_HW); CurrencyAmount pvApproximation = METHOD_APP.presentValue(CPN_CMS, BUNDLE_HW); assertEquals( "Coupon CMS - Hull-White - present value - approximation", pvApproximation.getAmount(), pvNumericalIntegration.getAmount(), TOLERANCE_PRICE_APP); } }
/** Tests on the construction of interest rate future option with up-front payment. */ public class InterestRateFutureOptionMarginSecurityTest { // EURIBOR 3M Index private static final Period TENOR = Period.ofMonths(3); private static final int SETTLEMENT_DAYS = 2; private static final Calendar CALENDAR = new MondayToFridayCalendar("A"); private static final DayCount DAY_COUNT_INDEX = DayCountFactory.INSTANCE.getDayCount("Actual/360"); private static final BusinessDayConvention BUSINESS_DAY = BusinessDayConventionFactory.INSTANCE.getBusinessDayConvention("Modified Following"); private static final boolean IS_EOM = true; private static final Currency CUR = Currency.EUR; private static final IborIndex IBOR_INDEX = new IborIndex(CUR, TENOR, SETTLEMENT_DAYS, DAY_COUNT_INDEX, BUSINESS_DAY, IS_EOM); // Future private static final ZonedDateTime SPOT_LAST_TRADING_DATE = DateUtils.getUTCDate(2012, 9, 19); private static final ZonedDateTime LAST_TRADING_DATE = ScheduleCalculator.getAdjustedDate(SPOT_LAST_TRADING_DATE, -SETTLEMENT_DAYS, CALENDAR); private static final double NOTIONAL = 1000000.0; // 1m private static final double FUTURE_FACTOR = 0.25; private static final String NAME = "EDU2"; private static final double STRIKE = 0.9850; private static final InterestRateFutureSecurityDefinition EDU2_DEFINITION = new InterestRateFutureSecurityDefinition( LAST_TRADING_DATE, IBOR_INDEX, NOTIONAL, FUTURE_FACTOR, NAME, CALENDAR); private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2010, 8, 18); private static final String DISCOUNTING_CURVE_NAME = "Funding"; private static final String FORWARD_CURVE_NAME = "Forward"; private static final String[] CURVES = {DISCOUNTING_CURVE_NAME, FORWARD_CURVE_NAME}; private static final InterestRateFutureSecurity EDU2 = EDU2_DEFINITION.toDerivative(REFERENCE_DATE, CURVES); // Option private static final ZonedDateTime EXPIRATION_DATE = DateUtils.getUTCDate(2011, 9, 16); private static final DayCount ACT_ACT = DayCountFactory.INSTANCE.getDayCount("Actual/Actual ISDA"); private static final double EXPIRATION_TIME = ACT_ACT.getDayCountFraction(REFERENCE_DATE, EXPIRATION_DATE); private static final boolean IS_CALL = true; private static final InterestRateFutureOptionPremiumSecurity OPTION_EDU2 = new InterestRateFutureOptionPremiumSecurity(EDU2, EXPIRATION_TIME, STRIKE, IS_CALL); @Test(expectedExceptions = IllegalArgumentException.class) public void testNullUnderlying() { new InterestRateFutureOptionPremiumSecurity(null, EXPIRATION_TIME, STRIKE, IS_CALL); } @Test public void getter() { assertEquals(EDU2, OPTION_EDU2.getUnderlyingFuture()); assertEquals(EXPIRATION_TIME, OPTION_EDU2.getExpirationTime()); assertEquals(STRIKE, OPTION_EDU2.getStrike()); assertEquals(IS_CALL, OPTION_EDU2.isCall()); assertEquals(DISCOUNTING_CURVE_NAME, OPTION_EDU2.getDiscountingCurveName()); assertEquals(FORWARD_CURVE_NAME, OPTION_EDU2.getForwardCurveName()); } @Test /** Tests the equal and hash code methods. */ public void equalHash() { final InterestRateFutureOptionPremiumSecurity newOption = new InterestRateFutureOptionPremiumSecurity(EDU2, EXPIRATION_TIME, STRIKE, IS_CALL); assertTrue(OPTION_EDU2.equals(newOption)); assertEquals(OPTION_EDU2.hashCode(), newOption.hashCode()); InterestRateFutureOptionPremiumSecurity modifiedOption; modifiedOption = new InterestRateFutureOptionPremiumSecurity(EDU2, EXPIRATION_TIME - 0.01, STRIKE, IS_CALL); assertFalse(OPTION_EDU2.equals(modifiedOption)); modifiedOption = new InterestRateFutureOptionPremiumSecurity(EDU2, EXPIRATION_TIME, STRIKE + 0.01, IS_CALL); assertFalse(OPTION_EDU2.equals(modifiedOption)); modifiedOption = new InterestRateFutureOptionPremiumSecurity(EDU2, EXPIRATION_TIME, STRIKE, !IS_CALL); assertFalse(OPTION_EDU2.equals(modifiedOption)); } }
/** * Tests for the methods related to interest rate securities pricing with Hull-White model convexity * adjustment. */ @Test(groups = TestGroup.UNIT) public class InterestRateFutureSecurityHullWhiteMethodTest { private static final MulticurveProviderDiscount MULTICURVES = MulticurveProviderDiscountDataSets.createMulticurveEurUsd(); private static final IborIndex[] INDEX_LIST = MulticurveProviderDiscountDataSets.getIndexesIborMulticurveEurUsd(); private static final IborIndex EURIBOR3M = INDEX_LIST[0]; private static final Currency EUR = EURIBOR3M.getCurrency(); private static final Calendar CALENDAR = MulticurveProviderDiscountDataSets.getEURCalendar(); // Future private static final ZonedDateTime SPOT_LAST_TRADING_DATE = DateUtils.getUTCDate(2012, 9, 19); private static final ZonedDateTime LAST_TRADING_DATE = ScheduleCalculator.getAdjustedDate(SPOT_LAST_TRADING_DATE, -EURIBOR3M.getSpotLag(), CALENDAR); private static final double NOTIONAL = 1000000.0; // 1m private static final double FUTURE_FACTOR = 0.25; private static final String NAME = "ERU2"; private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2011, 5, 12); private static final InterestRateFutureSecurityDefinition ERU2_DEFINITION = new InterestRateFutureSecurityDefinition( LAST_TRADING_DATE, EURIBOR3M, NOTIONAL, FUTURE_FACTOR, NAME, CALENDAR); private static final InterestRateFutureSecurity ERU2 = ERU2_DEFINITION.toDerivative(REFERENCE_DATE); private static final double MEAN_REVERSION = 0.01; private static final double[] VOLATILITY = new double[] {0.01, 0.011, 0.012, 0.013, 0.014}; private static final double[] VOLATILITY_TIME = new double[] {0.5, 1.0, 2.0, 5.0}; private static final HullWhiteOneFactorPiecewiseConstantParameters MODEL_PARAMETERS = new HullWhiteOneFactorPiecewiseConstantParameters( MEAN_REVERSION, VOLATILITY, VOLATILITY_TIME); private static final HullWhiteOneFactorProviderDiscount HW_MULTICURVES = new HullWhiteOneFactorProviderDiscount(MULTICURVES, MODEL_PARAMETERS, EUR); private static final HullWhiteOneFactorPiecewiseConstantInterestRateModel MODEL = new HullWhiteOneFactorPiecewiseConstantInterestRateModel(); private static final InterestRateFutureSecurityHullWhiteMethod METHOD_IRFUT_HW = InterestRateFutureSecurityHullWhiteMethod.getInstance(); private static final MarketQuoteHullWhiteCalculator MQHWC = MarketQuoteHullWhiteCalculator.getInstance(); private static final MarketQuoteCurveSensitivityHullWhiteCalculator MQCSHWC = MarketQuoteCurveSensitivityHullWhiteCalculator.getInstance(); private static final ConvexityAdjustmentHullWhiteCalculator CAHWC = ConvexityAdjustmentHullWhiteCalculator.getInstance(); private static final ParRateHullWhiteCalculator PRHWC = ParRateHullWhiteCalculator.getInstance(); private static final double SHIFT_FD = 1.0E-6; private static final SimpleParameterSensitivityParameterCalculator< HullWhiteOneFactorProviderInterface> SPSHWC = new SimpleParameterSensitivityParameterCalculator<>(MQCSHWC); private static final SimpleParameterSensitivityHullWhiteDiscountInterpolatedFDCalculator SPSHWC_FD = new SimpleParameterSensitivityHullWhiteDiscountInterpolatedFDCalculator(MQHWC, SHIFT_FD); private static final double TOLERANCE_PRICE = 1.0E-10; private static final double TOLERANCE_PRICE_DELTA = 1.0E-8; @Test /** Test the price computed from the curves and HW parameters. */ public void price() { final double price = METHOD_IRFUT_HW.price(ERU2, HW_MULTICURVES); final double forward = MULTICURVES.getSimplyCompoundForwardRate( EURIBOR3M, ERU2.getFixingPeriodStartTime(), ERU2.getFixingPeriodEndTime(), ERU2.getFixingPeriodAccrualFactor()); final double factor = MODEL.futuresConvexityFactor( MODEL_PARAMETERS, ERU2.getTradingLastTime(), ERU2.getFixingPeriodStartTime(), ERU2.getFixingPeriodEndTime()); final double expectedPrice = 1.0 - factor * forward + (1 - factor) / ERU2.getFixingPeriodAccrualFactor(); assertEquals( "InterestRateFutureSecurityHullWhiteProviderMethod: price", expectedPrice, price, TOLERANCE_PRICE); } @Test /** Test the par rate computed from the curves and HW parameters. Par rate = 1-price. */ public void parRate() { final double price = METHOD_IRFUT_HW.price(ERU2, HW_MULTICURVES); final double parRateExpected = 1.0d - price; final double parRateComputed = METHOD_IRFUT_HW.parRate(ERU2, HW_MULTICURVES); assertEquals( "InterestRateFutureSecurityHullWhiteProviderMethod: parRate", parRateExpected, parRateComputed, TOLERANCE_PRICE); } @Test /** Test the par rate computed from the method and the calculator. */ public void parRateMethodVsCalculator() { final double parRateMethod = METHOD_IRFUT_HW.parRate(ERU2, HW_MULTICURVES); final double parRateCalculator = ERU2.accept(PRHWC, HW_MULTICURVES); assertEquals( "InterestRateFutureSecurityHullWhiteProviderMethod: parRate", parRateMethod, parRateCalculator, TOLERANCE_PRICE); } @Test /** Test the price as "MarketQuote" */ public void marketQuote() { final double priceMethod = METHOD_IRFUT_HW.price(ERU2, HW_MULTICURVES); final double marketQuote = ERU2.accept(MQHWC, HW_MULTICURVES); assertEquals( "InterestRateFutureSecurityHullWhiteProviderMethod: price", priceMethod, marketQuote, TOLERANCE_PRICE); } @Test /** Test the convexity adjustment */ public void convexityAdjustment() { final double price = METHOD_IRFUT_HW.price(ERU2, HW_MULTICURVES); final double forward = MULTICURVES.getSimplyCompoundForwardRate( EURIBOR3M, ERU2.getFixingPeriodStartTime(), ERU2.getFixingPeriodEndTime(), ERU2.getFixingPeriodAccrualFactor()); final double convexityAdjustment = METHOD_IRFUT_HW.convexityAdjustment(ERU2, HW_MULTICURVES); assertEquals( "InterestRateFutureSecurityHullWhiteProviderMethod: convexity adjustment", price - (1.0d - forward), convexityAdjustment, TOLERANCE_PRICE); final double caCalculator = ERU2.accept(CAHWC, HW_MULTICURVES); assertEquals( "DeliverableSwapFuturesSecurityDefinition: convexity adjustment", caCalculator, convexityAdjustment, TOLERANCE_PRICE); } @Test /** Test the price curve sensitivity versus a finite difference computation. */ public void priceCurveSensitivity() { final SimpleParameterSensitivity pcsExact = SPSHWC.calculateSensitivity(ERU2, HW_MULTICURVES, MULTICURVES.getAllNames()); final SimpleParameterSensitivity pcsFD = SPSHWC_FD.calculateSensitivity(ERU2, HW_MULTICURVES); AssertSensitivityObjects.assertEquals( "DeliverableSwapFuturesSecurityHullWhiteMethod: priceCurveSensitivity", pcsExact, pcsFD, TOLERANCE_PRICE_DELTA); } }
public class CouponInflationYearOnYearInterpolationDefinitionTest { private static final String NAME = "Euro HICP x"; private static final Currency CUR = Currency.EUR; private static final IndexPrice PRICE_INDEX = new IndexPrice(NAME, CUR); private static final Calendar CALENDAR = new MondayToFridayCalendar("A"); private static final BusinessDayConvention BUSINESS_DAY = BusinessDayConventionFactory.INSTANCE.getBusinessDayConvention("Modified Following"); private static final ZonedDateTime START_DATE = DateUtils.getUTCDate(2008, 8, 18); private static final Period COUPON_TENOR = Period.ofYears(10); private static final ZonedDateTime PAYMENT_DATE = ScheduleCalculator.getAdjustedDate(START_DATE, COUPON_TENOR, BUSINESS_DAY, CALENDAR); private static final ZonedDateTime ACCRUAL_END_DATE = PAYMENT_DATE; private static final ZonedDateTime ACCRUAL_START_DATE = ACCRUAL_END_DATE.minusMonths(12); private static final double NOTIONAL = 98765432; private static final int MONTH_LAG = 3; private static final ZonedDateTime[] REFERENCE_START_DATE = new ZonedDateTime[2]; static { REFERENCE_START_DATE[0] = ACCRUAL_START_DATE.minusMonths(MONTH_LAG).withDayOfMonth(1); REFERENCE_START_DATE[1] = REFERENCE_START_DATE[0].plusMonths(1); } private static final ZonedDateTime[] REFERENCE_END_DATE = new ZonedDateTime[2]; static { REFERENCE_END_DATE[0] = PAYMENT_DATE.minusMonths(MONTH_LAG).withDayOfMonth(1); REFERENCE_END_DATE[1] = REFERENCE_END_DATE[0].plusMonths(1); } private static final double WEIGHT_START = 0.2; private static final double WEIGHT_END = 0.8; private static final CouponInflationYearOnYearInterpolationDefinition YoY_COUPON_DEFINITION = new CouponInflationYearOnYearInterpolationDefinition( CUR, PAYMENT_DATE, ACCRUAL_START_DATE, ACCRUAL_END_DATE, 1.0, NOTIONAL, PRICE_INDEX, MONTH_LAG, 3, REFERENCE_START_DATE, REFERENCE_END_DATE, false, WEIGHT_START, WEIGHT_END); private static final String DISCOUNTING_CURVE_NAME = "Discounting"; private static final String PRICE_INDEX_CURVE_NAME = "Price index"; private static final String[] CURVE_NAMES = new String[] {DISCOUNTING_CURVE_NAME, PRICE_INDEX_CURVE_NAME}; private static final DayCount ACT_ACT = DayCountFactory.INSTANCE.getDayCount("Actual/Actual ISDA"); @Test(expectedExceptions = IllegalArgumentException.class) public void testNullCurrency() { new CouponInflationYearOnYearInterpolationDefinition( null, PAYMENT_DATE, ACCRUAL_START_DATE, PAYMENT_DATE, 1.0, NOTIONAL, PRICE_INDEX, MONTH_LAG, 3, REFERENCE_START_DATE, REFERENCE_END_DATE, false, WEIGHT_START, WEIGHT_END); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullPay() { new CouponInflationYearOnYearInterpolationDefinition( CUR, null, ACCRUAL_START_DATE, PAYMENT_DATE, 1.0, NOTIONAL, PRICE_INDEX, MONTH_LAG, 3, REFERENCE_START_DATE, REFERENCE_END_DATE, false, WEIGHT_START, WEIGHT_END); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullStart() { new CouponInflationYearOnYearInterpolationDefinition( CUR, PAYMENT_DATE, null, PAYMENT_DATE, 1.0, NOTIONAL, PRICE_INDEX, MONTH_LAG, 3, REFERENCE_START_DATE, REFERENCE_END_DATE, false, WEIGHT_START, WEIGHT_END); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullEnd() { new CouponInflationYearOnYearInterpolationDefinition( CUR, PAYMENT_DATE, ACCRUAL_START_DATE, null, 1.0, NOTIONAL, PRICE_INDEX, MONTH_LAG, 3, REFERENCE_START_DATE, REFERENCE_END_DATE, false, WEIGHT_START, WEIGHT_END); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullIndex() { new CouponInflationYearOnYearInterpolationDefinition( CUR, PAYMENT_DATE, ACCRUAL_START_DATE, PAYMENT_DATE, 1.0, NOTIONAL, null, MONTH_LAG, 3, REFERENCE_START_DATE, REFERENCE_END_DATE, false, WEIGHT_START, WEIGHT_END); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullRefStart() { new CouponInflationYearOnYearInterpolationDefinition( CUR, PAYMENT_DATE, ACCRUAL_START_DATE, PAYMENT_DATE, 1.0, NOTIONAL, PRICE_INDEX, MONTH_LAG, 3, null, REFERENCE_END_DATE, false, WEIGHT_START, WEIGHT_END); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullRefEnd() { new CouponInflationYearOnYearInterpolationDefinition( CUR, PAYMENT_DATE, ACCRUAL_START_DATE, PAYMENT_DATE, 1.0, NOTIONAL, PRICE_INDEX, MONTH_LAG, 3, REFERENCE_START_DATE, null, false, WEIGHT_START, WEIGHT_END); } @Test /** Tests the class getter. */ public void getter() { assertEquals("Inflation Year on Year coupon: getter", CUR, YoY_COUPON_DEFINITION.getCurrency()); assertEquals( "Inflation Year on Year coupon: getter", PAYMENT_DATE, YoY_COUPON_DEFINITION.getPaymentDate()); assertEquals( "Inflation Year on Year coupon: getter", ACCRUAL_START_DATE, YoY_COUPON_DEFINITION.getAccrualStartDate()); assertEquals( "Inflation Year on Year coupon: getter", ACCRUAL_END_DATE, YoY_COUPON_DEFINITION.getAccrualEndDate()); assertEquals( "Inflation Year on Year coupon: getter", 1.0, YoY_COUPON_DEFINITION.getPaymentYearFraction()); assertEquals( "Inflation Year on Year coupon: getter", NOTIONAL, YoY_COUPON_DEFINITION.getNotional()); assertEquals( "Inflation Year on Year coupon: getter", PRICE_INDEX, YoY_COUPON_DEFINITION.getPriceIndex()); assertEquals( "Inflation Year on Year coupon: getter", REFERENCE_START_DATE, YoY_COUPON_DEFINITION.getReferenceStartDate()); assertEquals( "Inflation Year on Year coupon: getter", REFERENCE_END_DATE, YoY_COUPON_DEFINITION.getReferenceEndDate()); assertEquals( "Inflation Year on Year coupon: getter", MONTH_LAG, YoY_COUPON_DEFINITION.getConventionalMonthLag()); } @Test /** Tests the equal and hash-code methods. */ public void equalHash() { assertEquals(YoY_COUPON_DEFINITION, YoY_COUPON_DEFINITION); CouponInflationYearOnYearInterpolationDefinition couponDuplicate = new CouponInflationYearOnYearInterpolationDefinition( CUR, PAYMENT_DATE, ACCRUAL_START_DATE, ACCRUAL_END_DATE, 1.0, NOTIONAL, PRICE_INDEX, MONTH_LAG, 3, REFERENCE_START_DATE, REFERENCE_END_DATE, false, WEIGHT_START, WEIGHT_END); assertEquals(YoY_COUPON_DEFINITION, couponDuplicate); assertEquals(YoY_COUPON_DEFINITION.hashCode(), couponDuplicate.hashCode()); CouponInflationYearOnYearInterpolationDefinition modified; modified = new CouponInflationYearOnYearInterpolationDefinition( CUR, ACCRUAL_END_DATE.minusDays(1), ACCRUAL_START_DATE, ACCRUAL_END_DATE, 1.0, NOTIONAL, PRICE_INDEX, MONTH_LAG, 3, REFERENCE_START_DATE, REFERENCE_END_DATE, false, WEIGHT_START, WEIGHT_END); assertFalse(YoY_COUPON_DEFINITION.equals(modified)); modified = new CouponInflationYearOnYearInterpolationDefinition( CUR, PAYMENT_DATE, ACCRUAL_START_DATE.minusDays(1), ACCRUAL_END_DATE, 1.0, NOTIONAL, PRICE_INDEX, MONTH_LAG, 3, REFERENCE_START_DATE, REFERENCE_END_DATE, false, WEIGHT_START, WEIGHT_END); assertFalse(YoY_COUPON_DEFINITION.equals(modified)); modified = new CouponInflationYearOnYearInterpolationDefinition( CUR, PAYMENT_DATE, ACCRUAL_START_DATE, ACCRUAL_END_DATE.minusDays(1), 1.0, NOTIONAL, PRICE_INDEX, MONTH_LAG, 3, REFERENCE_START_DATE, REFERENCE_END_DATE, false, WEIGHT_START, WEIGHT_END); assertFalse(YoY_COUPON_DEFINITION.equals(modified)); final ZonedDateTime[] modifiedReferenceStartTime = new ZonedDateTime[2]; modifiedReferenceStartTime[0] = REFERENCE_START_DATE[0]; modifiedReferenceStartTime[1] = REFERENCE_START_DATE[1].minusDays(1); modified = new CouponInflationYearOnYearInterpolationDefinition( CUR, PAYMENT_DATE, ACCRUAL_START_DATE, ACCRUAL_END_DATE, 1.0, NOTIONAL, PRICE_INDEX, MONTH_LAG, 3, modifiedReferenceStartTime, REFERENCE_END_DATE, false, WEIGHT_START, WEIGHT_END); assertFalse(YoY_COUPON_DEFINITION.equals(modified)); final ZonedDateTime[] modifiedReferenceEndTime = new ZonedDateTime[2]; modifiedReferenceEndTime[0] = REFERENCE_START_DATE[0]; modifiedReferenceEndTime[1] = REFERENCE_START_DATE[1].minusDays(1); modified = new CouponInflationYearOnYearInterpolationDefinition( CUR, PAYMENT_DATE, ACCRUAL_START_DATE, ACCRUAL_END_DATE, 1.0, NOTIONAL, PRICE_INDEX, MONTH_LAG, 3, REFERENCE_START_DATE, modifiedReferenceEndTime, false, WEIGHT_START, WEIGHT_END); assertFalse(YoY_COUPON_DEFINITION.equals(modified)); modified = new CouponInflationYearOnYearInterpolationDefinition( CUR, PAYMENT_DATE, ACCRUAL_START_DATE, ACCRUAL_END_DATE, 2.0, NOTIONAL, PRICE_INDEX, MONTH_LAG, 3, REFERENCE_START_DATE, REFERENCE_END_DATE, false, WEIGHT_START, WEIGHT_END); assertFalse(YoY_COUPON_DEFINITION.equals(modified)); modified = new CouponInflationYearOnYearInterpolationDefinition( CUR, PAYMENT_DATE, ACCRUAL_START_DATE, ACCRUAL_END_DATE, 1.0, NOTIONAL + 10.0, PRICE_INDEX, MONTH_LAG, 3, REFERENCE_START_DATE, REFERENCE_END_DATE, false, WEIGHT_START, WEIGHT_END); assertFalse(YoY_COUPON_DEFINITION.equals(modified)); modified = new CouponInflationYearOnYearInterpolationDefinition( CUR, PAYMENT_DATE, ACCRUAL_START_DATE, ACCRUAL_END_DATE, 1.0, NOTIONAL, PRICE_INDEX, MONTH_LAG, 3, REFERENCE_START_DATE, REFERENCE_END_DATE, false, WEIGHT_START + .1, WEIGHT_END); assertFalse(YoY_COUPON_DEFINITION.equals(modified)); modified = new CouponInflationYearOnYearInterpolationDefinition( CUR, PAYMENT_DATE, ACCRUAL_START_DATE, ACCRUAL_END_DATE, 1.0, NOTIONAL, PRICE_INDEX, MONTH_LAG, 3, REFERENCE_START_DATE, REFERENCE_END_DATE, false, WEIGHT_START, WEIGHT_END + .1); assertFalse(YoY_COUPON_DEFINITION.equals(modified)); } @Test /** Tests the first based on indexation lag. */ public void from2() { CouponInflationYearOnYearInterpolationDefinition constructor = new CouponInflationYearOnYearInterpolationDefinition( CUR, PAYMENT_DATE, ACCRUAL_START_DATE, PAYMENT_DATE, 1.0, NOTIONAL, PRICE_INDEX, MONTH_LAG, 3, REFERENCE_START_DATE, REFERENCE_END_DATE, false, WEIGHT_START, WEIGHT_END); CouponInflationYearOnYearInterpolationDefinition from = CouponInflationYearOnYearInterpolationDefinition.from( ACCRUAL_START_DATE, PAYMENT_DATE, NOTIONAL, PRICE_INDEX, MONTH_LAG, false, WEIGHT_START, WEIGHT_END); assertEquals("Inflation zero-coupon : from", constructor, from); } @Test public void toDerivativesNoData() { final ZonedDateTime pricingDate = DateUtils.getUTCDate(2011, 7, 29); Coupon yearOnYearCouponConverted = YoY_COUPON_DEFINITION.toDerivative(pricingDate, CURVE_NAMES); double paymentTime = ACT_ACT.getDayCountFraction(pricingDate, PAYMENT_DATE); final double referenceStartTime0 = ACT_ACT.getDayCountFraction(pricingDate, REFERENCE_START_DATE[0]); final double referenceEndTime0 = ACT_ACT.getDayCountFraction(pricingDate, REFERENCE_END_DATE[0]); final double referenceStartTime1 = ACT_ACT.getDayCountFraction(pricingDate, REFERENCE_START_DATE[1]); final double referenceEndTime1 = ACT_ACT.getDayCountFraction(pricingDate, REFERENCE_END_DATE[1]); final double naturalPaymentStartPaymentTime = ACT_ACT.getDayCountFraction(pricingDate, ACCRUAL_START_DATE); final double naturalPaymentEndPaymentTime = ACT_ACT.getDayCountFraction(pricingDate, ACCRUAL_END_DATE); final double[] referenceStartTime = new double[2]; final double[] referenceEndTime = new double[2]; referenceStartTime[0] = referenceStartTime0; referenceStartTime[1] = referenceStartTime1; referenceEndTime[0] = referenceEndTime0; referenceEndTime[1] = referenceEndTime1; CouponInflationYearOnYearInterpolation yearOnYearCoupon = new CouponInflationYearOnYearInterpolation( CUR, paymentTime, 1.0, NOTIONAL, PRICE_INDEX, referenceStartTime, naturalPaymentStartPaymentTime, referenceEndTime, naturalPaymentEndPaymentTime, false, WEIGHT_START, WEIGHT_END); assertEquals( "Inflation year on year coupon: toDerivative", yearOnYearCouponConverted, yearOnYearCoupon); } @Test public void toDerivativesStartMonthNotknown() { final ZonedDateTime pricingDate = DateUtils.getUTCDate(2011, 7, 29); final DoubleTimeSeries<ZonedDateTime> priceIndexTS = ImmutableZonedDateTimeDoubleTimeSeries.ofUTC( new ZonedDateTime[] { DateUtils.getUTCDate(2017, 5, 1), DateUtils.getUTCDate(2017, 6, 1), DateUtils.getUTCDate(2018, 5, 1), DateUtils.getUTCDate(2018, 6, 1) }, new double[] {127.23, 127.43, 128.23, 128.43}); Coupon yearOnYearCouponConverted = YoY_COUPON_DEFINITION.toDerivative(pricingDate, priceIndexTS, CURVE_NAMES); double paymentTime = ACT_ACT.getDayCountFraction(pricingDate, PAYMENT_DATE); final double referenceStartTime0 = ACT_ACT.getDayCountFraction(pricingDate, REFERENCE_START_DATE[0]); final double referenceEndTime0 = ACT_ACT.getDayCountFraction(pricingDate, REFERENCE_END_DATE[0]); final double referenceStartTime1 = ACT_ACT.getDayCountFraction(pricingDate, REFERENCE_START_DATE[1]); final double referenceEndTime1 = ACT_ACT.getDayCountFraction(pricingDate, REFERENCE_END_DATE[1]); final double naturalPaymentStartPaymentTime = ACT_ACT.getDayCountFraction(pricingDate, ACCRUAL_START_DATE); final double naturalPaymentEndPaymentTime = ACT_ACT.getDayCountFraction(pricingDate, ACCRUAL_END_DATE); final double[] referenceStartTime = new double[2]; final double[] referenceEndTime = new double[2]; referenceStartTime[0] = referenceStartTime0; referenceStartTime[1] = referenceStartTime1; referenceEndTime[0] = referenceEndTime0; referenceEndTime[1] = referenceEndTime1; CouponInflationYearOnYearInterpolation yearOnYearCoupon = new CouponInflationYearOnYearInterpolation( CUR, paymentTime, 1.0, NOTIONAL, PRICE_INDEX, referenceStartTime, naturalPaymentStartPaymentTime, referenceEndTime, naturalPaymentEndPaymentTime, false, WEIGHT_START, WEIGHT_END); assertEquals( "Inflation zero-coupon: toDerivative", yearOnYearCoupon, yearOnYearCouponConverted); } @Test public void toDerivativesStartMonthKnown() { final ZonedDateTime pricingDate = DateUtils.getUTCDate(2018, 6, 25); final DoubleTimeSeries<ZonedDateTime> priceIndexTS = ImmutableZonedDateTimeDoubleTimeSeries.ofUTC( new ZonedDateTime[] { DateUtils.getUTCDate(2017, 5, 1), DateUtils.getUTCDate(2017, 6, 1), DateUtils.getUTCDate(2018, 5, 1), DateUtils.getUTCDate(2018, 6, 1) }, new double[] {127.23, 127.43, 128.23, 128.43}); Coupon zeroCouponConverted = YoY_COUPON_DEFINITION.toDerivative(pricingDate, priceIndexTS, CURVE_NAMES); double paymentTime = ACT_ACT.getDayCountFraction(pricingDate, PAYMENT_DATE); CouponFixed zeroCoupon = new CouponFixed( CUR, paymentTime, DISCOUNTING_CURVE_NAME, 1.0, NOTIONAL, (WEIGHT_END * 128.23 + (1 - WEIGHT_END) * 128.43) / (WEIGHT_START * 127.23 + (1 - WEIGHT_START) * 127.43) - 1.0); assertEquals("Inflation zero-coupon: toDerivative", zeroCoupon, zeroCouponConverted); } }
/** Tests the interest rate future option with margin transaction description. */ @Test public class InterestRateFutureOptionPremiumTransactionDefinitionTest { private static final HolidayCalendar CALENDAR = HolidayCalendars.SAT_SUN; private static final IborIndex IBOR_INDEX = IndexIborMaster.getInstance().getIndex("EURIBOR3M"); // Future option mid-curve 1Y private static final ZonedDateTime SPOT_LAST_TRADING_DATE = DateUtils.getUTCDate(2012, 9, 19); private static final ZonedDateTime LAST_TRADING_DATE = ScheduleCalculator.getAdjustedDate( SPOT_LAST_TRADING_DATE, -IBOR_INDEX.getSpotLag(), CALENDAR); private static final double NOTIONAL = 1000000.0; // 1m private static final double FUTURE_FACTOR = 0.25; private static final String NAME = "ERU2"; private static final double STRIKE = 0.9895; private static final InterestRateFutureSecurityDefinition ERU2 = new InterestRateFutureSecurityDefinition( LAST_TRADING_DATE, IBOR_INDEX, NOTIONAL, FUTURE_FACTOR, NAME, CALENDAR); private static final ZonedDateTime EXPIRATION_DATE = DateUtils.getUTCDate(2011, 9, 16); private static final boolean IS_CALL = true; private static final InterestRateFutureOptionPremiumSecurityDefinition OPTION_EDU2 = new InterestRateFutureOptionPremiumSecurityDefinition(ERU2, EXPIRATION_DATE, STRIKE, IS_CALL); // Transaction private static final int QUANTITY = -123; private static final ZonedDateTime PREMIUM_DATE = DateUtils.getUTCDate(2011, 5, 12); private static final double TRADE_PRICE = 0.0050; private static final InterestRateFutureOptionPremiumTransactionDefinition OPTION_TRANSACTION = new InterestRateFutureOptionPremiumTransactionDefinition( OPTION_EDU2, QUANTITY, PREMIUM_DATE, TRADE_PRICE); // Derivative private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2010, 8, 18); private static final String DISCOUNTING_CURVE_NAME = "Funding"; private static final String FORWARD_CURVE_NAME = "Forward"; private static final String[] CURVES = {DISCOUNTING_CURVE_NAME, FORWARD_CURVE_NAME}; @Test(expectedExceptions = IllegalArgumentException.class) public void testNullUnderlying() { new InterestRateFutureOptionPremiumTransactionDefinition( null, QUANTITY, PREMIUM_DATE, TRADE_PRICE); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullTradeDate() { new InterestRateFutureOptionPremiumTransactionDefinition( OPTION_EDU2, QUANTITY, null, TRADE_PRICE); } @Test /** Tests the class getters. */ public void getter() { assertEquals(OPTION_EDU2, OPTION_TRANSACTION.getUnderlyingOption()); assertEquals(QUANTITY, OPTION_TRANSACTION.getQuantity()); assertEquals(PREMIUM_DATE, OPTION_TRANSACTION.getPremium().getPaymentDate()); assertEquals(TRADE_PRICE, OPTION_TRANSACTION.getTradePrice()); } @Test /** Tests the equal and hashCode methods. */ public void equalHash() { final InterestRateFutureOptionPremiumTransactionDefinition other = new InterestRateFutureOptionPremiumTransactionDefinition( OPTION_EDU2, QUANTITY, PREMIUM_DATE, TRADE_PRICE); assertTrue(OPTION_TRANSACTION.equals(other)); assertTrue(OPTION_TRANSACTION.hashCode() == other.hashCode()); InterestRateFutureOptionPremiumTransactionDefinition modifidOption; modifidOption = new InterestRateFutureOptionPremiumTransactionDefinition( OPTION_EDU2, QUANTITY + 1, PREMIUM_DATE, TRADE_PRICE); assertFalse(OPTION_TRANSACTION.equals(modifidOption)); modifidOption = new InterestRateFutureOptionPremiumTransactionDefinition( OPTION_EDU2, QUANTITY, LAST_TRADING_DATE, TRADE_PRICE); assertFalse(OPTION_TRANSACTION.equals(modifidOption)); modifidOption = new InterestRateFutureOptionPremiumTransactionDefinition( OPTION_EDU2, QUANTITY, PREMIUM_DATE, TRADE_PRICE - 0.00001); assertFalse(OPTION_TRANSACTION.equals(modifidOption)); } @Test /** Tests the toDerivative method when the reference date is before the premium settlement. */ public void toDerivativeBeforeSettlement() { final InterestRateFutureOptionPremiumTransaction transactionConverted = OPTION_TRANSACTION.toDerivative(REFERENCE_DATE); final InterestRateFutureOptionPremiumSecurity security = OPTION_EDU2.toDerivative(REFERENCE_DATE); final double premiumTime = TimeCalculator.getTimeBetween(REFERENCE_DATE, PREMIUM_DATE); final InterestRateFutureOptionPremiumTransaction transaction = new InterestRateFutureOptionPremiumTransaction( security, QUANTITY, premiumTime, TRADE_PRICE); assertEquals("Option on future: to derivative", transaction, transactionConverted); } @Test /** Tests the toDerivative method when the reference date is on the premium settlement. */ public void toDerivativeOnSettlement() { final ZonedDateTime referenceDate = PREMIUM_DATE; final InterestRateFutureOptionPremiumTransaction transactionConverted = OPTION_TRANSACTION.toDerivative(referenceDate); final InterestRateFutureOptionPremiumSecurity security = OPTION_EDU2.toDerivative(referenceDate); final double premiumTime = 0.0; final InterestRateFutureOptionPremiumTransaction transaction = new InterestRateFutureOptionPremiumTransaction( security, QUANTITY, premiumTime, TRADE_PRICE); assertEquals("Option on future: to derivative", transaction, transactionConverted); } @Test /** Tests the toDerivative method when the reference date is after the premium settlement. */ public void toDerivativeAfterSettlement() { final ZonedDateTime referenceDate = PREMIUM_DATE.plusDays(1); final InterestRateFutureOptionPremiumTransaction transactionConverted = OPTION_TRANSACTION.toDerivative(referenceDate); final InterestRateFutureOptionPremiumSecurity security = OPTION_EDU2.toDerivative(referenceDate); final double premiumTime = 0.0; final double price = 0.0; // The payment is in the past and is represented by a 0 payment today. final InterestRateFutureOptionPremiumTransaction transaction = new InterestRateFutureOptionPremiumTransaction(security, QUANTITY, premiumTime, price); assertEquals("Option on future: to derivative", transaction, transactionConverted); } }
/** Tests related to the pricing of cash-settled swaption in Hull-White one factor model. */ public class SwaptionCashFixedIborHullWhiteMethodTest { private static final Currency CUR = Currency.EUR; private static final Calendar CALENDAR = new MondayToFridayCalendar("A"); private static final BusinessDayConvention BUSINESS_DAY = BusinessDayConventionFactory.INSTANCE.getBusinessDayConvention("Modified Following"); private static final boolean IS_EOM = true; private static final int SETTLEMENT_DAYS = 2; private static final Period IBOR_TENOR = Period.ofMonths(6); private static final DayCount IBOR_DAY_COUNT = DayCountFactory.INSTANCE.getDayCount("Actual/360"); private static final IborIndex IBOR_INDEX = new IborIndex( CUR, IBOR_TENOR, SETTLEMENT_DAYS, CALENDAR, IBOR_DAY_COUNT, BUSINESS_DAY, IS_EOM); private static final int SWAP_TENOR_YEAR = 5; private static final Period SWAP_TENOR = Period.ofYears(SWAP_TENOR_YEAR); private static final Period FIXED_PAYMENT_PERIOD = Period.ofMonths(12); private static final DayCount FIXED_DAY_COUNT = DayCountFactory.INSTANCE.getDayCount("30/360"); private static final IndexSwap CMS_INDEX = new IndexSwap(FIXED_PAYMENT_PERIOD, FIXED_DAY_COUNT, IBOR_INDEX, SWAP_TENOR); private static final ZonedDateTime EXPIRY_DATE = DateUtils.getUTCDate(2016, 7, 7); private static final ZonedDateTime SETTLEMENT_DATE = ScheduleCalculator.getAdjustedDate(EXPIRY_DATE, SETTLEMENT_DAYS, CALENDAR); private static final double NOTIONAL = 100000000; // 100m private static final double RATE = 0.0325; private static final boolean FIXED_IS_PAYER = true; private static final SwapFixedIborDefinition SWAP_PAYER_DEFINITION = SwapFixedIborDefinition.from(SETTLEMENT_DATE, CMS_INDEX, NOTIONAL, RATE, FIXED_IS_PAYER); private static final SwapFixedIborDefinition SWAP_RECEIVER_DEFINITION = SwapFixedIborDefinition.from(SETTLEMENT_DATE, CMS_INDEX, NOTIONAL, RATE, !FIXED_IS_PAYER); private static final boolean IS_LONG = true; private static final SwaptionCashFixedIborDefinition SWAPTION_PAYER_LONG_DEFINITION = SwaptionCashFixedIborDefinition.from(EXPIRY_DATE, SWAP_PAYER_DEFINITION, IS_LONG); private static final SwaptionCashFixedIborDefinition SWAPTION_RECEIVER_LONG_DEFINITION = SwaptionCashFixedIborDefinition.from(EXPIRY_DATE, SWAP_RECEIVER_DEFINITION, IS_LONG); private static final SwaptionCashFixedIborDefinition SWAPTION_PAYER_SHORT_DEFINITION = SwaptionCashFixedIborDefinition.from(EXPIRY_DATE, SWAP_PAYER_DEFINITION, !IS_LONG); private static final SwaptionCashFixedIborDefinition SWAPTION_RECEIVER_SHORT_DEFINITION = SwaptionCashFixedIborDefinition.from(EXPIRY_DATE, SWAP_RECEIVER_DEFINITION, !IS_LONG); // to derivatives private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2011, 7, 7); private static final String FUNDING_CURVE_NAME = "Funding"; private static final String FORWARD_CURVE_NAME = "Forward"; private static final String[] CURVES_NAME = {FUNDING_CURVE_NAME, FORWARD_CURVE_NAME}; private static final YieldCurveBundle CURVES = TestsDataSetsSABR.createCurves1(); private static final FixedCouponSwap<Coupon> SWAP_PAYER = SWAP_PAYER_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME); private static final SwaptionCashFixedIbor SWAPTION_PAYER_LONG = SWAPTION_PAYER_LONG_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME); private static final SwaptionCashFixedIbor SWAPTION_RECEIVER_LONG = SWAPTION_RECEIVER_LONG_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME); private static final SwaptionCashFixedIbor SWAPTION_PAYER_SHORT = SWAPTION_PAYER_SHORT_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME); private static final SwaptionCashFixedIbor SWAPTION_RECEIVER_SHORT = SWAPTION_RECEIVER_SHORT_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME); // Calculator private static final SwaptionCashFixedIborHullWhiteNumericalIntegrationMethod METHOD_HW_INTEGRATION = new SwaptionCashFixedIborHullWhiteNumericalIntegrationMethod(); private static final SwaptionCashFixedIborHullWhiteApproximationMethod METHOD_HW_APPROXIMATION = new SwaptionCashFixedIborHullWhiteApproximationMethod(); private static final HullWhiteOneFactorPiecewiseConstantParameters PARAMETERS_HW = TestsDataSetsHullWhite.createHullWhiteParameters(); private static final HullWhiteOneFactorPiecewiseConstantDataBundle BUNDLE_HW = new HullWhiteOneFactorPiecewiseConstantDataBundle(PARAMETERS_HW, CURVES); @Test /** Tests long/short parity. */ public void longShortParity() { CurrencyAmount pvLong = METHOD_HW_INTEGRATION.presentValue(SWAPTION_PAYER_LONG, BUNDLE_HW); CurrencyAmount pvShort = METHOD_HW_INTEGRATION.presentValue(SWAPTION_PAYER_SHORT, BUNDLE_HW); assertEquals( "Swaption cash - Hull-White - present value - long/short parity", pvLong.getAmount(), -pvShort.getAmount(), 1E-2); } @Test /** Tests long/short parity. */ public void scaling() { double scale = 12.3; SwapFixedIborDefinition scaledSwapDefinition = SwapFixedIborDefinition.from( SETTLEMENT_DATE, CMS_INDEX, scale * NOTIONAL, RATE, FIXED_IS_PAYER); SwaptionCashFixedIborDefinition scaledSwaptionDefinition = SwaptionCashFixedIborDefinition.from(EXPIRY_DATE, scaledSwapDefinition, IS_LONG); SwaptionCashFixedIbor scaledSwaption = scaledSwaptionDefinition.toDerivative(REFERENCE_DATE, CURVES_NAME); CurrencyAmount pvOriginal = METHOD_HW_INTEGRATION.presentValue(SWAPTION_PAYER_LONG, BUNDLE_HW); CurrencyAmount pvScaled = METHOD_HW_INTEGRATION.presentValue(scaledSwaption, BUNDLE_HW); assertEquals( "Swaption cash - Hull-White - present value - scaling", scale * pvOriginal.getAmount(), pvScaled.getAmount(), 1E-1); } @Test /** Compare approximate formula with numerical integration. */ public void comparison() { double bp1 = 10000; CurrencyAmount pvPayerLongExplicit = METHOD_HW_APPROXIMATION.presentValue(SWAPTION_PAYER_LONG, BUNDLE_HW); CurrencyAmount pvPayerLongIntegration = METHOD_HW_INTEGRATION.presentValue(SWAPTION_PAYER_LONG, BUNDLE_HW); assertEquals( "Swaption cash - Hull-White - present value - explicit/numerical integration", pvPayerLongExplicit.getAmount() / NOTIONAL * bp1, pvPayerLongIntegration.getAmount() / NOTIONAL * bp1, 3.0E-1); CurrencyAmount pvPayerShortExplicit = METHOD_HW_APPROXIMATION.presentValue(SWAPTION_PAYER_SHORT, BUNDLE_HW); CurrencyAmount pvPayerShortIntegration = METHOD_HW_INTEGRATION.presentValue(SWAPTION_PAYER_SHORT, BUNDLE_HW); assertEquals( "Swaption cash - Hull-White - present value - explicit/numerical integration", pvPayerShortExplicit.getAmount() / NOTIONAL * bp1, pvPayerShortIntegration.getAmount() / NOTIONAL * bp1, 3.0E-1); CurrencyAmount pvReceiverLongExplicit = METHOD_HW_APPROXIMATION.presentValue(SWAPTION_RECEIVER_LONG, BUNDLE_HW); CurrencyAmount pvReceiverLongIntegration = METHOD_HW_INTEGRATION.presentValue(SWAPTION_RECEIVER_LONG, BUNDLE_HW); assertEquals( "Swaption cash - Hull-White - present value - explicit/numerical integration", pvReceiverLongExplicit.getAmount() / NOTIONAL * bp1, pvReceiverLongIntegration.getAmount() / NOTIONAL * bp1, 5.0E-1); CurrencyAmount pvReceiverShortExplicit = METHOD_HW_APPROXIMATION.presentValue(SWAPTION_RECEIVER_SHORT, BUNDLE_HW); CurrencyAmount pvReceiverShortIntegration = METHOD_HW_INTEGRATION.presentValue(SWAPTION_RECEIVER_SHORT, BUNDLE_HW); assertEquals( "Swaption cash - Hull-White - present value - explicit/numerical integration", pvReceiverShortExplicit.getAmount() / NOTIONAL * bp1, pvReceiverShortIntegration.getAmount() / NOTIONAL * bp1, 5.0E-1); } @Test /** Tests the Hull-White parameters sensitivity. */ public void hullWhiteSensitivity() { double[] hwSensitivity = METHOD_HW_APPROXIMATION.presentValueHullWhiteSensitivity(SWAPTION_PAYER_LONG, BUNDLE_HW); int nbVolatility = PARAMETERS_HW.getVolatility().length; double shiftVol = 1.0E-6; double[] volatilityBumped = new double[nbVolatility]; System.arraycopy(PARAMETERS_HW.getVolatility(), 0, volatilityBumped, 0, nbVolatility); double[] volatilityTime = new double[nbVolatility - 1]; System.arraycopy(PARAMETERS_HW.getVolatilityTime(), 1, volatilityTime, 0, nbVolatility - 1); double[] pvBumpedPlus = new double[nbVolatility]; double[] pvBumpedMinus = new double[nbVolatility]; HullWhiteOneFactorPiecewiseConstantParameters parametersBumped = new HullWhiteOneFactorPiecewiseConstantParameters( PARAMETERS_HW.getMeanReversion(), volatilityBumped, volatilityTime); HullWhiteOneFactorPiecewiseConstantDataBundle bundleBumped = new HullWhiteOneFactorPiecewiseConstantDataBundle(parametersBumped, CURVES); double[] hwSensitivityExpected = new double[nbVolatility]; for (int loopvol = 0; loopvol < nbVolatility; loopvol++) { volatilityBumped[loopvol] += shiftVol; parametersBumped.setVolatility(volatilityBumped); pvBumpedPlus[loopvol] = METHOD_HW_APPROXIMATION.presentValue(SWAPTION_PAYER_LONG, bundleBumped).getAmount(); volatilityBumped[loopvol] -= 2 * shiftVol; parametersBumped.setVolatility(volatilityBumped); pvBumpedMinus[loopvol] = METHOD_HW_APPROXIMATION.presentValue(SWAPTION_PAYER_LONG, bundleBumped).getAmount(); hwSensitivityExpected[loopvol] = (pvBumpedPlus[loopvol] - pvBumpedMinus[loopvol]) / (2 * shiftVol); assertEquals( "Swaption - Hull-White sensitivity adjoint: derivative " + loopvol + " - difference:" + (hwSensitivityExpected[loopvol] - hwSensitivity[loopvol]), hwSensitivityExpected[loopvol], hwSensitivity[loopvol], 2.0E+5); volatilityBumped[loopvol] = PARAMETERS_HW.getVolatility()[loopvol]; } } @Test(enabled = true) /** Tests approximation error. "enabled = false" for the standard testing. */ public void errorAnalysis() { double bp1 = 10000; double errorLimit = 5.0E-1; // 0.5 bp ParRateCalculator prc = ParRateCalculator.getInstance(); double forward = prc.visit(SWAP_PAYER, CURVES); double[] strikeRel = new double[] {-0.0250, -0.0150, -0.0050, 0.0, 0.0050, 0.0150, 0.0250}; double[] pvPayerApproximation = new double[strikeRel.length]; double[] pvPayerIntegration = new double[strikeRel.length]; double[] pvReceiverApproximation = new double[strikeRel.length]; double[] pvReceiverIntegration = new double[strikeRel.length]; for (int loopstrike = 0; loopstrike < strikeRel.length; loopstrike++) { SwapFixedIborDefinition swapStrikePayerDefinition = SwapFixedIborDefinition.from( SETTLEMENT_DATE, CMS_INDEX, bp1, forward + strikeRel[loopstrike], FIXED_IS_PAYER); SwaptionCashFixedIborDefinition swaptionStrikePayerDefinition = SwaptionCashFixedIborDefinition.from(EXPIRY_DATE, swapStrikePayerDefinition, IS_LONG); SwaptionCashFixedIbor swaptionStrikePayer = swaptionStrikePayerDefinition.toDerivative(REFERENCE_DATE, CURVES_NAME); pvPayerApproximation[loopstrike] = METHOD_HW_APPROXIMATION.presentValue(swaptionStrikePayer, BUNDLE_HW).getAmount(); pvPayerIntegration[loopstrike] = METHOD_HW_INTEGRATION.presentValue(swaptionStrikePayer, BUNDLE_HW).getAmount(); assertEquals( "Swaption cash - Hull-White - present value - explicit/numerical integration", pvPayerApproximation[loopstrike], pvPayerIntegration[loopstrike], errorLimit); SwapFixedIborDefinition swapStrikeReceiverDefinition = SwapFixedIborDefinition.from( SETTLEMENT_DATE, CMS_INDEX, bp1, forward + strikeRel[loopstrike], !FIXED_IS_PAYER); SwaptionCashFixedIborDefinition swaptionStrikeReceiverDefinition = SwaptionCashFixedIborDefinition.from(EXPIRY_DATE, swapStrikeReceiverDefinition, IS_LONG); SwaptionCashFixedIbor swaptionStrikeReceiver = swaptionStrikeReceiverDefinition.toDerivative(REFERENCE_DATE, CURVES_NAME); pvReceiverApproximation[loopstrike] = METHOD_HW_APPROXIMATION.presentValue(swaptionStrikeReceiver, BUNDLE_HW).getAmount(); pvReceiverIntegration[loopstrike] = METHOD_HW_INTEGRATION.presentValue(swaptionStrikeReceiver, BUNDLE_HW).getAmount(); assertEquals( "Swaption cash - Hull-White - present value - explicit/numerical integration", pvReceiverApproximation[loopstrike], pvReceiverIntegration[loopstrike], errorLimit); } } @Test /** Tests the curve sensitivity. */ public void presentValueCurveSensitivity() { InterestRateCurveSensitivity pvsSwaption = METHOD_HW_APPROXIMATION.presentValueCurveSensitivity(SWAPTION_PAYER_LONG, BUNDLE_HW); pvsSwaption = pvsSwaption.cleaned(); final double deltaTolerancePrice = 1.0E+4; // Testing note: Sensitivity is for a movement of 1. 1E+2 = 1 cent for a 1 bp move. Tolerance // increased to cope with numerical imprecision of finite difference. final double deltaShift = 1.0E-6; // 1. Forward curve sensitivity final String bumpedCurveName = "Bumped Curve"; final SwaptionCashFixedIbor swptBumpedForward = SWAPTION_PAYER_LONG_DEFINITION.toDerivative( REFERENCE_DATE, new String[] {CURVES_NAME[0], bumpedCurveName}); DoubleAVLTreeSet forwardTime = new DoubleAVLTreeSet(); for (int loopcpn = 0; loopcpn < SWAPTION_PAYER_LONG.getUnderlyingSwap().getSecondLeg().getNumberOfPayments(); loopcpn++) { CouponIbor cpn = (CouponIbor) SWAPTION_PAYER_LONG.getUnderlyingSwap().getSecondLeg().getNthPayment(loopcpn); forwardTime.add(cpn.getFixingPeriodStartTime()); forwardTime.add(cpn.getFixingPeriodEndTime()); } double[] nodeTimesForward = forwardTime.toDoubleArray(); final double[] sensiForwardMethod = SensitivityFiniteDifference.curveSensitivity( swptBumpedForward, BUNDLE_HW, CURVES_NAME[1], bumpedCurveName, nodeTimesForward, deltaShift, METHOD_HW_APPROXIMATION); final List<DoublesPair> sensiPvForward = pvsSwaption.getSensitivities().get(CURVES_NAME[1]); for (int loopnode = 0; loopnode < sensiForwardMethod.length; loopnode++) { final DoublesPair pairPv = sensiPvForward.get(loopnode); assertEquals( "Sensitivity swaption pv to forward curve: Node " + loopnode, nodeTimesForward[loopnode], pairPv.getFirst(), 1E-8); assertEquals( "Sensitivity finite difference method: node sensitivity " + loopnode, sensiForwardMethod[loopnode], pairPv.second, deltaTolerancePrice); } // 2. Discounting curve sensitivity final SwaptionCashFixedIbor swptBumpedDisc = SWAPTION_PAYER_LONG_DEFINITION.toDerivative( REFERENCE_DATE, new String[] {bumpedCurveName, CURVES_NAME[1]}); DoubleAVLTreeSet discTime = new DoubleAVLTreeSet(); discTime.add(SWAPTION_PAYER_LONG.getSettlementTime()); for (int loopcpn = 0; loopcpn < SWAPTION_PAYER_LONG.getUnderlyingSwap().getSecondLeg().getNumberOfPayments(); loopcpn++) { CouponIbor cpn = (CouponIbor) SWAPTION_PAYER_LONG.getUnderlyingSwap().getSecondLeg().getNthPayment(loopcpn); discTime.add(cpn.getPaymentTime()); } double[] nodeTimesDisc = discTime.toDoubleArray(); final double[] sensiDiscMethod = SensitivityFiniteDifference.curveSensitivity( swptBumpedDisc, BUNDLE_HW, CURVES_NAME[0], bumpedCurveName, nodeTimesDisc, deltaShift, METHOD_HW_APPROXIMATION); assertEquals( "Sensitivity finite difference method: number of node", 11, sensiDiscMethod.length); final List<DoublesPair> sensiPvDisc = pvsSwaption.getSensitivities().get(CURVES_NAME[0]); for (int loopnode = 0; loopnode < sensiDiscMethod.length; loopnode++) { final DoublesPair pairPv = sensiPvDisc.get(loopnode); assertEquals( "Sensitivity swaption pv to forward curve: Node " + loopnode, nodeTimesDisc[loopnode], pairPv.getFirst(), 1E-8); assertEquals( "Sensitivity finite difference method: node sensitivity", sensiDiscMethod[loopnode], pairPv.second, deltaTolerancePrice); } } @Test(enabled = false) /** Tests of performance. "enabled = false" for the standard testing. */ public void performance() { long startTime, endTime; final int nbTest = 1000; CurrencyAmount pvPayerLongExplicit = CurrencyAmount.of(CUR, 0.0); CurrencyAmount pvPayerLongIntegration = CurrencyAmount.of(CUR, 0.0); startTime = System.currentTimeMillis(); for (int looptest = 0; looptest < nbTest; looptest++) { pvPayerLongExplicit = METHOD_HW_APPROXIMATION.presentValue(SWAPTION_PAYER_LONG, BUNDLE_HW); } endTime = System.currentTimeMillis(); System.out.println( nbTest + " pv swaption Hull-White approximation method: " + (endTime - startTime) + " ms"); // Performance note: HW price: 8-Jul-11: On Mac Pro 3.2 GHz Quad-Core Intel Xeon: 330 ms for // 10000 swaptions. startTime = System.currentTimeMillis(); for (int looptest = 0; looptest < nbTest; looptest++) { METHOD_HW_APPROXIMATION.presentValueHullWhiteSensitivity(SWAPTION_PAYER_LONG, BUNDLE_HW); } endTime = System.currentTimeMillis(); System.out.println( nbTest + " HW sensitivity swaption Hull-White approximation method: " + (endTime - startTime) + " ms"); // Performance note: HW parameters sensitivity: 8-Jul-11: On Mac Pro 3.2 GHz Quad-Core Intel // Xeon: 525 ms for 10000 swaptions. startTime = System.currentTimeMillis(); for (int looptest = 0; looptest < nbTest; looptest++) { METHOD_HW_APPROXIMATION.presentValueCurveSensitivity(SWAPTION_PAYER_LONG, BUNDLE_HW); } endTime = System.currentTimeMillis(); System.out.println( nbTest + " curve sensitivity swaption Hull-White approximation method: " + (endTime - startTime) + " ms"); // Performance note: HW curve sensitivity: 8-Jul-11: On Mac Pro 3.2 GHz Quad-Core Intel Xeon: // 550 ms for 10000 swaptions. startTime = System.currentTimeMillis(); for (int looptest = 0; looptest < nbTest; looptest++) { pvPayerLongIntegration = METHOD_HW_INTEGRATION.presentValue(SWAPTION_PAYER_LONG, BUNDLE_HW); } endTime = System.currentTimeMillis(); System.out.println( nbTest + " cash swaption Hull-White numerical integration method: " + (endTime - startTime) + " ms"); // Performance note: HW numerical integration: 8-Jul-11: On Mac Pro 3.2 GHz Quad-Core Intel // Xeon: 1300 ms for 10000 swaptions. double difference = 0.0; difference = pvPayerLongExplicit.getAmount() - pvPayerLongIntegration.getAmount(); System.out.println("Difference: " + difference); } }
/** * @param date The date to use when converting to the derivative form, not null * @param settlementDate The settlement date, not null * @param data The index time series * @return The derivative form */ public BondInterestIndexedSecurity<PaymentFixed, Coupon> toDerivative( final ZonedDateTime date, final ZonedDateTime settlementDate, final DoubleTimeSeries<ZonedDateTime> data) { ArgumentChecker.notNull(date, "date"); ArgumentChecker.notNull(settlementDate, "settlement date"); double settlementTime; if (settlementDate.isBefore(date)) { settlementTime = 0.0; } else { settlementTime = TimeCalculator.getTimeBetween(date, settlementDate); } final Annuity<PaymentFixed> nominal = (Annuity<PaymentFixed>) getNominal().toDerivative(date, data); final AnnuityDefinition<CouponDefinition> couponDefinition = (AnnuityDefinition<CouponDefinition>) getCoupons().trimBefore(settlementDate); final CouponDefinition[] couponExPeriodArray = new CouponDefinition[couponDefinition.getNumberOfPayments()]; System.arraycopy( couponDefinition.getPayments(), 0, couponExPeriodArray, 0, couponDefinition.getNumberOfPayments()); if (getExCouponDays() != 0) { final ZonedDateTime exDividendDate = ScheduleCalculator.getAdjustedDate( couponDefinition.getNthPayment(0).getPaymentDate(), -getExCouponDays(), getCalendar()); if (settlementDate.isAfter(exDividendDate)) { // Implementation note: Ex-dividend period: the next coupon is not received but its date is // required for yield calculation couponExPeriodArray[0] = new CouponFixedDefinition(couponDefinition.getNthPayment(0), 0.0); } } final AnnuityDefinition<PaymentDefinition> couponDefinitionExPeriod = new AnnuityDefinition<PaymentDefinition>(couponExPeriodArray, getCalendar()); final Annuity<Coupon> couponStandard = (Annuity<Coupon>) couponDefinitionExPeriod.toDerivative(date, data); final Annuity<PaymentFixed> nominalStandard = nominal.trimBefore(settlementTime); final double accruedInterest = accruedInterest(settlementDate); final double factorSpot = getDayCount() .getAccruedInterest( couponDefinition.getNthPayment(0).getAccrualStartDate(), settlementDate, couponDefinition.getNthPayment(0).getAccrualEndDate(), 1.0, _couponPerYear); final double factorPeriod = getDayCount() .getAccruedInterest( couponDefinition.getNthPayment(0).getAccrualStartDate(), couponDefinition.getNthPayment(0).getAccrualEndDate(), couponDefinition.getNthPayment(0).getAccrualEndDate(), 1.0, _couponPerYear); final double factorToNextCoupon = (factorPeriod - factorSpot) / factorPeriod; final PaymentFixedDefinition nominalLast = getNominal().getNthPayment(getNominal().getNumberOfPayments() - 1); final ZonedDateTime settlementDate2 = settlementDate.isBefore(date) ? date : settlementDate; final double notional = settlementDate.isBefore(date) ? 0.0 : 1.0; final PaymentFixedDefinition settlementDefinition = new PaymentFixedDefinition(nominalLast.getCurrency(), settlementDate2, notional); final PaymentFixed settlement = settlementDefinition.toDerivative(date); return new BondInterestIndexedSecurity<>( nominalStandard, couponStandard, settlementTime, accruedInterest, factorToNextCoupon, _yieldConvention, _couponPerYear, settlement, getIssuerEntity(), _priceIndex); }
/** * Builder of Inflation capital index bond from financial details. The first coupon date is * provided to cope with short or long first coupons. The notional and the coupon reference index * are monthly index (no interpolation). * * @param priceIndex The price index associated to the bond. * @param monthLag The lag in month between the index validity and the coupon dates. * @param startDate The bond start date. * @param firstCouponDate The bond first coupon date. Used for short/long first coupon. * @param maturityDate The bond maturity date. * @param couponPeriod The period between coupon payments. * @param notional The bond notional. * @param realRate The bond nominal rate. * @param businessDay The business day convention to compute the payment days. * @param settlementDays Standard number of days between trade date and trade settlement. Used for * clean price and yield computation. * @param calendar The payment calendar. * @param dayCount The coupon day count convention. * @param yieldConvention The yield (to maturity) computation convention. * @param isEOM The end-of-month flag. * @param issuer The bond issuer name. * @return The bond. */ public static BondInterestIndexedSecurityDefinition< PaymentFixedDefinition, CouponInflationYearOnYearMonthlyWithMarginDefinition> fromMonthly( final IndexPrice priceIndex, final int monthLag, final ZonedDateTime startDate, final ZonedDateTime firstCouponDate, final ZonedDateTime maturityDate, final Period couponPeriod, final double notional, final double realRate, final BusinessDayConvention businessDay, final int settlementDays, final Calendar calendar, final DayCount dayCount, final YieldConvention yieldConvention, final boolean isEOM, final LegalEntity issuer) { // Nominal construction final PaymentFixedDefinition[] nominalPayment = new PaymentFixedDefinition[] { new PaymentFixedDefinition( priceIndex.getCurrency(), businessDay.adjustDate(calendar, maturityDate), notional) }; final AnnuityDefinition<PaymentFixedDefinition> nominalAnnuity = new AnnuityDefinition<>(nominalPayment, calendar); // Coupon construction final ZonedDateTime[] paymentDatesUnadjusted = ScheduleCalculator.getUnadjustedDateSchedule( firstCouponDate, maturityDate, couponPeriod, true, false); final ZonedDateTime[] paymentDates = ScheduleCalculator.getAdjustedDateSchedule( paymentDatesUnadjusted, businessDay, calendar, false); final CouponInflationYearOnYearMonthlyWithMarginDefinition[] coupons = new CouponInflationYearOnYearMonthlyWithMarginDefinition[paymentDates.length + 1]; coupons[0] = CouponInflationYearOnYearMonthlyWithMarginDefinition.from( realRate, startDate, ScheduleCalculator.getAdjustedDate(firstCouponDate, 0, calendar), notional, priceIndex, monthLag, monthLag, true); coupons[1] = CouponInflationYearOnYearMonthlyWithMarginDefinition.from( realRate, firstCouponDate, paymentDates[0], notional, priceIndex, monthLag, monthLag, true); for (int loopcpn = 1; loopcpn < paymentDates.length; loopcpn++) { coupons[loopcpn + 1] = CouponInflationYearOnYearMonthlyWithMarginDefinition.from( realRate, paymentDatesUnadjusted[loopcpn - 1], paymentDates[loopcpn], notional, priceIndex, monthLag, monthLag, true); } final AnnuityDefinition<CouponInflationYearOnYearMonthlyWithMarginDefinition> couponAnnuity = new AnnuityDefinition<>(coupons, calendar); return new BondInterestIndexedSecurityDefinition<>( nominalAnnuity, couponAnnuity, DEFAULT_EX_COUPON_DAYS, settlementDays, calendar, dayCount, yieldConvention, isEOM, monthLag, issuer); }
/** Tests related to the pricing of physical delivery swaption in Hull-White one factor model. */ public class SwaptionPhysicalFixedIborHullWhiteMethodTest { private static final MulticurveProviderDiscount MULTICURVES = MulticurveProviderDiscountDataSets.createMulticurveEurUsd(); private static final IborIndex EURIBOR6M = MulticurveProviderDiscountDataSets.getIndexesIborMulticurveEurUsd()[1]; private static final Currency EUR = EURIBOR6M.getCurrency(); private static final Calendar CALENDAR = MulticurveProviderDiscountDataSets.getEURCalendar(); private static final HullWhiteOneFactorPiecewiseConstantParameters HW_PARAMETERS = HullWhiteDataSets.createHullWhiteParameters(); private static final HullWhiteOneFactorProviderDiscount HW_MULTICURVES = new HullWhiteOneFactorProviderDiscount(MULTICURVES, HW_PARAMETERS, EUR); private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2011, 7, 7); // Swaption 5Yx5Y private static final int SPOT_LAG = EURIBOR6M.getSpotLag(); private static final int SWAP_TENOR_YEAR = 5; private static final Period SWAP_TENOR = Period.ofYears(SWAP_TENOR_YEAR); private static final GeneratorSwapFixedIbor EUR1YEURIBOR6M = GeneratorSwapFixedIborMaster.getInstance().getGenerator("EUR1YEURIBOR6M", CALENDAR); private static final ZonedDateTime EXPIRY_DATE = DateUtils.getUTCDate(2016, 7, 7); private static final boolean IS_LONG = true; private static final ZonedDateTime SETTLEMENT_DATE = ScheduleCalculator.getAdjustedDate(EXPIRY_DATE, SPOT_LAG, CALENDAR); private static final double NOTIONAL = 100000000; // 100m private static final double RATE = 0.0175; private static final boolean FIXED_IS_PAYER = true; private static final SwapFixedIborDefinition SWAP_PAYER_DEFINITION = SwapFixedIborDefinition.from( SETTLEMENT_DATE, SWAP_TENOR, EUR1YEURIBOR6M, NOTIONAL, RATE, FIXED_IS_PAYER); private static final SwapFixedIborDefinition SWAP_RECEIVER_DEFINITION = SwapFixedIborDefinition.from( SETTLEMENT_DATE, SWAP_TENOR, EUR1YEURIBOR6M, NOTIONAL, RATE, !FIXED_IS_PAYER); private static final SwaptionPhysicalFixedIborDefinition SWAPTION_LONG_PAYER_DEFINITION = SwaptionPhysicalFixedIborDefinition.from(EXPIRY_DATE, SWAP_PAYER_DEFINITION, IS_LONG); private static final SwaptionPhysicalFixedIborDefinition SWAPTION_LONG_RECEIVER_DEFINITION = SwaptionPhysicalFixedIborDefinition.from(EXPIRY_DATE, SWAP_RECEIVER_DEFINITION, IS_LONG); private static final SwaptionPhysicalFixedIborDefinition SWAPTION_SHORT_PAYER_DEFINITION = SwaptionPhysicalFixedIborDefinition.from(EXPIRY_DATE, SWAP_PAYER_DEFINITION, !IS_LONG); private static final SwaptionPhysicalFixedIborDefinition SWAPTION_SHORT_RECEIVER_DEFINITION = SwaptionPhysicalFixedIborDefinition.from(EXPIRY_DATE, SWAP_RECEIVER_DEFINITION, !IS_LONG); private static final SwapFixedCoupon<Coupon> SWAP_RECEIVER = SWAP_RECEIVER_DEFINITION.toDerivative(REFERENCE_DATE); private static final SwaptionPhysicalFixedIbor SWAPTION_LONG_PAYER = SWAPTION_LONG_PAYER_DEFINITION.toDerivative(REFERENCE_DATE); private static final SwaptionPhysicalFixedIbor SWAPTION_LONG_RECEIVER = SWAPTION_LONG_RECEIVER_DEFINITION.toDerivative(REFERENCE_DATE); private static final SwaptionPhysicalFixedIbor SWAPTION_SHORT_PAYER = SWAPTION_SHORT_PAYER_DEFINITION.toDerivative(REFERENCE_DATE); private static final SwaptionPhysicalFixedIbor SWAPTION_SHORT_RECEIVER = SWAPTION_SHORT_RECEIVER_DEFINITION.toDerivative(REFERENCE_DATE); // Calculator private static final SwaptionPhysicalFixedIborHullWhiteMethod METHOD_HW = SwaptionPhysicalFixedIborHullWhiteMethod.getInstance(); private static final SwapFixedCouponDiscountingMethod METHOD_SWAP = SwapFixedCouponDiscountingMethod.getInstance(); private static final CashFlowEquivalentCalculator CFEC = CashFlowEquivalentCalculator.getInstance(); private static final ParRateDiscountingCalculator PRDC = ParRateDiscountingCalculator.getInstance(); private static final PresentValueDiscountingCalculator PVDC = PresentValueDiscountingCalculator.getInstance(); private static final PresentValueCurveSensitivityDiscountingCalculator PVCSDC = PresentValueCurveSensitivityDiscountingCalculator.getInstance(); private static final PresentValueHullWhiteCalculator PVHWC = PresentValueHullWhiteCalculator.getInstance(); private static final PresentValueCurveSensitivityHullWhiteCalculator PVCSHWC = PresentValueCurveSensitivityHullWhiteCalculator.getInstance(); private static final double SHIFT = 1.0E-6; private static final ParameterSensitivityParameterCalculator<HullWhiteOneFactorProviderInterface> PS_HW_C = new ParameterSensitivityParameterCalculator<>(PVCSHWC); private static final ParameterSensitivityHullWhiteDiscountInterpolatedFDCalculator PS_HW_FDC = new ParameterSensitivityHullWhiteDiscountInterpolatedFDCalculator(PVHWC, SHIFT); private static final SwaptionPhysicalFixedIborHullWhiteNumericalIntegrationMethod METHOD_HW_INTEGRATION = SwaptionPhysicalFixedIborHullWhiteNumericalIntegrationMethod.getInstance(); private static final SwaptionPhysicalFixedIborHullWhiteApproximationMethod METHOD_HW_APPROXIMATION = SwaptionPhysicalFixedIborHullWhiteApproximationMethod.getInstance(); private static final int NB_PATH = 12500; private static final HullWhiteMonteCarloMethod METHOD_HW_MONTECARLO = new HullWhiteMonteCarloMethod(new NormalRandomNumberGenerator(0.0, 1.0), NB_PATH); private static final HullWhiteOneFactorPiecewiseConstantInterestRateModel MODEL = new HullWhiteOneFactorPiecewiseConstantInterestRateModel(); private static final ProbabilityDistribution<Double> NORMAL = new NormalDistribution(0, 1); private static final double TOLERANCE_PV = 1.0E-2; private static final double TOLERANCE_PV_DELTA = 1.0E+0; // Testing note: Sensitivity is for a movement of 1. 1E+2 = 1 cent for a 1 bp move. @Test /** Test the present value. */ public void presentValueExplicit() { final MultipleCurrencyAmount pv = METHOD_HW.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES); final double timeToExpiry = SWAPTION_LONG_PAYER.getTimeToExpiry(); final AnnuityPaymentFixed cfe = CFEC.visitSwap(SWAPTION_LONG_PAYER.getUnderlyingSwap(), MULTICURVES); final int numberOfPayments = cfe.getNumberOfPayments(); final double alpha[] = new double[numberOfPayments]; final double disccf[] = new double[numberOfPayments]; for (int loopcf = 0; loopcf < numberOfPayments; loopcf++) { alpha[loopcf] = MODEL.alpha( HW_PARAMETERS, 0.0, timeToExpiry, timeToExpiry, cfe.getNthPayment(loopcf).getPaymentTime()); disccf[loopcf] = MULTICURVES.getDiscountFactor(EUR, cfe.getNthPayment(loopcf).getPaymentTime()) * cfe.getNthPayment(loopcf).getAmount(); } final double kappa = MODEL.kappa(disccf, alpha); double pvExpected = 0.0; for (int loopcf = 0; loopcf < numberOfPayments; loopcf++) { pvExpected += disccf[loopcf] * NORMAL.getCDF(-kappa - alpha[loopcf]); } assertEquals( "Swaption physical - Hull-White - present value", pvExpected, pv.getAmount(EUR), 1E-2); final MultipleCurrencyAmount pv2 = METHOD_HW.presentValue(SWAPTION_LONG_PAYER, cfe, HW_MULTICURVES); assertEquals("Swaption physical - Hull-White - present value", pv, pv2); } @Test /** Tests long/short parity. */ public void longShortParityExplicit() { final MultipleCurrencyAmount pvLong = METHOD_HW.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES); final MultipleCurrencyAmount pvShort = METHOD_HW.presentValue(SWAPTION_SHORT_PAYER, HW_MULTICURVES); assertEquals( "Swaption physical - Hull-White - present value - long/short parity", pvLong.getAmount(EUR), -pvShort.getAmount(EUR), TOLERANCE_PV); } @Test /** Tests payer/receiver/swap parity. */ public void payerReceiverParityExplicit() { final MultipleCurrencyAmount pvReceiverLong = METHOD_HW.presentValue(SWAPTION_LONG_RECEIVER, HW_MULTICURVES); final MultipleCurrencyAmount pvPayerShort = METHOD_HW.presentValue(SWAPTION_SHORT_PAYER, HW_MULTICURVES); final MultipleCurrencyAmount pvSwap = SWAP_RECEIVER.accept(PVDC, MULTICURVES); assertEquals( "Swaption physical - Hull-White - present value - payer/receiver/swap parity", pvReceiverLong.getAmount(EUR) + pvPayerShort.getAmount(EUR), pvSwap.getAmount(EUR), TOLERANCE_PV); } @Test /** Tests the method against the present value calculator. */ public void presentValueMethodVsCalculator() { final MultipleCurrencyAmount pvMethod = METHOD_HW.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES); final MultipleCurrencyAmount pvCalculator = SWAPTION_LONG_PAYER.accept(PVHWC, HW_MULTICURVES); assertEquals( "SwaptionPhysicalFixedIborSABRMethod: present value : method and calculator", pvMethod, pvCalculator); } @Test /** Compare explicit formula with numerical integration. */ public void presentValueNumericalIntegration() { final MultipleCurrencyAmount pvPayerLongExplicit = METHOD_HW.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES); final MultipleCurrencyAmount pvPayerLongIntegration = METHOD_HW_INTEGRATION.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES); assertEquals( "Swaption physical - Hull-White - present value - explicit/numerical integration", pvPayerLongExplicit.getAmount(EUR), pvPayerLongIntegration.getAmount(EUR), TOLERANCE_PV); final MultipleCurrencyAmount pvPayerShortExplicit = METHOD_HW.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES); final MultipleCurrencyAmount pvPayerShortIntegration = METHOD_HW_INTEGRATION.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES); assertEquals( "Swaption physical - Hull-White - present value - explicit/numerical integration", pvPayerShortExplicit.getAmount(EUR), pvPayerShortIntegration.getAmount(EUR), TOLERANCE_PV); final MultipleCurrencyAmount pvReceiverLongExplicit = METHOD_HW.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES); final MultipleCurrencyAmount pvReceiverLongIntegration = METHOD_HW_INTEGRATION.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES); assertEquals( "Swaption physical - Hull-White - present value - explicit/numerical integration", pvReceiverLongExplicit.getAmount(EUR), pvReceiverLongIntegration.getAmount(EUR), TOLERANCE_PV); final MultipleCurrencyAmount pvReceiverShortExplicit = METHOD_HW.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES); final MultipleCurrencyAmount pvReceiverShortIntegration = METHOD_HW_INTEGRATION.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES); assertEquals( "Swaption physical - Hull-White - present value - explicit/numerical integration", pvReceiverShortExplicit.getAmount(EUR), pvReceiverShortIntegration.getAmount(EUR), TOLERANCE_PV); } @Test /** Compare explicit formula with approximated formula. */ public void presentValueApproximation() { final BlackImpliedVolatilityFormula implied = new BlackImpliedVolatilityFormula(); final double forward = SWAPTION_LONG_PAYER .getUnderlyingSwap() .accept(ParRateDiscountingCalculator.getInstance(), MULTICURVES); final double pvbp = METHOD_SWAP.presentValueBasisPoint(SWAPTION_LONG_PAYER.getUnderlyingSwap(), MULTICURVES); final MultipleCurrencyAmount pvPayerLongExplicit = METHOD_HW.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES); final MultipleCurrencyAmount pvPayerLongApproximation = METHOD_HW_APPROXIMATION.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES); final BlackFunctionData data = new BlackFunctionData(forward, pvbp, 0.20); final double volExplicit = implied.getImpliedVolatility(data, SWAPTION_LONG_PAYER, pvPayerLongExplicit.getAmount(EUR)); final double volApprox = implied.getImpliedVolatility( data, SWAPTION_LONG_PAYER, pvPayerLongApproximation.getAmount(EUR)); assertEquals( "Swaption physical - Hull-White - present value - explicit/approximation", pvPayerLongExplicit.getAmount(EUR), pvPayerLongApproximation.getAmount(EUR), 5.0E+2); assertEquals( "Swaption physical - Hull-White - present value - explicit/approximation", volExplicit, volApprox, 2.5E-4); // 0.025% final MultipleCurrencyAmount pvReceiverLongExplicit = METHOD_HW.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES); final MultipleCurrencyAmount pvReceiverLongApproximation = METHOD_HW_APPROXIMATION.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES); assertEquals( "Swaption physical - Hull-White - present value - explicit/numerical integration", pvReceiverLongExplicit.getAmount(EUR), pvReceiverLongApproximation.getAmount(EUR), 5.0E+2); } @Test /** Approximation analysis. */ public void presentValueApproximationAnalysis() { final NormalImpliedVolatilityFormula implied = new NormalImpliedVolatilityFormula(); final int nbStrike = 20; final double[] pvExplicit = new double[nbStrike + 1]; final double[] pvApproximation = new double[nbStrike + 1]; final double[] strike = new double[nbStrike + 1]; final double[] volExplicit = new double[nbStrike + 1]; final double[] volApprox = new double[nbStrike + 1]; final double strikeRange = 0.010; final SwapFixedCoupon<Coupon> swap = SWAP_PAYER_DEFINITION.toDerivative(REFERENCE_DATE); final double forward = swap.accept(PRDC, MULTICURVES); final double pvbp = METHOD_SWAP.presentValueBasisPoint(swap, MULTICURVES); for (int loopstrike = 0; loopstrike <= nbStrike; loopstrike++) { strike[loopstrike] = forward - strikeRange + 3 * strikeRange * loopstrike / nbStrike; // From forward-strikeRange to forward+2*strikeRange final SwapFixedIborDefinition swapDefinition = SwapFixedIborDefinition.from( SETTLEMENT_DATE, SWAP_TENOR, EUR1YEURIBOR6M, NOTIONAL, strike[loopstrike], FIXED_IS_PAYER); final SwaptionPhysicalFixedIborDefinition swaptionDefinition = SwaptionPhysicalFixedIborDefinition.from(EXPIRY_DATE, swapDefinition, IS_LONG); final SwaptionPhysicalFixedIbor swaption = swaptionDefinition.toDerivative(REFERENCE_DATE); pvExplicit[loopstrike] = METHOD_HW.presentValue(swaption, HW_MULTICURVES).getAmount(EUR); pvApproximation[loopstrike] = METHOD_HW_APPROXIMATION.presentValue(swaption, HW_MULTICURVES).getAmount(EUR); final NormalFunctionData data = new NormalFunctionData(forward, pvbp, 0.01); volExplicit[loopstrike] = implied.getImpliedVolatility(data, swaption, pvExplicit[loopstrike]); volApprox[loopstrike] = implied.getImpliedVolatility(data, swaption, pvApproximation[loopstrike]); assertEquals( "Swaption physical - Hull-White - implied volatility - explicit/approximation", volExplicit[loopstrike], volApprox[loopstrike], 1.0E-3); // 0.10% } } @Test(enabled = true) /** Compare explicit formula with Monte-Carlo and long/short and payer/receiver parities. */ public void presentValueMonteCarlo() { HullWhiteMonteCarloMethod methodMC; methodMC = new HullWhiteMonteCarloMethod( new NormalRandomNumberGenerator(0.0, 1.0, new MersenneTwister()), NB_PATH); // Seed fixed to the DEFAULT_SEED for testing purposes. final MultipleCurrencyAmount pvPayerLongExplicit = METHOD_HW.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES); final MultipleCurrencyAmount pvPayerLongMC = methodMC.presentValue(SWAPTION_LONG_PAYER, EUR, HW_MULTICURVES); assertEquals( "Swaption physical - Hull-White - Monte Carlo", pvPayerLongExplicit.getAmount(EUR), pvPayerLongMC.getAmount(EUR), 1.0E+4); final double pvMCPreviousRun = 4221400.891; assertEquals( "Swaption physical - Hull-White - Monte Carlo", pvMCPreviousRun, pvPayerLongMC.getAmount(EUR), TOLERANCE_PV); methodMC = new HullWhiteMonteCarloMethod( new NormalRandomNumberGenerator(0.0, 1.0, new MersenneTwister()), NB_PATH); final MultipleCurrencyAmount pvPayerShortMC = methodMC.presentValue(SWAPTION_SHORT_PAYER, EUR, HW_MULTICURVES); assertEquals( "Swaption physical - Hull-White - Monte Carlo", -pvPayerLongMC.getAmount(EUR), pvPayerShortMC.getAmount(EUR), TOLERANCE_PV); final MultipleCurrencyAmount pvReceiverLongMC = methodMC.presentValue(SWAPTION_LONG_RECEIVER, EUR, HW_MULTICURVES); final MultipleCurrencyAmount pvSwap = SWAP_RECEIVER.accept(PVDC, MULTICURVES); assertEquals( "Swaption physical - Hull-White - Monte Carlo - payer/receiver/swap parity", pvReceiverLongMC.getAmount(EUR) + pvPayerShortMC.getAmount(EUR), pvSwap.getAmount(EUR), 1.0E+5); } @Test /** Tests the Hull-White parameters sensitivity for the explicit formula. */ public void presentValueHullWhiteSensitivityExplicit() { final double[] hwSensitivity = METHOD_HW.presentValueHullWhiteSensitivity(SWAPTION_LONG_PAYER, HW_MULTICURVES); final int nbVolatility = HW_PARAMETERS.getVolatility().length; final double shiftVol = 1.0E-6; final double[] volatilityBumped = new double[nbVolatility]; System.arraycopy(HW_PARAMETERS.getVolatility(), 0, volatilityBumped, 0, nbVolatility); final double[] volatilityTime = new double[nbVolatility - 1]; System.arraycopy(HW_PARAMETERS.getVolatilityTime(), 1, volatilityTime, 0, nbVolatility - 1); final double[] pvBumpedPlus = new double[nbVolatility]; final double[] pvBumpedMinus = new double[nbVolatility]; final HullWhiteOneFactorPiecewiseConstantParameters parametersBumped = new HullWhiteOneFactorPiecewiseConstantParameters( HW_PARAMETERS.getMeanReversion(), volatilityBumped, volatilityTime); final HullWhiteOneFactorProviderDiscount bundleBumped = new HullWhiteOneFactorProviderDiscount(MULTICURVES, parametersBumped, EUR); for (int loopvol = 0; loopvol < nbVolatility; loopvol++) { volatilityBumped[loopvol] += shiftVol; parametersBumped.setVolatility(volatilityBumped); pvBumpedPlus[loopvol] = METHOD_HW.presentValue(SWAPTION_LONG_PAYER, bundleBumped).getAmount(EUR); volatilityBumped[loopvol] -= 2 * shiftVol; parametersBumped.setVolatility(volatilityBumped); pvBumpedMinus[loopvol] = METHOD_HW.presentValue(SWAPTION_LONG_PAYER, bundleBumped).getAmount(EUR); assertEquals( "Swaption - Hull-White sensitivity adjoint: derivative " + loopvol + " - difference:" + ((pvBumpedPlus[loopvol] - pvBumpedMinus[loopvol]) / (2 * shiftVol) - hwSensitivity[loopvol]), (pvBumpedPlus[loopvol] - pvBumpedMinus[loopvol]) / (2 * shiftVol), hwSensitivity[loopvol], TOLERANCE_PV_DELTA); volatilityBumped[loopvol] = HW_PARAMETERS.getVolatility()[loopvol]; } } @Test /** Tests long/short parity. */ public void presentValueHullWhiteSensitivitylongShortParityExplicit() { final double[] pvhwsLong = METHOD_HW.presentValueHullWhiteSensitivity(SWAPTION_LONG_PAYER, HW_MULTICURVES); final double[] pvhwsShort = METHOD_HW.presentValueHullWhiteSensitivity(SWAPTION_SHORT_PAYER, HW_MULTICURVES); for (int loophw = 0; loophw < pvhwsLong.length; loophw++) { assertEquals( "Swaption physical - Hull-White - presentValueHullWhiteSensitivity - long/short parity", pvhwsLong[loophw], -pvhwsShort[loophw], TOLERANCE_PV_DELTA); } } @Test /** Tests payer/receiver/swap parity. */ public void presentValueHullWhiteSensitivitypayerReceiverParityExplicit() { final double[] pvhwsReceiverLong = METHOD_HW.presentValueHullWhiteSensitivity(SWAPTION_LONG_RECEIVER, HW_MULTICURVES); final double[] pvhwsPayerShort = METHOD_HW.presentValueHullWhiteSensitivity(SWAPTION_SHORT_PAYER, HW_MULTICURVES); for (int loophw = 0; loophw < pvhwsReceiverLong.length; loophw++) { assertEquals( "Swaption physical - Hull-White - present value - payer/receiver/swap parity", 0, pvhwsReceiverLong[loophw] + pvhwsPayerShort[loophw], TOLERANCE_PV_DELTA); } } @Test /** Tests present value curve sensitivity when the valuation date is on trade date. */ public void presentValueCurveSensitivity() { final MultipleCurrencyParameterSensitivity pvpsExact = PS_HW_C.calculateSensitivity( SWAPTION_SHORT_RECEIVER, HW_MULTICURVES, HW_MULTICURVES.getMulticurveProvider().getAllNames()); final MultipleCurrencyParameterSensitivity pvpsFD = PS_HW_FDC.calculateSensitivity(SWAPTION_SHORT_RECEIVER, HW_MULTICURVES); AssertSensivityObjects.assertEquals( "SwaptionPhysicalFixedIborSABRMethod: presentValueCurveSensitivity ", pvpsExact, pvpsFD, TOLERANCE_PV_DELTA); } @Test(enabled = false) /** Tests present value curve sensitivity when the valuation date is on trade date. */ public void presentValueCurveSensitivityStability() { // 5Yx5Y final MultipleCurrencyParameterSensitivity pvpsExact = PS_HW_C.calculateSensitivity( SWAPTION_SHORT_RECEIVER, HW_MULTICURVES, HW_MULTICURVES.getMulticurveProvider().getAllNames()); final double derivativeExact = pvpsExact.totalSensitivity(MULTICURVES.getFxRates(), EUR); final double startingShift = 1.0E-4; final double ratio = Math.sqrt(2.0); final int nbShift = 55; final double[] eps = new double[nbShift + 1]; final double[] derivative_FD = new double[nbShift]; final double[] diff = new double[nbShift]; eps[0] = startingShift; for (int loopshift = 0; loopshift < nbShift; loopshift++) { final ParameterSensitivityHullWhiteDiscountInterpolatedFDCalculator fdShift = new ParameterSensitivityHullWhiteDiscountInterpolatedFDCalculator(PVHWC, eps[loopshift]); final MultipleCurrencyParameterSensitivity pvpsFD = fdShift.calculateSensitivity(SWAPTION_SHORT_RECEIVER, HW_MULTICURVES); derivative_FD[loopshift] = pvpsFD.totalSensitivity(MULTICURVES.getFxRates(), EUR); diff[loopshift] = derivative_FD[loopshift] - derivativeExact; eps[loopshift + 1] = eps[loopshift] / ratio; } // 1Mx5Y final Period expirationPeriod = Period.ofDays( 1); // Period.ofDays(1); Period.ofDays(7); Period.ofMonths(1); Period.ofYears(1); // Period.ofYears(10); final ZonedDateTime expiryDateExp = ScheduleCalculator.getAdjustedDate(REFERENCE_DATE, expirationPeriod, EURIBOR6M, CALENDAR); final ZonedDateTime settlementDateExp = ScheduleCalculator.getAdjustedDate(expiryDateExp, SPOT_LAG, CALENDAR); final double ATM = 0.0151; // 1W: 1.52% - 1M: 1.52% - 1Y: 1.51% - 10Y: 1.51% final SwapFixedIborDefinition swapExpx5YDefinition = SwapFixedIborDefinition.from( settlementDateExp, SWAP_TENOR, EUR1YEURIBOR6M, NOTIONAL, ATM, !FIXED_IS_PAYER); final SwaptionPhysicalFixedIborDefinition swaptionExpx5YDefinition = SwaptionPhysicalFixedIborDefinition.from(EXPIRY_DATE, swapExpx5YDefinition, !IS_LONG); final SwaptionPhysicalFixedIbor swaptionExpx5Y = swaptionExpx5YDefinition.toDerivative(REFERENCE_DATE); // final double forward = swaptionExpx5Y.getUnderlyingSwap().accept(PRDC, MULTICURVES); final MultipleCurrencyParameterSensitivity pvpsExactExp = PS_HW_C.calculateSensitivity( swaptionExpx5Y, HW_MULTICURVES, HW_MULTICURVES.getMulticurveProvider().getAllNames()); final double derivativeExactExp = pvpsExactExp.totalSensitivity(MULTICURVES.getFxRates(), EUR); final double[] derivative_FDExp = new double[nbShift]; final double[] diffExp = new double[nbShift]; for (int loopshift = 0; loopshift < nbShift; loopshift++) { final ParameterSensitivityHullWhiteDiscountInterpolatedFDCalculator fdShift = new ParameterSensitivityHullWhiteDiscountInterpolatedFDCalculator(PVHWC, eps[loopshift]); final MultipleCurrencyParameterSensitivity pvpsFD = fdShift.calculateSensitivity(swaptionExpx5Y, HW_MULTICURVES); derivative_FDExp[loopshift] = pvpsFD.totalSensitivity(MULTICURVES.getFxRates(), EUR); diffExp[loopshift] = derivative_FDExp[loopshift] - derivativeExactExp; } // int t = 0; // t++; } @Test /** Tests long/short parity. */ public void presentValueCurveSensitivityLongShortParityExplicit() { final MultipleCurrencyMulticurveSensitivity pvhwsLong = METHOD_HW.presentValueCurveSensitivity(SWAPTION_LONG_PAYER, HW_MULTICURVES); final MultipleCurrencyMulticurveSensitivity pvhwsShort = METHOD_HW.presentValueCurveSensitivity(SWAPTION_SHORT_PAYER, HW_MULTICURVES); AssertSensivityObjects.assertEquals( "Swaption physical - Hull-White - presentValueCurveSensitivity - long/short parity", pvhwsLong, pvhwsShort.multipliedBy(-1.0), TOLERANCE_PV_DELTA); } @Test /** Tests payer/receiver/swap parity. */ public void presentValueCurveSensitivityPayerReceiverParityExplicit() { final MultipleCurrencyMulticurveSensitivity pvhwsReceiverLong = METHOD_HW.presentValueCurveSensitivity(SWAPTION_LONG_RECEIVER, HW_MULTICURVES); final MultipleCurrencyMulticurveSensitivity pvhwsPayerShort = METHOD_HW.presentValueCurveSensitivity(SWAPTION_SHORT_PAYER, HW_MULTICURVES); final MultipleCurrencyMulticurveSensitivity pvSwap = SWAP_RECEIVER.accept(PVCSDC, MULTICURVES); AssertSensivityObjects.assertEquals( "Swaption physical - Hull-White - presentValueCurveSensitivity - payer/receiver/swap parity", pvSwap.cleaned(TOLERANCE_PV_DELTA), pvhwsReceiverLong.plus(pvhwsPayerShort).cleaned(TOLERANCE_PV_DELTA), TOLERANCE_PV_DELTA); } @Test /** Tests the curve sensitivity in Monte Carlo approach. */ public void presentValueCurveSensitivityMonteCarlo() { final double toleranceDelta = 1.0E+6; // 100 USD by bp final MultipleCurrencyMulticurveSensitivity pvcsExplicit = METHOD_HW .presentValueCurveSensitivity(SWAPTION_LONG_PAYER, HW_MULTICURVES) .cleaned(TOLERANCE_PV_DELTA); final HullWhiteMonteCarloMethod methodMC = new HullWhiteMonteCarloMethod( new NormalRandomNumberGenerator(0.0, 1.0, new MersenneTwister()), NB_PATH); final MultipleCurrencyMulticurveSensitivity pvcsMC = methodMC .presentValueCurveSensitivity(SWAPTION_LONG_PAYER, EUR, HW_MULTICURVES) .cleaned(TOLERANCE_PV_DELTA); AssertSensivityObjects.assertEquals( "Swaption physical - Hull-White - presentValueCurveSensitivity - payer/receiver/swap parity", pvcsExplicit, pvcsMC, toleranceDelta); } @Test(enabled = false) /** Tests of performance. "enabled = false" for the standard testing. */ public void performance() { long startTime, endTime; final int nbTest = 1000; MultipleCurrencyAmount pvPayerLongExplicit = MultipleCurrencyAmount.of(EUR, 0.0); MultipleCurrencyAmount pvPayerLongIntegration = MultipleCurrencyAmount.of(EUR, 0.0); MultipleCurrencyAmount pvPayerLongApproximation = MultipleCurrencyAmount.of(EUR, 0.0); @SuppressWarnings("unused") MultipleCurrencyAmount pvPayerLongMC = MultipleCurrencyAmount.of(EUR, 0.0); double[] pvhws = METHOD_HW.presentValueHullWhiteSensitivity(SWAPTION_LONG_PAYER, HW_MULTICURVES); MultipleCurrencyMulticurveSensitivity pvcs = METHOD_HW.presentValueCurveSensitivity(SWAPTION_LONG_PAYER, HW_MULTICURVES); startTime = System.currentTimeMillis(); for (int looptest = 0; looptest < nbTest; looptest++) { pvPayerLongExplicit = METHOD_HW.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES); } endTime = System.currentTimeMillis(); System.out.println( nbTest + " pv swaption Hull-White explicit method: " + (endTime - startTime) + " ms"); // Performance note: HW price: 19-Nov-2012: On Mac Pro 3.2 GHz Quad-Core Intel Xeon: 380 ms for // 10000 swaptions. startTime = System.currentTimeMillis(); for (int looptest = 0; looptest < nbTest; looptest++) { pvhws = METHOD_HW.presentValueHullWhiteSensitivity(SWAPTION_LONG_PAYER, HW_MULTICURVES); } endTime = System.currentTimeMillis(); System.out.println( nbTest + " HW sensitivity swaption Hull-White explicit method: " + (endTime - startTime) + " ms"); // Performance note: HW sensitivity (3): 19-Nov-2012: On Mac Pro 3.2 GHz Quad-Core Intel Xeon: // 430 ms for 10000 swaptions. startTime = System.currentTimeMillis(); for (int looptest = 0; looptest < nbTest; looptest++) { pvcs = METHOD_HW.presentValueCurveSensitivity(SWAPTION_LONG_PAYER, HW_MULTICURVES); } endTime = System.currentTimeMillis(); System.out.println( nbTest + " curve sensitivity swaption Hull-White explicit method: " + (endTime - startTime) + " ms"); // Performance note: curve sensitivity (40): 19-Nov-2012: On Mac Pro 3.2 GHz Quad-Core Intel // Xeon: 855 ms for 10000 swaptions. startTime = System.currentTimeMillis(); for (int looptest = 0; looptest < nbTest; looptest++) { pvhws = METHOD_HW.presentValueHullWhiteSensitivity(SWAPTION_LONG_PAYER, HW_MULTICURVES); pvcs = METHOD_HW.presentValueCurveSensitivity(SWAPTION_LONG_PAYER, HW_MULTICURVES); pvhws = METHOD_HW.presentValueHullWhiteSensitivity(SWAPTION_LONG_PAYER, HW_MULTICURVES); } endTime = System.currentTimeMillis(); System.out.println( nbTest + " price/delta/vega swaption Hull-White explicit method: " + (endTime - startTime) + " ms"); // Performance note: present value/delta/vega: 19-Nov-2012: On Mac Pro 3.2 GHz Quad-Core Intel // Xeon: 1730 ms for 10000 swaptions. startTime = System.currentTimeMillis(); for (int looptest = 0; looptest < nbTest; looptest++) { pvPayerLongIntegration = METHOD_HW_INTEGRATION.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES); } endTime = System.currentTimeMillis(); System.out.println( nbTest + " swaption Hull-White numerical integration method: " + (endTime - startTime) + " ms"); // Performance note: HW numerical integration: 19-Nov-2012: On Mac Pro 3.2 GHz Quad-Core Intel // Xeon: 1700 ms for 10000 swaptions. startTime = System.currentTimeMillis(); for (int looptest = 0; looptest < nbTest; looptest++) { pvPayerLongApproximation = METHOD_HW_APPROXIMATION.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES); } endTime = System.currentTimeMillis(); System.out.println( nbTest + " swaption Hull-White approximation method: " + (endTime - startTime) + " ms"); // Performance note: HW approximation: 19-Nov-2012: On Mac Pro 3.2 GHz Quad-Core Intel Xeon: 250 // ms for 10000 swaptions. startTime = System.currentTimeMillis(); for (int looptest = 0; looptest < nbTest; looptest++) { pvPayerLongMC = METHOD_HW_MONTECARLO.presentValue(SWAPTION_LONG_PAYER, EUR, HW_MULTICURVES); } endTime = System.currentTimeMillis(); System.out.println( nbTest + " swaption Hull-White Monte Carlo method (" + NB_PATH + " paths): " + (endTime - startTime) + " ms"); // Performance note: HW approximation: 18-Aug-11: On Mac Pro 3.2 GHz Quad-Core Intel Xeon: 9200 // ms for 1000 swaptions (12500 paths). final double difference = pvPayerLongExplicit.getAmount(EUR) - pvPayerLongIntegration.getAmount(EUR); final double difference2 = pvPayerLongExplicit.getAmount(EUR) - pvPayerLongApproximation.getAmount(EUR); // double difference3 = pvPayerLongExplicit.getAmount(CUR) - pvPayerLongMC.getAmount(CUR); System.out.println("Difference explicit-integration: " + difference); System.out.println("Difference explicit-approximation: " + difference2); // System.out.println("Difference explicit-Monte Carlo: " + difference3); System.out.println("Curve sensitivity: " + pvcs.toString()); System.out.println("HW sensitivity: " + Arrays.toString(pvhws)); } @Test(enabled = false) /** Tests of performance. "enabled = false" for the standard testing. */ public void performanceCurveSensitivity() { long startTime, endTime; final int nbTest = 25; MultipleCurrencyAmount pvMC = MultipleCurrencyAmount.of(EUR, 0.0); final MultipleCurrencyMulticurveSensitivity pvcsExplicit = METHOD_HW.presentValueCurveSensitivity(SWAPTION_LONG_PAYER, HW_MULTICURVES); MultipleCurrencyMulticurveSensitivity pvcsMC = pvcsExplicit; final HullWhiteMonteCarloMethod methodMC = new HullWhiteMonteCarloMethod( new NormalRandomNumberGenerator(0.0, 1.0, new MersenneTwister()), NB_PATH); startTime = System.currentTimeMillis(); for (int looptest = 0; looptest < nbTest; looptest++) { pvMC = METHOD_HW_MONTECARLO.presentValue(SWAPTION_LONG_PAYER, EUR, HW_MULTICURVES); } endTime = System.currentTimeMillis(); System.out.println( nbTest + " swaption Hull-White Monte Carlo method (" + NB_PATH + " paths): " + (endTime - startTime) + " ms / price:" + pvMC.toString()); // Performance note: HW approximation: 03-Dec-2012: On Mac Pro 3.2 GHz Quad-Core Intel Xeon: 250 // ms for 25 swaptions (12500 paths). startTime = System.currentTimeMillis(); for (int looptest = 0; looptest < nbTest; looptest++) { pvcsMC = methodMC.presentValueCurveSensitivity(SWAPTION_LONG_PAYER, EUR, HW_MULTICURVES); } endTime = System.currentTimeMillis(); System.out.println( nbTest + " curve sensitivity swaption Hull-White MC method: (" + NB_PATH + " paths) " + (endTime - startTime) + " ms / risk:" + pvcsMC.toString()); // Performance note: curve sensitivity (40): 03-Dec-2012: On Mac Pro 3.2 GHz Quad-Core Intel // Xeon: 600 ms for 25 swaptions (12500 paths). } }