/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.analytics.ircurve;
import java.io.Serializable;
import org.apache.commons.lang.ObjectUtils;
import org.fudgemsg.FudgeField;
import org.fudgemsg.FudgeMsg;
import org.fudgemsg.MutableFudgeMsg;
import org.fudgemsg.mapping.FudgeDeserializer;
import org.fudgemsg.mapping.FudgeSerializer;
import com.opengamma.financial.fudgemsg.FixedIncomeStripFudgeBuilder;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.time.DateUtils;
import com.opengamma.util.time.Tenor;
/**
* A fixed income strip. <b>Note that the futures are assumed to be quarterly.</b>
*/
public class FixedIncomeStrip implements Serializable, Comparable<FixedIncomeStrip> {
private static final long serialVersionUID = 1L;
private final StripInstrumentType _instrumentType;
private final Tenor _curveNodePointTime;
private final String _conventionName;
private final int _nthFutureFromTenor;
private final int _periodsPerYear;
private final Tenor _payTenor;
private final Tenor _receiveTenor;
private final Tenor _resetTenor;
private final IndexType _payIndexType;
private final IndexType _receiveIndexType;
private final IndexType _indexType;
private final FixedIncomeStrip _strip1;
private final FixedIncomeStrip _strip2;
private final OperationType _operation;
/**
* Creates a strip for non-future and non-basis swap instruments.
*
* @param instrumentType the instrument type
* @param curveNodePointTime the time of the curve node point
* @param conventionName the name of the yield curve specification builder configuration
*/
public FixedIncomeStrip(final StripInstrumentType instrumentType, final Tenor curveNodePointTime, final String conventionName) {
ArgumentChecker.notNull(instrumentType, "InstrumentType");
ArgumentChecker.isTrue(instrumentType != StripInstrumentType.FUTURE, "Cannot handle futures in this constructor");
ArgumentChecker.isTrue(instrumentType != StripInstrumentType.PERIODIC_ZERO_DEPOSIT, "Cannot handle periodic zero deposits in this constructor");
ArgumentChecker.isTrue(instrumentType != StripInstrumentType.BASIS_SWAP, "Cannot handle basis swaps in this constructor");
ArgumentChecker.notNull(curveNodePointTime, "Tenor");
ArgumentChecker.notNull(conventionName, "ConventionName");
_instrumentType = instrumentType;
_curveNodePointTime = curveNodePointTime;
_nthFutureFromTenor = 0;
_periodsPerYear = 0;
_conventionName = conventionName;
_payTenor = null;
_receiveTenor = null;
_payIndexType = null;
_receiveIndexType = null;
_resetTenor = null;
_indexType = null;
_strip1 = null;
_strip2 = null;
_operation = null;
}
/**
* Creates a future strip.
*
* @param instrumentType the instrument type
* @param curveNodePointTime the time of the curve node point
* @param conventionName the name of the convention to use to resolve the strip into a security
* @param nthFutureFromTenor how many futures to step through from the curveDate + the tenor. 1-based, must be >0.
* e.g. 3 (tenor = 1YR) => 3rd quarterly future after curveDate + 1YR.
*/
public FixedIncomeStrip(final StripInstrumentType instrumentType, final Tenor curveNodePointTime, final int nthFutureFromTenor, final String conventionName) {
ArgumentChecker.isTrue(instrumentType == StripInstrumentType.FUTURE, "Strip type for this constructor must be a future");
ArgumentChecker.notNull(curveNodePointTime, "Tenor");
ArgumentChecker.isTrue(nthFutureFromTenor > 0, "Number of future must be greater than zero");
ArgumentChecker.notNull(conventionName, "ConventionName");
_instrumentType = instrumentType;
_curveNodePointTime = curveNodePointTime;
_nthFutureFromTenor = nthFutureFromTenor;
_conventionName = conventionName;
_periodsPerYear = 0;
_payTenor = null;
_receiveTenor = null;
_payIndexType = null;
_receiveIndexType = null;
_resetTenor = null;
_indexType = null;
_strip1 = null;
_strip2 = null;
_operation = null;
}
/**
* Creates a zero deposit strip
* @param instrumentType The instrument type
* @param curveNodePointTime The time of the curve node point
* @param periodsPerYear The number of periods per year
* @param isPeriodicZeroDepositStrip Is this instrument a periodic zero deposit strip
* @param conventionName The name of the convention to use to resolve the strip into a security
*/
public FixedIncomeStrip(final StripInstrumentType instrumentType, final Tenor curveNodePointTime, final int periodsPerYear, final boolean isPeriodicZeroDepositStrip,
final String conventionName) {
ArgumentChecker.isTrue(instrumentType == StripInstrumentType.PERIODIC_ZERO_DEPOSIT, "Strip type for this constructor must be a periodic zero deposit");
ArgumentChecker.isTrue(isPeriodicZeroDepositStrip, "Must have flag indicating periodic zero deposit set to true");
ArgumentChecker.notNull(curveNodePointTime, "Tenor");
ArgumentChecker.isTrue(periodsPerYear > 0, "Number of periods per year must be greater than zero");
ArgumentChecker.notNull(conventionName, "ConventionName");
_instrumentType = instrumentType;
_curveNodePointTime = curveNodePointTime;
_periodsPerYear = periodsPerYear;
_nthFutureFromTenor = 0;
_conventionName = conventionName;
_payTenor = null;
_receiveTenor = null;
_payIndexType = null;
_receiveIndexType = null;
_resetTenor = null;
_indexType = null;
_strip1 = null;
_strip2 = null;
_operation = null;
}
/**
* Creates a basis swap strip where the two legs are on the same type of index (e.g. a USD 3M Fed Funds / 6M Libor swap)
* @param instrumentType The instrument type
* @param curveNodePointTime The time of the curve node point
* @param payTenor The pay tenor
* @param receiveTenor The receive tenor
* @param payIndexType The pay index type
* @param receiveIndexType The receive index type
* @param conventionName The name of the convention to use to resolve the strip into a security
*/
public FixedIncomeStrip(final StripInstrumentType instrumentType, final Tenor curveNodePointTime, final Tenor payTenor, final Tenor receiveTenor,
final IndexType payIndexType, final IndexType receiveIndexType, final String conventionName) {
ArgumentChecker.isTrue(instrumentType == StripInstrumentType.BASIS_SWAP, "Strip type for this constructor must be a basis swap");
ArgumentChecker.notNull(curveNodePointTime, "curve node tenor");
ArgumentChecker.notNull(payTenor, "pay tenor");
ArgumentChecker.notNull(receiveTenor, "receive tenor");
ArgumentChecker.notNull(conventionName, "convention name");
_instrumentType = instrumentType;
_curveNodePointTime = curveNodePointTime;
_periodsPerYear = 0;
_nthFutureFromTenor = 0;
_conventionName = conventionName;
_payTenor = payTenor;
_receiveTenor = receiveTenor;
_payIndexType = payIndexType;
_receiveIndexType = receiveIndexType;
_resetTenor = null;
_indexType = null;
_strip1 = null;
_strip2 = null;
_operation = null;
}
/**
* Creates a basis swap strip where the two legs are on the same type of index (e.g. a USD 3M Fed Funds / 6M Libor swap)
* @param instrumentType The instrument type
* @param curveNodePointTime The time of the curve node point
* @param resetTenor The reset tenor
* @param indexType The index type
* @param conventionName The name of the convention to use to resolve the strip into a security
*/
public FixedIncomeStrip(final StripInstrumentType instrumentType, final Tenor curveNodePointTime, final Tenor resetTenor,
final IndexType indexType, final String conventionName) {
ArgumentChecker.notNull(curveNodePointTime, "curve node tenor");
ArgumentChecker.notNull(resetTenor, "reset tenor");
ArgumentChecker.notNull(conventionName, "convention name");
_instrumentType = instrumentType;
_curveNodePointTime = curveNodePointTime;
_periodsPerYear = 0;
_nthFutureFromTenor = 0;
_conventionName = conventionName;
_resetTenor = resetTenor;
_indexType = indexType;
_payTenor = null;
_receiveTenor = null;
_payIndexType = null;
_receiveIndexType = null;
_strip1 = null;
_strip2 = null;
_operation = null;
}
public FixedIncomeStrip(final FixedIncomeStrip strip1, final FixedIncomeStrip strip2, final OperationType operation, final Tenor curveNodePointTime,
final String conventionName) {
ArgumentChecker.notNull(strip1, "strip 1");
ArgumentChecker.notNull(strip2, "strip 2");
ArgumentChecker.notNull(operation, "operation");
ArgumentChecker.notNull(curveNodePointTime, "curve node point time");
ArgumentChecker.notNull(conventionName, "convention name");
_strip1 = strip1;
_strip2 = strip2;
_operation = operation;
_curveNodePointTime = curveNodePointTime;
_conventionName = conventionName;
_instrumentType = StripInstrumentType.SPREAD;
_periodsPerYear = 0;
_nthFutureFromTenor = 0;
_payTenor = null;
_receiveTenor = null;
_payIndexType = null;
_receiveIndexType = null;
_resetTenor = null;
_indexType = null;
}
//-------------------------------------------------------------------------
/**
* Gets the instrument type used to construct this strip.
*
* @return the instrument type, not null
*/
public StripInstrumentType getInstrumentType() {
if (_strip1 != null) {
throw new IllegalStateException("Cannot get strip instrument type for a spread strip " + toString());
}
return _instrumentType;
}
/**
* Gets the curve node point in time.
*
* @return a tenor representing the time of the curve node point, not null
*/
public Tenor getCurveNodePointTime() {
return _curveNodePointTime;
}
/**
* Get the number of the quarterly IR futures after the tenor to choose.
* NOTE: THIS DOESN'T REFER TO A GENERIC FUTURE
*
* @return the number of futures after the tenor
* @throws IllegalStateException if called on a non-future strip
*/
public int getNumberOfFuturesAfterTenor() {
if (_instrumentType != StripInstrumentType.FUTURE) {
throw new IllegalStateException("Cannot get number of futures after tenor for a non-future strip " + toString());
}
return _nthFutureFromTenor;
}
/**
* Get the periods per year of a periodic zero deposit security
*
* @return the number of periods per year
* @throws IllegalStateException if called on a non-periodic zero deposit strip
*/
public int getPeriodsPerYear() {
if (_instrumentType != StripInstrumentType.PERIODIC_ZERO_DEPOSIT) {
throw new IllegalStateException("Cannot get number of periods per year for a non-periodic zero deposit strip " + toString());
}
return _periodsPerYear;
}
/**
* Gets the name of the convention used to resolve this strip definition into a security.
*
* @return the name, not null
*/
public String getConventionName() {
return _conventionName;
}
/**
* Calculates the tenor of a strip. For all instruments except futures, this is the same as that entered on construction.
* For futures, this is the start tenor + (3 * future number)
* @return The effective tenor of the strip
*/
public Tenor getEffectiveTenor() {
return Tenor.of(getInstrumentType() == StripInstrumentType.FUTURE ?
getCurveNodePointTime().getPeriod().plusMonths(3 * getNumberOfFuturesAfterTenor()) : getCurveNodePointTime().getPeriod());
}
/**
* Gets the pay tenor for a basis swap
* @return The pay tenor
*/
public Tenor getPayTenor() {
if (_instrumentType != StripInstrumentType.BASIS_SWAP) {
throw new IllegalStateException("Cannot get the pay tenor for an instrument that is not a basis swap; have " + toString());
}
return _payTenor;
}
/**
* Gets the receive tenor for a basis swap
* @return The receive tenor
*/
public Tenor getReceiveTenor() {
if (_instrumentType != StripInstrumentType.BASIS_SWAP) {
throw new IllegalStateException("Cannot get the receive tenor for an instrument that is not a basis swap; have " + toString());
}
return _receiveTenor;
}
/**
* Gets the reset tenor.
* @return The reset tenor
*/
public Tenor getResetTenor() {
return _resetTenor;
}
/**
* Gets the pay index type for a basis swap
* @return The pay index type
*/
public IndexType getPayIndexType() {
if (_instrumentType != StripInstrumentType.BASIS_SWAP) {
throw new IllegalStateException("Cannot get the pay index type for an instrument that is not a basis swap; have " + toString());
}
return _payIndexType;
}
/**
* Gets the receive tenor for a basis swap
* @return The receive tenor
*/
public IndexType getReceiveIndexType() {
if (_instrumentType != StripInstrumentType.BASIS_SWAP) {
throw new IllegalStateException("Cannot get the receive index type for an instrument that is not a basis swap; have " + toString());
}
return _receiveIndexType;
}
/**
* Gets the index type.
* @return The receive tenor
*/
public IndexType getIndexType() {
return _indexType;
}
public FixedIncomeStrip getStrip1() {
if (_instrumentType != StripInstrumentType.SPREAD) {
throw new IllegalStateException("Cannot get the first strip for an instrument that is not a spread strip " + toString());
}
return _strip1;
}
public FixedIncomeStrip getStrip2() {
if (_instrumentType != StripInstrumentType.SPREAD) {
throw new IllegalStateException("Cannot get the second strip for an instrument that is not a spread strip " + toString());
}
return _strip2;
}
public OperationType getOperation() {
if (_instrumentType != StripInstrumentType.SPREAD) {
throw new IllegalStateException("Cannot get the operation for an instrument that is not a spread strip " + toString());
}
return _operation;
}
//-------------------------------------------------------------------------
@Override
public int compareTo(final FixedIncomeStrip other) {
int result = DateUtils.estimatedDuration(getEffectiveTenor().getPeriod()).compareTo(DateUtils.estimatedDuration(other.getEffectiveTenor().getPeriod()));
if (result != 0) {
return result;
}
if (_instrumentType == StripInstrumentType.SPREAD) {
result = getStrip1().compareTo(other.getStrip1());
if (result != 0) {
return result;
}
result = getStrip2().compareTo(other.getStrip2());
if (result != 0) {
return result;
}
return getOperation().ordinal() - other.getOperation().ordinal();
}
result = getInstrumentType().ordinal() - other.getInstrumentType().ordinal();
if (result != 0) {
return result;
} else if (getInstrumentType() == StripInstrumentType.FUTURE) {
result = getNumberOfFuturesAfterTenor() - other.getNumberOfFuturesAfterTenor();
} else if (getInstrumentType() == StripInstrumentType.PERIODIC_ZERO_DEPOSIT) {
result = getPeriodsPerYear() - other.getPeriodsPerYear();
} else if (getInstrumentType() == StripInstrumentType.SWAP || getInstrumentType() == StripInstrumentType.OIS_SWAP && getIndexType() != null) {
result = ObjectUtils.compare(getResetTenor(), other.getResetTenor());
if (result != 0) {
return result;
}
return ObjectUtils.compare(getIndexType(), other.getIndexType());
} else if (getInstrumentType() == StripInstrumentType.BASIS_SWAP) {
result = getPayTenor().compareTo(other.getPayTenor());
if (result != 0) {
return result;
}
result = ObjectUtils.compare(getReceiveTenor(), other.getReceiveTenor());
if (result != 0) {
return result;
}
result = ObjectUtils.compare(getPayIndexType(), other.getPayIndexType());
if (result != 0) {
return result;
}
result = ObjectUtils.compare(getReceiveIndexType(), other.getReceiveIndexType());
}
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof FixedIncomeStrip) {
final FixedIncomeStrip other = (FixedIncomeStrip) obj;
if (_instrumentType == StripInstrumentType.SPREAD) {
return ObjectUtils.equals(_curveNodePointTime, other._curveNodePointTime) &&
ObjectUtils.equals(_conventionName, other._conventionName) &&
ObjectUtils.equals(_strip1, other._strip1) &&
ObjectUtils.equals(_strip2, other._strip2) &&
_operation == other._operation;
}
final boolean result = ObjectUtils.equals(_curveNodePointTime, other._curveNodePointTime) &&
ObjectUtils.equals(_conventionName, other._conventionName) &&
_instrumentType == other._instrumentType;
if (getInstrumentType() == StripInstrumentType.FUTURE) {
return result && _nthFutureFromTenor == other._nthFutureFromTenor;
}
if (getInstrumentType() == StripInstrumentType.PERIODIC_ZERO_DEPOSIT) {
return result && _periodsPerYear == other._periodsPerYear;
}
if (getInstrumentType() == StripInstrumentType.SWAP || getInstrumentType() == StripInstrumentType.OIS_SWAP && getIndexType() != null) {
return result &&
ObjectUtils.equals(getResetTenor(), other.getResetTenor()) &&
ObjectUtils.equals(getIndexType(), other.getIndexType());
}
if (getInstrumentType() == StripInstrumentType.BASIS_SWAP) {
return result &&
ObjectUtils.equals(getPayTenor(), other.getPayTenor()) &&
ObjectUtils.equals(getReceiveTenor(), other.getReceiveTenor()) &&
getPayIndexType() == other.getPayIndexType() &&
getReceiveIndexType() == other.getReceiveIndexType();
}
return result;
}
return false;
}
@Override
public int hashCode() {
return _curveNodePointTime.hashCode();
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("FixedIncomeStrip[");
if (_instrumentType == StripInstrumentType.SPREAD) {
sb.append(_strip1.toString());
sb.append(_operation.getSymbol());
sb.append(_strip2.toString());
sb.append("]");
return sb.toString();
}
sb.append("instrument type=");
sb.append(getInstrumentType());
sb.append(", ");
sb.append("tenor=");
sb.append(getCurveNodePointTime());
sb.append(", ");
if (getInstrumentType() == StripInstrumentType.FUTURE) {
sb.append("future number after tenor=");
sb.append(getNumberOfFuturesAfterTenor());
sb.append(", ");
} else if (getInstrumentType() == StripInstrumentType.PERIODIC_ZERO_DEPOSIT) {
sb.append("periods per year=");
sb.append(getPeriodsPerYear());
sb.append(", ");
} else if (getInstrumentType() == StripInstrumentType.SWAP || getInstrumentType() == StripInstrumentType.OIS_SWAP && getIndexType() != null) {
sb.append("reset tenor=");
sb.append(getResetTenor());
sb.append(" on ");
sb.append(getIndexType());
sb.append(", ");
} else if (getInstrumentType() == StripInstrumentType.BASIS_SWAP) {
sb.append("pay tenor=");
sb.append(getPayTenor());
sb.append(" on ");
sb.append(getPayIndexType());
sb.append(", receive tenor=");
sb.append(getReceiveTenor());
sb.append(" on ");
sb.append(getReceiveIndexType());
sb.append(", ");
}
sb.append("convention name=");
sb.append(getConventionName());
sb.append("]");
return sb.toString();
}
//-------------------------------------------------------------------------
// REVIEW: jim 22-Aug-2010 -- get rid of these and use the builder directly
public void toFudgeMsg(final FudgeSerializer serializer, final MutableFudgeMsg message) {
final FixedIncomeStripFudgeBuilder builder = new FixedIncomeStripFudgeBuilder();
final MutableFudgeMsg container = builder.buildMessage(serializer, this);
for (final FudgeField field : container.getAllFields()) {
message.add(field);
}
}
// REVIEW: jim 22-Aug-2010 -- get rid of these and use the builder directly
public static FixedIncomeStrip fromFudgeMsg(final FudgeDeserializer deserializer, final FudgeMsg message) {
final FixedIncomeStripFudgeBuilder builder = new FixedIncomeStripFudgeBuilder();
return builder.buildObject(deserializer, message);
}
}