/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.instrument.future;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang.ObjectUtils;
import org.threeten.bp.LocalDate;
import org.threeten.bp.ZonedDateTime;
import org.threeten.bp.format.DateTimeFormatter;
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.future.derivative.FederalFundsFutureSecurity;
import com.opengamma.analytics.util.time.TimeCalculator;
import com.opengamma.financial.convention.businessday.BusinessDayConvention;
import com.opengamma.financial.convention.businessday.BusinessDayConventions;
import com.opengamma.financial.convention.calendar.Calendar;
import com.opengamma.timeseries.DoubleTimeSeries;
import com.opengamma.timeseries.date.localdate.ImmutableLocalDateDoubleTimeSeries;
import com.opengamma.timeseries.date.localdate.LocalDateDoubleTimeSeries;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.money.Currency;
/**
* Description of an Federal Funds Futures security.
*/
public class FederalFundsFutureSecurityDefinition extends FuturesSecurityDefinition<FederalFundsFutureSecurity>
implements InstrumentDefinitionWithData<FederalFundsFutureSecurity, DoubleTimeSeries<ZonedDateTime>> {
/**
* The OIS-like index on which the future fixes.
*/
private final IndexON _index;
/**
* The dates of the fixing periods (start and end). There is one date more than period.
*/
private final ZonedDateTime[] _fixingPeriodDates;
/**
* The accrual factors (or year fractions) associated to the fixing periods in the Index day count convention.
*/
private final double[] _fixingPeriodAccrualFactor;
/**
* The total accrual factor for all fixing periods. Sum of the elements of _fixingPeriodAccrualFactor.
*/
private double _fixingTotalAccrualFactor;
/**
* The future notional.
*/
private final double _notional;
/**
* The future margining accrual factor. Usually a standardized number of 1/12 for a 30-day future.
*/
private final double _marginAccrualFactor;
/**
* The future name.
*/
private final String _name;
/**
* The preceding business day convention.
*/
private static final BusinessDayConvention BUSINESS_DAY_PRECEDING = BusinessDayConventions.PRECEDING;
/**
* The following business day convention.
*/
private static final BusinessDayConvention BUSINESS_DAY_FOLLOWING = BusinessDayConventions.FOLLOWING;
/** Constructor from all details.
* @param lastTradingDate The future last trading date. Usually the last business day of the month.
* @param index The OIS-like index on which the future fixes.
* @param fixingPeriodDate The dates of the fixing periods (start and end). There is one date more than period.
* @param fixingPeriodAccrualFactor The accrual factors (or year fractions) associated to the fixing periods in the Index day count convention.
* @param notional The future notional.
* @param paymentAccrualFactor The future payment accrual factor. Usually a standardized number of 1/12 for a 30-day future.
* @param name The future name.
*/
public FederalFundsFutureSecurityDefinition(final ZonedDateTime lastTradingDate, final IndexON index, final ZonedDateTime[] fixingPeriodDate,
final double[] fixingPeriodAccrualFactor, final double notional, final double paymentAccrualFactor, final String name) {
super(lastTradingDate);
ArgumentChecker.notNull(lastTradingDate, "Last trading date");
ArgumentChecker.notNull(index, "Index overnight");
ArgumentChecker.notNull(fixingPeriodDate, "Fixing period dates");
ArgumentChecker.notNull(fixingPeriodAccrualFactor, "Fixing period accrual factors");
ArgumentChecker.notNull(name, "Name");
ArgumentChecker.isTrue(fixingPeriodDate.length == fixingPeriodAccrualFactor.length + 1, "Fixing dates length should be fixing accrual factors + 1.");
_index = index;
_fixingPeriodDates = fixingPeriodDate;
_fixingPeriodAccrualFactor = fixingPeriodAccrualFactor;
_notional = notional;
_marginAccrualFactor = paymentAccrualFactor;
_name = name;
_fixingTotalAccrualFactor = 0.0;
for (final double element : _fixingPeriodAccrualFactor) {
_fixingTotalAccrualFactor += element;
}
}
/**
* Builder for a given month. The future start on the first business day of the month and ends on the first business day of the next month.
* The last trading date is the last good business day of the month.
* @param monthDate Any date in the future month.
* @param index The overnight index.
* @param notional The future notional.
* @param paymentAccrualFactor The future payment accrual factor. Usually a standardized number of 1/12 for a 30-day future.
* @param name The future name.
* @param calendar The holiday calendar for the overnight rate.
* @return The future.
*/
public static FederalFundsFutureSecurityDefinition from(final ZonedDateTime monthDate, final IndexON index, final double notional, final double paymentAccrualFactor,
final String name, final Calendar calendar) {
ArgumentChecker.notNull(monthDate, "Reference date");
ArgumentChecker.notNull(index, "Index overnight");
final ZonedDateTime periodFirstDate = BUSINESS_DAY_FOLLOWING.adjustDate(calendar, monthDate.withDayOfMonth(1));
final ZonedDateTime periodLastDate = BUSINESS_DAY_FOLLOWING.adjustDate(calendar, monthDate.withDayOfMonth(1).plusMonths(1));
final ZonedDateTime last = BUSINESS_DAY_PRECEDING.adjustDate(calendar, periodLastDate.minusDays(1));
final List<ZonedDateTime> fixingList = new ArrayList<>();
ZonedDateTime date = periodFirstDate;
while (!date.isAfter(periodLastDate)) {
fixingList.add(date);
date = BUSINESS_DAY_FOLLOWING.adjustDate(calendar, date.plusDays(1));
}
final ZonedDateTime[] fixingDate = fixingList.toArray(new ZonedDateTime[fixingList.size()]);
final double[] fixingAccrualFactor = new double[fixingDate.length - 1];
for (int loopfix = 0; loopfix < fixingDate.length - 1; loopfix++) {
fixingAccrualFactor[loopfix] = index.getDayCount().getDayCountFraction(fixingDate[loopfix], fixingDate[loopfix + 1], calendar);
}
return new FederalFundsFutureSecurityDefinition(last, index, fixingDate, fixingAccrualFactor, notional, paymentAccrualFactor, name);
}
/**
* Builder of the CBOT Federal Funds futures for a given month. The future start on the first business day of the month and ends on the first business day of the next month.
* The last trading date is the last good business day of the month. The notional is 5m. The payment accrual fraction is 1/12. The name is "FF" + month in format "MMMYY".
* @param monthDate Any date in the future month.
* @param index The overnight index.
* @param calendar The holiday calendar for the overnight rate.
* @return The future.
*/
public static FederalFundsFutureSecurityDefinition fromFedFund(final ZonedDateTime monthDate, final IndexON index, final Calendar calendar) {
final double notionalFedFund = 5000000;
final double accrualFedFund = 1.0 / 12.0;
return from(monthDate, index, notionalFedFund, accrualFedFund, "FF" + monthDate.toString(DateTimeFormatter.ofPattern("MMMyy")), calendar);
}
/**
* Gets the OIS-like index on which the future fixes.
* @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.
*/
public ZonedDateTime[] getFixingPeriodDate() {
return _fixingPeriodDates;
}
/**
* 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;
}
/**
* Gets the total accrual factor for all fixing periods.
* @return The accrual factor.
*/
public double getFixingTotalAccrualFactor() {
return _fixingTotalAccrualFactor;
}
/**
* Gets the future notional.
* @return The notional.
*/
public double getNotional() {
return _notional;
}
/**
* Gets the future payment accrual factor. Usually a standardized number of 1/12 for a 30-day future.
* @return The payment accrual factor.
*/
public double getMarginAccrualFactor() {
return _marginAccrualFactor;
}
/**
* Gets the future name.
* @return The future name.
*/
public String getName() {
return _name;
}
/**
* Gets the future currency.
* @return The currency.
*/
public Currency getCurrency() {
return _index.getCurrency();
}
@Override
public String toString() {
return _name + " - index: " + _index.toString() + " - start date: " + _fixingPeriodDates[0].toString(DateTimeFormatter.ofPattern("ddMMMyy"));
}
@Override
public FederalFundsFutureSecurity toDerivative(final ZonedDateTime date) {
ArgumentChecker.notNull(date, "Date");
ArgumentChecker.isTrue(!date.isAfter(_fixingPeriodDates[_index.getPublicationLag()]), "Date should not be after the fixing period start date");
final double lastTradingTime = TimeCalculator.getTimeBetween(date, getLastTradingDate());
final double[] fixingPeriodTime = TimeCalculator.getTimeBetween(date, _fixingPeriodDates);
return new FederalFundsFutureSecurity(_index, 0.0, fixingPeriodTime, lastTradingTime, _fixingPeriodAccrualFactor, _fixingTotalAccrualFactor, _notional, _marginAccrualFactor, _name);
}
/**
* {@inheritDoc}
* @param indexFixingTimeSeries The time series of the ON index. It is used if the date is in the future month.
* The date of the time series is the start fixing period date.
*/
@Override
public FederalFundsFutureSecurity toDerivative(final ZonedDateTime valZdt, final DoubleTimeSeries<ZonedDateTime> indexFixingTimeSeries) {
ArgumentChecker.notNull(valZdt, "valZdt - valuation date as ZonedDateTime");
final LocalDate valDate = valZdt.toLocalDate();
ArgumentChecker.isTrue(!valDate.isAfter(_fixingPeriodDates[_fixingPeriodDates.length - 1].toLocalDate()), "valuation date is after last trading date");
final LocalDate firstPublicationDate = _fixingPeriodDates[_index.getPublicationLag()].toLocalDate(); // This is often one business day following the first fixing date
if (valDate.isBefore(firstPublicationDate)) {
return toDerivative(valZdt);
}
// FIXME Historical time series do not have time information to begin with.
final ZonedDateTime[] instants = indexFixingTimeSeries.timesArray();
final LocalDate[] dates = new LocalDate[indexFixingTimeSeries.size()];
for (int i = 0; i < instants.length; i++) {
dates[i] = instants[i].toLocalDate();
}
final LocalDateDoubleTimeSeries indexFixingDateSeries = ImmutableLocalDateDoubleTimeSeries.of(dates, indexFixingTimeSeries.valuesArray());
final double lastTradingTime = TimeCalculator.getTimeBetween(valZdt, getLastTradingDate());
int fixedPeriod = 0;
double accruedInterest = 0.0;
while ((fixedPeriod < _fixingPeriodDates.length - 1) && valDate.isAfter(_fixingPeriodDates[fixedPeriod + _index.getPublicationLag()].toLocalDate())) {
final LocalDate currentDate = _fixingPeriodDates[fixedPeriod].toLocalDate();
// Fixing should have taken place already
final Double fixedRate = indexFixingDateSeries.getValue(currentDate);
if (fixedRate == null) {
final LocalDate latestDate = indexFixingDateSeries.getLatestTime();
throw new OpenGammaRuntimeException("Could not get fixing value of index " + _index.getName() + " for date " + currentDate + ". The last data is available on " + latestDate);
}
accruedInterest += _fixingPeriodAccrualFactor[fixedPeriod] * fixedRate;
fixedPeriod++;
}
if (fixedPeriod < _fixingPeriodDates.length - 1) { // Some FF period left
final Double fixedRate = indexFixingTimeSeries.getValue(_fixingPeriodDates[fixedPeriod]);
if (fixedRate != null) { // Fixed already
accruedInterest += _fixingPeriodAccrualFactor[fixedPeriod] * fixedRate;
fixedPeriod++;
}
if (fixedPeriod < _fixingPeriodDates.length - 1) { // Some FF period left
final double[] fixingPeriodTime = new double[_fixingPeriodDates.length - fixedPeriod];
final double[] fixingPeriodAccrualFactor = new double[_fixingPeriodDates.length - 1 - fixedPeriod];
for (int loopfix = 0; loopfix < _fixingPeriodDates.length - fixedPeriod; loopfix++) {
fixingPeriodTime[loopfix] = TimeCalculator.getTimeBetween(valZdt, _fixingPeriodDates[loopfix + fixedPeriod]);
}
System.arraycopy(_fixingPeriodAccrualFactor, fixedPeriod, fixingPeriodAccrualFactor, 0, _fixingPeriodDates.length - 1 - fixedPeriod);
return new FederalFundsFutureSecurity(_index, accruedInterest, fixingPeriodTime, lastTradingTime, fixingPeriodAccrualFactor, _fixingTotalAccrualFactor, _notional,
_marginAccrualFactor, _name);
}
return new FederalFundsFutureSecurity(_index, accruedInterest, new double[] {TimeCalculator.getTimeBetween(valZdt, _fixingPeriodDates[_fixingPeriodDates.length - 1]) },
lastTradingTime, new double[0], _fixingTotalAccrualFactor, _notional, _marginAccrualFactor, _name); // Only one period left
}
return new FederalFundsFutureSecurity(_index, accruedInterest, new double[] {TimeCalculator.getTimeBetween(valZdt, _fixingPeriodDates[_fixingPeriodDates.length - 1]) },
lastTradingTime, new double[0], _fixingTotalAccrualFactor, _notional, _marginAccrualFactor, _name); // Only one period left
}
@Override
public <U, V> V accept(final InstrumentDefinitionVisitor<U, V> visitor, final U data) {
ArgumentChecker.notNull(visitor, "visitor");
return visitor.visitFederalFundsFutureSecurityDefinition(this, data);
}
@Override
public <V> V accept(final InstrumentDefinitionVisitor<?, V> visitor) {
ArgumentChecker.notNull(visitor, "visitor");
return visitor.visitFederalFundsFutureSecurityDefinition(this);
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + Arrays.hashCode(_fixingPeriodAccrualFactor);
result = prime * result + Arrays.hashCode(_fixingPeriodDates);
long temp;
temp = Double.doubleToLongBits(_fixingTotalAccrualFactor);
result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result + _index.hashCode();
temp = Double.doubleToLongBits(_marginAccrualFactor);
result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result + _name.hashCode();
temp = Double.doubleToLongBits(_notional);
result = prime * result + (int) (temp ^ (temp >>> 32));
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;
}
FederalFundsFutureSecurityDefinition other = (FederalFundsFutureSecurityDefinition) obj;
if (!Arrays.equals(_fixingPeriodAccrualFactor, other._fixingPeriodAccrualFactor)) {
return false;
}
if (!Arrays.equals(_fixingPeriodDates, other._fixingPeriodDates)) {
return false;
}
if (Double.doubleToLongBits(_fixingTotalAccrualFactor) != Double.doubleToLongBits(other._fixingTotalAccrualFactor)) {
return false;
}
if (!ObjectUtils.equals(_index, other._index)) {
return false;
}
if (Double.doubleToLongBits(_marginAccrualFactor) != Double.doubleToLongBits(other._marginAccrualFactor)) {
return false;
}
if (!ObjectUtils.equals(_name, other._name)) {
return false;
}
if (Double.doubleToLongBits(_notional) != Double.doubleToLongBits(other._notional)) {
return false;
}
return true;
}
}