/** * Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.instrument.annuity; import java.util.ArrayList; import java.util.List; import org.threeten.bp.Period; import org.threeten.bp.ZonedDateTime; import com.opengamma.analytics.financial.instrument.index.IborIndex; import com.opengamma.analytics.financial.instrument.payment.CouponDefinition; import com.opengamma.analytics.financial.instrument.payment.CouponFixedDefinition; import com.opengamma.analytics.financial.instrument.payment.CouponIborGearingDefinition; import com.opengamma.analytics.financial.instrument.payment.CouponIborRatchetDefinition; import com.opengamma.analytics.financial.interestrate.annuity.derivative.AnnuityCouponIborRatchet; import com.opengamma.analytics.financial.interestrate.payments.derivative.Coupon; import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponFixed; import com.opengamma.analytics.financial.schedule.ScheduleCalculator; import com.opengamma.analytics.util.time.TimeCalculator; import com.opengamma.financial.convention.calendar.Calendar; import com.opengamma.timeseries.DoubleTimeSeries; import com.opengamma.util.ArgumentChecker; /** * A wrapper class for a AnnuityDefinition containing mainly CouponIborRatchetDefinition. The first coupon should be a CouponFixedDefinition or a CouponIborGearingDefinition. */ public class AnnuityCouponIborRatchetDefinition extends AnnuityCouponDefinition<CouponDefinition> { /** * Array used for the list conversion. */ protected static final Coupon[] EMPTY_ARRAY_CPN = new Coupon[0]; /** * Constructor from a list of Ibor-like coupons. * @param payments The Ibor coupons. * @param calendar The calendar */ public AnnuityCouponIborRatchetDefinition(final CouponDefinition[] payments, final Calendar calendar) { super(payments, calendar); ArgumentChecker.isTrue((payments[0] instanceof CouponFixedDefinition) || (payments[0] instanceof CouponIborGearingDefinition), "First coupon should be CouponFixedDefinition or a CouponIborGearingDefinition"); for (int looppay = 1; looppay < payments.length; looppay++) { ArgumentChecker.isTrue((payments[looppay] instanceof CouponIborRatchetDefinition), "Next coupons should be CouponIborRatchetDefinition"); } } /** * Build a Ratchet Ibor annuity with fixed first coupon. All the ratchet coupons have the same coefficients. * @param settlementDate The annuity settlement date. * @param annuityTenor The annuity tenor. * @param notional The notional. * @param index The Ibor index. * @param isPayer The payer (true) / receiver (false) flag. * @param firstCouponFixedRate The rate of the first coupon. * @param mainCoefficients The coefficients of the main payment (before floor and cap). Array of length 3. The first coefficient is the previous coupon factor, * the second is the Ibor factor and the third is the additive term. * @param floorCoefficients The coefficients of the floor. Array of length 3. The first coefficient is the previous coupon factor, * the second is the Ibor factor and the third is the additive term. * @param capCoefficients The coefficients of the cap. Array of length 3. The first coefficient is the previous coupon factor, * the second is the Ibor factor and the third is the additive term. * @param calendar The holiday calendar for the ibor leg. * @return The annuity. */ public static AnnuityCouponIborRatchetDefinition withFirstCouponFixed(final ZonedDateTime settlementDate, final Period annuityTenor, final double notional, final IborIndex index, final boolean isPayer, final double firstCouponFixedRate, final double[] mainCoefficients, final double[] floorCoefficients, final double[] capCoefficients, final Calendar calendar) { final ZonedDateTime[] paymentDates = ScheduleCalculator.getAdjustedDateSchedule(settlementDate, annuityTenor, index.getTenor(), index.getBusinessDayConvention(), calendar, index.isEndOfMonth()); final CouponDefinition[] coupons = new CouponDefinition[paymentDates.length]; final double notionalSign = notional * (isPayer ? -1.0 : 1.0); coupons[0] = new CouponFixedDefinition(index.getCurrency(), paymentDates[0], settlementDate, paymentDates[0], index.getDayCount().getDayCountFraction(settlementDate, paymentDates[0], calendar), notionalSign, firstCouponFixedRate); for (int loopcpn = 1; loopcpn < paymentDates.length; loopcpn++) { coupons[loopcpn] = new CouponIborRatchetDefinition(index.getCurrency(), paymentDates[loopcpn], paymentDates[loopcpn - 1], paymentDates[loopcpn], index.getDayCount().getDayCountFraction(paymentDates[loopcpn - 1], paymentDates[loopcpn], calendar), notionalSign, ScheduleCalculator.getAdjustedDate(paymentDates[loopcpn - 1], -index.getSpotLag(), calendar), index, mainCoefficients, floorCoefficients, capCoefficients, calendar); } return new AnnuityCouponIborRatchetDefinition(coupons, calendar); } /** * Build a Ratchet Ibor annuity with Ibor gearing first coupon. The factor and spread of the first coupon are the one of the main part of the Ratchet. * All the Ratchet coupons have the same coefficients. * @param settlementDate The annuity settlement date. * @param annuityTenor The annuity tenor. * @param notional The notional. * @param index The Ibor index. * @param isPayer The payer (true) / receiver (false) flag. * @param mainCoefficients The coefficients of the main payment (before floor and cap). Array of length 3. * @param floorCoefficients The coefficients of the floor. Array of length 3. * @param capCoefficients The coefficients of the cap. Array of length 3. * @param calendar The holiday calendar for the ibor leg. * @return The annuity. */ public static AnnuityCouponIborRatchetDefinition withFirstCouponIborGearing(final ZonedDateTime settlementDate, final Period annuityTenor, final double notional, final IborIndex index, final boolean isPayer, final double[] mainCoefficients, final double[] floorCoefficients, final double[] capCoefficients, final Calendar calendar) { final ZonedDateTime[] paymentDates = ScheduleCalculator.getAdjustedDateSchedule(settlementDate, annuityTenor, index.getTenor(), index.getBusinessDayConvention(), calendar, index.isEndOfMonth()); final CouponDefinition[] coupons = new CouponDefinition[paymentDates.length]; final double notionalSign = notional * (isPayer ? -1.0 : 1.0); coupons[0] = CouponIborGearingDefinition.from(settlementDate, paymentDates[0], index.getDayCount().getDayCountFraction(settlementDate, paymentDates[0], calendar), notionalSign, index, mainCoefficients[2], mainCoefficients[1], calendar); for (int loopcpn = 1; loopcpn < paymentDates.length; loopcpn++) { coupons[loopcpn] = new CouponIborRatchetDefinition(index.getCurrency(), paymentDates[loopcpn], paymentDates[loopcpn - 1], paymentDates[loopcpn], index.getDayCount().getDayCountFraction(paymentDates[loopcpn - 1], paymentDates[loopcpn], calendar), notionalSign, ScheduleCalculator.getAdjustedDate(paymentDates[loopcpn - 1], -index.getSpotLag(), calendar), index, mainCoefficients, floorCoefficients, capCoefficients, calendar); } return new AnnuityCouponIborRatchetDefinition(coupons, calendar); } @Override public AnnuityCouponIborRatchet toDerivative(final ZonedDateTime date, final DoubleTimeSeries<ZonedDateTime> indexFixingTS) { ArgumentChecker.notNull(date, "date"); final List<Coupon> cpnList = new ArrayList<>(); final List<Double> fixedCpn = new ArrayList<>(); final boolean[] isFixed = new boolean[getNumberOfPayments()]; // List of fixing if (getPayments()[0] instanceof CouponFixedDefinition) { // CouponFixedDefinition isFixed[0] = true; } else { // CouponIborGearingDefinition isFixed[0] = (indexFixingTS.getValue(((CouponIborGearingDefinition) getPayments()[0]).getFixingDate())) != null; } for (int loopcpn = 1; loopcpn < getNumberOfPayments(); loopcpn++) { isFixed[loopcpn] = (indexFixingTS.getValue(((CouponIborRatchetDefinition) getPayments()[loopcpn]).getFixingDate())) != null; } // Already fixed coupons if (getPayments()[0] instanceof CouponFixedDefinition) { // CouponFixedDefinition fixedCpn.add(((CouponFixedDefinition) getNthPayment(0)).getRate()); } else { // CouponIborDefinition if (isFixed[0]) { final CouponIborGearingDefinition cpnIbor = (CouponIborGearingDefinition) getPayments()[0]; fixedCpn.add(indexFixingTS.getValue(cpnIbor.getFixingDate()) * cpnIbor.getFactor() + cpnIbor.getSpread()); } } for (int loopcpn = 1; loopcpn < getNumberOfPayments(); loopcpn++) { if (isFixed[loopcpn]) { final CouponIborRatchetDefinition cpnRatchet = (CouponIborRatchetDefinition) getPayments()[loopcpn]; final double ibor = indexFixingTS.getValue(cpnRatchet.getFixingDate()); final double cpnMain = cpnRatchet.getMainCoefficients()[0] * fixedCpn.get(loopcpn - 1) + cpnRatchet.getMainCoefficients()[1] * ibor + cpnRatchet.getMainCoefficients()[2]; final double cpnFloor = cpnRatchet.getFloorCoefficients()[0] * fixedCpn.get(loopcpn - 1) + cpnRatchet.getFloorCoefficients()[1] * ibor + cpnRatchet.getFloorCoefficients()[2]; final double cpnCap = cpnRatchet.getCapCoefficients()[0] * fixedCpn.get(loopcpn - 1) + cpnRatchet.getCapCoefficients()[1] * ibor + cpnRatchet.getCapCoefficients()[2]; final double cpnActual = Math.min(Math.max(cpnFloor, cpnMain), cpnCap); fixedCpn.add(cpnActual); } } // Derivatives final CouponDefinition cpn0 = getNthPayment(0); if (!date.isAfter(cpn0.getPaymentDate())) { if (isFixed[0]) { cpnList.add(new CouponFixed(getCurrency(), TimeCalculator.getTimeBetween(date, cpn0.getPaymentDate()), cpn0.getPaymentYearFraction(), cpn0.getNotional(), fixedCpn.get(0), cpn0.getAccrualStartDate(), cpn0.getAccrualEndDate())); } else { // CouponIborGearingDefinition cpnList.add(((CouponIborGearingDefinition) cpn0).toDerivative(date)); } } for (int loopcpn = 1; loopcpn < getNumberOfPayments(); loopcpn++) { final CouponDefinition cpn = getNthPayment(loopcpn); if (!date.isAfter(getNthPayment(loopcpn).getPaymentDate())) { if (isFixed[loopcpn]) { cpnList.add(new CouponFixed(getCurrency(), TimeCalculator.getTimeBetween(date, cpn.getPaymentDate()), cpn.getPaymentYearFraction(), cpn.getNotional(), fixedCpn .get(loopcpn), cpn.getAccrualStartDate(), cpn.getAccrualEndDate())); } else { cpnList.add(((CouponIborRatchetDefinition) cpn).toDerivative(date)); } } } return new AnnuityCouponIborRatchet(cpnList.toArray(EMPTY_ARRAY_CPN)); } }