/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.analytics.volatility.surface;
import org.threeten.bp.LocalDate;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.core.id.ExternalSchemes;
import com.opengamma.financial.analytics.volatility.surface.BloombergFXOptionVolatilitySurfaceInstrumentProvider.FXVolQuoteType;
import com.opengamma.id.ExternalId;
import com.opengamma.id.ExternalScheme;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.time.Tenor;
import com.opengamma.util.tuple.Pair;
/**
* Autogenerates Bloomberg FX option volatility surface codes given a tenor, quote type (ATM, butterfly, risk reversal) and distance from
* ATM.
*/
public class BloombergFXOptionVolatilitySurfaceInstrumentProvider implements SurfaceInstrumentProvider<Tenor, Pair<Number, FXVolQuoteType>> {
/** Type of the volatility quote */
public enum FXVolQuoteType {
/** ATM */
ATM,
/** Risk-reversal */
RISK_REVERSAL,
/** Butterfly */
BUTTERFLY;
}
/** The FX prefix */
private final String _fxPrefix; //expecting something like USDJPY
/** The postfix */
private final String _postfix; //expecting Curncy
/** The data field name */
private final String _dataFieldName; //expecting MarketDataRequirementNames.MARKET_VALUE
/** The Bloomberg scheme name */
private final ExternalScheme _scheme; // e.g. BLOOMBERG_TICKER_WEAK
/**
* Sets the scheme to weak tickers.
* @param fxPrefix The FX prefix, not null
* @param postfix The postfix, not null
* @param dataFieldName The data field name, not null
*/
public BloombergFXOptionVolatilitySurfaceInstrumentProvider(final String fxPrefix, final String postfix, final String dataFieldName) {
this(fxPrefix, postfix, dataFieldName, ExternalSchemes.BLOOMBERG_TICKER_WEAK.getName());
}
/**
* @param fxPrefix The FX prefix, not null
* @param postfix The code postfix, not null
* @param dataFieldName The data field name, not null
* @param schemeName The scheme name, not null. Must be one of {@link ExternalSchemes#BLOOMBERG_BUID}, {@link ExternalSchemes#BLOOMBERG_BUID_WEAK},
* {@link ExternalSchemes#BLOOMBERG_TCM}, {@link ExternalSchemes#BLOOMBERG_TICKER} or {@link ExternalSchemes#BLOOMBERG_TICKER_WEAK}
*/
public BloombergFXOptionVolatilitySurfaceInstrumentProvider(final String fxPrefix, final String postfix, final String dataFieldName,
final String schemeName) {
ArgumentChecker.notNull(fxPrefix, "fx prefix");
ArgumentChecker.notNull(postfix, "postfix");
ArgumentChecker.notNull(dataFieldName, "data field name");
ArgumentChecker.notNull(schemeName, "scheme name");
final boolean schemeTest = schemeName.equals(ExternalSchemes.BLOOMBERG_BUID.getName()) ||
schemeName.equals(ExternalSchemes.BLOOMBERG_BUID_WEAK.getName()) ||
schemeName.equals(ExternalSchemes.BLOOMBERG_TCM.getName()) ||
schemeName.equals(ExternalSchemes.BLOOMBERG_TICKER.getName()) ||
schemeName.equals(ExternalSchemes.BLOOMBERG_TICKER_WEAK.getName());
ArgumentChecker.isTrue(schemeTest, "scheme name {} was not appropriate for Bloomberg data");
_fxPrefix = fxPrefix;
_postfix = postfix;
_dataFieldName = dataFieldName;
_scheme = ExternalScheme.of(schemeName);
}
/**
* Gets the FX prefix.
* @return The FX prefix
*/
public String getFXPrefix() {
return _fxPrefix;
}
/**
* Gets the code postfix.
* @return The code postfix
*/
public String getPostfix() {
return _postfix;
}
@Override
public String getDataFieldName() {
return _dataFieldName;
}
/**
* Gets the scheme name.
* @return The scheme name
*/
public String getSchemeName() {
return _scheme.getName();
}
@Override
public ExternalId getInstrument(final Tenor tenor, final Pair<Number, FXVolQuoteType> volDeltaQuoteType) {
return createFXVolatilityCode(tenor, volDeltaQuoteType);
}
@Override
public ExternalId getInstrument(final Tenor tenor, final Pair<Number, FXVolQuoteType> volDeltaQuoteType, final LocalDate surfaceDate) {
return createFXVolatilityCode(tenor, volDeltaQuoteType);
}
private ExternalId createFXVolatilityCode(final Tenor tenor, final Pair<Number, FXVolQuoteType> volDeltaQuoteType) {
final StringBuffer ticker = new StringBuffer();
ticker.append(_fxPrefix);
final int delta = volDeltaQuoteType.getFirst().intValue();
final FXVolQuoteType quoteType = volDeltaQuoteType.getSecond();
String bbgCode = "";
if (delta == 0) {
if (quoteType == FXVolQuoteType.ATM) {
bbgCode += "V";
} else {
throw new OpenGammaRuntimeException("Asked for an ATM code with non-zero delta");
}
} else {
switch (quoteType) {
case ATM:
throw new OpenGammaRuntimeException("Asked for an ATM code with non-zero delta");
case RISK_REVERSAL:
bbgCode += delta + "R";
break;
case BUTTERFLY:
bbgCode += delta + "B";
break;
default:
throw new OpenGammaRuntimeException("Should never happen - have all quote types in enum");
}
}
//TODO I'm sure this isn't the best way to do this
if (tenor.getPeriod().getYears() != 0) {
bbgCode += tenor.getPeriod().getYears() + "Y";
} else if (tenor.getPeriod().getMonths() != 0) {
bbgCode += tenor.getPeriod().getMonths() + "M";
} else if (tenor.getPeriod().getDays() != 0 && tenor.getPeriod().getDays() % 7 == 0) {
bbgCode += tenor.getPeriod().getDays() / 7 + "W";
} else {
throw new OpenGammaRuntimeException("Can only handle periods of year, month and week");
}
ticker.append(bbgCode);
ticker.append(" ");
ticker.append(_postfix);
return ExternalId.of(_scheme, ticker.toString());
}
@Override
public int hashCode() {
return getFXPrefix().hashCode() + getPostfix().hashCode() + getDataFieldName().hashCode() + getSchemeName().hashCode();
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof BloombergFXOptionVolatilitySurfaceInstrumentProvider)) {
return false;
}
final BloombergFXOptionVolatilitySurfaceInstrumentProvider other = (BloombergFXOptionVolatilitySurfaceInstrumentProvider) obj;
return getFXPrefix().equals(other.getFXPrefix()) &&
getPostfix().equals(other.getPostfix()) &&
getDataFieldName().equals(other.getDataFieldName()) &&
getSchemeName().equals(other.getSchemeName());
}
}