/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.interestrate.swap.provider; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import java.util.ArrayList; import java.util.List; import org.testng.annotations.Test; import org.threeten.bp.Period; import org.threeten.bp.ZonedDateTime; import com.opengamma.analytics.env.AnalyticsEnvironment; import com.opengamma.analytics.financial.datasets.CalendarGBP; import com.opengamma.analytics.financial.instrument.annuity.AnnuityDefinition; import com.opengamma.analytics.financial.instrument.index.GeneratorAttributeIR; import com.opengamma.analytics.financial.instrument.index.GeneratorLegIbor; import com.opengamma.analytics.financial.instrument.index.IborIndex; import com.opengamma.analytics.financial.instrument.index.IndexIborMaster; import com.opengamma.analytics.financial.instrument.payment.CouponFloatingDefinition; import com.opengamma.analytics.financial.instrument.payment.CouponIborSpreadDefinition; import com.opengamma.analytics.financial.instrument.payment.PaymentDefinition; import com.opengamma.analytics.financial.instrument.swap.SwapDefinition; import com.opengamma.analytics.financial.interestrate.payments.derivative.Payment; import com.opengamma.analytics.financial.interestrate.swap.derivative.Swap; import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve; import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurve; import com.opengamma.analytics.financial.provider.calculator.discounting.PresentValueDiscountingCalculator; import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount; import com.opengamma.analytics.financial.schedule.ScheduleCalculator; import com.opengamma.analytics.math.curve.InterpolatedDoublesCurve; import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolatorFactory; import com.opengamma.analytics.math.interpolation.Interpolator1D; import com.opengamma.analytics.math.interpolation.Interpolator1DFactory; import com.opengamma.financial.convention.StubType; import com.opengamma.financial.convention.calendar.Calendar; import com.opengamma.financial.convention.daycount.DayCount; import com.opengamma.financial.convention.daycount.DayCounts; import com.opengamma.timeseries.precise.zdt.ImmutableZonedDateTimeDoubleTimeSeries; import com.opengamma.timeseries.precise.zdt.ZonedDateTimeDoubleTimeSeries; import com.opengamma.util.money.Currency; import com.opengamma.util.money.MultipleCurrencyAmount; import com.opengamma.util.test.TestGroup; import com.opengamma.util.time.DateUtils; /** * JPY swap pricing example */ @Test(groups = TestGroup.UNIT) public class JPYSwapExampleTest { private static final double NOTIONAL = 1e10; private static final double SPREAD = 5.625e-4; private static final Calendar baseCalendar = new CalendarGBP("GBP"); private static final DayCount ACT360 = DayCounts.ACT_360; private static final PresentValueDiscountingCalculator PVDC = PresentValueDiscountingCalculator.getInstance(); private static final ZonedDateTime TRADE_DATE = DateUtils.getUTCDate(2014, 9, 25); private static final ZonedDateTime SPOT_DATE = DateUtils.getUTCDate(2012, 5, 28); //This just ensures the the swap does start on the spot date private static final ZonedDateTime REF_DATE = ScheduleCalculator.getAdjustedDate(SPOT_DATE, -2, baseCalendar); private static final double[] JPY_LIBOR_KNOT_TIMES = new double[] {0.002739726027397, 0.019178082191781, 0.082191780821918, 0.164383561643836, 0.249315068493151, 0.498630136986301, 0.747945205479452, 1, 1.4986301369863, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 20, 25, 30, 40 }; private static final double[] JPY_LIBOR_ZERO_RATES = new double[] {0.0003983567270378, 0.00042993388819736, 0.00067505895387277, 0.00098218263696675, 0.00113130668887412, 0.00165679051768223, 0.00168257001572176, 0.00168506355236911, 0.00169161137040334, 0.00169823165591862, 0.00181755633568928, 0.00211702455993688, 0.00258936207292861, 0.00321085530122632, 0.00394155442314442, 0.00473508721069115, 0.00556448628047427, 0.00642596697808084, 0.00824342986342501, 0.0109670505368749, 0.0145690757463316, 0.0165410883373974, 0.0176675111617229, 0.0193229312672143 }; private static final double[] JPY_TONA_KNOT_TIMES = new double[] {0.002739726027397, 0.010958904109589, 0.03013698630137, 0.052054794520548, 0.068493150684932, 0.093150684931507, 0.175342465753425, 0.26027397260274, 0.345205479452055, 0.424657534246575, 0.50958904109589, 0.594520547945205, 0.673972602739726, 0.758904109589041, 0.841095890410959, 0.931506849315068, 1.01095890410959, 1.50958904109589, 2.01369863013699, 3.01369863013699, 4.01095890410959, 5.01643835616438, 6.01643835616438, 7.01643835616438, 8.01643835616438, 9.01643835616438, 10.0219178082192, 12.0191780821918, 15.0191780821918, 20.0246575342466, 25.027397260274, 30.0328767123288, 40.0383561643836 }; private static final double[] JPY_TONA_ZERO_RATES = new double[] {0.00072999927002197, 0.0007299981750037, 0.00066314695313092, 0.00064706696417918, 0.00065227308829961, 0.00064219125327023, 0.00063715670784559, 0.0006245856060934, 0.00061520134618019, 0.00061107386102671, 0.00061053345407946, 0.00060574710125102, 0.00060356977766275, 0.00059386362023272, 0.00059168152420429, 0.00060136570133023, 0.00060123121671593, 0.00061419067541201, 0.00062173581024485, 0.00063094292287806, 0.00083093896837371, 0.00121193817346258, 0.00167523291256022, 0.00220255228407544, 0.00277575634756025, 0.00340181606947232, 0.00412035000329854, 0.00569398049447993, 0.00831519889255234, 0.0118449175150898, 0.0137208249673008, 0.0147877697459616, 0.0163278079744461 }; private static final double[] JPY_LIBOR1M_KNOT_TIMES = new double[] {0.093150684931507, 0.175342465753425, 0.26027397260274, 0.50958904109589, 0.758904109589041, 1.01095890410959, 2.01369863013699, 3.01369863013699, 4.01095890410959, 5.01643835616438, 6.01643835616438, 7.01643835616438, 8.01643835616438, 9.01643835616438, 10.0219178082192, 12.0191780821918, 15.0191780821918, 20.0246575342466, 25.027397260274, 30.0328767123288, 40.0383561643836 }; private static final double[] JPY_LIBOR1M_ZERO_RATES = new double[] {0.00076039290463677, 0.00085541975703918, 0.00085746558022983, 0.00084703351338364, 0.00086660627173753, 0.00088558915472231, 0.00094250789602142, 0.00103628104751685, 0.00129852708371005, 0.00174873977892968, 0.00230710677319014, 0.00297319240390701, 0.0036869560813269, 0.00443427512954406, 0.0052289115325722, 0.0069071518209529, 0.00963132272163178, 0.0132127380932934, 0.0151274377392894, 0.0162307228355892, 0.0178372504710364 }; private static final double[] JPY_LIBOR3M_KNOT_TIMES = new double[] {0.26027397260274, 0.345205479452055, 0.424657534246575, 0.50958904109589, 0.594520547945205, 0.673972602739726, 0.758904109589041, 0.841095890410959, 0.931506849315068, 1.01095890410959, 2.01369863013699, 3.01369863013699, 4.01095890410959, 5.01643835616438, 6.01643835616438, 7.01643835616438, 8.01643835616438, 9.01643835616438, 10.0219178082192, 12.0191780821918, 15.0191780821918, 20.0246575342466, 25.027397260274, 30.0328767123288, 40.0383561643836 }; private static final double[] JPY_LIBOR3M_ZERO_RATES = new double[] {0.00118029715571636, 0.00121615239802092, 0.00121091360452696, 0.00121039784037458, 0.0012308486011696, 0.00122522063625026, 0.00122894767585858, 0.00123597797522836, 0.00123298977684211, 0.00123847455076616, 0.00125551354486698, 0.00132417586532975, 0.001598731796945, 0.00202382426358414, 0.00259507579856051, 0.00324843884416917, 0.00396209177364413, 0.00470918765974003, 0.00551604005478606, 0.00726879530751577, 0.00988817116221545, 0.0134557814886542, 0.0153555390663917, 0.0164277010011057, 0.0180361268910519 }; private static final YieldAndDiscountCurve JPY_LIBOR_CURVE; private static final YieldAndDiscountCurve JPY_TONA_CURVE; private static final YieldAndDiscountCurve JPY_LIBOR1M_CURVE; private static final YieldAndDiscountCurve JPY_LIBOR3M_CURVE; private static final MulticurveProviderDiscount SINGLE_CURVE; private static final MulticurveProviderDiscount TRIPLE_CURVE; private static final IndexIborMaster MASTER_IBOR = IndexIborMaster.getInstance(); private static final IborIndex JPYLIBOR1M = MASTER_IBOR.getIndex("JPYLIBOR1M"); private static final IborIndex JPYLIBOR3M = MASTER_IBOR.getIndex("JPYLIBOR3M"); private static final Currency CCY = JPYLIBOR1M.getCurrency(); private static final ZonedDateTimeDoubleTimeSeries TS_JPYLIBOR1M = ImmutableZonedDateTimeDoubleTimeSeries.ofUTC( new ZonedDateTime[] {DateUtils.getUTCDate(2014, 8, 26), DateUtils.getUTCDate(2014, 8, 27), DateUtils.getUTCDate(2014, 8, 28), DateUtils.getUTCDate(2014, 9, 23) }, new double[] {0.0009, 0.0009143, 0.0009, 0.00075}); private static final ZonedDateTimeDoubleTimeSeries TS_JPYLIBOR3M = ImmutableZonedDateTimeDoubleTimeSeries.ofUTC( new ZonedDateTime[] {DateUtils.getUTCDate(2014, 8, 26), DateUtils.getUTCDate(2014, 8, 27), DateUtils.getUTCDate(2014, 8, 28), DateUtils.getUTCDate(2014, 9, 23) }, new double[] {0.0012786, 0.0012786, 0.0012786, 0.0011929 }); private static final ZonedDateTimeDoubleTimeSeries[] TS_ARRAY_JPYLIBOR1M_JPYLIBOR3M = new ZonedDateTimeDoubleTimeSeries[] {TS_JPYLIBOR1M, TS_JPYLIBOR3M }; static { Interpolator1D interpolator = CombinedInterpolatorExtrapolatorFactory.getInterpolator(Interpolator1DFactory.LINEAR, Interpolator1DFactory.FLAT_EXTRAPOLATOR); JPY_LIBOR_CURVE = new YieldCurve("JPY Libor", new InterpolatedDoublesCurve(JPY_LIBOR_KNOT_TIMES, JPY_LIBOR_ZERO_RATES, interpolator, true)); JPY_TONA_CURVE = new YieldCurve("JPY Tona", new InterpolatedDoublesCurve(JPY_TONA_KNOT_TIMES, JPY_TONA_ZERO_RATES, interpolator, true)); JPY_LIBOR1M_CURVE = new YieldCurve("JPY Libor 1M", new InterpolatedDoublesCurve(JPY_LIBOR1M_KNOT_TIMES, JPY_LIBOR1M_ZERO_RATES, interpolator, true)); JPY_LIBOR3M_CURVE = new YieldCurve("JPY Libor 3M", new InterpolatedDoublesCurve(JPY_LIBOR3M_KNOT_TIMES, JPY_LIBOR3M_ZERO_RATES, interpolator, true)); SINGLE_CURVE = new MulticurveProviderDiscount(); SINGLE_CURVE.setOrReplaceCurve(CCY, JPY_LIBOR_CURVE); SINGLE_CURVE.setOrReplaceCurve(JPYLIBOR1M, JPY_LIBOR_CURVE); SINGLE_CURVE.setOrReplaceCurve(JPYLIBOR3M, JPY_LIBOR_CURVE); TRIPLE_CURVE = new MulticurveProviderDiscount(); TRIPLE_CURVE.setOrReplaceCurve(CCY, JPY_TONA_CURVE); TRIPLE_CURVE.setOrReplaceCurve(JPYLIBOR1M, JPY_LIBOR1M_CURVE); TRIPLE_CURVE.setOrReplaceCurve(JPYLIBOR3M, JPY_LIBOR3M_CURVE); } private static final double EPS = 1.0e-12; private void assertRelative(String message, double expected, double obtained, double relTol) { double ref = Math.max(Math.abs(obtained), 1.0); assertEquals(message, expected, obtained, ref * relTol); } /** * Test swap definition, pv and principal */ @Test public void testSwap() { boolean print = false; /* Build swap */ Period swapTenor = Period.ofYears(5); AnalyticsEnvironment.setInstance(AnalyticsEnvironment.getInstance().toBuilder().modelDayCount(DayCounts.ACT_365) .build()); GeneratorAttributeIR att = new GeneratorAttributeIR(swapTenor); GeneratorLegIbor leg1Gen = new GeneratorLegIbor("1M", CCY, JPYLIBOR1M, JPYLIBOR1M.getTenor(), JPYLIBOR1M.getSpotLag(), 0, JPYLIBOR1M.getBusinessDayConvention(), JPYLIBOR1M.isEndOfMonth(), StubType.SHORT_START, false, baseCalendar, baseCalendar); GeneratorLegIbor leg2Gen = new GeneratorLegIbor("3M", CCY, JPYLIBOR3M, JPYLIBOR3M.getTenor(), JPYLIBOR3M.getSpotLag(), 0, JPYLIBOR3M.getBusinessDayConvention(), JPYLIBOR3M.isEndOfMonth(), StubType.SHORT_START, false, baseCalendar, baseCalendar); AnnuityDefinition<?> leg1 = leg1Gen.generateInstrument(REF_DATE, SPREAD, -NOTIONAL, att); AnnuityDefinition<?> leg2 = leg2Gen.generateInstrument(REF_DATE, 0, NOTIONAL, att); SwapDefinition swapDef = new SwapDefinition(leg1, leg2); if (print) { System.out.println(swapDef); } /* Check swap definition */ AnnuityDefinition<? extends PaymentDefinition> firstLeg = swapDef.getFirstLeg(); int nFirst = firstLeg.getNumberOfPayments(); double[] expectedFirstFixingPeriod = new double[] { 0.08611111111111111, 0.08888888888888889, 0.08611111111111111, 0.08611111111111111, 0.09166666666666666, 0.08611111111111111, 0.08333333333333333, 0.08611111111111111, 0.08611111111111111, 0.07777777777777778, 0.09166666666666666, 0.08333333333333333, 0.08611111111111111, 0.09166666666666666, 0.08611111111111111, 0.09166666666666666, 0.08611111111111111, 0.08611111111111111, 0.08888888888888889, 0.08611111111111111, 0.08611111111111111, 0.08611111111111111, 0.08611111111111111, 0.08333333333333333, 0.09166666666666666, 0.08611111111111111, 0.08611111111111111, 0.08888888888888889, 0.08333333333333333, 0.08611111111111111, 0.09166666666666666, 0.08611111111111111, 0.08333333333333333, 0.08888888888888889, 0.08611111111111111, 0.08333333333333333, 0.08888888888888889, 0.08333333333333333, 0.08611111111111111, 0.09166666666666666, 0.08333333333333333, 0.09166666666666666, 0.08611111111111111, 0.08611111111111111, 0.08888888888888889, 0.08611111111111111, 0.08611111111111111, 0.09166666666666666, 0.08333333333333333, 0.08333333333333333, 0.09166666666666666, 0.08611111111111111, 0.08333333333333333, 0.08611111111111111, 0.08333333333333333, 0.09166666666666666, 0.08055555555555556, 0.08611111111111111, 0.08611111111111111, 0.09166666666666666}; double[] expectedFirstPaymentPeriod = new double[] {0.08611111111111111, 0.08888888888888889, 0.08055555555555556, 0.08611111111111111, 0.08611111111111111, 0.08333333333333333, 0.08333333333333333, 0.08611111111111111, 0.08611111111111111, 0.07777777777777778, 0.08888888888888889, 0.08055555555555556, 0.08611111111111111, 0.08611111111111111, 0.08333333333333333, 0.09166666666666666, 0.07777777777777778, 0.08611111111111111, 0.08888888888888889, 0.08055555555555556, 0.08611111111111111, 0.07777777777777778, 0.08611111111111111, 0.08333333333333333, 0.09166666666666666, 0.07777777777777778, 0.08611111111111111, 0.08888888888888889, 0.08055555555555556, 0.08611111111111111, 0.08611111111111111, 0.08333333333333333, 0.08333333333333333, 0.08611111111111111, 0.08055555555555556, 0.08333333333333333, 0.08888888888888889, 0.08055555555555556, 0.08611111111111111, 0.08611111111111111, 0.08333333333333333, 0.09166666666666666, 0.08055555555555556, 0.08333333333333333, 0.08888888888888889, 0.08055555555555556, 0.08333333333333333, 0.09166666666666666, 0.07777777777777778, 0.08333333333333333, 0.09166666666666666, 0.08055555555555556, 0.08333333333333333, 0.08611111111111111, 0.08333333333333333, 0.09166666666666666, 0.08055555555555556, 0.07777777777777778, 0.08611111111111111, 0.08888888888888889 }; double[] expectedFirstSpreadAmounts = new double[] {-484375, -500000, -453125, -484375, -484375, -468750, -468750, -484375, -484375, -437500, -500000, -453125, -484375, -484375, -468750, -515625, -437500, -484375, -500000, -453125, -484375, -437500, -484375, -468750, -515625, -437500, -484375, -500000, -453125, -484375, -484375, -468750, -468750, -484375, -453125, -468750, -500000, -453125, -484375, -484375, -468750, -515625, -453125, -468750, -500000, -453125, -468750, -515625, -437500, -468750, -515625, -453125, -468750, -484375, -468750, -515625, -453125, -437500, -484375, -500000 }; for (int i = 0; i < nFirst; ++i) { assertTrue(firstLeg.getNthPayment(i) instanceof CouponIborSpreadDefinition); CouponIborSpreadDefinition coupon = (CouponIborSpreadDefinition) firstLeg.getNthPayment(i); assertRelative("testSwap, first leg", expectedFirstFixingPeriod[i], coupon.getFixingPeriodAccrualFactor(), EPS); assertRelative("testSwap, first leg", expectedFirstPaymentPeriod[i], coupon.getPaymentYearFraction(), EPS); assertRelative("testSwap, first leg", expectedFirstSpreadAmounts[i], coupon.getSpreadAmount(), EPS); } AnnuityDefinition<? extends PaymentDefinition> secondLeg = swapDef.getSecondLeg(); int nSecond = secondLeg.getNumberOfPayments(); double[] expectedSecondFixingPeriod = new double[] { 0.25555555555555554, 0.25555555555555554, 0.25555555555555554, 0.25555555555555554, 0.25555555555555554, 0.25555555555555554, 0.25555555555555554, 0.25277777777777777, 0.25555555555555554, 0.25555555555555554, 0.25277777777777777, 0.25277777777777777, 0.25555555555555554, 0.2611111111111111, 0.25277777777777777, 0.25555555555555554, 0.25555555555555554, 0.25555555555555554, 0.25555555555555554, 0.25555555555555554 }; double[] expectedSecondPaymentPeriod = new double[] {0.25555555555555554, 0.25555555555555554, 0.25555555555555554, 0.24722222222222223, 0.25555555555555554, 0.25555555555555554, 0.25555555555555554, 0.24722222222222223, 0.25555555555555554, 0.25555555555555554, 0.25277777777777777, 0.25, 0.25555555555555554, 0.2611111111111111, 0.25277777777777777, 0.25555555555555554, 0.25277777777777777, 0.25, 0.25555555555555554, 0.25277777777777777 }; double expectedSecondSpreadAmounts = 0.0; for (int i = 0; i < nSecond; ++i) { assertTrue(secondLeg.getNthPayment(i) instanceof CouponIborSpreadDefinition); CouponIborSpreadDefinition coupon = (CouponIborSpreadDefinition) secondLeg.getNthPayment(i); // System.out.print(coupon.getFixingPeriodAccrualFactor() + ", "); assertRelative("testSwap, second leg", expectedSecondFixingPeriod[i], coupon.getFixingPeriodAccrualFactor(), EPS); assertRelative("testSwap, second leg", expectedSecondPaymentPeriod[i], coupon.getPaymentYearFraction(), EPS); assertRelative("testSwap, second leg", expectedSecondSpreadAmounts, coupon.getSpreadAmount(), EPS); } /* Check PV and principal */ SwapDefinition swapMd = removeUnclearedCoupon(swapDef, TRADE_DATE, baseCalendar, baseCalendar); Swap<? extends Payment, ? extends Payment> swap = swapMd.toDerivative(TRADE_DATE, TS_ARRAY_JPYLIBOR1M_JPYLIBOR3M); MultipleCurrencyAmount pv1 = swap.accept(PVDC, SINGLE_CURVE); assertRelative("testSwap, single curve PV", -1.3644528540332057E7, pv1.getAmount(CCY), EPS); //-1.370494358316157E7 if (print) { System.out.println("single curve PV: " + pv1); } MultipleCurrencyAmount pv2 = swap.accept(PVDC, TRIPLE_CURVE); assertRelative("testSwap, Multi-curve PV", -5993019.03856796, pv2.getAmount(CCY), EPS); //-5988005.581088446 if (print) { System.out.println("Multi-curve PV: " + pv2); } ZonedDateTime fixingDate1M = getLastFixing(leg1, TRADE_DATE); ZonedDateTime fixingDate3M = getLastFixing(leg2, TRADE_DATE); if (fixingDate1M.isBefore(TRADE_DATE) && fixingDate3M.isBefore(TRADE_DATE)) { double fixed1M = TS_JPYLIBOR1M.getValue(fixingDate1M); double fixed3M = TS_JPYLIBOR3M.getValue(fixingDate3M); double accrued1M = -ACT360.getDayCountFraction(fixingDate1M, TRADE_DATE) * NOTIONAL * (fixed1M + SPREAD); double accrued3M = ACT360.getDayCountFraction(fixingDate3M, TRADE_DATE) * NOTIONAL * (fixed3M); if (print) { System.out.println("accrued1M: " + accrued1M); System.out.println("accrued3M: " + accrued3M); } double pricipal = pv2.getAmount(CCY) - (accrued1M + accrued3M); if (print) { System.out.println("pricipal: " + pricipal); } assertRelative("testSwap, Principal", -5839769.03856796, pricipal, EPS); // -5834755.581088446 } AnalyticsEnvironment.setInstance(AnalyticsEnvironment.DEFAULT); } private SwapDefinition removeUnclearedCoupon(SwapDefinition swap, ZonedDateTime data, Calendar firstLegCalendar, Calendar secondLegCalendar) { AnnuityDefinition<? extends PaymentDefinition> firstLeg = removeUnclearedCoupon(swap.getFirstLeg(), data, firstLegCalendar); AnnuityDefinition<? extends PaymentDefinition> secondLeg = removeUnclearedCoupon(swap.getSecondLeg(), data, secondLegCalendar); return new SwapDefinition(firstLeg, secondLeg); } private AnnuityDefinition<PaymentDefinition> removeUnclearedCoupon( AnnuityDefinition<? extends PaymentDefinition> annuity, ZonedDateTime date, Calendar calendar) { PaymentDefinition[] payments = annuity.getPayments(); final List<PaymentDefinition> resultList = new ArrayList<>(); ZonedDateTime adjDate = ScheduleCalculator.getAdjustedDate(date, 3, calendar); for (final PaymentDefinition payment : payments) { if (!adjDate.isAfter(payment.getPaymentDate())) { resultList.add(payment); } } return new AnnuityDefinition<>(resultList.toArray(new PaymentDefinition[resultList.size()]), calendar); } /** * Print multicurve test, for debugging */ @Test(enabled = false) public void printMulticurve() { System.out.println(SINGLE_CURVE); System.out.println(CCY); System.out.println(JPYLIBOR1M.getDayCount() + "\t" + JPYLIBOR1M.getTenor() + "\t" + JPYLIBOR1M.getSpotLag()); System.out.println(JPYLIBOR3M.getDayCount() + "\t" + JPYLIBOR3M.getTenor() + "\t" + JPYLIBOR3M.getSpotLag()); System.out.println(JPYLIBOR3M); } private ZonedDateTime getLastFixing(AnnuityDefinition<?> leg, ZonedDateTime now) { for (PaymentDefinition p : leg.getPayments()) { if (p.getPaymentDate().isAfter(now)) { return ((CouponFloatingDefinition) p).getFixingDate(); } } throw new IllegalArgumentException("all payments in past"); } }