com.opengamma.analytics.financial.instrument.inflation.CouponInflationZeroCouponInterpolationDefinition.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.analytics.financial.instrument.inflation.CouponInflationZeroCouponInterpolationDefinition.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.inflation;

import java.util.Arrays;

import javax.time.calendar.ZonedDateTime;

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

import com.opengamma.analytics.financial.instrument.InstrumentDefinitionVisitor;
import com.opengamma.analytics.financial.instrument.InstrumentDefinitionWithData;
import com.opengamma.analytics.financial.instrument.index.IndexPrice;
import com.opengamma.analytics.financial.interestrate.inflation.derivative.CouponInflationZeroCouponInterpolation;
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.Payment;
import com.opengamma.analytics.util.time.TimeCalculator;
import com.opengamma.util.money.Currency;
import com.opengamma.util.timeseries.DoubleTimeSeries;

/**
 * Class describing an zero-coupon inflation coupon were the inflation figure are interpolated between monthly inflation figures.
 * The start index value is known when the coupon is traded/issued.
 * The index for a given month is given in the yield curve and in the time series on the first of the month.
 * The pay-off is (Index_End / Index_Start - X) with X=0 for notional payment and X=1 for no notional payment.
 */
public class CouponInflationZeroCouponInterpolationDefinition extends CouponInflationDefinition
        implements InstrumentDefinitionWithData<Payment, DoubleTimeSeries<ZonedDateTime>> {

    /**
     * The reference date for the index at the coupon start. May not be relevant as the index value is known.
     */
    private final ZonedDateTime _referenceStartDate;
    /**
     * The index value at the start of the coupon.
     */
    private final double _indexStartValue;
    /**
     * The reference dates for the index at the coupon end. Two months are required for the interpolation. 
     * There is usually a difference of two or three month between the reference date and the payment date.
     */
    private final ZonedDateTime[] _referenceEndDate;
    /**
     * The weight on the first month index in the interpolation.
     */
    private final double _weight;
    /**
     * The date on which the end index is expected to be known. The index is usually known two week after the end of the reference month. 
     * The date is only an "expected date" as the index publication could be delayed for different reasons. The date should not be enforced to strictly in pricing and instrument creation.
     */
    private final ZonedDateTime _fixingEndDate;
    /**
     * Flag indicating if the notional is paid (true) or not (false).
     */
    private final boolean _payNotional;
    /**
     * The lag in month between the index validity and the coupon dates.
     */
    private final int _monthLag;

    /**
     * Constructor for zero-coupon inflation coupon.
     * @param currency The coupon currency.
     * @param paymentDate The 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 priceIndex The price index associated to the coupon. 
     * @param monthLag The lag in month between the index validity and the coupon dates.
     * @param referenceStartDate The reference date for the index at the coupon start.
     * @param indexStartValue The index value at the start of the coupon.
     * @param referenceEndDate The reference date for the index at the coupon end.
     * @param fixingEndDate The date on which the end index is expected to be known.
     * @param payNotional Flag indicating if the notional is paid (true) or not (false).
     */
    public CouponInflationZeroCouponInterpolationDefinition(Currency currency, ZonedDateTime paymentDate,
            ZonedDateTime accrualStartDate, ZonedDateTime accrualEndDate, double paymentYearFraction,
            double notional, IndexPrice priceIndex, int monthLag, ZonedDateTime referenceStartDate,
            double indexStartValue, ZonedDateTime[] referenceEndDate, ZonedDateTime fixingEndDate,
            boolean payNotional) {
        super(currency, paymentDate, accrualStartDate, accrualEndDate, paymentYearFraction, notional, priceIndex);
        Validate.notNull(referenceStartDate, "Reference start date");
        Validate.notNull(referenceEndDate, "Reference end date");
        Validate.notNull(fixingEndDate, "Fixing end date");
        this._referenceStartDate = referenceStartDate;
        this._indexStartValue = indexStartValue;
        this._referenceEndDate = referenceEndDate;
        this._fixingEndDate = fixingEndDate;
        _weight = 1.0 - (getPaymentDate().getDayOfMonth() - 1.0)
                / getPaymentDate().getMonthOfYear().getLastDayOfMonth(getPaymentDate().isLeapYear());
        _payNotional = payNotional;
        _monthLag = monthLag;
    }

    /**
     * Builder for inflation zero-coupon. 
     * The accrualStartDate is used for the referenceStartDate. The paymentDate is used for accrualEndDate. The paymentYearFraction is 1.0.
     * @param accrualStartDate Start date of the accrual period.
     * @param paymentDate The payment date.
     * @param notional Coupon notional.
     * @param priceIndex The price index associated to the coupon. 
     * @param monthLag The lag in month between the index validity and the coupon dates.
     * @param indexStartValue The index value at the start of the coupon.
     * @param referenceEndDate The reference date for the index at the coupon end.
     * @param fixingEndDate The date on which the end index is expected to be known.
     * @param payNotional Flag indicating if the notional is paid (true) or not (false).
     * @return The coupon.
     */
    public static CouponInflationZeroCouponInterpolationDefinition from(final ZonedDateTime accrualStartDate,
            final ZonedDateTime paymentDate, final double notional, final IndexPrice priceIndex, int monthLag,
            double indexStartValue, ZonedDateTime[] referenceEndDate, ZonedDateTime fixingEndDate,
            boolean payNotional) {
        Validate.notNull(priceIndex, "Price index");
        return new CouponInflationZeroCouponInterpolationDefinition(priceIndex.getCurrency(), paymentDate,
                accrualStartDate, paymentDate, 1.0, notional, priceIndex, monthLag, accrualStartDate,
                indexStartValue, referenceEndDate, fixingEndDate, payNotional);
    }

    /**
     * Builder for inflation zero-coupon based on an inflation lag and the index publication lag. The fixing date is the publication lag after the last reference month.
     * @param accrualStartDate Start date of the accrual period.
     * @param paymentDate The payment date.
     * @param notional Coupon notional.
     * @param priceIndex The price index associated to the coupon. 
     * @param indexStartValue The index value at the start of the coupon.
     * @param monthLag The lag in month between the index validity and the coupon dates.
     * @param payNotional Flag indicating if the notional is paid (true) or not (false).
     * @return The inflation zero-coupon.
     */
    public static CouponInflationZeroCouponInterpolationDefinition from(final ZonedDateTime accrualStartDate,
            final ZonedDateTime paymentDate, final double notional, final IndexPrice priceIndex,
            final double indexStartValue, final int monthLag, boolean payNotional) {
        ZonedDateTime referenceStartDate = accrualStartDate.minusMonths(monthLag);
        ZonedDateTime refInterpolatedDate = paymentDate.minusMonths(monthLag);
        ZonedDateTime[] referenceEndDate = new ZonedDateTime[2];
        referenceEndDate[0] = refInterpolatedDate.withDayOfMonth(1);
        referenceEndDate[1] = referenceEndDate[0].plusMonths(1);
        ZonedDateTime fixingDate = referenceEndDate[1].minusDays(1).withDayOfMonth(1).plusMonths(2)
                .plus(priceIndex.getPublicationLag());
        return new CouponInflationZeroCouponInterpolationDefinition(priceIndex.getCurrency(), paymentDate,
                accrualStartDate, paymentDate, 1.0, notional, priceIndex, monthLag, referenceStartDate,
                indexStartValue, referenceEndDate, fixingDate, payNotional);
    }

    /**
     * Builder for inflation zero-coupon based on an inflation lag and the index publication lag. The fixing date is the publication lag after the last reference month.
     * The index start value is calculated from a time series. The index value required for the coupon should be in the time series.
     * @param accrualStartDate Start date of the accrual period.
     * @param paymentDate The payment date.
     * @param notional Coupon notional.
     * @param priceIndex The price index associated to the coupon. 
     * @param priceIndexTimeSeries The time series with the relevant price index values.
     * @param monthLag The lag in month between the index validity and the coupon dates.
     * @param payNotional Flag indicating if the notional is paid (true) or not (false).
     * @return The inflation zero-coupon.
     */
    public static CouponInflationZeroCouponInterpolationDefinition from(final ZonedDateTime accrualStartDate,
            final ZonedDateTime paymentDate, final double notional, final IndexPrice priceIndex,
            final DoubleTimeSeries<ZonedDateTime> priceIndexTimeSeries, final int monthLag, boolean payNotional) {
        ZonedDateTime refInterpolatedDate = accrualStartDate.minusMonths(monthLag);
        ZonedDateTime[] referenceStartDate = new ZonedDateTime[2];
        referenceStartDate[0] = refInterpolatedDate.withDayOfMonth(1);
        referenceStartDate[1] = referenceStartDate[0].plusMonths(1);
        double weightStart = 1.0 - (accrualStartDate.getDayOfMonth() - 1.0)
                / accrualStartDate.getMonthOfYear().getLastDayOfMonth(accrualStartDate.isLeapYear());
        Double[] knownIndex = new Double[] { priceIndexTimeSeries.getValue(referenceStartDate[0]),
                priceIndexTimeSeries.getValue(referenceStartDate[1]) };
        double indexStartValue = 0;
        if ((knownIndex[0] != null) && (knownIndex[1] != null)) { // Fixing fully known
            indexStartValue = weightStart * knownIndex[0] + (1 - weightStart) * knownIndex[1]; // Interpolated index
        } else {
            Validate.isTrue(false, "Required price index fixing unavailable");
        }
        return from(accrualStartDate, paymentDate, notional, priceIndex, indexStartValue, monthLag, payNotional);
    }

    /**
     * Gets the reference date for the index at the coupon start.
     * @return The reference date for the index at the coupon start.
     */
    public ZonedDateTime getReferenceStartDate() {
        return _referenceStartDate;
    }

    /**
     * Gets the index value at the start of the coupon.
     * @return The index value at the start of the coupon.
     */
    public double getIndexStartValue() {
        return _indexStartValue;
    }

    /**
     * Gets the reference dates for the index at the coupon end.
     * @return The reference date for the index at the coupon end.
     */
    public ZonedDateTime[] getReferenceEndDate() {
        return _referenceEndDate;
    }

    /**
     * Gets the date on which the end index is expected to be known.
     * @return The date on which the end index is expected to be known.
     */
    public ZonedDateTime getFixingEndDate() {
        return _fixingEndDate;
    }

    /**
     * Gets the weight on the first month index in the interpolation.
     * @return The weight.
     */
    public double getWeight() {
        return _weight;
    }

    /**
     * Gets the pay notional flag.
     * @return The flag.
     */
    public boolean payNotional() {
        return _payNotional;
    }

    /**
     * Gets the lag in month between the index validity and the coupon dates.
     * @return The lag.
     */
    public int getMonthLag() {
        return _monthLag;
    }

    @Override
    public CouponInflationDefinition with(ZonedDateTime paymentDate, ZonedDateTime accrualStartDate,
            ZonedDateTime accrualEndDate, double notional) {
        ZonedDateTime refInterpolatedDate = accrualEndDate.minusMonths(_monthLag);
        ZonedDateTime[] referenceEndDate = new ZonedDateTime[2];
        referenceEndDate[0] = refInterpolatedDate.withDayOfMonth(1);
        referenceEndDate[1] = referenceEndDate[0].plusMonths(1);
        ZonedDateTime fixingDate = referenceEndDate[1].minusDays(1).withDayOfMonth(1).plusMonths(2)
                .plus(getPriceIndex().getPublicationLag());
        return new CouponInflationZeroCouponInterpolationDefinition(getCurrency(), paymentDate, accrualStartDate,
                accrualEndDate, getPaymentYearFraction(), getNotional(), getPriceIndex(), _monthLag,
                getReferenceStartDate(), getIndexStartValue(), referenceEndDate, fixingDate, payNotional());
    }

    @Override
    public CouponInflationZeroCouponInterpolation toDerivative(ZonedDateTime date, String... yieldCurveNames) {
        Validate.notNull(date, "date");
        Validate.isTrue(!date.isAfter(getPaymentDate()),
                "Do not have any fixing data but are asking for a derivative after the payment date");
        Validate.notNull(yieldCurveNames, "yield curve names");
        Validate.isTrue(yieldCurveNames.length > 0, "at least one curve required");
        Validate.isTrue(!date.isAfter(getPaymentDate()), "date is after payment date");
        double paymentTime = TimeCalculator.getTimeBetween(date, getPaymentDate());
        double[] referenceEndTime = new double[2];
        referenceEndTime[0] = TimeCalculator.getTimeBetween(date, getReferenceEndDate()[0]);
        referenceEndTime[1] = TimeCalculator.getTimeBetween(date, getReferenceEndDate()[1]);
        double fixingTime = TimeCalculator.getTimeBetween(date, getFixingEndDate());
        final String discountingCurveName = yieldCurveNames[0];
        return new CouponInflationZeroCouponInterpolation(getCurrency(), paymentTime, discountingCurveName,
                getPaymentYearFraction(), getNotional(), getPriceIndex(), _indexStartValue, referenceEndTime,
                _weight, fixingTime, _payNotional);
    }

    @Override
    public Coupon toDerivative(ZonedDateTime date, DoubleTimeSeries<ZonedDateTime> priceIndexTimeSeries,
            String... yieldCurveNames) {
        Validate.notNull(date, "date");
        Validate.notNull(yieldCurveNames, "yield curve names");
        Validate.isTrue(yieldCurveNames.length > 0, "at least one curve required");
        Validate.isTrue(!date.isAfter(getPaymentDate()), "date is after payment date");
        double paymentTime = TimeCalculator.getTimeBetween(date, getPaymentDate());
        final String discountingCurveName = yieldCurveNames[0];
        boolean fixingKnown = false;
        double rate = 0.0;
        if (!_fixingEndDate.isAfter(date)) { // Fixing data to be checked
            Double[] knownIndex = new Double[] { priceIndexTimeSeries.getValue(_referenceEndDate[0]),
                    priceIndexTimeSeries.getValue(_referenceEndDate[1]) };
            if ((knownIndex[0] != null) && (knownIndex[1] != null)) { // Fixing fully known
                fixingKnown = true;
                double indexEnd = _weight * knownIndex[0] + (1 - _weight) * knownIndex[1]; // Interpolated index
                rate = indexEnd / _indexStartValue - (_payNotional ? 0.0 : 1.0);
            }

        }
        if (fixingKnown) {
            return new CouponFixed(getCurrency(), paymentTime, discountingCurveName, 1.0, getNotional(), rate);
        }
        double fixingTime = 0; // The reference index is expected to be known but is not known yet.
        if (_fixingEndDate.isAfter(date)) {
            fixingTime = TimeCalculator.getTimeBetween(date, _fixingEndDate);
        }
        double[] referenceEndTime = new double[2];
        referenceEndTime[0] = TimeCalculator.getTimeBetween(date, _referenceEndDate[0]);
        referenceEndTime[1] = TimeCalculator.getTimeBetween(date, _referenceEndDate[1]);
        return new CouponInflationZeroCouponInterpolation(getCurrency(), paymentTime, discountingCurveName,
                getPaymentYearFraction(), getNotional(), getPriceIndex(), _indexStartValue, referenceEndTime,
                _weight, fixingTime, _payNotional);
    }

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

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

    @Override
    public String toString() {
        return super.toString() + ", reference=[" + _referenceEndDate[0] + ", " + _referenceEndDate[1]
                + "], fixing=" + _fixingEndDate;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = super.hashCode();
        result = prime * result + _fixingEndDate.hashCode();
        long temp;
        temp = Double.doubleToLongBits(_indexStartValue);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        result = prime * result + Arrays.hashCode(_referenceEndDate);
        result = prime * result + _referenceStartDate.hashCode();
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!super.equals(obj)) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        CouponInflationZeroCouponInterpolationDefinition other = (CouponInflationZeroCouponInterpolationDefinition) obj;
        if (!ObjectUtils.equals(_fixingEndDate, other._fixingEndDate)) {
            return false;
        }
        if (Double.doubleToLongBits(_indexStartValue) != Double.doubleToLongBits(other._indexStartValue)) {
            return false;
        }
        if (!Arrays.equals(_referenceEndDate, other._referenceEndDate)) {
            return false;
        }
        if (!ObjectUtils.equals(_referenceStartDate, other._referenceStartDate)) {
            return false;
        }
        return true;
    }

}