com.opengamma.financial.convention.daycount.AccruedInterestCalculator.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.financial.convention.daycount.AccruedInterestCalculator.java

Source

/**
 * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.financial.convention.daycount;

import java.util.Arrays;

import org.apache.commons.lang.Validate;
import org.threeten.bp.LocalDate;
import org.threeten.bp.ZoneOffset;
import org.threeten.bp.ZonedDateTime;

import com.opengamma.financial.convention.StubCalculator;
import com.opengamma.financial.convention.StubType;
import com.opengamma.financial.convention.calendar.Calendar;

/**
 * Utility to calculate the accrued interest.
 */
public final class AccruedInterestCalculator {

    /**
     * Restricted constructor.
     */
    private AccruedInterestCalculator() {
    }

    /**
     * Calculates the accrued interest for a {@code ZonedDateTime}.
     * 
     * @param dayCount  the day count convention, not null
     * @param settlementDate  the settlement date, not null
     * @param nominalDates  the nominalDates, not null, no null elements
     * @param coupon  the coupon value
     * @param paymentsPerYear  the number of payments per year, one, two, three, four, six or twelve
     * @param isEndOfMonthConvention  whether to use end of month rules
     * @param exDividendDays the number of ex-dividend days
     * @param calendar The working day calendar to be used in calculating ex-dividend dates, not null
     * @return the accrued interest
     */
    public static double getAccruedInterest(final DayCount dayCount, final ZonedDateTime settlementDate,
            final ZonedDateTime[] nominalDates, final double coupon, final int paymentsPerYear,
            final boolean isEndOfMonthConvention, final int exDividendDays, final Calendar calendar) {
        Validate.notNull(dayCount, "day-count");
        Validate.notNull(settlementDate, "date");
        Validate.noNullElements(nominalDates, "nominalDates");
        Validate.notNull(calendar, "calendar");
        Validate.isTrue(paymentsPerYear > 0);
        Validate.isTrue(exDividendDays >= 0);
        final int i = Arrays.binarySearch(nominalDates, settlementDate);
        if (i > 0) {
            return 0;
        }
        final int index = -i - 2;
        final int length = nominalDates.length;
        Validate.isTrue(index >= 0, "Settlement date is before first accrual date");
        Validate.isTrue(index < length, "Settlement date is after maturity date");
        final double accruedInterest = getAccruedInterest(dayCount, index, length, nominalDates[index],
                settlementDate, nominalDates[index + 1], coupon, paymentsPerYear, isEndOfMonthConvention);
        ZonedDateTime exDividendDate = nominalDates[index + 1];
        for (int j = 0; j < exDividendDays; j++) {
            while (!calendar.isWorkingDay(exDividendDate.toLocalDate())) {
                exDividendDate = exDividendDate.minusDays(1);
            }
            exDividendDate = exDividendDate.minusDays(1);
        }
        if (exDividendDays != 0 && exDividendDate.isBefore(settlementDate)) {
            return accruedInterest - coupon;
        }
        return accruedInterest;
    }

    /**
     * Calculates the accrued interest for a {@code ZonedDateTime}.
     * 
     * @param dayCount  the day count convention, not null
     * @param settlementDate  the settlement date, not null
     * @param nominalDates  the nominalDates, not null, no null elements
     * @param coupon  the coupon value
     * @param paymentsPerYear  the number of payments per year, one, two, three, four, six or twelve
     * @param isEndOfMonthConvention  whether to use end of month rules
     * @param exDividendDays the number of ex-dividend days
     * @param index The index of the previous coupon in the nominalDates array
     * @param calendar The working day calendar to be used in calculating ex-dividend dates, not null
     * @return the accrued interest
     */
    public static double getAccruedInterest(final DayCount dayCount, final ZonedDateTime settlementDate,
            final ZonedDateTime[] nominalDates, final double coupon, final double paymentsPerYear,
            final boolean isEndOfMonthConvention, final int exDividendDays, final int index,
            final Calendar calendar) {
        Validate.notNull(dayCount, "day-count");
        Validate.notNull(settlementDate, "date");
        Validate.noNullElements(nominalDates, "nominalDates");
        Validate.notNull(calendar, "calendar");
        Validate.isTrue(paymentsPerYear > 0);
        Validate.isTrue(exDividendDays >= 0);
        final int length = nominalDates.length;
        Validate.isTrue(index >= 0 && index < length);
        final double accruedInterest = getAccruedInterest(dayCount, index, length, nominalDates[index],
                settlementDate, nominalDates[index + 1], coupon, paymentsPerYear, isEndOfMonthConvention);
        ZonedDateTime exDividendDate = nominalDates[index + 1];
        for (int i = 0; i < exDividendDays; i++) {
            while (!calendar.isWorkingDay(exDividendDate.toLocalDate())) {
                exDividendDate = exDividendDate.minusDays(1);
            }
            exDividendDate = exDividendDate.minusDays(1);
        }
        if (exDividendDays != 0 && exDividendDate.isBefore(settlementDate)) {
            return accruedInterest - coupon;
        }
        return accruedInterest;
    }

