/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.instrument.payment; import org.threeten.bp.LocalDate; import org.threeten.bp.Period; import org.threeten.bp.ZonedDateTime; import com.opengamma.analytics.financial.instrument.InstrumentDefinitionVisitor; import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponFixedCompounding; import com.opengamma.analytics.financial.schedule.ScheduleCalculator; import com.opengamma.analytics.util.time.TimeCalculator; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.money.Currency; /** * Class describing a fixed compounded coupon. The fixed rate is compounded over several sub-periods. * The amount paid is equal to * $$ * \begin{equation*} * \left(\prod_{i=1}^n (1+\delta_i r) \right)-1 * \end{equation*} * $$ * where the $\delta_i$ are the accrual factors of the sub periods and the $r$ the fixed rate for the same periods. */ public final class CouponFixedCompoundingDefinition extends CouponDefinition { /** * The fixed rate. * All the coupon sub-periods use the same fixed rate. */ private final double _rate; /** * The start dates of the accrual sub-periods. */ private final ZonedDateTime[] _accrualStartDates; /** * The end dates of the accrual sub-periods. */ private final ZonedDateTime[] _accrualEndDates; /** * The accrual factors (or year fraction) associated to the sub-periods. */ private final double[] _paymentAccrualFactors; /** * Constructor. * @param currency The coupon currency. * @param paymentDate The coupon payment date. * @param accrualStartDate The start date of the accrual period. * @param accrualEndDate The end date of the accrual period. * @param paymentAccrualFactor The accrual factor of the accrual period. * @param notional The coupon notional. * @param rate Fixed rate. * @param accrualStartDates The start dates of the accrual sub-periods. * @param accrualEndDates The end dates of the accrual sub-periods. * @param paymentAccrualFactors The accrual factors (or year fraction) associated to the sub-periods. */ private CouponFixedCompoundingDefinition(final Currency currency, final ZonedDateTime paymentDate, final ZonedDateTime accrualStartDate, final ZonedDateTime accrualEndDate, final double paymentAccrualFactor, final double notional, final double rate, final ZonedDateTime[] accrualStartDates, final ZonedDateTime[] accrualEndDates, final double[] paymentAccrualFactors) { super(currency, paymentDate, accrualStartDate, accrualEndDate, paymentAccrualFactor, notional); ArgumentChecker.isTrue(accrualStartDates.length == accrualEndDates.length, "Accrual start and end dates should have same length"); _rate = rate; _accrualStartDates = accrualStartDates; _accrualEndDates = accrualEndDates; _paymentAccrualFactors = paymentAccrualFactors; } /** * Builds a fixed compounded coupon from all the details. * @param currency The coupon currency. * @param paymentDate The coupon payment date. * @param accrualStartDate The start date of the accrual period. * @param accrualEndDate The end date of the accrual period. * @param paymentAccrualFactor The accrual factor of the accrual period. * @param notional The coupon notional. * @param rate Fixed rate. * @param accrualStartDates The start dates of the accrual sub-periods. * @param accrualEndDates The end dates of the accrual sub-periods. * @param paymentAccrualFactors The accrual factors (or year fraction) associated to the sub-periods. * @return The compounded coupon. */ public static CouponFixedCompoundingDefinition from(final Currency currency, final ZonedDateTime paymentDate, final ZonedDateTime accrualStartDate, final ZonedDateTime accrualEndDate, final double paymentAccrualFactor, final double notional, final double rate, final ZonedDateTime[] accrualStartDates, final ZonedDateTime[] accrualEndDates, final double[] paymentAccrualFactors) { return new CouponFixedCompoundingDefinition(currency, paymentDate, accrualStartDate, accrualEndDate, paymentAccrualFactor, notional, rate, accrualStartDates, accrualEndDates, paymentAccrualFactors); } /** * Builds an fixed compounded coupon from the accrual and payment details. * @param currency The coupon currency. * @param paymentDate The coupon payment date. * @param notional The coupon notional. * @param rate The fixed rate. * @param accrualStartDates The start dates of the accrual sub-periods. * @param accrualEndDates The end dates of the accrual sub-periods. * @param paymentAccrualFactors The accrual factors (or year fraction) associated to the sub-periods. * @return The compounded coupon. */ public static CouponFixedCompoundingDefinition from(final Currency currency, final ZonedDateTime paymentDate, final ZonedDateTime[] accrualStartDates, final ZonedDateTime[] accrualEndDates, final double[] paymentAccrualFactors, final double notional, final double rate) { final int nbSubPeriod = accrualEndDates.length; final ZonedDateTime accrualStartDate = accrualStartDates[0]; final ZonedDateTime accrualEndDate = accrualEndDates[nbSubPeriod - 1]; double paymentAccrualFactor = 0.0; for (int loopsub = 0; loopsub < nbSubPeriod; loopsub++) { paymentAccrualFactor += paymentAccrualFactors[loopsub]; } return new CouponFixedCompoundingDefinition(currency, paymentDate, accrualStartDate, accrualEndDate, paymentAccrualFactor, notional, rate, accrualStartDates, accrualEndDates, paymentAccrualFactors); } /** * Builds a fixed compounded coupon from a total period. * If required the stub of the sub-periods will be short and last. The payment date is the start accrual date plus the tenor in the index conventions. * @param currency The currency. * @param accrualStartDate The first accrual date. * @param accrualEndDate The last accrual date. * @param notional The coupon notional. * @param tenorPeriod Perriod between two consecutive accrual dates(usually 1 year). * @param rate The fixed rate. * @return The compounded coupon. */ public static CouponFixedCompoundingDefinition from(final Currency currency, final ZonedDateTime accrualStartDate, final ZonedDateTime accrualEndDate, final double notional, final Period tenorPeriod, final double rate) { final ZonedDateTime[] accrualEndDates = ScheduleCalculator.getUnadjustedDateSchedule(accrualStartDate, accrualEndDate, tenorPeriod, true, false); final int nbSubPeriod = accrualEndDates.length; final ZonedDateTime[] accrualStartDates = new ZonedDateTime[nbSubPeriod]; accrualStartDates[0] = accrualStartDate; System.arraycopy(accrualEndDates, 0, accrualStartDates, 1, nbSubPeriod - 1); final double[] paymentAccrualFactors = new double[nbSubPeriod]; for (int loopsub = 0; loopsub < nbSubPeriod; loopsub++) { paymentAccrualFactors[loopsub] = TimeCalculator.getTimeBetween(accrualStartDates[loopsub], accrualEndDates[loopsub]); } return from(currency, accrualEndDates[nbSubPeriod - 1], accrualStartDates, accrualEndDates, paymentAccrualFactors, notional, rate); } /** * Builds a fixed compounded coupon from a tenor in years. This constructor is needed for inflation ZC swap. * If required the stub of the sub-periods will be short and last. The payment date is the start accrual date plus the tenor in the index conventions. * @param currency The currency. * @param accrualStartDate The first accrual date. * @param paymentDate The payment date. * @param notional The coupon notional. * @param tenor The total coupon tenor. * @param rate The fixed rate. * @return The compounded coupon. */ public static CouponFixedCompoundingDefinition from(final Currency currency, final ZonedDateTime accrualStartDate, final ZonedDateTime paymentDate, final double notional, final int tenor, final double rate) { final ZonedDateTime[] accrualEndDates = ScheduleCalculator.getUnadjustedDateSchedule(accrualStartDate, accrualStartDate.plus(Period.ofYears(tenor)), Period.ofYears(1), true, false); final int nbSubPeriod = accrualEndDates.length; final ZonedDateTime[] accrualStartDates = new ZonedDateTime[nbSubPeriod]; accrualStartDates[0] = accrualStartDate; System.arraycopy(accrualEndDates, 0, accrualStartDates, 1, nbSubPeriod - 1); final double[] paymentAccrualFactors = new double[nbSubPeriod]; for (int loopsub = 0; loopsub < nbSubPeriod; loopsub++) { paymentAccrualFactors[loopsub] = 1.0; } return from(currency, paymentDate, accrualStartDates, accrualEndDates, paymentAccrualFactors, notional, rate); } public double getRate() { return _rate; } public ZonedDateTime[] getAccrualStartDates() { return _accrualStartDates; } public ZonedDateTime[] getAccrualEndDates() { return _accrualEndDates; } public double[] getPaymentAccrualFactors() { return _paymentAccrualFactors; } @Override public String toString() { return "CouponFixedCompoundingDefinition [_rate=" + _rate + "]"; } @Override public CouponFixedCompounding toDerivative(final ZonedDateTime date) { ArgumentChecker.notNull(date, "date"); final LocalDate dayConversion = date.toLocalDate(); ArgumentChecker.isTrue(!dayConversion.isAfter(getPaymentDate().toLocalDate()), "date is after payment date"); final double paymentTime = TimeCalculator.getTimeBetween(date, getPaymentDate()); return new CouponFixedCompounding(getCurrency(), paymentTime, getPaymentYearFraction(), getNotional(), getPaymentAccrualFactors(), getRate()); } @Override public <U, V> V accept(final InstrumentDefinitionVisitor<U, V> visitor, final U data) { ArgumentChecker.notNull(visitor, "visitor"); return visitor.visitCouponFixedCompoundingDefinition(this, data); } @Override public <V> V accept(final InstrumentDefinitionVisitor<?, V> visitor) { ArgumentChecker.notNull(visitor, "visitor"); return visitor.visitCouponFixedCompoundingDefinition(this); } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); long temp; temp = Double.doubleToLongBits(_rate); result = prime * result + (int) (temp ^ (temp >>> 32)); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (getClass() != obj.getClass()) { return false; } final CouponFixedCompoundingDefinition other = (CouponFixedCompoundingDefinition) obj; if (Double.doubleToLongBits(_rate) != Double.doubleToLongBits(other._rate)) { return false; } return true; } }