/** * Copyright (C) 2013 - 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.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; // Some future options which appear to exist don't seem to fit the exchange rules - need to investigate these /** * Expiry calculator for gold future options. */ public final class GoldFutureOptionExpiryCalculator implements ExchangeTradedInstrumentExpiryCalculator { /** Name of the calculator */ public static final String NAME = "GoldFutureOptionExpiryCalculator"; /** Singleton. */ private static final GoldFutureOptionExpiryCalculator INSTANCE = new GoldFutureOptionExpiryCalculator(); /** Adjuster. */ private static final TemporalAdjuster LAST_DAY_ADJUSTER = TemporalAdjusters.lastDayOfMonth(); /** * Gets the singleton instance. * * @return the instance, not null */ public static GoldFutureOptionExpiryCalculator getInstance() { return INSTANCE; } /** * Restricted constructor. */ private GoldFutureOptionExpiryCalculator() { } //------------------------------------------------------------------------- /** * Expiry date of Gold Future Options: * Four days before the end of the month preceding the option month. * If expiration falls on a Friday or a day before a holiday it moves to the previous business day * See http://www.cmegroup.com/trading/metals/precious/gold_contractSpecs_options.html#prodType=AME * * @param n the n'th expiry date after today, greater than zero * @param today the valuation date, not null * @param holidayCalendar the holiday calendar, not null * @return the expiry date, not null */ @Override public LocalDate getExpiryDate(final int n, final LocalDate today, final Calendar holidayCalendar) { ArgumentChecker.isTrue(n > 0, "n must be greater than zero; have {}", n); ArgumentChecker.notNull(today, "today"); ArgumentChecker.notNull(holidayCalendar, "holiday calendar"); // as options expire 1 month before futures need to get future expiry after this nth option (n + 1) final LocalDate futuresExpiry = GoldFutureExpiryCalculator.getInstance().getExpiryMonth(n + 1, today); int nBusinessDays = 4; LocalDate date = futuresExpiry.minusMonths(1).with(LAST_DAY_ADJUSTER); if (holidayCalendar.isWorkingDay(date)) { nBusinessDays--; } // go back to 4 business days while (nBusinessDays > 0) { date = date.minusDays(1); if (holidayCalendar.isWorkingDay(date)) { nBusinessDays--; } } // If day is a Friday or immediately precedes a holiday (e.g. a Friday) it moves to the previous business day if (!holidayCalendar.isWorkingDay(date.plusDays(1))) { date = date.minusDays(1); while (!holidayCalendar.isWorkingDay(date)) { date = date.minusDays(1); } } return date; } @Override public LocalDate getExpiryMonth(final int n, final LocalDate today) { return GoldFutureExpiryCalculator.getInstance().getExpiryMonth(n, today); } @Override public String getName() { return NAME; } }