/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.basics.date;
import java.time.LocalDate;
import org.joda.convert.FromString;
import org.joda.convert.ToString;
import com.opengamma.strata.basics.ReferenceData;
import com.opengamma.strata.basics.schedule.Frequency;
import com.opengamma.strata.collect.named.ExtendedEnum;
import com.opengamma.strata.collect.named.Named;
/**
* A convention defining how to calculate fractions of a year.
* <p>
* The purpose of this convention is to define how to convert dates into numeric year fractions.
* The is of use when calculating accrued interest over time.
* <p>
* The most common implementations are provided in {@link DayCounts}.
* Additional implementations may be added by implementing this interface.
* <p>
* All implementations of this interface must be immutable and thread-safe.
*/
public interface DayCount
extends Named {
/**
* Obtains an instance from the specified unique name.
*
* @param uniqueName the unique name
* @return the day count
* @throws IllegalArgumentException if the name is not known
*/
@FromString
public static DayCount of(String uniqueName) {
return extendedEnum().lookup(uniqueName);
}
/**
* Obtains an instance of the 'Bus/252' day count based on a specific calendar.
* <p>
* The 'Bus/252' day count is unusual in that it relies on a specific holiday calendar.
* The calendar is stored within the day count.
* <p>
* To avoid widespread complexity in the system, the holiday calendar associated
* with 'Bus/252' holiday calendars is looked up using the
* {@linkplain ReferenceData#standard() standard reference data}.
* <p>
* This day count is typically used in Brazil.
*
* @param calendar the holiday calendar
* @return the day count
*/
public static DayCount ofBus252(HolidayCalendarId calendar) {
return Business252DayCount.INSTANCE.of(calendar.resolve(ReferenceData.standard()));
}
/**
* Gets the extended enum helper.
* <p>
* This helper allows instances of the day count to be looked up.
* It also provides the complete set of available instances.
*
* @return the extended enum helper
*/
public static ExtendedEnum<DayCount> extendedEnum() {
return DayCounts.ENUM_LOOKUP;
}
//-------------------------------------------------------------------------
/**
* Gets the year fraction between the specified dates.
* <p>
* Given two dates, this method returns the fraction of a year between these
* dates according to the convention. The dates must be in order.
* <p>
* This uses a simple {@link ScheduleInfo} which has the end-of-month convention
* set to true, but throws an exception for other methods.
* Certain implementations of {@code DayCount} need the missing information,
* and thus will throw an exception.
*
* @param firstDate the first date
* @param secondDate the second date, on or after the first date
* @return the year fraction
* @throws IllegalArgumentException if the dates are not in order
* @throws UnsupportedOperationException if the year fraction cannot be obtained
*/
public default double yearFraction(LocalDate firstDate, LocalDate secondDate) {
return yearFraction(firstDate, secondDate, DayCounts.SIMPLE_SCHEDULE_INFO);
}
/**
* Gets the year fraction between the specified dates.
* <p>
* Given two dates, this method returns the fraction of a year between these
* dates according to the convention. The dates must be in order.
*
* @param firstDate the first date
* @param secondDate the second date, on or after the first date
* @param scheduleInfo the schedule information
* @return the year fraction, zero or greater
* @throws IllegalArgumentException if the dates are not in order
* @throws UnsupportedOperationException if the year fraction cannot be obtained
*/
public abstract double yearFraction(LocalDate firstDate, LocalDate secondDate, ScheduleInfo scheduleInfo);
/**
* Gets the relative year fraction between the specified dates.
* <p>
* Given two dates, this method returns the fraction of a year between these
* dates according to the convention.
* The result of this method will be negative if the first date is after the second date.
* The result is calculated using {@link #yearFraction(LocalDate, LocalDate, ScheduleInfo)}.
* <p>
* This uses a simple {@link ScheduleInfo} which has the end-of-month convention
* set to true, but throws an exception for other methods.
* Certain implementations of {@code DayCount} need the missing information,
* and thus will throw an exception.
*
* @param firstDate the first date
* @param secondDate the second date, which may be before the first date
* @return the year fraction, may be negative
* @throws UnsupportedOperationException if the year fraction cannot be obtained
*/
public default double relativeYearFraction(LocalDate firstDate, LocalDate secondDate) {
return relativeYearFraction(firstDate, secondDate, DayCounts.SIMPLE_SCHEDULE_INFO);
}
/**
* Gets the relative year fraction between the specified dates.
* <p>
* Given two dates, this method returns the fraction of a year between these
* dates according to the convention.
* The result of this method will be negative if the first date is after the second date.
* The result is calculated using {@link #yearFraction(LocalDate, LocalDate, ScheduleInfo)}.
*
* @param firstDate the first date
* @param secondDate the second date, which may be before the first date
* @param scheduleInfo the schedule information
* @return the year fraction, may be negative
* @throws UnsupportedOperationException if the year fraction cannot be obtained
*/
public default double relativeYearFraction(LocalDate firstDate, LocalDate secondDate, ScheduleInfo scheduleInfo) {
if (secondDate.isBefore(firstDate)) {
return -yearFraction(secondDate, firstDate, scheduleInfo);
}
return yearFraction(firstDate, secondDate, scheduleInfo);
}
/**
* Calculates the number of days between the specified dates using the rules of this day count.
* <p>
* A day count is typically defines as a count of days divided by a year estimate.
* This method returns the count of days, which is the numerator of the division.
* For example, the 'Act/Act' day count will return the actual number of days between
* the two dates, but the '30/360 ISDA' will return a value based on 30 day months.
*
* @param firstDate the first date
* @param secondDate the second date, which may be before the first date
* @return the number of days, as determined by the day count
*/
public abstract int days(LocalDate firstDate, LocalDate secondDate);
/**
* Gets the name that uniquely identifies this convention.
* <p>
* This name is used in serialization and can be parsed using {@link #of(String)}.
*
* @return the unique name
*/
@ToString
@Override
public abstract String getName();
//-------------------------------------------------------------------------
/**
* Information about the schedule necessary to calculate the day count.
* <p>
* Some {@link DayCount} implementations require additional information about the schedule.
* Implementations of this interface provide that information.
*/
public interface ScheduleInfo {
/**
* Gets the start date of the schedule.
* <p>
* The first date in the schedule.
* If the schedule adjusts for business days, then this is the adjusted date.
* <p>
* This throws an exception by default.
*
* @return the schedule start date
* @throws UnsupportedOperationException if the date cannot be obtained
*/
public default LocalDate getStartDate() {
throw new UnsupportedOperationException("The start date of the schedule is required");
}
/**
* Gets the end date of the schedule.
* <p>
* The last date in the schedule.
* If the schedule adjusts for business days, then this is the adjusted date.
* <p>
* This throws an exception by default.
*
* @return the schedule end date
* @throws UnsupportedOperationException if the date cannot be obtained
*/
public default LocalDate getEndDate() {
throw new UnsupportedOperationException("The end date of the schedule is required");
}
/**
* Gets the end date of the schedule period.
* <p>
* This is called when a day count requires the end date of the schedule period.
* <p>
* This throws an exception by default.
*
* @param date the date to find the period end date for
* @return the period end date
* @throws UnsupportedOperationException if the date cannot be obtained
*/
public default LocalDate getPeriodEndDate(LocalDate date) {
throw new UnsupportedOperationException("The end date of the schedule period is required");
}
/**
* Gets the periodic frequency of the schedule period.
* <p>
* This is called when a day count requires the periodic frequency of the schedule.
* <p>
* This throws an exception by default.
*
* @return the periodic frequency
* @throws UnsupportedOperationException if the frequency cannot be obtained
*/
public default Frequency getFrequency() {
throw new UnsupportedOperationException("The frequency of the schedule is required");
}
/**
* Checks if the end of month convention is in use.
* <p>
* This is called when a day count needs to know whether the end-of-month convention is in use.
* <p>
* This is true by default.
*
* @return true if the end of month convention is in use
*/
public default boolean isEndOfMonthConvention() {
return true;
}
}
}