    /**
     * Calculates the accrued interest for a {@code LocalDate}.
     * 
     * @param dayCount  the day count convention, not null
     * @param settlementDate  the settlement date, not null
     * @param nominalDates  the nominalDates, not null, no null elements
     * @param coupon  the coupon value
     * @param paymentsPerYear  the number of payments per year, one, two, three, four, six or twelve
     * @param isEndOfMonthConvention  whether to use end of month rules
     * @param exDividendDays the number of ex-dividend days
     * @param calendar The working day calendar to be used in calculating ex-dividend dates, not null
     * @return the accrued interest
     */
    //TODO one where you can pass in array of coupons
    public static double getAccruedInterest(final DayCount dayCount, final LocalDate settlementDate,
            final LocalDate[] nominalDates, final double coupon, final double paymentsPerYear,
            final boolean isEndOfMonthConvention, final int exDividendDays, final Calendar calendar) {
        Validate.notNull(dayCount, "day-count");
        Validate.notNull(settlementDate, "date");
        Validate.noNullElements(nominalDates, "nominalDates");
        Validate.notNull(calendar, "calendar");
        Validate.isTrue(paymentsPerYear > 0);
        Validate.isTrue(exDividendDays >= 0);
        final int i = Arrays.binarySearch(nominalDates, settlementDate);
        if (i > 0) {
            return 0;
        }
        final int index = -i - 2;
        final int length = nominalDates.length;
        if (index < 0) {
            throw new IllegalArgumentException("Settlement date is before first accrual date");
        }
        if (index == length) {
            throw new IllegalArgumentException("Settlement date is after maturity date");
        }
        final ZonedDateTime previousCouponDate = nominalDates[index].atStartOfDay(ZoneOffset.UTC);
        final ZonedDateTime date = settlementDate.atStartOfDay(ZoneOffset.UTC);
        final ZonedDateTime nextCouponDate = nominalDates[index + 1].atStartOfDay(ZoneOffset.UTC);
        final double accruedInterest = getAccruedInterest(dayCount, index, length, previousCouponDate, date,
                nextCouponDate, coupon, paymentsPerYear, isEndOfMonthConvention);
        LocalDate exDividendDate = nominalDates[index + 1];
        for (int j = 0; j < exDividendDays; j++) {
            while (!calendar.isWorkingDay(exDividendDate)) {
                exDividendDate = exDividendDate.minusDays(1);
            }
            exDividendDate = exDividendDate.minusDays(1);
        }
        if (exDividendDays != 0 && exDividendDate.isBefore(settlementDate)) {
            return accruedInterest - coupon;
        }
        return accruedInterest;
    }

