/** * Returns a periodic frequency backed by a period of years. * * @param years the number of years * @return the periodic frequency * @throws IllegalArgumentException if years is negative, zero or over 1,000 */ public static Frequency ofYears(int years) { if (years > MAX_YEARS) { throw new IllegalArgumentException("Years must not exceed 1,000"); } return new Frequency(Period.ofYears(years)); }
/** * A periodic frequency used by financial products that have a specific event every so often. * * <p>Frequency is primarily intended to be used to subdivide events within a year. * * <p>A frequency is allowed to be any non-negative period of days, weeks, month or years. This * class provides constants for common frequencies which are best used by static import. * * <p>A special value, 'Term', is provided for when there are no subdivisions of the entire term. * This is also know as 'zero-coupon' or 'once'. It is represented using the period 10,000 years, * which allows addition/subtraction to work, producing a date after the end of the term. * * <p>Each frequency is based on a {@link Period}. The months and years of the period are not * normalized, thus it is possible to have a frequency of 12 months and a different one of 1 year. * When used, standard date addition rules apply, thus there is no difference between them. Call * {@link #normalized()} to apply normalization. * * <p>The periodic frequency is often expressed as a number of events per year. The {@link * #eventsPerYear()} method can be used to obtain this for common frequencies. * * <h4>Usage</h4> * * {@code Frequency} implements {@code TemporalAmount} allowing it to be directly added to a date: * * <pre> * LocalDate later = baseDate.plus(frequency); * </pre> */ public final class Frequency implements TemporalAmount, Serializable { /** Serialization version. */ private static final long serialVersionUID = 1; /** The artificial maximum length of a normal tenor in years. */ private static final int MAX_YEARS = 1_000; /** The artificial maximum length of a normal tenor in months. */ private static final int MAX_MONTHS = MAX_YEARS * 12; /** The artificial length in years of the 'Term' frequency. */ private static final int TERM_YEARS = 10_000; /** * A periodic frequency of one day. Also known as daily. There are considered to be 364 events per * year with this frequency. */ public static final Frequency P1D = ofDays(1); /** * A periodic frequency of 1 week (7 days). Also known as weekly. There are considered to be 52 * events per year with this frequency. */ public static final Frequency P1W = ofWeeks(1); /** * A periodic frequency of 2 weeks (14 days). Also known as bi-weekly. There are considered to be * 26 events per year with this frequency. */ public static final Frequency P2W = ofWeeks(2); /** * A periodic frequency of 4 weeks (28 days). Also known as lunar. There are considered to be 13 * events per year with this frequency. */ public static final Frequency P4W = ofWeeks(4); /** * A periodic frequency of 13 weeks (91 days). There are considered to be 4 events per year with * this frequency. */ public static final Frequency P13W = ofWeeks(13); /** * A periodic frequency of 26 weeks (182 days). There are considered to be 2 events per year with * this frequency. */ public static final Frequency P26W = ofWeeks(26); /** * A periodic frequency of 52 weeks (364 days). There is considered to be 1 event per year with * this frequency. */ public static final Frequency P52W = ofWeeks(52); /** * A periodic frequency of 1 month. Also known as monthly. There are 12 events per year with this * frequency. */ public static final Frequency P1M = ofMonths(1); /** * A periodic frequency of 2 months. Also known as bi-monthly. There are 6 events per year with * this frequency. */ public static final Frequency P2M = ofMonths(2); /** * A periodic frequency of 3 months. Also known as quarterly. There are 4 events per year with * this frequency. */ public static final Frequency P3M = ofMonths(3); /** A periodic frequency of 4 months. There are 3 events per year with this frequency. */ public static final Frequency P4M = ofMonths(4); /** * A periodic frequency of 6 months. Also known as semi-annual. There are 2 events per year with * this frequency. */ public static final Frequency P6M = ofMonths(6); /** * A periodic frequency of 12 months (1 year). Also known as annual. There is 1 event per year * with this frequency. */ public static final Frequency P12M = ofMonths(12); /** * A periodic frequency matching the term. Also known as zero-coupon. This is represented using * the period 10,000 years. There are no events per year with this frequency. */ public static final Frequency TERM = new Frequency(Period.ofYears(TERM_YEARS), "Term"); /** The period of the frequency. */ private final Period period; /** The name of the frequency. */ private final String name; // ------------------------------------------------------------------------- /** * Obtains a periodic frequency from a {@code Period}. * * <p>The period normally consists of either days and weeks, or months and years. It must also be * positive and non-zero. * * <p>If the number of days is an exact multiple of 7 it will be converted to weeks. Months are * not normalized into years. * * <p>The maximum tenor length is 1,000 years. * * @param period the period to convert to a periodic frequency * @return the periodic frequency * @throws IllegalArgumentException if the period is negative, zero or too large */ public static Frequency of(Period period) { ArgChecker.notNull(period, "period"); int days = period.getDays(); long months = period.toTotalMonths(); if (months == 0 && days != 0) { return ofDays(days); } if (months > MAX_MONTHS) { throw new IllegalArgumentException("Period must not exceed 1000 years"); } return new Frequency(period); } /** * Returns a periodic frequency backed by a period of days. * * <p>If the number of days is an exact multiple of 7 it will be converted to weeks. * * @param days the number of days * @return the periodic frequency * @throws IllegalArgumentException if days is negative or zero */ public static Frequency ofDays(int days) { if (days % 7 == 0) { return ofWeeks(days / 7); } return new Frequency(Period.ofDays(days)); } /** * Returns a periodic frequency backed by a period of weeks. * * @param weeks the number of weeks * @return the periodic frequency * @throws IllegalArgumentException if weeks is negative or zero */ public static Frequency ofWeeks(int weeks) { return new Frequency(Period.ofWeeks(weeks), "P" + weeks + "W"); } /** * Returns a periodic frequency backed by a period of months. * * <p>Months are not normalized into years. * * @param months the number of months * @return the periodic frequency * @throws IllegalArgumentException if months is negative, zero or over 12,000 */ public static Frequency ofMonths(int months) { if (months > MAX_MONTHS) { throw new IllegalArgumentException("Months must not exceed 12,000"); } return new Frequency(Period.ofMonths(months)); } /** * Returns a periodic frequency backed by a period of years. * * @param years the number of years * @return the periodic frequency * @throws IllegalArgumentException if years is negative, zero or over 1,000 */ public static Frequency ofYears(int years) { if (years > MAX_YEARS) { throw new IllegalArgumentException("Years must not exceed 1,000"); } return new Frequency(Period.ofYears(years)); } // ------------------------------------------------------------------------- /** * Parses a formatted string representing the frequency. * * <p>The format can either be based on ISO-8601, such as 'P3M' or without the 'P' prefix e.g. * '2W'. * * <p>The period must be positive and non-zero. * * @param toParse the string representing the frequency * @return the frequency * @throws IllegalArgumentException if the frequency cannot be parsed */ @FromString public static Frequency parse(String toParse) { ArgChecker.notNull(toParse, "toParse"); if (toParse.equalsIgnoreCase("Term")) { return TERM; } String prefixed = toParse.startsWith("P") ? toParse : "P" + toParse; try { return Frequency.of(Period.parse(prefixed)); } catch (DateTimeParseException ex) { throw new IllegalArgumentException(ex); } } // ------------------------------------------------------------------------- /** * Creates a periodic frequency. * * @param period the period to represent */ private Frequency(Period period) { this(period, period.toString()); } /** * Creates a periodic frequency. * * @param period the period to represent * @param name the name */ private Frequency(Period period, String name) { ArgChecker.notNull(period, "period"); ArgChecker.isFalse(period.isZero(), "Period must not be zero"); ArgChecker.isFalse(period.isNegative(), "Period must not be negative"); this.period = period; this.name = name; } // safe deserialization private Object readResolve() { if (this.equals(TERM)) { return TERM; } return of(period); } // ------------------------------------------------------------------------- /** * Gets the underlying period of the frequency. * * @return the period */ public Period getPeriod() { return period; } /** * Checks if the periodic frequency is the 'Term' instance. * * <p>The term instance corresponds to there being no subdivisions of the entire term. * * @return true if this is the 'Term' instance */ public boolean isTerm() { return this == TERM; } // ------------------------------------------------------------------------- /** * Normalizes the months and years of this tenor. * * <p>This method returns a tenor of an equivalent length but with any number of months greater * than 12 normalized into a combination of months and years. * * @return the normalized tenor */ public Frequency normalized() { Period norm = period.normalized(); return (norm != period ? Frequency.of(norm) : this); } // ------------------------------------------------------------------------- /** * Checks if the periodic frequency is week-based. * * <p>A week-based frequency consists of an integral number of weeks. There must be no day, month * or year element. * * @return true if this is week-based */ public boolean isWeekBased() { return period.toTotalMonths() == 0 && period.getDays() % 7 == 0; } /** * Checks if the periodic frequency is month-based. * * <p>A month-based frequency consists of an integral number of months. Any year-based frequency * is also counted as month-based. There must be no day or week element. * * @return true if this is month-based */ public boolean isMonthBased() { return period.toTotalMonths() > 0 && period.getDays() == 0 && isTerm() == false; } // ------------------------------------------------------------------------- /** * Calculates the number of events that occur in a year. * * <p>The number of events per year is the number of times that the period occurs per year. Not * all periodic frequency instances can be converted to an integer events per year. All constants * declared on this class will return a result. * * <p>Month-based and year-based periodic frequencies are converted by dividing 12 by the number * of months. Only the following periodic frequencies return a value - P1M, P2M, P3M, P4M, P6M, * P1Y. * * <p>Day-based and week-based periodic frequencies are converted by dividing 364 by the number of * days. Only the following periodic frequencies return a value - P1D, P2D, P4D, P1W, P2W, P4W, * P13W, P26W, P52W. * * <p>The 'Term' periodic frequency returns zero. * * @return the number of events per year * @throws IllegalArgumentException if unable to calculate the number of events per year */ public int eventsPerYear() { if (isTerm()) { return 0; } long months = period.toTotalMonths(); int days = period.getDays(); if (isMonthBased()) { if (12 % months == 0) { return (int) (12 / months); } } else if (months == 0 && 364 % days == 0) { return (int) (364 / days); } throw new IllegalArgumentException("Unable to calculate events per year: " + this); } // ------------------------------------------------------------------------- /** * Exactly divides this frequency by another. * * <p>This calculates the integer division of this frequency by the specified frequency. If the * result is not an integer, an exception is thrown. * * <p>Month-based and year-based periodic frequencies are calculated by dividing the total number * of months. For example, P6M divided by P3M results in 2, and P2Y divided by P6M returns 4. * * <p>Day-based and week-based periodic frequencies are calculated by dividing the total number of * days. For example, P26W divided by P13W results in 2, and P2W divided by P1D returns 14. * * <p>The 'Term' frequency throws an exception. * * @param other the other frequency to divide into this one * @return this frequency divided by the other frequency * @throws IllegalArgumentException if the frequency does not exactly divide into this one */ public int exactDivide(Frequency other) { ArgChecker.notNull(other, "other"); if (isMonthBased() && other.isMonthBased()) { long paymentMonths = getPeriod().toTotalMonths(); long accrualMonths = other.getPeriod().toTotalMonths(); if ((paymentMonths % accrualMonths) == 0) { return Math.toIntExact(paymentMonths / accrualMonths); } } else if (period.toTotalMonths() == 0 && other.period.toTotalMonths() == 0) { long paymentDays = getPeriod().getDays(); long accrualDays = other.getPeriod().getDays(); if ((paymentDays % accrualDays) == 0) { return Math.toIntExact(paymentDays / accrualDays); } } throw new IllegalArgumentException( Messages.format("Frequency '{}' is not a multiple of '{}'", this, other)); } // ------------------------------------------------------------------------- /** * Gets the value of the specified unit. * * <p>This will return a value for the years, months and days units. Note that weeks are not * included. All other units throw an exception. * * <p>The 'Term' period is returned as a period of 10,000 years. * * <p>This method implements {@link TemporalAmount}. It is not intended to be called directly. * * @param unit the unit to query * @return the value of the unit * @throws UnsupportedTemporalTypeException if the unit is not supported */ @Override public long get(TemporalUnit unit) { return period.get(unit); } /** * Gets the unit of this periodic frequency. * * <p>This returns a list containing years, months and days. Note that weeks are not included. * * <p>The 'Term' period is returned as a period of 10,000 years. * * <p>This method implements {@link TemporalAmount}. It is not intended to be called directly. * * @return a list containing the years, months and days units */ @Override public List<TemporalUnit> getUnits() { return period.getUnits(); } /** * Adds the period of this frequency to the specified date. * * <p>This method implements {@link TemporalAmount}. It is not intended to be called directly. Use * {@link LocalDate#plus(TemporalAmount)} instead. * * @param temporal the temporal object to add to * @return the result with this frequency added * @throws DateTimeException if unable to add * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal addTo(Temporal temporal) { // special case for performance if (temporal instanceof LocalDate) { LocalDate date = (LocalDate) temporal; return date.plusMonths(period.toTotalMonths()).plusDays(period.getDays()); } return period.addTo(temporal); } /** * Subtracts the period of this frequency from the specified date. * * <p>This method implements {@link TemporalAmount}. It is not intended to be called directly. Use * {@link LocalDate#minus(TemporalAmount)} instead. * * @param temporal the temporal object to subtract from * @return the result with this frequency subtracted * @throws DateTimeException if unable to subtract * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal subtractFrom(Temporal temporal) { // special case for performance if (temporal instanceof LocalDate) { LocalDate date = (LocalDate) temporal; return date.minusMonths(period.toTotalMonths()).minusDays(period.getDays()); } return period.subtractFrom(temporal); } // ------------------------------------------------------------------------- /** * Checks if this periodic frequency equals another periodic frequency. * * <p>The comparison checks the frequency period. * * @param obj the other frequency, null returns false * @return true if equal */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } Frequency other = (Frequency) obj; return period.equals(other.period); } /** * Returns a suitable hash code for the periodic frequency. * * @return the hash code */ @Override public int hashCode() { return period.hashCode(); } /** * Returns a formatted string representing the periodic frequency. * * <p>The format is a combination of the quantity and unit, such as P1D, P2W, P3M, P4Y. The 'Term' * amount is returned as 'Term'. * * @return the formatted frequency */ @ToString @Override public String toString() { return name; } }
@Test public void consistencyTest() { final double tol = 1.e-12; final LocalDate optionExpiry = getNextIMMDate(TRADE_DATE).minusDays(1); final double timeToExpiry = ACT365F.yearFraction(TRADE_DATE, optionExpiry); final CDSAnalytic fwdCDX = FACTORY.makeCDX(optionExpiry, Period.ofYears(5)); final CDSAnalytic fwdStartingCDX = fwdCDX.withOffset(timeToExpiry); final double[] indexPUF = new double[] {0.0556, 0.0582, 0.0771, 0.0652}; final CDSAnalytic[] indexCDS = FACTORY.makeCDX(TRADE_DATE, INDEX_PILLARS); final IntrinsicIndexDataBundle adjCurves = PSA.adjustCurves(indexPUF, indexCDS, INDEX_COUPON, YIELD_CURVE, INTRINSIC_DATA); final double fwdSpread = INDEX_CAL.defaultAdjustedForwardSpread( fwdStartingCDX, timeToExpiry, YIELD_CURVE, adjCurves); final double fwdAnnuity = INDEX_CAL.indexAnnuity(fwdStartingCDX, YIELD_CURVE, adjCurves); final BlackIndexOptionPricer pricerWithFwd = new BlackIndexOptionPricer( fwdCDX, timeToExpiry, YIELD_CURVE, INDEX_COUPON, fwdSpread, fwdAnnuity); final BlackIndexOptionPricer pricerNoFwd = new BlackIndexOptionPricer(fwdCDX, timeToExpiry, YIELD_CURVE, INDEX_COUPON, adjCurves); final boolean[] payer = new boolean[] {true, false}; final double vol = 0.4; final double strikeSpread = 0.015; final ISDACompliantYieldCurve fwdYC = YIELD_CURVE.withOffset(timeToExpiry); final double strikePrice = CONVERTER.quotedSpreadToPUF(fwdCDX, INDEX_COUPON, fwdYC, strikeSpread); final IndexOptionStrike exPriceAmount = new ExerciseAmount(strikePrice); final IndexOptionStrike exSpreadAmount = new SpreadBasedStrike(strikeSpread); for (int i = 0; i < 2; ++i) { /** Consistency between option pricing methods */ final double premFromPrice = pricerWithFwd.getOptionPremium(exPriceAmount, vol, payer[i]); final double premFromSpread = pricerWithFwd.getOptionPremium(exSpreadAmount, vol, payer[i]); final double premFromPriceBare = pricerWithFwd.getOptionPriceForPriceQuotedIndex(strikePrice, vol, payer[i]); final double premFromSpreadBare = pricerWithFwd.getOptionPriceForSpreadQuotedIndex(strikeSpread, vol, payer[i]); assertEquals(premFromPrice, premFromSpread, tol); assertEquals(premFromSpread, premFromPriceBare, tol); assertEquals(premFromPriceBare, premFromSpreadBare, tol); /** Consistency between constructors */ final double PremFromPriceNoFwd = pricerNoFwd.getOptionPremium(exPriceAmount, vol, payer[i]); assertEquals(premFromPrice, PremFromPriceNoFwd, tol); /** Consistency with implied vol */ final double vol1 = pricerWithFwd.getImpliedVolatility(exPriceAmount, premFromPrice, payer[i]); final double vol2 = pricerWithFwd.getImpliedVolatility(exSpreadAmount, premFromSpread, payer[i]); final double vol3 = pricerWithFwd.getImpliedVolForExercisePrice(strikePrice, premFromPriceBare, payer[i]); final double vol4 = pricerWithFwd.getImpliedVolForSpreadStrike(strikeSpread, premFromSpreadBare, payer[i]); assertEquals(vol, vol1, tol); assertEquals(vol, vol2, tol); assertEquals(vol, vol3, tol); assertEquals(vol, vol4, tol); } }
/** * Test class for the replication method for CMS caplet/floorlet using a SABR smile with * extrapolation. */ @Test public class CapFloorCMSSABRExtrapolationRightReplicationMethodTest { 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 HolidayCalendar CALENDAR = MulticurveProviderDiscountDataSets.getEURCalendar(); private static final SABRInterestRateParameters SABR_PARAMETER = SABRDataSets.createSABR1(); private static final GeneratorSwapFixedIbor EUR1YEURIBOR6M = GeneratorSwapFixedIborMaster.getInstance().getGenerator("EUR1YEURIBOR6M", CALENDAR); private static final SABRSwaptionProviderDiscount SABR_MULTICURVES = new SABRSwaptionProviderDiscount(MULTICURVES, SABR_PARAMETER, EUR1YEURIBOR6M); // Swap 5Y private static final BusinessDayConvention BUSINESS_DAY = BusinessDayConventions.MODIFIED_FOLLOWING; private static final boolean IS_EOM = true; private static final Period ANNUITY_TENOR = Period.ofYears(5); private static final ZonedDateTime SETTLEMENT_DATE = DateUtils.getUTCDate(2020, 4, 28); // Fixed leg: Semi-annual bond private static final Period FIXED_PAYMENT_PERIOD = Period.ofMonths(6); private static final DayCount FIXED_DAY_COUNT = DayCounts.THIRTY_U_360; private static final double RATE = 0.0325; private static final boolean FIXED_IS_PAYER = true; private static final AnnuityCouponFixedDefinition FIXED_ANNUITY = AnnuityCouponFixedDefinition.from( EUR, SETTLEMENT_DATE, ANNUITY_TENOR, FIXED_PAYMENT_PERIOD, CALENDAR, FIXED_DAY_COUNT, BUSINESS_DAY, IS_EOM, 1.0, RATE, FIXED_IS_PAYER); // Ibor leg: quarterly money private static final AnnuityCouponIborDefinition IBOR_ANNUITY = AnnuityCouponIborDefinition.from( SETTLEMENT_DATE, ANNUITY_TENOR, 1.0, EURIBOR6M, !FIXED_IS_PAYER, CALENDAR); // CMS coupon construction private static final IndexSwap CMS_INDEX = new IndexSwap(FIXED_PAYMENT_PERIOD, FIXED_DAY_COUNT, EURIBOR6M, ANNUITY_TENOR, CALENDAR); private static final SwapFixedIborDefinition SWAP_DEFINITION = new SwapFixedIborDefinition(FIXED_ANNUITY, IBOR_ANNUITY); private static final ZonedDateTime FIXING_DATE = ScheduleCalculator.getAdjustedDate(SETTLEMENT_DATE, -EURIBOR6M.getSpotLag(), CALENDAR); private static final ZonedDateTime ACCRUAL_START_DATE = SETTLEMENT_DATE; // pre-fixed private static final ZonedDateTime ACCRUAL_END_DATE = ScheduleCalculator.getAdjustedDate( ACCRUAL_START_DATE, FIXED_PAYMENT_PERIOD, BUSINESS_DAY, CALENDAR); private static final ZonedDateTime PAYMENT_DATE = ACCRUAL_END_DATE; private static final DayCount PAYMENT_DAY_COUNT = DayCounts.ACT_360; private static final double ACCRUAL_FACTOR = DayCountUtils.yearFraction(PAYMENT_DAY_COUNT, ACCRUAL_START_DATE, ACCRUAL_END_DATE); private static final double NOTIONAL = 10000000; // 10m private static final CouponCMSDefinition CMS_COUPON_RECEIVER_DEFINITION = CouponCMSDefinition.from( PAYMENT_DATE, ACCRUAL_START_DATE, ACCRUAL_END_DATE, ACCRUAL_FACTOR, NOTIONAL, FIXING_DATE, SWAP_DEFINITION, CMS_INDEX); private static final CouponCMSDefinition CMS_COUPON_PAYER_DEFINITION = CouponCMSDefinition.from( PAYMENT_DATE, ACCRUAL_START_DATE, ACCRUAL_END_DATE, ACCRUAL_FACTOR, -NOTIONAL, FIXING_DATE, SWAP_DEFINITION, CMS_INDEX); // Cap/Floor construction private static final double STRIKE = 0.04; private static final boolean IS_CAP = true; private static final CapFloorCMSDefinition CMS_CAP_LONG_DEFINITION = CapFloorCMSDefinition.from(CMS_COUPON_RECEIVER_DEFINITION, STRIKE, IS_CAP); private static final CapFloorCMSDefinition CMS_CAP_SHORT_DEFINITION = CapFloorCMSDefinition.from(CMS_COUPON_PAYER_DEFINITION, STRIKE, IS_CAP); private static final CapFloorCMSDefinition CMS_CAP_0_DEFINITION = CapFloorCMSDefinition.from(CMS_COUPON_RECEIVER_DEFINITION, 0.0, IS_CAP); // to derivatives private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2010, 8, 18); private static final CouponCMS CMS_COUPON = (CouponCMS) CMS_COUPON_RECEIVER_DEFINITION.toDerivative(REFERENCE_DATE); private static final CapFloorCMS CMS_CAP_0 = (CapFloorCMS) CMS_CAP_0_DEFINITION.toDerivative(REFERENCE_DATE); private static final CapFloorCMS CMS_CAP_LONG = (CapFloorCMS) CMS_CAP_LONG_DEFINITION.toDerivative(REFERENCE_DATE); private static final CapFloorCMS CMS_CAP_SHORT = (CapFloorCMS) CMS_CAP_SHORT_DEFINITION.toDerivative(REFERENCE_DATE); // Calculators & methods private static final CapFloorCMSSABRReplicationMethod METHOD_STANDARD_CAP = CapFloorCMSSABRReplicationMethod.getDefaultInstance(); private static final CouponCMSSABRReplicationMethod METHOD_STANDARD_CPN = CouponCMSSABRReplicationMethod.getInstance(); private static final CouponCMSDiscountingMethod METHOD_DSC_CPN = CouponCMSDiscountingMethod.getInstance(); private static final double CUT_OFF_STRIKE = 0.10; private static final double MU = 2.50; private static final CapFloorCMSSABRExtrapolationRightReplicationMethod METHOD_EXTRAPOLATION_CAP = new CapFloorCMSSABRExtrapolationRightReplicationMethod(CUT_OFF_STRIKE, MU); private static final CouponCMSSABRExtrapolationRightReplicationMethod METHOD_EXTRAPOLATION_CPN = new CouponCMSSABRExtrapolationRightReplicationMethod(CUT_OFF_STRIKE, MU); // Calculators private static final PresentValueSABRSwaptionRightExtrapolationCalculator PVSSXC = new PresentValueSABRSwaptionRightExtrapolationCalculator(CUT_OFF_STRIKE, MU); private static final PresentValueCurveSensitivitySABRSwaptionRightExtrapolationCalculator PVCSSSXC = new PresentValueCurveSensitivitySABRSwaptionRightExtrapolationCalculator( CUT_OFF_STRIKE, MU); private static final PresentValueSABRSensitivitySABRSwaptionRightExtrapolationCalculator PVSSSSXC = new PresentValueSABRSensitivitySABRSwaptionRightExtrapolationCalculator( CUT_OFF_STRIKE, MU); private static final double SHIFT = 1.0E-6; private static final ParameterSensitivityParameterCalculator<SABRSwaptionProviderInterface> PS_SS_C = new ParameterSensitivityParameterCalculator<>(PVCSSSXC); private static final ParameterSensitivitySABRSwaptionDiscountInterpolatedFDCalculator PS_SS_FDC = new ParameterSensitivitySABRSwaptionDiscountInterpolatedFDCalculator(PVSSXC, SHIFT); private static final double TOLERANCE_PV = 1.0E-2; private static final double TOLERANCE_PV_DELTA = 5.0E+3; // 0.01 currency unit for 1 bp. /** * Test the present value for a CMS coupon with pricing by replication in the SABR with * extrapolation framework. The present value is tested against hard-coded value and cap of strike * 0. */ public void presentValue() { // CMS cap/floor with strike 0 has the same price as a CMS coupon. final double priceCouponStd = METHOD_STANDARD_CPN.presentValue(CMS_COUPON, SABR_MULTICURVES).getAmount(EUR).getAmount(); final double rateCouponStd = priceCouponStd / (CMS_COUPON.getPaymentYearFraction() * CMS_COUPON.getNotional() * MULTICURVES.getDiscountFactor(EUR, CMS_COUPON.getPaymentTime())); final double priceCouponExtra = METHOD_EXTRAPOLATION_CPN .presentValue(CMS_COUPON, SABR_MULTICURVES) .getAmount(EUR) .getAmount(); final double rateCouponExtra = priceCouponExtra / (CMS_COUPON.getPaymentYearFraction() * CMS_COUPON.getNotional() * MULTICURVES.getDiscountFactor(EUR, CMS_COUPON.getPaymentTime())); final double priceCouponNoAdj = METHOD_DSC_CPN.presentValue(CMS_COUPON, MULTICURVES).getAmount(EUR).getAmount(); final double rateCouponNoAdj = priceCouponNoAdj / (CMS_COUPON.getPaymentYearFraction() * CMS_COUPON.getNotional() * MULTICURVES.getDiscountFactor(EUR, CMS_COUPON.getPaymentTime())); assertEquals( "Extrapolation: comparison with standard method", rateCouponStd > rateCouponExtra, true); assertEquals( "Extrapolation: comparison with no convexity adjustment", rateCouponExtra > rateCouponNoAdj, true); final double rateCouponExtraExpected = 0.0189864; // From previous run. assertEquals("Extrapolation: hard-coded value", rateCouponExtraExpected, rateCouponExtra, 1E-6); final double priceCap0Extra = METHOD_EXTRAPOLATION_CAP .presentValue(CMS_CAP_0, SABR_MULTICURVES) .getAmount(EUR) .getAmount(); assertEquals( "Extrapolation: CMS coupon vs Cap 0", priceCouponExtra, priceCap0Extra, TOLERANCE_PV); } /** * Tests the price of CMS coupon and cap/floor using replication in the SABR framework. Method v * Calculator. */ public void presentValueMethodVsCalculator() { final double pvMethod = METHOD_EXTRAPOLATION_CAP .presentValue(CMS_CAP_LONG, SABR_MULTICURVES) .getAmount(EUR) .getAmount(); final double pvCalculator = CMS_CAP_LONG.accept(PVSSXC, SABR_MULTICURVES).getAmount(EUR).getAmount(); assertEquals( "CMS cap/floor SABR: Present value : method vs calculator", pvMethod, pvCalculator, TOLERANCE_PV); } /** * Test the present value for a CMS cap with pricing by replication in the SABR with extrapolation * framework. The present value is tested against hard-coded value and a long/short parity is * tested. */ public void presentValueReplicationCap() { // CMS cap/floor with strike 0 has the same price as a CMS coupon. final double priceCapLongStd = METHOD_STANDARD_CAP.presentValue(CMS_CAP_LONG, SABR_MULTICURVES).getAmount(EUR).getAmount(); final double priceCapLongExtra = METHOD_EXTRAPOLATION_CAP .presentValue(CMS_CAP_LONG, SABR_MULTICURVES) .getAmount(EUR) .getAmount(); final double priceCapShortExtra = METHOD_EXTRAPOLATION_CAP .presentValue(CMS_CAP_SHORT, SABR_MULTICURVES) .getAmount(EUR) .getAmount(); assertEquals( "CMS cap by replication - Extrapolation: comparison with standard method", priceCapLongStd > priceCapLongExtra, true); final double priceCapExtraExpected = 30696.572; // From previous run. assertEquals( "CMS cap by replication - Extrapolation: hard-coded value", priceCapExtraExpected, priceCapLongExtra, TOLERANCE_PV); assertEquals( "CMS cap by replication - Extrapolation: long/short parity", -priceCapShortExtra, priceCapLongExtra, TOLERANCE_PV); } /** * Test the present value rate sensitivity for a CMS cap with pricing by replication in the SABR * with extrapolation framework. */ public void presentValueCurveSensitivity() { final MultipleCurrencyParameterSensitivity pvpsCapLongExact = PS_SS_C.calculateSensitivity( CMS_CAP_LONG, SABR_MULTICURVES, SABR_MULTICURVES.getMulticurveProvider().getAllNames()); final MultipleCurrencyParameterSensitivity pvpsCapLongFD = PS_SS_FDC.calculateSensitivity(CMS_CAP_LONG, SABR_MULTICURVES); AssertSensitivityObjects.assertEquals( "SwaptionPhysicalFixedIborSABRMethod: presentValueCurveSensitivity ", pvpsCapLongExact, pvpsCapLongFD, TOLERANCE_PV_DELTA); final MultipleCurrencyParameterSensitivity pvpsCapShortExact = PS_SS_C.calculateSensitivity( CMS_CAP_SHORT, SABR_MULTICURVES, SABR_MULTICURVES.getMulticurveProvider().getAllNames()); final MultipleCurrencyParameterSensitivity pvpsCapShortFD = PS_SS_FDC.calculateSensitivity(CMS_CAP_SHORT, SABR_MULTICURVES); AssertSensitivityObjects.assertEquals( "SwaptionPhysicalFixedIborSABRMethod: presentValueCurveSensitivity ", pvpsCapShortExact, pvpsCapShortFD, TOLERANCE_PV_DELTA); } /** * Test the present value rate sensitivity for a CMS cap with pricing by replication in the SABR * with extrapolation framework. Method v Calculator. */ public void presentValueCurveSensitivityMethodVsCalculator() { final MultipleCurrencyMulticurveSensitivity pvcsMethod = METHOD_EXTRAPOLATION_CAP.presentValueCurveSensitivity(CMS_CAP_LONG, SABR_MULTICURVES); final MultipleCurrencyMulticurveSensitivity pvcsCalculator = CMS_CAP_LONG.accept(PVCSSSXC, SABR_MULTICURVES); AssertSensitivityObjects.assertEquals( "CMS cap/floor SABR: Present value : method vs calculator", pvcsMethod, pvcsCalculator, TOLERANCE_PV_DELTA); } /** Tests the cap present value SABR parameters sensitivity vs finite difference. */ public void presentValueSABRSensitivity() { final double pv = METHOD_EXTRAPOLATION_CAP .presentValue(CMS_CAP_LONG, SABR_MULTICURVES) .getAmount(EUR) .getAmount(); final PresentValueSABRSensitivityDataBundle pvsCapLong = METHOD_EXTRAPOLATION_CAP.presentValueSABRSensitivity(CMS_CAP_LONG, SABR_MULTICURVES); // SABR sensitivity vs finite difference final double shift = 0.0001; final double shiftAlpha = 0.00001; final double maturity = CMS_CAP_LONG .getUnderlyingSwap() .getFixedLeg() .getNthPayment( CMS_CAP_LONG.getUnderlyingSwap().getFixedLeg().getNumberOfPayments() - 1) .getPaymentTime() - CMS_CAP_LONG.getSettlementTime(); final DoublesPair expectedExpiryTenor = DoublesPair.of(CMS_CAP_LONG.getFixingTime(), maturity); // Alpha sensitivity vs finite difference computation final SABRInterestRateParameters sabrParameterAlphaBumped = SABRDataSets.createSABR1AlphaBumped(shiftAlpha); final SABRSwaptionProviderDiscount sabrBundleAlphaBumped = new SABRSwaptionProviderDiscount(MULTICURVES, sabrParameterAlphaBumped, EUR1YEURIBOR6M); final double pvLongPayerAlphaBumped = METHOD_EXTRAPOLATION_CAP .presentValue(CMS_CAP_LONG, sabrBundleAlphaBumped) .getAmount(EUR) .getAmount(); final double expectedAlphaSensi = (pvLongPayerAlphaBumped - pv) / shiftAlpha; assertEquals("Number of alpha sensitivity", pvsCapLong.getAlpha().getMap().keySet().size(), 1); assertEquals( "Alpha sensitivity expiry/tenor", pvsCapLong.getAlpha().getMap().keySet().contains(expectedExpiryTenor), true); assertEquals( "Alpha sensitivity value", expectedAlphaSensi, pvsCapLong.getAlpha().getMap().get(expectedExpiryTenor), TOLERANCE_PV_DELTA); // Rho sensitivity vs finite difference computation final SABRInterestRateParameters sabrParameterRhoBumped = SABRDataSets.createSABR1RhoBumped(); final SABRSwaptionProviderDiscount sabrBundleRhoBumped = new SABRSwaptionProviderDiscount(MULTICURVES, sabrParameterRhoBumped, EUR1YEURIBOR6M); final double pvLongPayerRhoBumped = METHOD_EXTRAPOLATION_CAP .presentValue(CMS_CAP_LONG, sabrBundleRhoBumped) .getAmount(EUR) .getAmount(); final double expectedRhoSensi = (pvLongPayerRhoBumped - pv) / shift; assertEquals("Number of rho sensitivity", pvsCapLong.getRho().getMap().keySet().size(), 1); assertEquals( "Rho sensitivity expiry/tenor", pvsCapLong.getRho().getMap().keySet().contains(expectedExpiryTenor), true); assertEquals( "Rho sensitivity value", expectedRhoSensi, pvsCapLong.getRho().getMap().get(expectedExpiryTenor), TOLERANCE_PV_DELTA); // Alpha sensitivity vs finite difference computation final SABRInterestRateParameters sabrParameterNuBumped = SABRDataSets.createSABR1NuBumped(); final SABRSwaptionProviderDiscount sabrBundleNuBumped = new SABRSwaptionProviderDiscount(MULTICURVES, sabrParameterNuBumped, EUR1YEURIBOR6M); final double pvLongPayerNuBumped = METHOD_EXTRAPOLATION_CAP .presentValue(CMS_CAP_LONG, sabrBundleNuBumped) .getAmount(EUR) .getAmount(); final double expectedNuSensi = (pvLongPayerNuBumped - pv) / shift; assertEquals("Number of nu sensitivity", pvsCapLong.getNu().getMap().keySet().size(), 1); assertTrue( "Nu sensitivity expiry/tenor", pvsCapLong.getNu().getMap().keySet().contains(expectedExpiryTenor)); assertEquals( "Nu sensitivity value", expectedNuSensi, pvsCapLong.getNu().getMap().get(expectedExpiryTenor), TOLERANCE_PV_DELTA); } /** Tests the coupon present value SABR parameters sensitivity vs finite difference. */ public void presentValueSABRSensitivityCoupon() { final double pv = METHOD_EXTRAPOLATION_CPN .presentValue(CMS_COUPON, SABR_MULTICURVES) .getAmount(EUR) .getAmount(); final PresentValueSABRSensitivityDataBundle pvsCpn = METHOD_EXTRAPOLATION_CPN.presentValueSABRSensitivity(CMS_COUPON, SABR_MULTICURVES); // SABR sensitivity vs finite difference final double shift = 0.0001; final double shiftAlpha = 0.00001; final double maturity = CMS_COUPON .getUnderlyingSwap() .getFixedLeg() .getNthPayment( CMS_COUPON.getUnderlyingSwap().getFixedLeg().getNumberOfPayments() - 1) .getPaymentTime() - CMS_COUPON.getSettlementTime(); final DoublesPair expectedExpiryTenor = DoublesPair.of(CMS_COUPON.getFixingTime(), maturity); // Alpha sensitivity vs finite difference computation final SABRInterestRateParameters sabrParameterAlphaBumped = SABRDataSets.createSABR1AlphaBumped(shiftAlpha); final SABRSwaptionProviderDiscount sabrBundleAlphaBumped = new SABRSwaptionProviderDiscount(MULTICURVES, sabrParameterAlphaBumped, EUR1YEURIBOR6M); final double pvLongPayerAlphaBumped = METHOD_EXTRAPOLATION_CPN .presentValue(CMS_COUPON, sabrBundleAlphaBumped) .getAmount(EUR) .getAmount(); final double expectedAlphaSensi = (pvLongPayerAlphaBumped - pv) / shiftAlpha; assertEquals("Number of alpha sensitivity", pvsCpn.getAlpha().getMap().keySet().size(), 1); assertEquals( "Alpha sensitivity expiry/tenor", pvsCpn.getAlpha().getMap().keySet().contains(expectedExpiryTenor), true); assertEquals( "Alpha sensitivity value", expectedAlphaSensi, pvsCpn.getAlpha().getMap().get(expectedExpiryTenor), TOLERANCE_PV_DELTA); // Rho sensitivity vs finite difference computation final SABRInterestRateParameters sabrParameterRhoBumped = SABRDataSets.createSABR1RhoBumped(); final SABRSwaptionProviderDiscount sabrBundleRhoBumped = new SABRSwaptionProviderDiscount(MULTICURVES, sabrParameterRhoBumped, EUR1YEURIBOR6M); final double pvLongPayerRhoBumped = METHOD_EXTRAPOLATION_CPN .presentValue(CMS_COUPON, sabrBundleRhoBumped) .getAmount(EUR) .getAmount(); final double expectedRhoSensi = (pvLongPayerRhoBumped - pv) / shift; assertEquals("Number of rho sensitivity", pvsCpn.getRho().getMap().keySet().size(), 1); assertEquals( "Rho sensitivity expiry/tenor", pvsCpn.getRho().getMap().keySet().contains(expectedExpiryTenor), true); assertEquals( "Rho sensitivity value", expectedRhoSensi, pvsCpn.getRho().getMap().get(expectedExpiryTenor), TOLERANCE_PV_DELTA); // Nu sensitivity vs finite difference computation final SABRInterestRateParameters sabrParameterNuBumped = SABRDataSets.createSABR1NuBumped(); final SABRSwaptionProviderDiscount sabrBundleNuBumped = new SABRSwaptionProviderDiscount(MULTICURVES, sabrParameterNuBumped, EUR1YEURIBOR6M); final double pvLongPayerNuBumped = METHOD_EXTRAPOLATION_CPN .presentValue(CMS_COUPON, sabrBundleNuBumped) .getAmount(EUR) .getAmount(); final double expectedNuSensi = (pvLongPayerNuBumped - pv) / shift; assertEquals("Number of nu sensitivity", pvsCpn.getNu().getMap().keySet().size(), 1); assertTrue( "Nu sensitivity expiry/tenor", pvsCpn.getNu().getMap().keySet().contains(expectedExpiryTenor)); assertEquals( "Nu sensitivity value", expectedNuSensi, pvsCpn.getNu().getMap().get(expectedExpiryTenor), TOLERANCE_PV_DELTA); } /** Tests the present value SABR parameters sensitivity: Method vs Calculator. */ public void presentValueSABRSensitivityMethodVsCalculator() { final PresentValueSABRSensitivityDataBundle pvssMethod = METHOD_EXTRAPOLATION_CAP.presentValueSABRSensitivity(CMS_CAP_LONG, SABR_MULTICURVES); final PresentValueSABRSensitivityDataBundle pvssCalculator = CMS_CAP_LONG.accept(PVSSSSXC, SABR_MULTICURVES); assertEquals( "CMS cap/floor SABR: Present value SABR sensitivity: method vs calculator", pvssMethod, pvssCalculator); } /** Tests the present value strike sensitivity: Cap. */ public void presentValueStrikeSensitivityCap() { final double[] strikes = new double[] {0.0001, 0.0010, 0.0050, 0.0100, 0.0200, 0.0400, 0.0500}; final int nbStrikes = strikes.length; final double shift = 1.0E-5; final double[] errorRelative = new double[nbStrikes]; for (int loopstrike = 0; loopstrike < nbStrikes; loopstrike++) { final CapFloorCMSDefinition cmsCapDefinition = CapFloorCMSDefinition.from(CMS_COUPON_RECEIVER_DEFINITION, strikes[loopstrike], IS_CAP); final CapFloorCMSDefinition cmsCapShiftUpDefinition = CapFloorCMSDefinition.from( CMS_COUPON_RECEIVER_DEFINITION, strikes[loopstrike] + shift, IS_CAP); final CapFloorCMSDefinition cmsCapShiftDoDefinition = CapFloorCMSDefinition.from( CMS_COUPON_RECEIVER_DEFINITION, strikes[loopstrike] - shift, IS_CAP); final CapFloorCMS cmsCap = (CapFloorCMS) cmsCapDefinition.toDerivative(REFERENCE_DATE); final CapFloorCMS cmsCapShiftUp = (CapFloorCMS) cmsCapShiftUpDefinition.toDerivative(REFERENCE_DATE); final CapFloorCMS cmsCapShiftDo = (CapFloorCMS) cmsCapShiftDoDefinition.toDerivative(REFERENCE_DATE); final double pvShiftUp = METHOD_EXTRAPOLATION_CAP .presentValue(cmsCapShiftUp, SABR_MULTICURVES) .getAmount(EUR) .getAmount(); final double pvShiftDo = METHOD_EXTRAPOLATION_CAP .presentValue(cmsCapShiftDo, SABR_MULTICURVES) .getAmount(EUR) .getAmount(); final double sensiExpected = (pvShiftUp - pvShiftDo) / (2 * shift); final double sensiComputed = METHOD_EXTRAPOLATION_CAP.presentValueStrikeSensitivity(cmsCap, SABR_MULTICURVES); errorRelative[loopstrike] = (sensiExpected - sensiComputed) / sensiExpected; assertEquals( "CMS cap/floor SABR: Present value strike sensitivity " + loopstrike, 0, errorRelative[loopstrike], 5.0E-3); } } /** * Tests to estimate the impact of mu on the CMS coupon pricing. "enabled = false" for the * standard testing. */ public void testPriceMultiMu() { final double[] mu = new double[] {1.10, 1.30, 1.55, 2.25, 3.50, 6.00, 15.0}; final int nbMu = mu.length; final double priceCouponStd = METHOD_STANDARD_CPN.presentValue(CMS_COUPON, SABR_MULTICURVES).getAmount(EUR).getAmount(); @SuppressWarnings("unused") final double rateCouponStd = priceCouponStd / (CMS_COUPON.getPaymentYearFraction() * CMS_COUPON.getNotional() * MULTICURVES.getDiscountFactor(EUR, CMS_COUPON.getPaymentTime())); final double[] priceCouponExtra = new double[nbMu]; final double[] rateCouponExtra = new double[nbMu]; for (int loopmu = 0; loopmu < nbMu; loopmu++) { final CouponCMSSABRExtrapolationRightReplicationMethod methodExtrapolation = new CouponCMSSABRExtrapolationRightReplicationMethod(CUT_OFF_STRIKE, mu[loopmu]); priceCouponExtra[loopmu] = methodExtrapolation.presentValue(CMS_COUPON, SABR_MULTICURVES).getAmount(EUR).getAmount(); rateCouponExtra[loopmu] = priceCouponExtra[loopmu] / (CMS_COUPON.getPaymentYearFraction() * CMS_COUPON.getNotional() * MULTICURVES.getDiscountFactor(EUR, CMS_COUPON.getPaymentTime())); } final double priceCouponNoAdj = METHOD_DSC_CPN.presentValue(CMS_COUPON, MULTICURVES).getAmount(EUR).getAmount(); final double rateCouponNoAdj = priceCouponNoAdj / (CMS_COUPON.getPaymentYearFraction() * CMS_COUPON.getNotional() * MULTICURVES.getDiscountFactor(EUR, CMS_COUPON.getPaymentTime())); for (int loopmu = 1; loopmu < nbMu; loopmu++) { assertTrue( "Extrapolation: comparison with standard method", rateCouponExtra[loopmu - 1] > rateCouponExtra[loopmu]); } assertTrue( "Extrapolation: comparison with standard method", rateCouponExtra[nbMu - 1] > rateCouponNoAdj); } }
@SuppressWarnings("unused") @Test public void limitTest() { final LocalDate optionExpiry = getNextIMMDate(TRADE_DATE).minusDays(1); final double timeToExpiry = ACT365F.yearFraction(TRADE_DATE, optionExpiry); final CDSAnalytic fwdCDX = FACTORY.makeCDX(optionExpiry, Period.ofYears(5)); final CDSAnalytic fwdStartingCDX = fwdCDX.withOffset(timeToExpiry); final double[] indexPUF = new double[] {0.0556, 0.0582, 0.0771, 0.0652}; final CDSAnalytic[] indexCDS = FACTORY.makeCDX(TRADE_DATE, INDEX_PILLARS); final IntrinsicIndexDataBundle adjCurves = PSA.adjustCurves(indexPUF, indexCDS, INDEX_COUPON, YIELD_CURVE, INTRINSIC_DATA); final double fwdSpread = INDEX_CAL.defaultAdjustedForwardSpread( fwdStartingCDX, timeToExpiry, YIELD_CURVE, adjCurves); final double fwdAnnuity = INDEX_CAL.indexAnnuity(fwdStartingCDX, YIELD_CURVE, adjCurves); final BlackIndexOptionPricer pricerWithFwd = new BlackIndexOptionPricer( fwdCDX, timeToExpiry, YIELD_CURVE, INDEX_COUPON, fwdSpread, fwdAnnuity); final double modStrikeLimit = INDEX_COUPON + fwdCDX.getLGD() / fwdAnnuity; final double vol = 0.4; final double payLargeSpLimit = fwdAnnuity * BlackFormulaRepository.price(fwdSpread, modStrikeLimit, timeToExpiry, vol, true); final double recLargeSpLimit = fwdAnnuity * BlackFormulaRepository.price(fwdSpread, modStrikeLimit, timeToExpiry, vol, false); final double largeStrikeSp = 75.0; final double payLargeStrikeSp = pricerWithFwd.getOptionPriceForSpreadQuotedIndex(largeStrikeSp, vol, true); final double recLargeStrikeSp = pricerWithFwd.getOptionPriceForSpreadQuotedIndex(largeStrikeSp, vol, false); assertEquals(payLargeSpLimit, payLargeStrikeSp, 1.e-12); assertEquals( recLargeSpLimit, recLargeStrikeSp, 1.e-3); // larger strike spread ends up with failure in root-finding /** Exception expected */ final double negativeTime = -0.5; try { new BlackIndexOptionPricer( fwdCDX, negativeTime, YIELD_CURVE, INDEX_COUPON, fwdSpread, fwdAnnuity); throw new RuntimeException(); } catch (final Exception e) { assertEquals("timeToExpiry must be positive. Value given " + negativeTime, e.getMessage()); } try { new BlackIndexOptionPricer( fwdStartingCDX, timeToExpiry, YIELD_CURVE, INDEX_COUPON, fwdSpread, fwdAnnuity); throw new RuntimeException(); } catch (final Exception e) { assertEquals("fwdCDS should be a Forward CDS", e.getMessage()); } final double negativeCoupon = -150.0 * 1.0e-4; try { new BlackIndexOptionPricer( fwdCDX, timeToExpiry, YIELD_CURVE, negativeCoupon, fwdSpread, fwdAnnuity); throw new RuntimeException(); } catch (final Exception e) { assertEquals("indexCoupon must be positive", e.getMessage()); } final double negativeFwdSp = -0.5; try { new BlackIndexOptionPricer( fwdCDX, timeToExpiry, YIELD_CURVE, INDEX_COUPON, negativeFwdSp, fwdAnnuity); throw new RuntimeException(); } catch (final Exception e) { assertEquals("defaultAdjustedFwdSpread must be positive", e.getMessage()); } final double negativeAnn = -0.3; try { new BlackIndexOptionPricer( fwdCDX, timeToExpiry, YIELD_CURVE, INDEX_COUPON, fwdSpread, negativeAnn); throw new RuntimeException(); } catch (final Exception e) { assertEquals("pvFwdAnnuity must be positive", e.getMessage()); } final double largeAnn = fwdCDX.getProtectionEnd() * 2.0; try { new BlackIndexOptionPricer( fwdCDX, timeToExpiry, YIELD_CURVE, INDEX_COUPON, fwdSpread, largeAnn); throw new RuntimeException(); } catch (final Exception e) { assertEquals( "Value of annuity of " + largeAnn + " is greater than length (in years) of forward CDS. Annuity should be given for unit notional", e.getMessage()); } final double smallStrike = -0.9; try { pricerWithFwd.getOptionPriceForPriceQuotedIndex(smallStrike, vol, true); throw new RuntimeException(); } catch (final Exception e) { assertTrue(e instanceof IllegalArgumentException); } final double largeStrike = 0.9; try { pricerWithFwd.getOptionPriceForPriceQuotedIndex(largeStrike, vol, true); throw new RuntimeException(); } catch (final Exception e) { assertTrue(e instanceof IllegalArgumentException); } try { pricerWithFwd.getOptionPriceForSpreadQuotedIndex(smallStrike, vol, true); throw new RuntimeException(); } catch (final Exception e) { assertTrue(e instanceof IllegalArgumentException); } }
/** * Build of curve in several blocks with relevant Jacobian matrices. EUR: discounting/ON forward; * USD: discounting/ON forward. Standard test data set: 2014-03-10 */ public class StandardDataSetsEURUSDForex { private static final ZonedDateTime NOW = DateUtils.getUTCDate(2014, 3, 10); private static final Interpolator1D INTERPOLATOR_LINEAR = CombinedInterpolatorExtrapolatorFactory.getInterpolator( Interpolator1DFactory.LINEAR, Interpolator1DFactory.FLAT_EXTRAPOLATOR, Interpolator1DFactory.FLAT_EXTRAPOLATOR); private static final LastTimeCalculator MATURITY_CALCULATOR = LastTimeCalculator.getInstance(); private static final double TOLERANCE_ROOT = 1.0E-10; private static final int STEP_MAX = 100; private static final HolidayCalendar TARGET = HolidayCalendars.EUTA; private static final HolidayCalendar NYC = HolidayCalendars.SAT_SUN; private static final Currency EUR = Currency.EUR; private static final Currency USD = Currency.USD; private static final double FX_EURUSD = 1.38775; private static final FxMatrix FX_MATRIX = FxMatrix.builder().addRate(EUR, USD, FX_EURUSD).build(); private static final double NOTIONAL = 1.0; private static final GeneratorSwapFixedON GENERATOR_OIS_EUR = GeneratorSwapFixedONMaster.getInstance().getGenerator("EUR1YEONIA", TARGET); private static final IndexON EUREONIA = GENERATOR_OIS_EUR.getIndex(); private static final GeneratorSwapFixedON GENERATOR_OIS_USD = GeneratorSwapFixedONMaster.getInstance().getGenerator("USD1YFEDFUND", NYC); private static final IndexON USDFEDFUND = GENERATOR_OIS_USD.getIndex(); private static final IndexON INDEX_ON_EUR = GENERATOR_OIS_EUR.getIndex(); private static final IndexON INDEX_ON_USD = GENERATOR_OIS_USD.getIndex(); private static final GeneratorDepositON GENERATOR_DEPOSIT_ON_EUR = new GeneratorDepositON("EUR Deposit ON", EUR, TARGET, INDEX_ON_EUR.getDayCount()); private static final GeneratorDepositON GENERATOR_DEPOSIT_ON_USD = new GeneratorDepositON("USD Deposit ON", USD, TARGET, INDEX_ON_USD.getDayCount()); private static final GeneratorForexSwap GENERATOR_FX_EURUSD = new GeneratorForexSwap( "EURUSD", EUR, USD, TARGET, 2, GENERATOR_OIS_EUR.getBusinessDayConvention(), true); private static final ZonedDateTimeDoubleTimeSeries TS_EMPTY = ImmutableZonedDateTimeDoubleTimeSeries.ofEmptyUTC(); private static final ZonedDateTimeDoubleTimeSeries TS_ON_USD_WITH_TODAY = ImmutableZonedDateTimeDoubleTimeSeries.ofUTC( new ZonedDateTime[] { DateUtils.getUTCDate(2011, 9, 27), DateUtils.getUTCDate(2011, 9, 28) }, new double[] {0.07, 0.08}); private static final ZonedDateTimeDoubleTimeSeries TS_ON_USD_WITHOUT_TODAY = ImmutableZonedDateTimeDoubleTimeSeries.ofUTC( new ZonedDateTime[] { DateUtils.getUTCDate(2011, 9, 27), DateUtils.getUTCDate(2011, 9, 28) }, new double[] {0.07, 0.08}); private static final ZonedDateTimeDoubleTimeSeries[] TS_FIXED_OIS_USD_WITH_TODAY = new ZonedDateTimeDoubleTimeSeries[] {TS_EMPTY, TS_ON_USD_WITH_TODAY}; private static final ZonedDateTimeDoubleTimeSeries[] TS_FIXED_OIS_USD_WITHOUT_TODAY = new ZonedDateTimeDoubleTimeSeries[] {TS_EMPTY, TS_ON_USD_WITHOUT_TODAY}; private static final String CURVE_NAME_DSC_EUR = "EUR Dsc"; private static final String CURVE_NAME_DSC_USD = "USD Dsc"; /** Market values for the dsc USD curve */ private static final double[] DSC_USD_MARKET_QUOTES = new double[] { 0.0015, 0.0015, 7.9E-4, 7.8E-4, 8.3E-4, 0.0009, 0.0010, 0.00112, 0.0030525, 0.00686, 0.0109, 0.01465, 0.01782, 0.02048, 0.02264, 0.02445, 0.02597 }; /** Generators for the dsc USD curve */ private static final GeneratorInstrument<? extends GeneratorAttribute>[] DSC_USD_GENERATORS = new GeneratorInstrument<?>[] { GENERATOR_DEPOSIT_ON_USD, GENERATOR_DEPOSIT_ON_USD, GENERATOR_OIS_USD, GENERATOR_OIS_USD, GENERATOR_OIS_USD, GENERATOR_OIS_USD, GENERATOR_OIS_USD, GENERATOR_OIS_USD, GENERATOR_OIS_USD, GENERATOR_OIS_USD, GENERATOR_OIS_USD, GENERATOR_OIS_USD, GENERATOR_OIS_USD, GENERATOR_OIS_USD, GENERATOR_OIS_USD, GENERATOR_OIS_USD, GENERATOR_OIS_USD }; /** Tenors for the dsc USD curve */ private static final Period[] DSC_USD_TENOR = new Period[] { Period.ofDays(0), Period.ofDays(1), Period.ofMonths(1), Period.ofMonths(2), Period.ofMonths(3), Period.ofMonths(6), Period.ofMonths(9), Period.ofYears(1), Period.ofYears(2), Period.ofYears(3), Period.ofYears(4), Period.ofYears(5), Period.ofYears(6), Period.ofYears(7), Period.ofYears(8), Period.ofYears(9), Period.ofYears(10) }; private static final GeneratorAttributeIR[] DSC_USD_ATTR = new GeneratorAttributeIR[DSC_USD_TENOR.length]; static { for (int loopins = 0; loopins < 2; loopins++) { DSC_USD_ATTR[loopins] = new GeneratorAttributeIR(DSC_USD_TENOR[loopins], Period.ZERO); } for (int loopins = 2; loopins < DSC_USD_TENOR.length; loopins++) { DSC_USD_ATTR[loopins] = new GeneratorAttributeIR(DSC_USD_TENOR[loopins]); } } /** Market values for the dsc EUR curve - calibrated on OIS */ private static final double[] DSC_EUR_MARKET_QUOTES = new double[] { 0.001725, 0.00170, 0.00196, 0.00193, 0.00186, 0.00181, 0.00172, 0.00174, 0.002015, 0.00321, 0.00491, 0.0068, 0.01061, 0.01539 }; /** Generators for the dsc EUR curve */ private static final GeneratorInstrument<? extends GeneratorAttribute>[] DSC_EUR_GENERATORS = new GeneratorInstrument<?>[] { GENERATOR_DEPOSIT_ON_EUR, GENERATOR_DEPOSIT_ON_EUR, GENERATOR_OIS_EUR, GENERATOR_OIS_EUR, GENERATOR_OIS_EUR, GENERATOR_OIS_EUR, GENERATOR_OIS_EUR, GENERATOR_OIS_EUR, GENERATOR_OIS_EUR, GENERATOR_OIS_EUR, GENERATOR_OIS_EUR, GENERATOR_OIS_EUR, GENERATOR_OIS_EUR, GENERATOR_OIS_EUR }; /** Tenors for the dsc EUR curve */ private static final Period[] DSC_EUR_TENOR = new Period[] { Period.ofDays(0), Period.ofDays(1), Period.ofMonths(1), Period.ofMonths(2), Period.ofMonths(3), Period.ofMonths(6), Period.ofMonths(9), Period.ofYears(1), Period.ofYears(2), Period.ofYears(3), Period.ofYears(4), Period.ofYears(5), Period.ofYears(7), Period.ofYears(10) }; private static final GeneratorAttributeIR[] DSC_EUR_ATTR = new GeneratorAttributeIR[DSC_EUR_TENOR.length]; static { for (int loopins = 0; loopins < 2; loopins++) { DSC_EUR_ATTR[loopins] = new GeneratorAttributeIR(DSC_EUR_TENOR[loopins], Period.ZERO); } for (int loopins = 2; loopins < DSC_EUR_TENOR.length; loopins++) { DSC_EUR_ATTR[loopins] = new GeneratorAttributeIR(DSC_EUR_TENOR[loopins]); } } /** Market values for the FX EUR USD FX swaps */ private static final double[] DSC_EURUSD_MARKET_FORWARD = new double[] { 1.387673, 1.387625, 1.3875895, 1.38755, 1.387566, 1.387777, 1.39303, 1.406789, 1.427726, 1.4525105 }; private static final int NB_DSC_EURUSD_QUOTES = DSC_EURUSD_MARKET_FORWARD.length; private static final double[] DSC_EURUSD_MARKET_QUOTES = new double[NB_DSC_EURUSD_QUOTES]; static { for (int loopquote = 0; loopquote < NB_DSC_EURUSD_QUOTES; loopquote++) { DSC_EURUSD_MARKET_QUOTES[loopquote] = DSC_EURUSD_MARKET_FORWARD[loopquote] - FX_EURUSD; } } /** Generators for the dsc FX curve */ private static final GeneratorInstrument<? extends GeneratorAttribute>[] DSC_EURUSD_GENERATORS = new GeneratorInstrument<?>[] { GENERATOR_FX_EURUSD, GENERATOR_FX_EURUSD, GENERATOR_FX_EURUSD, GENERATOR_FX_EURUSD, GENERATOR_FX_EURUSD, GENERATOR_FX_EURUSD, GENERATOR_FX_EURUSD, GENERATOR_FX_EURUSD, GENERATOR_FX_EURUSD, GENERATOR_FX_EURUSD }; /** Tenors for the dsc FX curve */ private static final Period[] DSC_EURUSD_TENOR = new Period[] { Period.ofMonths(1), Period.ofMonths(2), Period.ofMonths(3), Period.ofMonths(6), Period.ofMonths(9), Period.ofYears(1), Period.ofYears(2), Period.ofYears(3), Period.ofYears(4), Period.ofYears(5) }; private static final GeneratorAttribute[] DSC_EURUSD_ATTR = new GeneratorAttribute[DSC_EUR_TENOR.length]; static { for (int loopins = 0; loopins < DSC_EURUSD_TENOR.length; loopins++) { DSC_EURUSD_ATTR[loopins] = new GeneratorAttributeFX(DSC_EURUSD_TENOR[loopins], FX_MATRIX); } } /** Standard USD discounting curve instrument definitions */ private static final InstrumentDefinition<?>[] DEFINITIONS_DSC_USD; /** Standard EUR discounting curve instrument definitions */ private static final InstrumentDefinition<?>[] DEFINITIONS_DSC_EUR; /** Standard EUR discounting curve instrument definitions */ private static final InstrumentDefinition<?>[] DEFINITIONS_DSC_FX; /** Units of curves */ private static final int[] NB_UNITS = new int[] {2, 2, 2}; private static final int NB_BLOCKS = NB_UNITS.length; private static final InstrumentDefinition<?>[][][][] DEFINITIONS_UNITS = new InstrumentDefinition<?>[NB_BLOCKS][][][]; private static final GeneratorYDCurve[][][] GENERATORS_UNITS = new GeneratorYDCurve[NB_BLOCKS][][]; private static final String[][][] NAMES_UNITS = new String[NB_BLOCKS][][]; private static final MulticurveProviderDiscount MULTICURVE_KNOWN_DATA = new MulticurveProviderDiscount(FX_MATRIX); private static final LinkedHashMap<String, Currency> DSC_MAP = new LinkedHashMap<>(); private static final LinkedHashMap<String, IndexON[]> FWD_ON_MAP = new LinkedHashMap<>(); private static final LinkedHashMap<String, IborIndex[]> FWD_IBOR_MAP = new LinkedHashMap<>(); static { DEFINITIONS_DSC_USD = getDefinitions(DSC_USD_MARKET_QUOTES, DSC_USD_GENERATORS, DSC_USD_ATTR); DEFINITIONS_DSC_EUR = getDefinitions(DSC_EUR_MARKET_QUOTES, DSC_EUR_GENERATORS, DSC_EUR_ATTR); DEFINITIONS_DSC_FX = getDefinitions(DSC_EURUSD_MARKET_QUOTES, DSC_EURUSD_GENERATORS, DSC_EURUSD_ATTR); for (int loopblock = 0; loopblock < NB_BLOCKS; loopblock++) { DEFINITIONS_UNITS[loopblock] = new InstrumentDefinition<?>[NB_UNITS[loopblock]][][]; GENERATORS_UNITS[loopblock] = new GeneratorYDCurve[NB_UNITS[loopblock]][]; NAMES_UNITS[loopblock] = new String[NB_UNITS[loopblock]][]; } DEFINITIONS_UNITS[0] = new InstrumentDefinition<?>[NB_UNITS[0]][][]; DEFINITIONS_UNITS[0][0] = new InstrumentDefinition<?>[][] {DEFINITIONS_DSC_USD}; DEFINITIONS_UNITS[0][1] = new InstrumentDefinition<?>[][] {DEFINITIONS_DSC_EUR}; DEFINITIONS_UNITS[1] = new InstrumentDefinition<?>[NB_UNITS[0]][][]; DEFINITIONS_UNITS[1][0] = new InstrumentDefinition<?>[][] {DEFINITIONS_DSC_USD}; DEFINITIONS_UNITS[1][1] = new InstrumentDefinition<?>[][] {DEFINITIONS_DSC_FX}; DEFINITIONS_UNITS[2] = new InstrumentDefinition<?>[NB_UNITS[0]][][]; DEFINITIONS_UNITS[2][0] = new InstrumentDefinition<?>[][] {DEFINITIONS_DSC_EUR}; DEFINITIONS_UNITS[2][1] = new InstrumentDefinition<?>[][] {DEFINITIONS_DSC_FX}; final GeneratorYDCurve genIntLin = new GeneratorCurveYieldInterpolated(MATURITY_CALCULATOR, INTERPOLATOR_LINEAR); GENERATORS_UNITS[0] = new GeneratorYDCurve[NB_UNITS[0]][]; GENERATORS_UNITS[0][0] = new GeneratorYDCurve[] {genIntLin}; GENERATORS_UNITS[0][1] = new GeneratorYDCurve[] {genIntLin}; GENERATORS_UNITS[1] = new GeneratorYDCurve[NB_UNITS[0]][]; GENERATORS_UNITS[1][0] = new GeneratorYDCurve[] {genIntLin}; GENERATORS_UNITS[1][1] = new GeneratorYDCurve[] {genIntLin}; GENERATORS_UNITS[2] = new GeneratorYDCurve[NB_UNITS[0]][]; GENERATORS_UNITS[2][0] = new GeneratorYDCurve[] {genIntLin}; GENERATORS_UNITS[2][1] = new GeneratorYDCurve[] {genIntLin}; NAMES_UNITS[0] = new String[NB_UNITS[0]][]; NAMES_UNITS[0][0] = new String[] {CURVE_NAME_DSC_USD}; NAMES_UNITS[0][1] = new String[] {CURVE_NAME_DSC_EUR}; NAMES_UNITS[1] = new String[NB_UNITS[0]][]; NAMES_UNITS[1][0] = new String[] {CURVE_NAME_DSC_USD}; NAMES_UNITS[1][1] = new String[] {CURVE_NAME_DSC_EUR}; NAMES_UNITS[2] = new String[NB_UNITS[0]][]; NAMES_UNITS[2][0] = new String[] {CURVE_NAME_DSC_EUR}; NAMES_UNITS[2][1] = new String[] {CURVE_NAME_DSC_USD}; DSC_MAP.put(CURVE_NAME_DSC_USD, USD); DSC_MAP.put(CURVE_NAME_DSC_EUR, EUR); FWD_ON_MAP.put(CURVE_NAME_DSC_USD, new IndexON[] {INDEX_ON_USD}); FWD_ON_MAP.put(CURVE_NAME_DSC_EUR, new IndexON[] {INDEX_ON_EUR}); } @SuppressWarnings("unchecked") public static InstrumentDefinition<?>[] getDefinitions( final double[] marketQuotes, @SuppressWarnings("rawtypes") final GeneratorInstrument[] generators, final GeneratorAttribute[] attribute) { final InstrumentDefinition<?>[] definitions = new InstrumentDefinition<?>[marketQuotes.length]; for (int loopmv = 0; loopmv < marketQuotes.length; loopmv++) { definitions[loopmv] = generators[loopmv].generateInstrument( NOW, marketQuotes[loopmv], NOTIONAL, attribute[loopmv]); } return definitions; } private static List<Pair<MulticurveProviderDiscount, CurveBuildingBlockBundle>> CURVES_PAR_SPREAD_MQ_WITHOUT_TODAY_BLOCK = new ArrayList<>(); // Calculators private static final ParSpreadMarketQuoteDiscountingCalculator PSMQDC = ParSpreadMarketQuoteDiscountingCalculator.getInstance(); private static final ParSpreadMarketQuoteCurveSensitivityDiscountingCalculator PSMQCSDC = ParSpreadMarketQuoteCurveSensitivityDiscountingCalculator.getInstance(); private static final MulticurveDiscountBuildingRepository CURVE_BUILDING_REPOSITORY = new MulticurveDiscountBuildingRepository(TOLERANCE_ROOT, TOLERANCE_ROOT, STEP_MAX); static { for (int loopblock = 0; loopblock < NB_BLOCKS; loopblock++) { CURVES_PAR_SPREAD_MQ_WITHOUT_TODAY_BLOCK.add( makeCurvesFromDefinitions( DEFINITIONS_UNITS[loopblock], GENERATORS_UNITS[loopblock], NAMES_UNITS[loopblock], MULTICURVE_KNOWN_DATA, PSMQDC, PSMQCSDC, false)); } } public static Pair<MulticurveProviderDiscount, CurveBuildingBlockBundle> getCurvesEUROisUSDOis() { return CURVES_PAR_SPREAD_MQ_WITHOUT_TODAY_BLOCK.get(0); } public static Pair<MulticurveProviderDiscount, CurveBuildingBlockBundle> getCurvesUSDOisEURFx() { return CURVES_PAR_SPREAD_MQ_WITHOUT_TODAY_BLOCK.get(1); } public static Pair<MulticurveProviderDiscount, CurveBuildingBlockBundle> getCurvesEUROisUSDFx() { return CURVES_PAR_SPREAD_MQ_WITHOUT_TODAY_BLOCK.get(2); } /** * Returns the array of overnight index used in the curve data set. * * @return The array: USDFEDFUND, EUREOINIA */ public static IndexON[] indexONArray() { return new IndexON[] {USDFEDFUND, EUREONIA}; } /** * Returns the array of calendars used in the curve data set. * * @return The array: NYC, TARGET */ public static HolidayCalendar[] calendarArray() { return new HolidayCalendar[] {NYC, TARGET}; } @SuppressWarnings("unchecked") private static Pair<MulticurveProviderDiscount, CurveBuildingBlockBundle> makeCurvesFromDefinitions( final InstrumentDefinition<?>[][][] definitions, final GeneratorYDCurve[][] curveGenerators, final String[][] curveNames, final MulticurveProviderDiscount knownData, final InstrumentDerivativeVisitor<ParameterProviderInterface, Double> calculator, final InstrumentDerivativeVisitor<ParameterProviderInterface, MulticurveSensitivity> sensitivityCalculator, final boolean withToday) { final int nUnits = definitions.length; final MultiCurveBundle<GeneratorYDCurve>[] curveBundles = new MultiCurveBundle[nUnits]; for (int i = 0; i < nUnits; i++) { final int nCurves = definitions[i].length; final SingleCurveBundle<GeneratorYDCurve>[] singleCurves = new SingleCurveBundle[nCurves]; for (int j = 0; j < nCurves; j++) { final int nInstruments = definitions[i][j].length; final InstrumentDerivative[] derivatives = new InstrumentDerivative[nInstruments]; final double[] rates = new double[nInstruments]; for (int k = 0; k < nInstruments; k++) { derivatives[k] = convert(definitions[i][j][k], withToday); rates[k] = initialGuess(definitions[i][j][k]); } final GeneratorYDCurve generator = curveGenerators[i][j].finalGenerator(derivatives); final double[] initialGuess = generator.initialGuess(rates); singleCurves[j] = new SingleCurveBundle<>(curveNames[i][j], derivatives, initialGuess, generator); } curveBundles[i] = new MultiCurveBundle<>(singleCurves); } return CURVE_BUILDING_REPOSITORY.makeCurvesFromDerivatives( curveBundles, knownData, DSC_MAP, FWD_IBOR_MAP, FWD_ON_MAP, calculator, sensitivityCalculator); } private static InstrumentDerivative convert( final InstrumentDefinition<?> instrument, final boolean withToday) { InstrumentDerivative ird; if (instrument instanceof SwapFixedONDefinition) { ird = ((SwapFixedONDefinition) instrument).toDerivative(NOW, getTSSwapFixedON(withToday)); } else { ird = instrument.toDerivative(NOW); } return ird; } private static ZonedDateTimeDoubleTimeSeries[] getTSSwapFixedON(final Boolean withToday) { return withToday ? TS_FIXED_OIS_USD_WITH_TODAY : TS_FIXED_OIS_USD_WITHOUT_TODAY; } private static double initialGuess(final InstrumentDefinition<?> instrument) { if (instrument instanceof SwapFixedONDefinition) { return ((SwapFixedONDefinition) instrument).getFixedLeg().getNthPayment(0).getRate(); } if (instrument instanceof SwapFixedIborDefinition) { return ((SwapFixedIborDefinition) instrument).getFixedLeg().getNthPayment(0).getRate(); } if (instrument instanceof ForwardRateAgreementDefinition) { return ((ForwardRateAgreementDefinition) instrument).getRate(); } if (instrument instanceof CashDefinition) { return ((CashDefinition) instrument).getRate(); } if (instrument instanceof InterestRateFutureTransactionDefinition) { return 1 - ((InterestRateFutureTransactionDefinition) instrument).getTradePrice(); } return 0.01; } }
/** * Test for curve calibration in USD and EUR. The USD curve is obtained by OIS and the EUR one by FX * Swaps from USD. */ @Test public class CalibrationZeroRateUsdEur2OisFxTest { private static final LocalDate VAL_DATE = LocalDate.of(2015, 11, 2); private static final CurveInterpolator INTERPOLATOR_LINEAR = CurveInterpolators.LINEAR; private static final CurveExtrapolator EXTRAPOLATOR_FLAT = CurveExtrapolators.FLAT; private static final DayCount CURVE_DC = ACT_365F; private static final String SCHEME = "CALIBRATION"; /** Curve names */ private static final String USD_DSCON_STR = "USD-DSCON-OIS"; private static final CurveName USD_DSCON_CURVE_NAME = CurveName.of(USD_DSCON_STR); private static final String EUR_DSC_STR = "EUR-DSC-FX"; private static final CurveName EUR_DSC_CURVE_NAME = CurveName.of(EUR_DSC_STR); /** Curves associations to currencies and indices. */ private static final Map<CurveName, Currency> DSC_NAMES = new HashMap<>(); private static final Map<CurveName, Set<Index>> IDX_NAMES = new HashMap<>(); private static final Map<Index, LocalDateDoubleTimeSeries> TS = new HashMap<>(); static { DSC_NAMES.put(USD_DSCON_CURVE_NAME, USD); Set<Index> usdFedFundSet = new HashSet<>(); usdFedFundSet.add(USD_FED_FUND); IDX_NAMES.put(USD_DSCON_CURVE_NAME, usdFedFundSet); } /** Data FX * */ private static final FxRate FX_RATE_EUR_USD = FxRate.of(EUR, USD, 1.10); /** Data for USD-DSCON curve */ /* Market values */ private static final double[] USD_DSC_MARKET_QUOTES = new double[] { 0.0016, 0.0022, 0.0013, 0.0016, 0.0020, 0.0026, 0.0033, 0.0039, 0.0053, 0.0066, 0.0090, 0.0111 }; private static final int USD_DSC_NB_NODES = USD_DSC_MARKET_QUOTES.length; private static final String[] USD_DSC_ID_VALUE = new String[] { "USD-ON", "USD-TN", "USD-OIS-1M", "USD-OIS-2M", "USD-OIS-3M", "USD-OIS-6M", "USD-OIS-9M", "USD-OIS-1Y", "USD-OIS-18M", "USD-OIS-2Y", "USD-OIS-3Y", "USD-OIS-4Y" }; /* Nodes */ private static final CurveNode[] USD_DSC_NODES = new CurveNode[USD_DSC_NB_NODES]; /* Tenors */ private static final int[] USD_DSC_DEPO_OFFSET = new int[] {0, 1}; private static final int USD_DSC_NB_DEPO_NODES = USD_DSC_DEPO_OFFSET.length; private static final Period[] USD_DSC_OIS_TENORS = new Period[] { Period.ofMonths(1), Period.ofMonths(2), Period.ofMonths(3), Period.ofMonths(6), Period.ofMonths(9), Period.ofYears(1), Period.ofMonths(18), Period.ofYears(2), Period.ofYears(3), Period.ofYears(4) }; private static final int USD_DSC_NB_OIS_NODES = USD_DSC_OIS_TENORS.length; static { USD_DSC_NODES[0] = TermDepositCurveNode.of( TermDepositTemplate.of(Period.ofDays(1), USD_DEPOSIT_T0), QuoteKey.of(StandardId.of(SCHEME, USD_DSC_ID_VALUE[0]))); USD_DSC_NODES[1] = TermDepositCurveNode.of( TermDepositTemplate.of(Period.ofDays(1), USD_DEPOSIT_T1), QuoteKey.of(StandardId.of(SCHEME, USD_DSC_ID_VALUE[1]))); for (int i = 0; i < USD_DSC_NB_OIS_NODES; i++) { USD_DSC_NODES[USD_DSC_NB_DEPO_NODES + i] = FixedOvernightSwapCurveNode.of( FixedOvernightSwapTemplate.of( Period.ZERO, Tenor.of(USD_DSC_OIS_TENORS[i]), USD_FIXED_1Y_FED_FUND_OIS), QuoteKey.of(StandardId.of(SCHEME, USD_DSC_ID_VALUE[USD_DSC_NB_DEPO_NODES + i]))); } } /** Data for EUR-DSC curve */ /* Market values */ private static final double[] EUR_DSC_MARKET_QUOTES = new double[] { 0.0004, 0.0012, 0.0019, 0.0043, 0.0074, 0.0109, 0.0193, 0.0294, 0.0519, 0.0757 }; private static final int EUR_DSC_NB_NODES = EUR_DSC_MARKET_QUOTES.length; private static final String[] EUR_DSC_ID_VALUE = new String[] { "EUR-USD-FX-1M", "EUR-USD-FX-2M", "EUR-USD-FX-3M", "EUR-USD-FX-6M", "EUR-USD-FX-9M", "EUR-USD-FX-1Y", "EUR-USD-FX-18M", "EUR-USD-FX-2Y", "EUR-USD-FX-3Y", "EUR-USD-FX-4Y" }; /* Nodes */ private static final CurveNode[] EUR_DSC_NODES = new CurveNode[EUR_DSC_NB_NODES]; /* Tenors */ private static final Period[] EUR_DSC_FX_TENORS = new Period[] { Period.ofMonths(1), Period.ofMonths(2), Period.ofMonths(3), Period.ofMonths(6), Period.ofMonths(9), Period.ofYears(1), Period.ofMonths(18), Period.ofYears(2), Period.ofYears(3), Period.ofYears(4) }; private static final int EUR_DSC_NB_FX_NODES = EUR_DSC_FX_TENORS.length; static { for (int i = 0; i < EUR_DSC_NB_FX_NODES; i++) { EUR_DSC_NODES[i] = FxSwapCurveNode.of( FxSwapTemplate.of(EUR_DSC_FX_TENORS[i], EUR_USD), QuoteKey.of(StandardId.of(SCHEME, EUR_DSC_ID_VALUE[i]))); } } /** All quotes for the curve calibration */ private static final ImmutableMarketData ALL_QUOTES; static { Map<MarketDataKey<?>, Object> map = new HashMap<>(); for (int i = 0; i < USD_DSC_NB_NODES; i++) { map.put(QuoteKey.of(StandardId.of(SCHEME, USD_DSC_ID_VALUE[i])), USD_DSC_MARKET_QUOTES[i]); } for (int i = 0; i < EUR_DSC_NB_NODES; i++) { map.put(QuoteKey.of(StandardId.of(SCHEME, EUR_DSC_ID_VALUE[i])), EUR_DSC_MARKET_QUOTES[i]); } map.put(FxRateKey.of(EUR, USD), FX_RATE_EUR_USD); ALL_QUOTES = ImmutableMarketData.of(map); } private static final DiscountingSwapProductPricer SWAP_PRICER = DiscountingSwapProductPricer.DEFAULT; private static final DiscountingTermDepositProductPricer DEPO_PRICER = DiscountingTermDepositProductPricer.DEFAULT; private static final DiscountingFxSwapProductPricer FX_PRICER = DiscountingFxSwapProductPricer.DEFAULT; private static final MarketQuoteSensitivityCalculator MQC = MarketQuoteSensitivityCalculator.DEFAULT; private static final CalibrationMeasures CALIBRATION_MEASURES = CalibrationMeasures.DEFAULT; private static final CurveCalibrator CALIBRATOR = CurveCalibrator.of(1e-9, 1e-9, 100, CALIBRATION_MEASURES); // Constants private static final double TOLERANCE_PV = 1.0E-6; private static final double TOLERANCE_PV_DELTA = 1.0E+3; private static final CurveGroupName CURVE_GROUP_NAME = CurveGroupName.of("USD-DSCON-EUR-DSC"); private static final InterpolatedNodalCurveDefinition USD_DSC_CURVE_DEFN = InterpolatedNodalCurveDefinition.builder() .name(USD_DSCON_CURVE_NAME) .xValueType(ValueType.YEAR_FRACTION) .yValueType(ValueType.ZERO_RATE) .dayCount(CURVE_DC) .interpolator(INTERPOLATOR_LINEAR) .extrapolatorLeft(EXTRAPOLATOR_FLAT) .extrapolatorRight(EXTRAPOLATOR_FLAT) .nodes(USD_DSC_NODES) .build(); private static final InterpolatedNodalCurveDefinition EUR_DSC_CURVE_DEFN = InterpolatedNodalCurveDefinition.builder() .name(EUR_DSC_CURVE_NAME) .xValueType(ValueType.YEAR_FRACTION) .yValueType(ValueType.ZERO_RATE) .dayCount(CURVE_DC) .interpolator(INTERPOLATOR_LINEAR) .extrapolatorLeft(EXTRAPOLATOR_FLAT) .extrapolatorRight(EXTRAPOLATOR_FLAT) .nodes(EUR_DSC_NODES) .build(); private static final CurveGroupDefinition CURVE_GROUP_CONFIG = CurveGroupDefinition.builder() .name(CURVE_GROUP_NAME) .addCurve(USD_DSC_CURVE_DEFN, USD, USD_FED_FUND) .addDiscountCurve(EUR_DSC_CURVE_DEFN, EUR) .build(); // ------------------------------------------------------------------------- public void calibration_present_value_oneGroup() { ImmutableRatesProvider result = CALIBRATOR.calibrate(CURVE_GROUP_CONFIG, VAL_DATE, ALL_QUOTES, TS); assertPresentValue(result); } private void assertPresentValue(ImmutableRatesProvider result) { // Test PV USD; List<Trade> usdTrades = new ArrayList<>(); for (int i = 0; i < USD_DSC_NODES.length; i++) { usdTrades.add(USD_DSC_NODES[i].trade(VAL_DATE, ALL_QUOTES)); } // Depo for (int i = 0; i < USD_DSC_NB_DEPO_NODES; i++) { CurrencyAmount pvDep = DEPO_PRICER.presentValue(((TermDepositTrade) usdTrades.get(i)).getProduct(), result); assertEquals(pvDep.getAmount(), 0.0, TOLERANCE_PV); } // OIS for (int i = 0; i < USD_DSC_NB_OIS_NODES; i++) { MultiCurrencyAmount pvOis = SWAP_PRICER.presentValue( ((SwapTrade) usdTrades.get(USD_DSC_NB_DEPO_NODES + i)).getProduct(), result); assertEquals(pvOis.getAmount(USD).getAmount(), 0.0, TOLERANCE_PV); } // Test PV EUR; List<Trade> eurTrades = new ArrayList<>(); for (int i = 0; i < EUR_DSC_NODES.length; i++) { eurTrades.add(EUR_DSC_NODES[i].trade(VAL_DATE, ALL_QUOTES)); } // Depo for (int i = 0; i < EUR_DSC_NB_FX_NODES; i++) { MultiCurrencyAmount pvFx = FX_PRICER.presentValue(((FxSwapTrade) eurTrades.get(i)).getProduct(), result); assertEquals(pvFx.convertedTo(USD, result).getAmount(), 0.0, TOLERANCE_PV); } } public void calibration_market_quote_sensitivity_one_group() { double shift = 1.0E-6; Function<MarketData, ImmutableRatesProvider> f = ov -> CALIBRATOR.calibrate(CURVE_GROUP_CONFIG, VAL_DATE, ov, TS); calibration_market_quote_sensitivity_check(f, shift); } private void calibration_market_quote_sensitivity_check( Function<MarketData, ImmutableRatesProvider> calibrator, double shift) { double notional = 100_000_000.0; double fx = 1.1111; double fxPts = 0.0012; FxSwapTrade trade = EUR_USD.toTrade( VAL_DATE, Period.ofWeeks(6), Period.ofMonths(5), BuySell.BUY, notional, fx, fxPts); ImmutableRatesProvider result = CALIBRATOR.calibrate(CURVE_GROUP_CONFIG, VAL_DATE, ALL_QUOTES, TS); PointSensitivities pts = FX_PRICER.presentValueSensitivity(trade.getProduct(), result); CurveCurrencyParameterSensitivities ps = result.curveParameterSensitivity(pts); CurveCurrencyParameterSensitivities mqs = MQC.sensitivity(ps, result); double pvUsd = FX_PRICER.presentValue(trade.getProduct(), result).getAmount(USD).getAmount(); double pvEur = FX_PRICER.presentValue(trade.getProduct(), result).getAmount(EUR).getAmount(); double[] mqsUsd1Computed = mqs.getSensitivity(USD_DSCON_CURVE_NAME, USD).getSensitivity().toArray(); for (int i = 0; i < USD_DSC_NB_NODES; i++) { Map<MarketDataKey<?>, Object> map = new HashMap<>(ALL_QUOTES.getValues()); map.put( QuoteKey.of(StandardId.of(SCHEME, USD_DSC_ID_VALUE[i])), USD_DSC_MARKET_QUOTES[i] + shift); ImmutableMarketData marketData = ImmutableMarketData.of(map); ImmutableRatesProvider rpShifted = calibrator.apply(marketData); double pvS = FX_PRICER.presentValue(trade.getProduct(), rpShifted).getAmount(USD).getAmount(); assertEquals(mqsUsd1Computed[i], (pvS - pvUsd) / shift, TOLERANCE_PV_DELTA); } double[] mqsUsd2Computed = mqs.getSensitivity(USD_DSCON_CURVE_NAME, EUR).getSensitivity().toArray(); for (int i = 0; i < USD_DSC_NB_NODES; i++) { Map<MarketDataKey<?>, Object> map = new HashMap<>(ALL_QUOTES.getValues()); map.put( QuoteKey.of(StandardId.of(SCHEME, USD_DSC_ID_VALUE[i])), USD_DSC_MARKET_QUOTES[i] + shift); ImmutableMarketData ov = ImmutableMarketData.of(map); ImmutableRatesProvider rpShifted = calibrator.apply(ov); double pvS = FX_PRICER.presentValue(trade.getProduct(), rpShifted).getAmount(EUR).getAmount(); assertEquals(mqsUsd2Computed[i], (pvS - pvEur) / shift, TOLERANCE_PV_DELTA); } double[] mqsEur1Computed = mqs.getSensitivity(EUR_DSC_CURVE_NAME, USD).getSensitivity().toArray(); for (int i = 0; i < EUR_DSC_NB_NODES; i++) { assertEquals(mqsEur1Computed[i], 0.0, TOLERANCE_PV_DELTA); } double[] mqsEur2Computed = mqs.getSensitivity(EUR_DSC_CURVE_NAME, EUR).getSensitivity().toArray(); for (int i = 0; i < EUR_DSC_NB_NODES; i++) { Map<MarketDataKey<?>, Object> map = new HashMap<>(ALL_QUOTES.getValues()); map.put( QuoteKey.of(StandardId.of(SCHEME, EUR_DSC_ID_VALUE[i])), EUR_DSC_MARKET_QUOTES[i] + shift); ImmutableMarketData marketData = ImmutableMarketData.of(map); ImmutableRatesProvider rpShifted = calibrator.apply(marketData); double pvS = FX_PRICER.presentValue(trade.getProduct(), rpShifted).getAmount(EUR).getAmount(); assertEquals(mqsEur2Computed[i], (pvS - pvEur) / shift, TOLERANCE_PV_DELTA, "Node " + i); } } }
/** Test. */ @Test public class CdsDatesLogicTest { private static final Period[] TENORS = new Period[] { Period.ofMonths(6), Period.ofYears(1), Period.ofYears(2), Period.ofYears(3), Period.ofYears(5), Period.ofYears(10) }; public void isCdsTest() { LocalDate date = LocalDate.of(2013, Month.MARCH, 20); assertTrue(isCdsDate(date)); date = LocalDate.of(2013, Month.APRIL, 20); assertFalse(isCdsDate(date)); date = LocalDate.of(2013, Month.DECEMBER, 23); assertFalse(isCdsDate(date)); } public void onCdsDateTest() { final LocalDate today = LocalDate.of(2013, Month.MARCH, 20); final LocalDate nextCds = getNextCdsDate(today); final LocalDate[] dates = getCdsDateSet(nextCds, TENORS); assertEquals(dates.length, TENORS.length); assertEquals(LocalDate.of(2013, Month.DECEMBER, 20), dates[0]); } public void cdsSetTest() { final LocalDate date = LocalDate.of(2013, Month.MARCH, 20); final int n = 5; final LocalDate[] cdsDates = getCdsDateSet(date, n); assertTrue(cdsDates.length == n); assertEquals(date, cdsDates[0]); assertEquals(LocalDate.of(2013, Month.JUNE, 20), cdsDates[1]); assertEquals(LocalDate.of(2013, Month.SEPTEMBER, 20), cdsDates[2]); assertEquals(LocalDate.of(2013, Month.DECEMBER, 20), cdsDates[3]); assertEquals(LocalDate.of(2014, Month.MARCH, 20), cdsDates[4]); } public void nonCdsDateTest() { final LocalDate today = LocalDate.of(2013, Month.MARCH, 26); final LocalDate nextCds = getNextCdsDate(today); final LocalDate[] dates = getCdsDateSet(nextCds, TENORS); assertEquals(dates.length, TENORS.length); assertEquals(LocalDate.of(2013, Month.DECEMBER, 20), dates[0]); } public void cdsDateM1Test() { final LocalDate today = LocalDate.of(2013, Month.MARCH, 19); final LocalDate nextCds = getNextCdsDate(today); final LocalDate[] dates = getCdsDateSet(nextCds, TENORS); assertEquals(dates.length, TENORS.length); assertEquals(LocalDate.of(2013, Month.SEPTEMBER, 20), dates[0]); } public void stepinCdsDateM2Test() { final LocalDate today = LocalDate.of(2013, Month.MARCH, 18); final LocalDate nextCds = getNextCdsDate(today); final LocalDate[] dates = getCdsDateSet(nextCds, TENORS); assertEquals(dates.length, TENORS.length); assertEquals(LocalDate.of(2013, Month.SEPTEMBER, 20), dates[0]); } public void nextCdsTest() { LocalDate today = LocalDate.of(2011, Month.JUNE, 21); LocalDate nextCds = getNextCdsDate(today); assertEquals(LocalDate.of(2011, Month.SEPTEMBER, 20), nextCds); today = LocalDate.of(2011, Month.JUNE, 20); nextCds = getNextCdsDate(today); assertEquals(LocalDate.of(2011, Month.SEPTEMBER, 20), nextCds); today = LocalDate.of(2011, Month.DECEMBER, 20); nextCds = getNextCdsDate(today); assertEquals(LocalDate.of(2012, Month.MARCH, 20), nextCds); today = LocalDate.of(2011, Month.JUNE, 18); nextCds = getNextCdsDate(today); assertEquals(LocalDate.of(2011, Month.JUNE, 20), nextCds); today = LocalDate.of(1976, Month.JULY, 30); nextCds = getNextCdsDate(today); assertEquals(LocalDate.of(1976, Month.SEPTEMBER, 20), nextCds); today = LocalDate.of(1977, Month.FEBRUARY, 13); nextCds = getNextCdsDate(today); assertEquals(LocalDate.of(1977, Month.MARCH, 20), nextCds); today = LocalDate.of(2013, Month.MARCH, 1); nextCds = getNextCdsDate(today); assertEquals(LocalDate.of(2013, Month.MARCH, 20), nextCds); today = LocalDate.of(2013, Month.DECEMBER, 25); nextCds = getNextCdsDate(today); assertEquals(LocalDate.of(2014, Month.MARCH, 20), nextCds); } public void prevCdsTest() { LocalDate today = LocalDate.of(2011, Month.JUNE, 21); LocalDate prevCds = getPreviousCdsDate(today); assertEquals(LocalDate.of(2011, Month.JUNE, 20), prevCds); today = LocalDate.of(2011, Month.JUNE, 20); prevCds = getPreviousCdsDate(today); assertEquals(LocalDate.of(2011, Month.MARCH, 20), prevCds); prevCds = getPreviousCdsDate(prevCds); assertEquals(LocalDate.of(2010, Month.DECEMBER, 20), prevCds); today = LocalDate.of(2011, Month.JUNE, 18); prevCds = getPreviousCdsDate(today); assertEquals(LocalDate.of(2011, Month.MARCH, 20), prevCds); today = LocalDate.of(1976, Month.JULY, 30); prevCds = getPreviousCdsDate(today); assertEquals(LocalDate.of(1976, Month.JUNE, 20), prevCds); today = LocalDate.of(1977, Month.FEBRUARY, 13); prevCds = getPreviousCdsDate(today); assertEquals(LocalDate.of(1976, Month.DECEMBER, 20), prevCds); today = LocalDate.of(2013, Month.MARCH, 1); prevCds = getPreviousCdsDate(today); assertEquals(LocalDate.of(2012, Month.DECEMBER, 20), prevCds); } public void isIndexRollDateTest() { LocalDate date0 = LocalDate.of(2013, 3, 14); LocalDate date1 = LocalDate.of(2013, 6, 20); LocalDate date2 = LocalDate.of(2013, 3, 20); LocalDate date3 = LocalDate.of(2013, 9, 20); assertFalse(isIndexRollDate(date0)); assertFalse(isIndexRollDate(date1)); assertTrue(isIndexRollDate(date2)); assertTrue(isIndexRollDate(date3)); } public void getNextIndexRollDateTest() { final LocalDate[] dates = new LocalDate[] { LocalDate.of(2013, 3, 14), LocalDate.of(2013, 6, 20), LocalDate.of(2013, 3, 20), LocalDate.of(2013, 9, 20), LocalDate.of(2013, 1, 21), LocalDate.of(2013, 3, 21), LocalDate.of(2013, 9, 19), LocalDate.of(2013, 9, 21), LocalDate.of(2013, 11, 21) }; final LocalDate[] datesExp = new LocalDate[] { LocalDate.of(2013, 3, 20), LocalDate.of(2013, 9, 20), LocalDate.of(2013, 9, 20), LocalDate.of(2014, 3, 20), LocalDate.of(2013, 3, 20), LocalDate.of(2013, 9, 20), LocalDate.of(2013, 9, 20), LocalDate.of(2014, 3, 20), LocalDate.of(2014, 3, 20) }; for (int i = 0; i < dates.length; ++i) { assertEquals(getNextIndexRollDate(dates[i]), datesExp[i]); } } // ------------------------------------------------------------------------- public void coverage() { coverPrivateConstructor(CdsDatesLogic.class); } }
/** Tests related to the construction of CMS cap/floor. */ @Test public class AnnuityCapFloorCMSDefinitionTest { private static final Currency CUR = Currency.EUR; private static final HolidayCalendar CALENDAR = HolidayCalendars.SAT_SUN; // Ibor index private static final BusinessDayConvention BUSINESS_DAY = BusinessDayConventions.MODIFIED_FOLLOWING; private static final boolean IS_EOM = true; private static final Period IBOR_TENOR = Period.ofMonths(3); private static final int IBOR_SETTLEMENT_DAYS = 2; private static final DayCount IBOR_DAY_COUNT = DayCounts.ACT_360; private static final IborIndex IBOR_INDEX = new IborIndex( CUR, IBOR_TENOR, IBOR_SETTLEMENT_DAYS, IBOR_DAY_COUNT, BUSINESS_DAY, IS_EOM, "Ibor"); // CMS 10Y private static final Period CMS_TENOR = Period.ofYears(10); private static final Period FIXED_PAYMENT_PERIOD = Period.ofMonths(6); private static final DayCount FIXED_DAY_COUNT = DayCounts.THIRTY_U_360; private static final IndexSwap CMS_INDEX = new IndexSwap(FIXED_PAYMENT_PERIOD, FIXED_DAY_COUNT, IBOR_INDEX, CMS_TENOR, CALENDAR); // Annuity private static final ZonedDateTime START_DATE = DateUtils.getUTCDate(2011, 3, 17); private static final Period ANNUITY_TENOR = Period.ofYears(5); private static final ZonedDateTime MATURITY_DATE = START_DATE.plus(ANNUITY_TENOR); private static final double NOTIONAL = 100000000; // 100m private static final Period LEG_PAYMENT_PERIOD = Period.ofMonths(12); private static final DayCount LEG_DAY_COUNT = DayCounts.ACT_365F; private static final boolean IS_PAYER = true; private static final double STRIKE = 0.04; private static final boolean IS_CAP = true; private static final AnnuityCapFloorCMSDefinition CMS_LEG = AnnuityCapFloorCMSDefinition.from( START_DATE, MATURITY_DATE, NOTIONAL, CMS_INDEX, LEG_PAYMENT_PERIOD, LEG_DAY_COUNT, IS_PAYER, STRIKE, IS_CAP, CALENDAR); @Test public void dates() { final IborIndex fakeIborIndex12 = new IborIndex( CUR, LEG_PAYMENT_PERIOD, IBOR_SETTLEMENT_DAYS, LEG_DAY_COUNT, BUSINESS_DAY, IS_EOM, "Ibor"); final AnnuityCouponIborDefinition iborLeg = AnnuityCouponIborDefinition.from( START_DATE, MATURITY_DATE, NOTIONAL, fakeIborIndex12, IS_PAYER, CALENDAR); for (int loopcpn = 0; loopcpn < iborLeg.getNumberOfPayments(); loopcpn++) { assertEquals( iborLeg.getNthPayment(loopcpn).getAccrualStartDate(), CMS_LEG.getNthPayment(loopcpn).getAccrualStartDate()); assertEquals( iborLeg.getNthPayment(loopcpn).getAccrualEndDate(), CMS_LEG.getNthPayment(loopcpn).getAccrualEndDate()); assertEquals( iborLeg.getNthPayment(loopcpn).getPaymentYearFraction(), CMS_LEG.getNthPayment(loopcpn).getPaymentYearFraction()); assertEquals( iborLeg.getNthPayment(loopcpn).getPaymentDate(), CMS_LEG.getNthPayment(loopcpn).getPaymentDate()); assertEquals( iborLeg.getNthPayment(loopcpn).getFixingDate(), CMS_LEG.getNthPayment(loopcpn).getFixingDate()); } } @Test public void common() { for (int loopcpn = 0; loopcpn < CMS_LEG.getNumberOfPayments(); loopcpn++) { assertEquals(CMS_INDEX, CMS_LEG.getNthPayment(loopcpn).getCMSIndex()); assertEquals( NOTIONAL * (IS_PAYER ? -1.0 : 1.0), CMS_LEG.getNthPayment(loopcpn).getNotional()); assertEquals(STRIKE, CMS_LEG.getNthPayment(loopcpn).getStrike()); assertEquals(IS_CAP, CMS_LEG.getNthPayment(loopcpn).isCap()); } final AnnuityCapFloorCMSDefinition cmsCapReceiver = AnnuityCapFloorCMSDefinition.from( START_DATE, MATURITY_DATE, NOTIONAL, CMS_INDEX, LEG_PAYMENT_PERIOD, LEG_DAY_COUNT, !IS_PAYER, STRIKE, IS_CAP, CALENDAR); for (int loopcpn = 0; loopcpn < CMS_LEG.getNumberOfPayments(); loopcpn++) { assertEquals(CMS_INDEX, cmsCapReceiver.getNthPayment(loopcpn).getCMSIndex()); assertEquals( -NOTIONAL * (IS_PAYER ? -1.0 : 1.0), cmsCapReceiver.getNthPayment(loopcpn).getNotional()); } } }