/** * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.financial.analytics.volatility.surface; import java.util.HashSet; import org.apache.commons.lang.Validate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.threeten.bp.LocalDate; import org.threeten.bp.Month; import org.threeten.bp.Period; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.Sets; import com.opengamma.financial.analytics.model.irfutureoption.FutureOptionUtils; import com.opengamma.financial.convention.expirycalc.BondFutureOptionExpiryCalculator; import com.opengamma.financial.convention.expirycalc.FedFundFutureAndFutureOptionMonthlyExpiryCalculator; import com.opengamma.financial.convention.expirycalc.SoybeanFutureExpiryCalculator; import com.opengamma.financial.convention.expirycalc.SoybeanFutureOptionExpiryCalculator; import com.opengamma.util.OpenGammaClock; /** * Utility methods for building Bloomberg Tickers on IR Futures and IR Future Options (Refer to Bloomberg page: WIR) */ public class BloombergFutureUtils { /** Set of prefixes for Standard IR Future Options */ public static final HashSet<String> STANDARD_PREFIX_SET = Sets.newHashSet("ER", "ED", "EF", "IR", "L "); /** Set of prefixes for MidCurve IR Future Options */ public static final HashSet<String> MIDCURVE_PREFIX_SET = Sets.newHashSet("0R"); /** Set of prefixes for which the logic of expiries has been tested */ private static final HashSet<String> TESTED_PREFIX_SET = Sets.newHashSet("ER", "0R"); /** * Bloomberg's two character prefixes for Interest Rate Futures * Be careful: Some of Bloomberg's code consist of 1 digit only with a trailing space! eg "L " for Sterling <p> * Be careful: Some of Bloomberg's codes begin with a numeral (e.g. Euribor Mid-curves 0R). This isn't permitted in a java enum. * Do NOT use name() to get string values of this enum as in this case, the trailing string will not be present * Instead, use toString, which can be, and has, been overridden. */ public enum IRFuturePrefix { /** USD, Eurodollar, 3-month, CME */ ED, /** EUR, Euro Euribor, 3-month, LIF */ ER, /** JPY, Euroyen, 3-month, SGX */ EF, /** AUD, 90 day Bankers' Acceptance, 3-month, SFE */ IR, /** GBP, Short Sterling, 3-month, LIF */ L { @Override public String toString() { return "L "; } } } /** Map of Month to Bloomberg Month Codes*/ public static final BiMap<Month, Character> MONTH_CODE; static { MONTH_CODE = HashBiMap.create(); MONTH_CODE.put(Month.JANUARY, 'F'); MONTH_CODE.put(Month.FEBRUARY, 'G'); MONTH_CODE.put(Month.MARCH, 'H'); MONTH_CODE.put(Month.APRIL, 'J'); MONTH_CODE.put(Month.MAY, 'K'); MONTH_CODE.put(Month.JUNE, 'M'); MONTH_CODE.put(Month.JULY, 'N'); MONTH_CODE.put(Month.AUGUST, 'Q'); MONTH_CODE.put(Month.SEPTEMBER, 'U'); MONTH_CODE.put(Month.OCTOBER, 'V'); MONTH_CODE.put(Month.NOVEMBER, 'X'); MONTH_CODE.put(Month.DECEMBER, 'Z'); } /** The logger */ private static final Logger LOG = LoggerFactory.getLogger(BloombergFutureUtils.class); /** * Produces the month-year string required to build the Bloomberg ticker for a <b>quarterly</b> future. * @param futurePrefix 2 character String of Future (eg ED, ER, IR) * @param nthFuture The n'th future following valuation date * @param curveDate Date curve is valid; valuation date * @return e.g. M10 (for June 2010) or Z3 (for December 2013), both valid as of valuation date 2012/04/10 */ public static final String getQuarterlyExpiryCodeForFutures(final String futurePrefix, final int nthFuture, final LocalDate curveDate) { //Year convention for historical data is specific to the futurePrefix final LocalDate twoDigitYearSwitch; final LocalDate today = LocalDate.now(OpenGammaClock.getInstance()); if (futurePrefix.equals("ED")) { twoDigitYearSwitch = today.minus(Period.ofDays(2)); } else { twoDigitYearSwitch = today.minus(Period.ofMonths(11)).minus(Period.ofDays(2)); } return getQuarterlyExpiryMonthYearCode(nthFuture, curveDate, twoDigitYearSwitch); } /** * Produces the month-year string required to build the Bloomberg ticker for a <b>monthly</b> future. * @param futurePrefix 2 character String of Future (eg ED, ER, IR) * @param nthFuture The n'th future following valuation date * @param curveDate Date curve is valid; valuation date * @return e.g. J10 (for April 2010) or Z3 (for December 2013), both valid as of valuation date 2012/04/10 */ public static final String getMonthlyExpiryCodeForFutures(final String futurePrefix, final int nthFuture, final LocalDate curveDate) { //Year convention for historical data is specific to the futurePrefix final LocalDate twoDigitYearSwitch; final LocalDate today = LocalDate.now(OpenGammaClock.getInstance()); if (futurePrefix.equals("ED")) { twoDigitYearSwitch = today.minus(Period.ofDays(2)); } else { twoDigitYearSwitch = today.minus(Period.ofMonths(11)).minus(Period.ofDays(2)); } if (futurePrefix.equals("FF")) { return getMonthlyExpiryFFMonthYearCode(nthFuture, curveDate, twoDigitYearSwitch); } return getMonthlyExpiryMonthYearCode(nthFuture, curveDate, twoDigitYearSwitch); } /** * Produces the month-year string required to build ExternalId for Bloomberg ticker of BondFutureSecurity * @param futurePrefix 2 character string of future (eg US, FV) * @param nthFuture The n'th future following valuation date * @param curveDate Date curve is valid; valuation date * @return e.g. M2 (for June 2012) or Z3 (for December 2013) */ public static final String getExpiryCodeForBondFutureOptions(final String futurePrefix, final int nthFuture, final LocalDate curveDate) { final StringBuilder futureCode = new StringBuilder(); final LocalDate expiry = BondFutureOptionExpiryCalculator.getInstance().getExpiryMonth(nthFuture, curveDate); futureCode.append(MONTH_CODE.get(expiry.getMonth())); final int yearsNum = expiry.getYear() % 10; futureCode.append(Integer.toString(yearsNum)); return futureCode.toString(); } /** * Produces the month-year string required to build ExternalId for Bloomberg ticker of IRFutureSecurity * NOTE: Eurodollar FutureOptions do not share the same naming convention for past expiries as their underlying futures! * It appears that Bloomberg doesn't switch to a two-digit convention... * @param futurePrefix 2 character String of Future (eg ED, ER, IR) * @param nthFuture The n'th future following valuation date * @param curveDate Date curve is valid; valuation date * @return e.g. M10 (for June 2010) or Z3 (for December 2013), both valid as of valuationDate 2012/04/10 */ public static final String getExpiryCodeForIRFutureOptions(final String futurePrefix, final int nthFuture, final LocalDate curveDate) { if (!TESTED_PREFIX_SET.contains(futurePrefix)) { LOG.debug("We recommended that you ask QR to test behaviour of IRFutureOption Volatility Surface's Expiries for prefix {}", futurePrefix); // The reason being that we have hard coded the behaviour of 6 consecutive months, then quarterly thereafter.. } final LocalDate expiry = FutureOptionUtils.getApproximateIRFutureOptionWithSerialOptionsExpiry(nthFuture, curveDate); return getMonthYearCode(expiry, expiry.minusYears(10)); } /** * Produces the month-year string required to build ExternalId for Bloomberg ticker of Soybean Future Options, * which have a different set of expiry months * @param futurePrefix 2 character string of future (eg "S ", "GC") * @param nthFuture The n'th future following valuation date * @param curveDate Date curve is valid; valuation date * @return e.g. M2 (for June 2012) or Z3 (for December 2013) */ public static final String getExpiryCodeForSoybeanFutureOptions(final String futurePrefix, final int nthFuture, final LocalDate curveDate) { final StringBuilder futureCode = new StringBuilder(); final LocalDate expiry = SoybeanFutureOptionExpiryCalculator.getInstance().getExpiryMonth(nthFuture, curveDate); futureCode.append(MONTH_CODE.get(expiry.getMonth())); final int yearsNum = expiry.getYear() % 10; futureCode.append(Integer.toString(yearsNum)); return futureCode.toString(); } /** * Produces the month-year string required to build ExternalId for Bloomberg ticker of Soybean Futures, * which have a different set of expiry months * @param futurePrefix 2 character string of future (eg "S ", "GC") * @param nthFuture The n'th future following valuation date * @param curveDate Date curve is valid; valuation date * @return e.g. M2 (for June 2012) or Z3 (for December 2013) */ public static final String getExpiryCodeForSoybeanFutures(final String futurePrefix, final int nthFuture, final LocalDate curveDate) { final StringBuilder futureCode = new StringBuilder(); final LocalDate expiry = SoybeanFutureExpiryCalculator.getInstance().getExpiryMonth(nthFuture, curveDate); futureCode.append(MONTH_CODE.get(expiry.getMonth())); final int yearsNum = expiry.getYear() % 10; futureCode.append(Integer.toString(yearsNum)); return futureCode.toString(); } /** * Given an expiry date, produces the month-year string required to build ExternalId for Bloomberg tickers * @param expiry Date of expiry for which a code is required. Really only a date in the right month is required. * @return e.g. M2 (for June 2012) or Z3 (for December 2013) */ public static final String getShortExpiryCode(final LocalDate expiry) { final StringBuilder futureCode = new StringBuilder(); futureCode.append(MONTH_CODE.get(expiry.getMonth())); final int yearsNum = expiry.getYear() % 10; futureCode.append(Integer.toString(yearsNum)); return futureCode.toString(); } /** * Produces the month-year string required to build ExternalId for Bloomberg tickers of IRFutureSecurity and IRFutureOptionSecurity. * @param nthQuarter The n'th quarter following valuation date * @param valuationDate valuation date * @param twoDigitYearDate Expired futures will, before this date, be referenced by a 2-digit year (eg 12 for 2012) as opposed to trading futures (eg 2 for 2012) * @return e.g. M10 (for June 2010) or Z3 (for December 2013), both valid as of valuationDate 2012/04/10 */ public static final String getQuarterlyExpiryMonthYearCode(final int nthQuarter, final LocalDate valuationDate, final LocalDate twoDigitYearDate) { Validate.isTrue(nthQuarter > 0, "nthFuture must be greater than 0."); final LocalDate expiry = FutureOptionUtils.getApproximateIRFutureQuarterlyExpiry(nthQuarter, valuationDate); return getMonthYearCode(expiry, twoDigitYearDate); } /** * Produces the month-year string required to build ExternalId for Bloomberg tickers of IRFutureSecurity and IRFutureOptionSecurity. * @param nthMonth The n'th month following valuation date * @param valuationDate valuation date * @param twoDigitYearDate Expired futures will, before this date, be referenced by a 2-digit year (eg 12 for 2012) as opposed to trading futures (eg 2 for 2012) * @return e.g. M10 (for June 2010) or Z3 (for December 2013), both valid as of valuationDate 2012/04/10 */ public static final String getMonthlyExpiryMonthYearCode(final int nthMonth, final LocalDate valuationDate, final LocalDate twoDigitYearDate) { Validate.isTrue(nthMonth > 0, "nthFuture must be greater than 0."); final LocalDate expiry = FutureOptionUtils.getIRFutureMonthlyExpiry(nthMonth, valuationDate); return getMonthYearCode(expiry, twoDigitYearDate); } public static final String getMonthlyExpiryFFMonthYearCode(final int nthMonth, final LocalDate valuationDate, final LocalDate twoDigitYearDate) { Validate.isTrue(nthMonth > 0, "nthFuture must be greater than 0."); final LocalDate expiry = FedFundFutureAndFutureOptionMonthlyExpiryCalculator.getInstance().getExpiryMonth(nthMonth, valuationDate); return getMonthYearCode(expiry, twoDigitYearDate); } /** * Produces Bloomberg's code for month and year * @param expiry Expiry date of instrument * @param twoDigitSwitch Instrument specific historical date when code moves from 1- to 2- digit year code * @return String (e.g. Z3, H09) */ public static final String getMonthYearCode(final LocalDate expiry, final LocalDate twoDigitSwitch) { final StringBuilder futureCode = new StringBuilder(); futureCode.append(MONTH_CODE.get(expiry.getMonth())); if (expiry.isBefore(twoDigitSwitch)) { final int yearsNum = expiry.getYear() % 100; if (yearsNum < 10) { futureCode.append("0"); // so we get '09' rather than '9' } futureCode.append(Integer.toString(yearsNum)); } else { futureCode.append(Integer.toString(expiry.getYear() % 10)); } return futureCode.toString(); } }