/**
* 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.text.DecimalFormat;
import java.util.HashMap;
import org.apache.commons.lang.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.threeten.bp.DayOfWeek;
import org.threeten.bp.LocalDate;
import com.opengamma.financial.analytics.ircurve.NextExpiryAdjuster;
import com.opengamma.financial.analytics.model.FutureOptionExpiries;
import com.opengamma.id.ExternalId;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.time.Tenor;
import com.opengamma.util.tuple.Pair;
/**
* Provides ExternalIds for equity future options used to build a volatility surface
*/
public class BloombergEquityIndexFutureOptionVolatilitySurfaceInstrumentProvider implements SurfaceInstrumentProvider<Pair<Integer, Tenor>, Double> {
/** The logger */
private static final Logger s_logger = LoggerFactory.getLogger(BloombergEquityIndexFutureOptionVolatilitySurfaceInstrumentProvider.class);
/** The strike formatter */
private static final DecimalFormat FORMATTER = new DecimalFormat("##.##");
/** The expiry rules */
private static final HashMap<String, FutureOptionExpiries> EXPIRY_RULES;
static {
EXPIRY_RULES = new HashMap<>();
EXPIRY_RULES.put("DEFAULT", FutureOptionExpiries.of(new NextExpiryAdjuster(3, DayOfWeek.FRIDAY, 1))); //TODO
}
private final String _futureOptionPrefix;
private final String _postfix;
private final String _dataFieldName;
private final Double _useCallAboveStrike;
private final String _exchangeIdName;
private final String _tickerSchemeName;
/**
* @param futureOptionPrefix the prefix to the resulting code (e.g. SP), not null
* @param postfix the postfix to the resulting code (e.g. Index), not null
* @param dataFieldName the name of the data field, not null.
* @param useCallAboveStrike the strike above which to use calls rather than puts, not null
* @param exchangeIdName the exchange id, not null
* @param tickerSchemeName the ticker scheme name, not null
*/
public BloombergEquityIndexFutureOptionVolatilitySurfaceInstrumentProvider(final String futureOptionPrefix, final String postfix, final String dataFieldName,
final Double useCallAboveStrike, final String exchangeIdName, final String tickerSchemeName) {
_futureOptionPrefix = futureOptionPrefix;
_postfix = postfix;
_dataFieldName = dataFieldName;
_useCallAboveStrike = useCallAboveStrike;
_exchangeIdName = exchangeIdName;
_tickerSchemeName = tickerSchemeName;
}
/**
* Provides an ExternalID for Bloomberg ticker,
* given a reference date and an integer offset, the n'th subsequent option <p>
* The format is prefix + month + year + callPutFlag + strike + postfix <p>
* e.g. SPH3C 1000.0 Index
* <p>
* @param nthOfPeriod n'th future following curve date, not null
* @param strike option's strike, expressed as price in %, e.g. 98.750, not null
* @param surfaceDate date of curve validity; valuation date, not null
* @return the id of the Bloomberg ticker
*/
@Override
public ExternalId getInstrument(final Pair<Integer, Tenor> nthOfPeriod, final Double strike, final LocalDate surfaceDate) {
ArgumentChecker.notNull(nthOfPeriod, "futureOptionNumber");
ArgumentChecker.notNull(strike, "strike");
ArgumentChecker.notNull(surfaceDate, "surface date");
final String prefix = getFutureOptionPrefix();
final StringBuffer ticker = new StringBuffer();
ticker.append(prefix);
FutureOptionExpiries expiryRule = EXPIRY_RULES.get(prefix); // TODO: Review whether we can hoist from loop in RawVolatilitySurfaceDataFunction.buildDataRequirements
if (expiryRule == null) {
s_logger.info("No expiry rule has been setup for " + prefix + ". Using Default of 3rd Friday.");
expiryRule = EXPIRY_RULES.get("DEFAULT");
}
final int nthExpiry = nthOfPeriod.getFirst();
final Tenor period = nthOfPeriod.getSecond();
final LocalDate expiry = expiryRule.getExpiry(nthExpiry, surfaceDate, period);
final String expiryCode = BloombergFutureUtils.getShortExpiryCode(expiry);
ticker.append(expiryCode);
ticker.append(strike > useCallAboveStrike() ? "C" : "P");
ticker.append(" ");
ticker.append(FORMATTER.format(strike));
ticker.append(" ");
ticker.append(getPostfix());
return ExternalId.of(getTickerSchemeName(), ticker.toString());
}
/**
* Gets the expiryRules.
* @return the expiryRules
*/
public static HashMap<String, FutureOptionExpiries> getExpiryRules() {
return EXPIRY_RULES;
}
@Override
public ExternalId getInstrument(final Pair<Integer, Tenor> xAxis, final Double yAxis) {
throw new UnsupportedOperationException("Must supply a surface date");
}
@Override
public String getDataFieldName() {
return _dataFieldName;
}
public Double useCallAboveStrike() {
return _useCallAboveStrike;
}
/**
* Gets the future option prefix.
* @return The future option prefix
*/
public String getFutureOptionPrefix() {
return _futureOptionPrefix;
}
/**
* Gets the postfix.
* @return The postfix
*/
public String getPostfix() {
return _postfix;
}
/**
* Gets the exchange id.
* @return The exchange id
*/
public String getExchangeIdName() {
return _exchangeIdName;
}
/**
* Gets the ticker scheme name.
* @return The ticker scheme name
*/
public String getTickerSchemeName() {
return _tickerSchemeName;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + _dataFieldName.hashCode();
result = prime * result + _exchangeIdName.hashCode();
result = prime * result + _futureOptionPrefix.hashCode();
result = prime * result + _postfix.hashCode();
result = prime * result + _tickerSchemeName.hashCode();
result = prime * result + _useCallAboveStrike.hashCode();
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof BloombergEquityIndexFutureOptionVolatilitySurfaceInstrumentProvider)) {
return false;
}
final BloombergEquityIndexFutureOptionVolatilitySurfaceInstrumentProvider other = (BloombergEquityIndexFutureOptionVolatilitySurfaceInstrumentProvider) obj;
if (Double.compare(_useCallAboveStrike, other._useCallAboveStrike) != 0) {
return false;
}
if (!ObjectUtils.equals(_futureOptionPrefix, other._futureOptionPrefix)) {
return false;
}
if (!ObjectUtils.equals(_exchangeIdName, other._exchangeIdName)) {
return false;
}
if (!ObjectUtils.equals(_tickerSchemeName, other._tickerSchemeName)) {
return false;
}
if (!ObjectUtils.equals(_dataFieldName, other._dataFieldName)) {
return false;
}
if (!ObjectUtils.equals(_postfix, other._postfix)) {
return false;
}
return true;
}
}