    /**
     * Calculates the accrued interest for a {@code LocalDate}.
     * 
     * @param dayCount  the day count convention, not null
     * @param settlementDate  the settlement date, not null
     * @param nominalDates  the nominalDates, not null, no null elements
     * @param coupon  the coupon value
     * @param paymentsPerYear  the number of payments per year, one, two, three, four, six or twelve
     * @param isEndOfMonthConvention  whether to use end of month rules
     * @param exDividendDays the number of ex-dividend days
     * @param index The index of the previous coupon in the nominalDates
     * @param calendar The working day calendar to be used in calculating ex-dividend dates, not null
     * @return the accrued interest
     */
    public static double getAccruedInterest(final DayCount dayCount, final LocalDate settlementDate,
            final LocalDate[] nominalDates, final double coupon, final double paymentsPerYear,
            final boolean isEndOfMonthConvention, final int exDividendDays, final int index,
            final Calendar calendar) {
        Validate.notNull(dayCount, "day-count");
        Validate.notNull(settlementDate, "date");
        Validate.noNullElements(nominalDates, "nominalDates");
        Validate.notNull(calendar, "calendar");
        Validate.isTrue(paymentsPerYear > 0);
        Validate.isTrue(exDividendDays >= 0);
        final int length = nominalDates.length;
        Validate.isTrue(index >= 0 && index < length);
        final ZonedDateTime previousCouponDate = nominalDates[index].atStartOfDay(ZoneOffset.UTC);
        final ZonedDateTime date = settlementDate.atStartOfDay(ZoneOffset.UTC);
        final ZonedDateTime nextCouponDate = nominalDates[index + 1].atStartOfDay(ZoneOffset.UTC);
        double accruedInterest;
        if (date.isAfter(nextCouponDate)) {
            accruedInterest = 0;
        } else {
            accruedInterest = getAccruedInterest(dayCount, index, length, previousCouponDate, date, nextCouponDate,
                    coupon, paymentsPerYear, isEndOfMonthConvention);
        }
        LocalDate exDividendDate = nominalDates[index + 1];
        for (int i = 0; i < exDividendDays; i++) {
            while (!calendar.isWorkingDay(exDividendDate)) {
                exDividendDate = exDividendDate.minusDays(1);
            }
            exDividendDate = exDividendDate.minusDays(1);
        }
        if (exDividendDays != 0 && exDividendDate.isBefore(settlementDate)) {
            return accruedInterest - coupon;
        }
        return accruedInterest;
    }

    /**
     * Calculates the accrued interest for a {@code LocalDate}.
     * 
     * @param dayCount  the day count convention, not null
     * @param settlementDate  the settlement date, not null
     * @param nominalDates  the nominalDates, not null, no null elements
     * @param settlementDates  the settlement dates, not null, no null elements
     * @param coupon  the coupon value
     * @param paymentsPerYear  the number of payments per year, one, two, three, four, six or twelve
     * @param isEndOfMonthConvention  whether to use end of month rules
     * @param exDividendDays the number of ex-dividend days
     * @param index The index of the previous coupon in the nominalDates
     * @param calendar The working day calendar used to calculate the ex-dividend date, not null
     * @return the accrued interest
     */
    public static double getAccruedInterest(final DayCount dayCount, final LocalDate settlementDate,
            final LocalDate[] nominalDates, final LocalDate[] settlementDates, final double coupon,
            final double paymentsPerYear, final boolean isEndOfMonthConvention, final int exDividendDays,
            final int index, final Calendar calendar) {
        Validate.notNull(dayCount, "day-count");
        Validate.notNull(settlementDate, "date");
        Validate.notNull(calendar, "calendar");
        Validate.noNullElements(nominalDates, "nominalDates");
        Validate.noNullElements(settlementDates, "settlementDates");
        Validate.isTrue(paymentsPerYear > 0);
        Validate.isTrue(exDividendDays >= 0);
        final int length = nominalDates.length;
        Validate.isTrue(index >= 0 && index < length);
        final LocalDate previousCouponDate = nominalDates[index];
        final LocalDate nextCouponDate = nominalDates[index + 1];
        double accruedInterest;
        if (settlementDate.isAfter(nextCouponDate)) {
            if (settlementDate.isBefore(settlementDates[index + 1])) {
                accruedInterest = coupon;
            } else {
                accruedInterest = 0;
            }
        } else {
            accruedInterest = getAccruedInterest(dayCount, index, length, previousCouponDate, settlementDate,
                    nextCouponDate, coupon, paymentsPerYear, isEndOfMonthConvention);
        }
        LocalDate exDividendDate = nominalDates[index + 1];
        for (int i = 0; i < exDividendDays; i++) {
            while (!calendar.isWorkingDay(exDividendDate)) {
                exDividendDate = exDividendDate.minusDays(1);
            }
            exDividendDate = exDividendDate.minusDays(1);
        }
        if (exDividendDays != 0 && exDividendDate.isBefore(settlementDate)) {
            return accruedInterest - coupon;
        }
        return accruedInterest;
    }

