/** * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.financial.convention.expirycalc; import org.threeten.bp.DayOfWeek; import org.threeten.bp.LocalDate; import org.threeten.bp.temporal.TemporalAdjuster; import org.threeten.bp.temporal.TemporalAdjusters; import com.opengamma.financial.convention.calendar.Calendar; import com.opengamma.util.ArgumentChecker; /** * Expiry calculator for IMM futures. */ public final class IMMFutureAndFutureOptionMonthlyExpiryCalculator implements ExchangeTradedInstrumentExpiryCalculator { /** Name of the calculator */ public static final String NAME = "IMMFutureOptionMonthlyExpiryCalculator"; /** Singleton. */ private static final IMMFutureAndFutureOptionMonthlyExpiryCalculator INSTANCE = new IMMFutureAndFutureOptionMonthlyExpiryCalculator(); /** Adjuster. */ private static final TemporalAdjuster THIRD_WEDNESDAY_ADJUSTER = TemporalAdjusters.dayOfWeekInMonth(3, DayOfWeek.WEDNESDAY); /** Adjuster. */ private static final TemporalAdjuster THIRD_MONDAY_ADJUSTER = TemporalAdjusters.dayOfWeekInMonth(3, DayOfWeek.MONDAY); /** Working days to settle. */ private static final int WORKING_DAYS_TO_SETTLE = 2; /** * Gets the singleton instance. * * @return the instance, not null */ public static IMMFutureAndFutureOptionMonthlyExpiryCalculator getInstance() { return INSTANCE; } /** * Restricted constructor. */ private IMMFutureAndFutureOptionMonthlyExpiryCalculator() { } //------------------------------------------------------------------------- @Override public LocalDate getExpiryDate(final int n, final LocalDate today, final Calendar holidayCalendar) { ArgumentChecker.isTrue(n > 0, "n must be greater than zero"); ArgumentChecker.notNull(today, "today"); ArgumentChecker.notNull(holidayCalendar, "holiday calendar"); LocalDate result; final LocalDate thirdWednesday = today.with(THIRD_WEDNESDAY_ADJUSTER); if (today.isAfter(adjustForSettlement(thirdWednesday, holidayCalendar))) { result = today.plusMonths(n).with(THIRD_WEDNESDAY_ADJUSTER); } else { result = today.plusMonths(n - 1).with(THIRD_WEDNESDAY_ADJUSTER); } return adjustForSettlement(result, holidayCalendar); } @Override public LocalDate getExpiryMonth(final int n, final LocalDate today) { ArgumentChecker.isTrue(n > 0, "n must be greater than zero"); ArgumentChecker.notNull(today, "today"); if (today.isAfter(today.with(THIRD_MONDAY_ADJUSTER))) { return today.plusMonths(n); } return today.plusMonths(n - 1); } private LocalDate adjustForSettlement(final LocalDate date, final Calendar holidayCalendar) { int days = 0; LocalDate result = date; while (days < WORKING_DAYS_TO_SETTLE) { result = result.minusDays(1); if (holidayCalendar.isWorkingDay(result)) { days++; } } return result; } @Override public String getName() { return NAME; } }