/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.bbg.loader;
import static com.opengamma.bbg.BloombergConstants.FIELD_ID_BBG_UNIQUE;
import static com.opengamma.bbg.BloombergConstants.FIELD_ID_CUSIP;
import static com.opengamma.bbg.BloombergConstants.FIELD_ID_ISIN;
import static com.opengamma.bbg.BloombergConstants.FIELD_ID_SEDOL1;
import static com.opengamma.bbg.BloombergConstants.FIELD_INDX_SOURCE;
import static com.opengamma.bbg.BloombergConstants.FIELD_PARSEKYABLE_DES;
import static com.opengamma.bbg.BloombergConstants.FIELD_SECURITY_DES;
import static com.opengamma.bbg.util.BloombergDataUtils.isValidField;
import java.util.Collections;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.fudgemsg.FudgeMsg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Sets;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.bbg.referencedata.ReferenceDataProvider;
import com.opengamma.bbg.util.BloombergDataUtils;
import com.opengamma.financial.security.index.IborIndex;
import com.opengamma.financial.security.index.Index;
import com.opengamma.financial.security.index.OvernightIndex;
import com.opengamma.financial.security.index.PriceIndex;
import com.opengamma.financial.security.index.SwapIndex;
import com.opengamma.id.ExternalId;
import com.opengamma.id.ExternalScheme;
import com.opengamma.master.security.ManageableSecurity;
import com.opengamma.util.time.Tenor;
/**
* Loads the data for an Index Future from Bloomberg.
*/
public class IndexLoader extends SecurityLoader {
private static final String BLOOMBERG_INDEX_TYPE = "Index";
/**
* Valid Security type values for this index
*/
public static final Set<String> VALID_SECURITY_TYPES = Collections.unmodifiableSet(Sets.newHashSet(BLOOMBERG_INDEX_TYPE));
/** Logger. */
private static final Logger s_logger = LoggerFactory.getLogger(IndexLoader.class);
/**
* The fields to load from Bloomberg.
*/
private static final Set<String> BLOOMBERG_INDEX_FIELDS = Collections.unmodifiableSet(Sets.newHashSet(
FIELD_SECURITY_DES,
FIELD_PARSEKYABLE_DES,
FIELD_INDX_SOURCE,
FIELD_ID_BBG_UNIQUE,
FIELD_ID_CUSIP,
FIELD_ID_ISIN,
FIELD_ID_SEDOL1));
private static final Pattern s_tenorFromDes = Pattern.compile("(.*?)(Overnight.*?|O\\/N.*?|OVERNIGHT.*?|Tomorrow[\\s\\/]Next.*?|T[\\s\\/]N.*?|TOM[\\s\\/]NEXT.*?|\\d+\\s*.*?)");
private static final Pattern s_overnight = Pattern.compile(".*?(Overnight|O\\/N|OVERNIGHT).*?");
private static final Pattern s_tomNext = Pattern.compile(".*?(Tomorrow[\\s\\/]Next|T[\\s\\/]N|TOM[\\s\\/]NEXT).*?");
private static final Pattern s_numberFromTimeUnit = Pattern.compile("(\\d+)\\s*(.*?)");
private static final String BLOOMBERG_CONVENTION_NAME = "BLOOMBERG_CONVENTION_NAME";
private static final String BLOOMBERG_INDEX_FAMILY = "BLOOMBERG_INDEX_FAMILY";
private static final String FED_FUNDS_SECURITY_DES = "Federal Funds Effective Rate U";
private static final Set<String> BLOOMBERG_SECURITY_DES_OVERNIGHT_EXCEPTIONS = Collections.unmodifiableSet(Sets.newHashSet(
FED_FUNDS_SECURITY_DES
));
/**
* Creates an instance.
* @param referenceDataProvider the provider, not null
*/
public IndexLoader(ReferenceDataProvider referenceDataProvider) {
super(s_logger, referenceDataProvider, SecurityType.INDEX);
}
//-------------------------------------------------------------------------
@Override
protected ManageableSecurity createSecurity(FudgeMsg fieldData) {
String securityDes = fieldData.getString(FIELD_SECURITY_DES);
String name = BloombergDataUtils.removeDuplicateWhiteSpace(fieldData.getString(FIELD_SECURITY_DES), " ");
String bbgUnique = fieldData.getString(FIELD_ID_BBG_UNIQUE);
String indexSource = fieldData.getString(FIELD_INDX_SOURCE);
if (!isValidField(bbgUnique)) {
s_logger.warn("bbgUnique is null, cannot construct index");
return null;
}
if (!isValidField(securityDes)) {
s_logger.warn("security description is null, cannot construct index");
return null;
}
if (!isValidField(indexSource)) {
s_logger.warn("index source is null, cannot construct index");
return null;
}
Index index;
Tenor tenor = decodeTenor(securityDes);
ExternalId conventionId = createConventionId(securityDes);
ExternalId familyId = ExternalId.of(ExternalScheme.of(BLOOMBERG_INDEX_FAMILY), conventionId.getValue());
if (indexSource.toUpperCase().contains("STAT") || securityDes.toUpperCase().contains("CPI")) {
// guess it's a price index as source is STATistics agency or description contains CPI. Crude, but hopefully effective.
index = new PriceIndex(name, conventionId);
index.setIndexFamilyId(null);
} else if (securityDes.toUpperCase().contains("ISDAFIX") && tenor != null) {
// guess it's a swap index
index = new SwapIndex(name, tenor, conventionId);
index.setIndexFamilyId(familyId);
} else if (tenor != null) {
// Ibor or overnight
if (tenor.equals(Tenor.ON)) {
index = new OvernightIndex(name, conventionId);
index.setIndexFamilyId(familyId);
} else {
index = new IborIndex(name, tenor, conventionId);
index.setIndexFamilyId(familyId);
}
index.setName(name);
} else {
s_logger.error("Could not load index {}, source={}, tenor={}, securityDes={}", name, indexSource, tenor, securityDes);
return null;
}
// set identifiers
parseIdentifiers(fieldData, index);
return index;
}
// public visible for tests
public static ExternalId createConventionId(String securityDes) {
Matcher matcher = s_tenorFromDes.matcher(securityDes);
if (matcher.matches()) {
String descriptionPart = matcher.group(1); // remember, groups are 1 indexed!
return ExternalId.of(ExternalScheme.of(BLOOMBERG_CONVENTION_NAME), descriptionPart.trim());
} else {
return ExternalId.of(ExternalScheme.of(BLOOMBERG_CONVENTION_NAME), securityDes.trim());
}
}
// public visible for tests
public static Tenor decodeTenor(String securityDes) {
if (BLOOMBERG_SECURITY_DES_OVERNIGHT_EXCEPTIONS.contains(securityDes)) {
return Tenor.ON;
}
Matcher matcher = s_tenorFromDes.matcher(securityDes);
if (matcher.matches()) {
String tenorPart = matcher.group(2); // remember, groups are 1 indexed!
if (s_overnight.matcher(tenorPart).matches()) {
return Tenor.ON;
} else if (s_tomNext.matcher(tenorPart).matches()) {
return Tenor.TN;
} else {
Matcher numberFromTimeMatcher = s_numberFromTimeUnit.matcher(tenorPart);
if (numberFromTimeMatcher.matches()) {
String numberStr = numberFromTimeMatcher.group(1).trim();
int number = Integer.parseInt(numberStr);
String timeUnit = numberFromTimeMatcher.group(2).trim().toUpperCase();
if (timeUnit.length() == 0) {
throw new OpenGammaRuntimeException("Could not decode tenor from description " + securityDes);
}
switch (timeUnit.charAt(0)) {
case 'D':
// assume days!
return Tenor.ofDays(number);
case 'M':
// assume months!
return Tenor.ofMonths(number);
case 'Y':
// assume years!
return Tenor.ofYears(number);
}
}
}
}
return null;
}
@Override
protected Set<String> getBloombergFields() {
return BLOOMBERG_INDEX_FIELDS;
}
}