/** * Copyright (C) 2011 - 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.ZonedDateTime; import com.opengamma.OpenGammaRuntimeException; import com.opengamma.analytics.financial.instrument.InstrumentDefinitionVisitor; import com.opengamma.analytics.financial.instrument.index.IborIndex; import com.opengamma.analytics.financial.interestrate.payments.derivative.Coupon; import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponFixed; import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponIborGearing; 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; import com.opengamma.util.money.Currency; /** * Class describing a Ibor-like floating coupon with a gearing (multiplicative) factor and a spread. The coupon payment is: notional * accrual factor * (factor * Ibor + spread). */ public class CouponIborGearingDefinition extends CouponFloatingDefinition { /** * Ibor-like index on which the coupon fixes. The index currency should be the same as the coupon currency. */ private final IborIndex _index; /** * The start date of the fixing period. */ private final ZonedDateTime _fixingPeriodStartDate; /** * The end date of the fixing period. */ private final ZonedDateTime _fixingPeriodEndDate; /** * The accrual factor (or year fraction) associated to the fixing period in the Index day count convention. */ private final double _fixingPeriodAccrualFactor; /** * The spread paid above the Ibor rate. */ private final double _spread; /** * The fixed amount related to the spread. */ private final double _spreadAmount; /** * The gearing (multiplicative) factor applied to the Ibor rate. */ private final double _factor; /** * Constructor of a Ibor-like floating coupon from the coupon details and the Ibor index. * * @param currency The payment currency. * @param paymentDate Coupon payment date. * @param accrualStartDate Start date of the accrual period. * @param accrualEndDate End date of the accrual period. * @param accrualFactor Accrual factor of the accrual period. * @param notional Coupon notional. * @param fixingDate The coupon fixing date. * @param index The coupon Ibor index. Should of the same currency as the payment. * @param spread The spread paid above the Ibor rate. * @param factor The gearing (multiplicative) factor applied to the Ibor rate. * @param calendar The holiday calendar for the ibor index. */ public CouponIborGearingDefinition(final Currency currency, final ZonedDateTime paymentDate, final ZonedDateTime accrualStartDate, final ZonedDateTime accrualEndDate, final double accrualFactor, final double notional, final ZonedDateTime fixingDate, final IborIndex index, final double spread, final double factor, final Calendar calendar) { super(currency, paymentDate, accrualStartDate, accrualEndDate, accrualFactor, notional, fixingDate); ArgumentChecker.notNull(index, "index"); ArgumentChecker.isTrue(currency.equals(index.getCurrency()), "index currency different from payment currency"); _index = index; _fixingPeriodStartDate = ScheduleCalculator.getAdjustedDate(fixingDate, _index.getSpotLag(), calendar); _fixingPeriodEndDate = ScheduleCalculator.getAdjustedDate(_fixingPeriodStartDate, index.getTenor(), index.getBusinessDayConvention(), calendar, index.isEndOfMonth()); _fixingPeriodAccrualFactor = index.getDayCount().getDayCountFraction(_fixingPeriodStartDate, _fixingPeriodEndDate, calendar); _spread = spread; _spreadAmount = spread * getNotional() * getPaymentYearFraction(); _factor = factor; } public CouponIborGearingDefinition( Currency currency, ZonedDateTime paymentDate, ZonedDateTime accrualStartDate, ZonedDateTime accrualEndDate, double accrualFactor, double notional, ZonedDateTime fixingDate, IborIndex index, ZonedDateTime fixingPeriodStartDate, ZonedDateTime fixingPeriodEndDate, double fixingPeriodAccrualFactor, double spread, double factor) { super(currency, paymentDate, accrualStartDate, accrualEndDate, accrualFactor, notional, fixingDate); ArgumentChecker.notNull(index, "index"); ArgumentChecker.isTrue(currency.equals(index.getCurrency()), "index currency different from payment currency"); _index = index; _fixingPeriodStartDate = fixingPeriodStartDate; _fixingPeriodEndDate = fixingPeriodEndDate; _fixingPeriodAccrualFactor = fixingPeriodAccrualFactor; _spread = spread; _spreadAmount = spread * getNotional() * getPaymentYearFraction(); _factor = factor; } /** * Builder from an Ibor coupon, the spread and the factor. * @param couponIbor An Ibor coupon. * @param spread The spread. * @param factor The gearing (multiplicative) factor applied to the Ibor rate. * @return The Ibor coupon with spread. */ public static CouponIborGearingDefinition from(final CouponIborDefinition couponIbor, final double spread, final double factor) { ArgumentChecker.notNull(couponIbor, "Ibor coupon"); return new CouponIborGearingDefinition(couponIbor.getCurrency(), couponIbor.getPaymentDate(), couponIbor.getAccrualStartDate(), couponIbor.getAccrualEndDate(), couponIbor.getPaymentYearFraction(), couponIbor.getNotional(), couponIbor.getFixingDate(), couponIbor.getIndex(), spread, factor, couponIbor.getCalendar()); } /** * Builder from the coupon accrual dates and accrual factor. The fixing period will use the index convention, starting on the accrual start date. * @param accrualStartDate The accrual start date. * @param accrualEndDate The accrual end date. * @param accrualFactor The payment accrual factor. * @param notional The coupon notional. * @param index The Ibor index. * @param spread The spread. * @param factor The gearing factor. * @param calendar The holiday calendar for the ibor index. * @return The coupon. */ public static CouponIborGearingDefinition from(final ZonedDateTime accrualStartDate, final ZonedDateTime accrualEndDate, final double accrualFactor, final double notional, final IborIndex index, final double spread, final double factor, final Calendar calendar) { ArgumentChecker.notNull(accrualStartDate, "Fixing date"); ArgumentChecker.notNull(accrualEndDate, "Fixing date"); ArgumentChecker.notNull(index, "Index"); final ZonedDateTime fixingDate = ScheduleCalculator.getAdjustedDate(accrualStartDate, -index.getSpotLag(), calendar); return new CouponIborGearingDefinition(index.getCurrency(), accrualEndDate, accrualStartDate, accrualEndDate, accrualFactor, notional, fixingDate, index, spread, factor, calendar); } /** * Gets the Ibor index of the instrument. * @return The index. */ public IborIndex getIndex() { return _index; } /** * Gets the start date of the fixing period. * @return The start date of the fixing period. */ public ZonedDateTime getFixingPeriodStartDate() { return _fixingPeriodStartDate; } /** * Gets the end date of the fixing period. * @return The end date of the fixing period. */ public ZonedDateTime getFixingPeriodEndDate() { return _fixingPeriodEndDate; } /** * Gets the accrual factor (or year fraction) associated to the fixing period in the Index day count convention. * @return The accrual factor. */ public double getFixingPeriodAccrualFactor() { return _fixingPeriodAccrualFactor; } /** * Gets the spread. * @return The spread. */ public double getSpread() { return _spread; } /** * Gets the fixed amount related to the spread. * @return The spread amount. */ public double getSpreadAmount() { return _spreadAmount; } /** * Gets the gearing (multiplicative) factor applied to the Ibor rate. * @return The factor. */ public double getFactor() { return _factor; } @Override public CouponIborGearing toDerivative(final ZonedDateTime dateTime) { ArgumentChecker.notNull(dateTime, "date"); final LocalDate dayConversion = dateTime.toLocalDate(); ArgumentChecker.isTrue(!dayConversion.isAfter(getFixingDate().toLocalDate()), "Do not have any fixing data but are asking for a derivative after the fixing date " + getFixingDate() + " " + dateTime); ArgumentChecker.isTrue(!dayConversion.isAfter(getPaymentDate().toLocalDate()), "date is after payment date"); final double paymentTime = TimeCalculator.getTimeBetween(dateTime, getPaymentDate()); final double fixingTime = TimeCalculator.getTimeBetween(dateTime, getFixingDate()); final double fixingPeriodStartTime = TimeCalculator.getTimeBetween(dateTime, getFixingPeriodStartDate()); final double fixingPeriodEndTime = TimeCalculator.getTimeBetween(dateTime, getFixingPeriodEndDate()); return new CouponIborGearing(getCurrency(), paymentTime, getPaymentYearFraction(), getNotional(), fixingTime, getIndex(), fixingPeriodStartTime, fixingPeriodEndTime, getFixingPeriodAccrualFactor(), _spread, _factor); } /** * {@inheritDoc} * If the fixing date is strictly before the conversion date and the fixing rate is not available, an exception is thrown; if the fixing rate is available a fixed coupon is returned. * If the fixing date is equal to the conversion date, if the fixing rate is available a fixed coupon is returned, if not a coupon Ibor with gearing is returned. * If the fixing date is strictly after the conversion date, a coupon Ibor with gearing is returned. * All the comparisons are between dates without time. */ @Override public Coupon toDerivative(final ZonedDateTime dateTime, final DoubleTimeSeries<ZonedDateTime> indexFixingTimeSeries) { ArgumentChecker.notNull(dateTime, "date"); final LocalDate dayConversion = dateTime.toLocalDate(); ArgumentChecker.notNull(indexFixingTimeSeries, "Index fixing time series"); ArgumentChecker.isTrue(!dayConversion.isAfter(getPaymentDate().toLocalDate()), "date is after payment date"); final double paymentTime = TimeCalculator.getTimeBetween(dateTime, getPaymentDate()); final LocalDate dayFixing = getFixingDate().toLocalDate(); if (dayConversion.equals(dayFixing)) { // The fixing is on the reference date; if known the fixing is used and if not, the floating coupon is created. final Double fixedRate = indexFixingTimeSeries.getValue(getFixingDate()); if (fixedRate != null) { return new CouponFixed(getCurrency(), paymentTime, getPaymentYearFraction(), getNotional(), _factor * fixedRate + _spread); } } if (dayConversion.isAfter(dayFixing)) { // The fixing is required final Double fixedRate = indexFixingTimeSeries.getValue(getFixingDate()); if (fixedRate == null) { throw new OpenGammaRuntimeException("Could not get fixing value for date " + getFixingDate()); } return new CouponFixed(getCurrency(), paymentTime, getPaymentYearFraction(), getNotional(), _factor * fixedRate + _spread); } final double fixingTime = TimeCalculator.getTimeBetween(dateTime, getFixingDate()); final double fixingPeriodStartTime = TimeCalculator.getTimeBetween(dateTime, getFixingPeriodStartDate()); final double fixingPeriodEndTime = TimeCalculator.getTimeBetween(dateTime, getFixingPeriodEndDate()); return new CouponIborGearing(getCurrency(), paymentTime, getPaymentYearFraction(), getNotional(), fixingTime, getIndex(), fixingPeriodStartTime, fixingPeriodEndTime, getFixingPeriodAccrualFactor(), _spread, _factor); } @Override public <U, V> V accept(final InstrumentDefinitionVisitor<U, V> visitor, final U data) { ArgumentChecker.notNull(visitor, "visitor"); return visitor.visitCouponIborGearingDefinition(this, data); } @Override public <V> V accept(final InstrumentDefinitionVisitor<?, V> visitor) { ArgumentChecker.notNull(visitor, "visitor"); return visitor.visitCouponIborGearingDefinition(this); } @Override public String toString() { return super.toString() + ", factor=" + _factor + ", spread=" + _spread + ", spread amount=" + _spreadAmount; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); long temp; temp = Double.doubleToLongBits(_factor); result = prime * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(_spread); result = prime * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(_spreadAmount); 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 CouponIborGearingDefinition other = (CouponIborGearingDefinition) obj; if (Double.doubleToLongBits(_factor) != Double.doubleToLongBits(other._factor)) { return false; } if (Double.doubleToLongBits(_spread) != Double.doubleToLongBits(other._spread)) { return false; } if (Double.doubleToLongBits(_spreadAmount) != Double.doubleToLongBits(other._spreadAmount)) { return false; } return true; } }