/** * 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); } }