    public static double getAccruedInterest(final DayCount dayCount, final int index, final int length,
            final ZonedDateTime previousCouponDate, final ZonedDateTime date, final ZonedDateTime nextCouponDate,
            final double coupon, final double paymentsPerYear, final boolean isEndOfMonthConvention) {
        if (dayCount instanceof ActualActualICMANormal) {
            if (isEndOfMonthConvention) {
                throw new IllegalArgumentException(
                        "Inconsistent definition; asked for accrual with EOM convention but are not using Actual/Actual ICMA");
            }
            final StubType stubType = getStubType(index, length, previousCouponDate, nextCouponDate,
                    paymentsPerYear, isEndOfMonthConvention);
            return ((ActualActualICMANormal) dayCount).getAccruedInterest(previousCouponDate, date, nextCouponDate,
                    coupon, paymentsPerYear, stubType);

        } else if (dayCount instanceof ActualActualICMA) {
            final StubType stubType = getStubType(index, length, previousCouponDate, nextCouponDate,
                    paymentsPerYear, isEndOfMonthConvention);
            return ((ActualActualICMA) dayCount).getAccruedInterest(previousCouponDate, date, nextCouponDate,
                    coupon, paymentsPerYear, stubType);

        } else if (dayCount instanceof ThirtyUThreeSixty) {
            return ((ThirtyUThreeSixty) dayCount).getAccruedInterest(previousCouponDate, date, coupon,
                    isEndOfMonthConvention);
        }
        return dayCount.getAccruedInterest(previousCouponDate, date, nextCouponDate, coupon, paymentsPerYear);
    }

    public static double getAccruedInterest(final DayCount dayCount, final int index, final int length,
            final LocalDate previousCouponDate, final LocalDate date, final LocalDate nextCouponDate,
            final double coupon, final double paymentsPerYear, final boolean isEndOfMonthConvention) {
        if (dayCount instanceof ActualActualICMANormal) {
            if (isEndOfMonthConvention) {
                throw new IllegalArgumentException(
                        "Inconsistent definition; asked for accrual with EOM convention but are not using Actual/Actual ICMA");
            }
            final StubType stubType = getStubType(index, length, previousCouponDate, nextCouponDate,
                    paymentsPerYear, isEndOfMonthConvention);
            return ((ActualActualICMANormal) dayCount).getAccruedInterest(previousCouponDate, date, nextCouponDate,
                    coupon, paymentsPerYear, stubType);

        } else if (dayCount instanceof ActualActualICMA) {
            final StubType stubType = getStubType(index, length, previousCouponDate, nextCouponDate,
                    paymentsPerYear, isEndOfMonthConvention);
            return ((ActualActualICMA) dayCount).getAccruedInterest(previousCouponDate, date, nextCouponDate,
                    coupon, paymentsPerYear, stubType);

        } else if (dayCount instanceof ThirtyUThreeSixty) {
            return ((ThirtyUThreeSixty) dayCount).getAccruedInterest(previousCouponDate, date, coupon,
                    isEndOfMonthConvention);
        }
        return dayCount.getAccruedInterest(previousCouponDate, date, nextCouponDate, coupon, paymentsPerYear);
    }

    private static StubType getStubType(final int index, final int length, final ZonedDateTime previousCouponDate,
            final ZonedDateTime nextCouponDate, final double paymentsPerYear,
            final boolean isEndOfMonthConvention) {
        StubType stubType;
        if (index == 0) {
            LocalDate[] schedule = new LocalDate[] { previousCouponDate.toLocalDate(),
                    nextCouponDate.toLocalDate() };
            stubType = StubCalculator.getStartStubType(schedule, paymentsPerYear, isEndOfMonthConvention);

        } else if (index == length - 2) {
            LocalDate[] schedule = new LocalDate[] { previousCouponDate.toLocalDate(),
                    nextCouponDate.toLocalDate() };
            stubType = StubCalculator.getEndStubType(schedule, paymentsPerYear, isEndOfMonthConvention);

        } else {
            stubType = StubType.NONE;
        }
        return stubType;
    }

    private static StubType getStubType(final int index, final int length, final LocalDate previousCouponDate,
            final LocalDate nextCouponDate, final double paymentsPerYear, final boolean isEndOfMonthConvention) {
        StubType stubType;
        if (index == 0) {
            LocalDate[] schedule = new LocalDate[] { previousCouponDate, nextCouponDate };
            stubType = StubCalculator.getStartStubType(schedule, paymentsPerYear, isEndOfMonthConvention);

        } else if (index == length - 2) {
            LocalDate[] schedule = new LocalDate[] { previousCouponDate, nextCouponDate };
            stubType = StubCalculator.getEndStubType(schedule, paymentsPerYear, isEndOfMonthConvention);

        } else {
            stubType = StubType.NONE;
        }
        return stubType;
    }
}