com.opengamma.analytics.financial.instrument.payment.CouponOISDefinition.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.analytics.financial.instrument.payment.CouponOISDefinition.java

Source

/**
 * 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 java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.time.calendar.LocalDate;
import javax.time.calendar.Period;
import javax.time.calendar.TimeZone;
import javax.time.calendar.ZonedDateTime;

import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.Validate;

import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.analytics.financial.instrument.InstrumentDefinitionVisitor;
import com.opengamma.analytics.financial.instrument.InstrumentDefinitionWithData;
import com.opengamma.analytics.financial.instrument.index.IndexON;
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.CouponOIS;
import com.opengamma.analytics.financial.interestrate.payments.derivative.Payment;
import com.opengamma.analytics.financial.schedule.ScheduleCalculator;
import com.opengamma.analytics.util.time.TimeCalculator;
import com.opengamma.financial.convention.businessday.BusinessDayConvention;
import com.opengamma.util.money.Currency;
import com.opengamma.util.timeseries.DoubleTimeSeries;
import com.opengamma.util.timeseries.localdate.LocalDateDoubleTimeSeries;

/**
 * Class describing a OIS-like floating coupon.
 */
public class CouponOISDefinition extends CouponDefinition
        implements InstrumentDefinitionWithData<Payment, DoubleTimeSeries<ZonedDateTime>> {

    /**
     * The OIS-like index on which the coupon fixes. The index currency should be the same as the coupon currency.
     */
    private final IndexON _index;
    /**
     * The dates of the fixing periods. The length is one greater than the number of periods, as it includes accrual start and end.
     */
    private final ZonedDateTime[] _fixingPeriodDate;
    /**
     * The accrual factors (or year fractions) associated to the fixing periods in the Index day count convention.
     */
    private final Double[] _fixingPeriodAccrualFactor;

    /**
     * Constructor from all the coupon details.
     * @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 paymentYearFraction Accrual factor of the accrual period.
     * @param notional Coupon notional.
     * @param index The OIS-like index on which the coupon fixes.
     * @param fixingPeriodStartDate The start date of the fixing period.
     * @param fixingPeriodEndDate The end date of the fixing period.
     */
    public CouponOISDefinition(final Currency currency, final ZonedDateTime paymentDate,
            final ZonedDateTime accrualStartDate, final ZonedDateTime accrualEndDate,
            final double paymentYearFraction, final double notional, final IndexON index,
            final ZonedDateTime fixingPeriodStartDate, final ZonedDateTime fixingPeriodEndDate) {
        super(currency, paymentDate, accrualStartDate, accrualEndDate, paymentYearFraction, notional);
        Validate.notNull(index, "CouponOISDefinition: index");
        Validate.notNull(fixingPeriodStartDate, "CouponOISDefinition: fixingPeriodStartDate");
        Validate.notNull(fixingPeriodEndDate, "CouponOISDefinition: fixingPeriodEndDate");
        Validate.isTrue(currency.equals(index.getCurrency()),
                "Coupon and index currencies are not compatible. Expected to be the same");
        _index = index;

        final List<ZonedDateTime> fixingDateList = new ArrayList<ZonedDateTime>();
        final List<Double> fixingAccrualFactorList = new ArrayList<Double>();

        ZonedDateTime currentDate = fixingPeriodStartDate;
        fixingDateList.add(currentDate);
        ZonedDateTime nextDate;
        while (currentDate.isBefore(fixingPeriodEndDate)) {
            nextDate = ScheduleCalculator.getAdjustedDate(currentDate, 1, index.getCalendar());
            fixingDateList.add(nextDate);
            fixingAccrualFactorList.add(index.getDayCount().getDayCountFraction(currentDate, nextDate));
            currentDate = nextDate;
        }
        _fixingPeriodDate = fixingDateList.toArray(new ZonedDateTime[0]);
        _fixingPeriodAccrualFactor = fixingAccrualFactorList.toArray(new Double[0]);
    }

    /**
     * Builder from financial details. The accrual and fixing start and end dates are the same. The day count for the payment is the same as the one for the index.
     * The payment date is adjusted by the publication lag and the settlement days.
     * @param index The OIS index.
     * @param settlementDate The coupon settlement date.
     * @param tenor The coupon tenor.
     * @param notional The notional.
     * @param settlementDays The number of days between last fixing and the payment (also called spot lag).
     * @param businessDayConvention The business day convention to compute the end date of the coupon.
     * @param isEOM The end-of-month convention to compute the end date of the coupon.
     * @return The OIS coupon.
     */
    public static CouponOISDefinition from(final IndexON index, final ZonedDateTime settlementDate,
            final Period tenor, final double notional, final int settlementDays,
            final BusinessDayConvention businessDayConvention, final boolean isEOM) {
        final ZonedDateTime fixingPeriodEndDate = ScheduleCalculator.getAdjustedDate(settlementDate, tenor,
                businessDayConvention, index.getCalendar(), isEOM);
        return from(index, settlementDate, fixingPeriodEndDate, notional, settlementDays);
    }

    /**
     * Builder from financial details. The accrual and fixing start and end dates are the same. The day count for the payment is the same as the one for the index.
     * The payment date is adjusted by the publication lag and the settlement days.
     * @param index The OIS index.
     * @param settlementDate The coupon settlement date.
     * @param fixingPeriodEndDate The last date of the fixing period. Interest accrues up to this date. If publicationLag==0, 1 day following publication. If lag==1, the publication date.
     * @param notional The notional.
     * @param settlementDays The number of days between last fixing date and the payment fate (also called payment lag).
     * @return The OIS coupon.
     */
    public static CouponOISDefinition from(final IndexON index, final ZonedDateTime settlementDate,
            final ZonedDateTime fixingPeriodEndDate, final double notional, final int settlementDays) {
        final ZonedDateTime paymentDate = ScheduleCalculator.getAdjustedDate(fixingPeriodEndDate,
                -1 + index.getPublicationLag() + settlementDays, index.getCalendar());
        final double paymentYearFraction = index.getDayCount().getDayCountFraction(settlementDate,
                fixingPeriodEndDate);
        return new CouponOISDefinition(index.getCurrency(), paymentDate, settlementDate, fixingPeriodEndDate,
                paymentYearFraction, notional, index, settlementDate, fixingPeriodEndDate);
    }

    /**
     * Gets the OIS index of the instrument.
     * @return The index.
     */
    public IndexON getIndex() {
        return _index;
    }

    /**
     * Gets the dates of the fixing periods (start and end). There is one date more than period.
     * @return The dates of the fixing periods.
     */
    public ZonedDateTime[] getFixingPeriodDate() {
        return _fixingPeriodDate;
    }

    /**
     * Gets the accrual factors (or year fractions) associated to the fixing periods in the Index day count convention.
     * @return The accrual factors.
     */
    public Double[] getFixingPeriodAccrualFactor() {
        return _fixingPeriodAccrualFactor;
    }

    @Override
    public CouponOIS toDerivative(final ZonedDateTime date, final String... yieldCurveNames) {
        Validate.notNull(date, "date");
        Validate.isTrue(!date.isAfter(_fixingPeriodDate[0]),
                "toDerivative method without time series as argument is only valid at dates where the first fixing has not yet been published.");
        Validate.isTrue(yieldCurveNames.length > 1, "at least two curves required");
        final double paymentTime = TimeCalculator.getTimeBetween(date, getPaymentDate());
        final double fixingPeriodStartTime = TimeCalculator.getTimeBetween(date, _fixingPeriodDate[0]);
        final double fixingPeriodEndTime = TimeCalculator.getTimeBetween(date,
                _fixingPeriodDate[_fixingPeriodDate.length - 1]);
        double fixingAccrualFactorTotal = 0.0;
        for (final Double element : _fixingPeriodAccrualFactor) {
            fixingAccrualFactorTotal += element;
        }
        final CouponOIS cpn = new CouponOIS(getCurrency(), paymentTime, yieldCurveNames[0],
                getPaymentYearFraction(), getNotional(), _index, fixingPeriodStartTime, fixingPeriodEndTime,
                fixingAccrualFactorTotal, getNotional(), yieldCurveNames[1]);
        return cpn;
    }

    @Override
    public Coupon toDerivative(final ZonedDateTime valZdt,
            final DoubleTimeSeries<ZonedDateTime> indexFixingTimeSeries, final String... yieldCurveNames) {
        Validate.isTrue(yieldCurveNames.length > 1, "at least two curves required");
        Validate.notNull(valZdt, "valZdt - valuation date as ZonedDateTime");
        final LocalDate valDate = valZdt.toLocalDate();
        Validate.isTrue(!valDate.isAfter(getPaymentDate().toLocalDate()), "valuation date is after payment date");
        final LocalDate firstPublicationDate = _fixingPeriodDate[_index.getPublicationLag()].toLocalDate(); // This is often one business day following the first fixing date
        if (valDate.isBefore(firstPublicationDate)) {
            return toDerivative(valZdt, yieldCurveNames);
        }

        // FIXME Historical time series do not have time information to begin with.
        // Remove this in FixedIncomeConverterDataProvider.getIndexTimeSeries st indexFixingTimeSeries contains dates when it gets here
        final LocalDateDoubleTimeSeries indexFixingDateSeries = indexFixingTimeSeries.toDateDoubleTimeSeries()
                .toLocalDateDoubleTimeSeries(TimeZone.UTC);

        // Accrue notional for fixings before today; up to and including yesterday
        int fixedPeriod = 0;
        double accruedNotional = getNotional();
        while (valDate.isAfter(_fixingPeriodDate[fixedPeriod + _index.getPublicationLag()].toLocalDate())
                && (fixedPeriod < _fixingPeriodDate.length - 1)) {

            final Double fixedRate = indexFixingDateSeries.getValue(_fixingPeriodDate[fixedPeriod].toLocalDate());

            if (fixedRate == null) {
                throw new OpenGammaRuntimeException(
                        "Could not get fixing value for date " + _fixingPeriodDate[fixedPeriod]);
            }
            accruedNotional *= 1 + _fixingPeriodAccrualFactor[fixedPeriod] * fixedRate;
            fixedPeriod++;
        }

        final double paymentTime = TimeCalculator.getTimeBetween(valZdt, getPaymentDate());
        if (fixedPeriod < _fixingPeriodDate.length - 1) { // Some OIS period left
            // Check to see if a fixing is available on current date
            final Double fixedRate = indexFixingDateSeries.getValue(_fixingPeriodDate[fixedPeriod].toLocalDate());
            if (fixedRate != null) { // There is!
                accruedNotional *= 1 + _fixingPeriodAccrualFactor[fixedPeriod] * fixedRate;
                fixedPeriod++;
            }
            if (fixedPeriod < _fixingPeriodDate.length - 1) { // More OIS period left
                final double fixingPeriodStartTime = TimeCalculator.getTimeBetween(valZdt,
                        _fixingPeriodDate[fixedPeriod]);
                final double fixingPeriodEndTime = TimeCalculator.getTimeBetween(valZdt,
                        _fixingPeriodDate[_fixingPeriodDate.length - 1]);
                double fixingAccrualFactorLeft = 0.0;
                for (int loopperiod = fixedPeriod; loopperiod < _fixingPeriodAccrualFactor.length; loopperiod++) {
                    fixingAccrualFactorLeft += _fixingPeriodAccrualFactor[loopperiod];
                }
                final CouponOIS cpn = new CouponOIS(getCurrency(), paymentTime, yieldCurveNames[0],
                        getPaymentYearFraction(), getNotional(), _index, fixingPeriodStartTime, fixingPeriodEndTime,
                        fixingAccrualFactorLeft, accruedNotional, yieldCurveNames[1]);
                return cpn;
            }
            return new CouponFixed(getCurrency(), paymentTime, yieldCurveNames[0], getPaymentYearFraction(),
                    getNotional(), (accruedNotional / getNotional() - 1.0) / getPaymentYearFraction());

        }

        // All fixed already
        return new CouponFixed(getCurrency(), paymentTime, yieldCurveNames[0], getPaymentYearFraction(),
                getNotional(), (accruedNotional / getNotional() - 1.0) / getPaymentYearFraction());
    }

    @Override
    public <U, V> V accept(final InstrumentDefinitionVisitor<U, V> visitor, final U data) {
        return visitor.visitCouponOIS(this, data);
    }

    @Override
    public <V> V accept(final InstrumentDefinitionVisitor<?, V> visitor) {
        return visitor.visitCouponOIS(this);
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = super.hashCode();
        result = prime * result + Arrays.hashCode(_fixingPeriodAccrualFactor);
        result = prime * result + Arrays.hashCode(_fixingPeriodDate);
        result = prime * result + _index.hashCode();
        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 CouponOISDefinition other = (CouponOISDefinition) obj;
        if (!Arrays.equals(_fixingPeriodAccrualFactor, other._fixingPeriodAccrualFactor)) {
            return false;
        }
        if (!Arrays.equals(_fixingPeriodDate, other._fixingPeriodDate)) {
            return false;
        }
        if (!ObjectUtils.equals(_index, other._index)) {
            return false;
        }
        return true;
    }

}