/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.aggregation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.opengamma.core.id.ExternalSchemes;
import com.opengamma.core.legalentity.LegalEntity;
import com.opengamma.core.legalentity.LegalEntitySource;
import com.opengamma.core.obligor.definition.Obligor;
import com.opengamma.core.position.Position;
import com.opengamma.core.position.impl.SimplePositionComparator;
import com.opengamma.core.security.Security;
import com.opengamma.core.security.SecuritySource;
import com.opengamma.financial.security.FinancialSecurity;
import com.opengamma.financial.security.FinancialSecurityVisitor;
import com.opengamma.financial.security.FinancialSecurityVisitorAdapter;
import com.opengamma.financial.security.cds.AbstractCreditDefaultSwapSecurity;
import com.opengamma.financial.security.cds.CreditDefaultSwapIndexDefinitionSecurity;
import com.opengamma.financial.security.cds.CreditDefaultSwapIndexSecurity;
import com.opengamma.financial.security.cds.CreditDefaultSwapSecurity;
import com.opengamma.financial.security.cds.LegacyVanillaCDSSecurity;
import com.opengamma.financial.security.cds.StandardVanillaCDSSecurity;
import com.opengamma.financial.security.equity.EquitySecurity;
import com.opengamma.financial.security.equity.GICSCode;
import com.opengamma.financial.security.option.CreditDefaultSwapOptionSecurity;
import com.opengamma.financial.security.option.EquityIndexOptionSecurity;
import com.opengamma.financial.security.option.EquityOptionSecurity;
import com.opengamma.id.ExternalId;
import com.opengamma.id.ExternalIdBundle;
/**
* Abstract aggregation function for bucketing equities and equity options by GICS code of the underlying
*/
public class GICSAggregationFunction implements AggregationFunction<String> {
private static final Logger s_logger = LoggerFactory.getLogger(GICSAggregationFunction.class);
private static final String UNKNOWN = "Unknown";
private boolean _useAttributes;
private final Comparator<Position> _comparator = new SimplePositionComparator();
private final CdsObligorSectorExtractor _obligorSectorExtractor;
private final CdsOptionObligorSectorExtractor _cdsOptionSectorExtractor;
private final CdsIndexFamilyExtractor _cdsIndexFamilyExtractor;
/**
* Enumerated type representing how specific the GICS code should be interpreted.
*/
public enum Level {
/**
* Sector
*/
SECTOR("Sector"),
/**
* Industry Group
*/
INDUSTRY_GROUP("Industry Group"),
/**
* Industry
*/
INDUSTRY("Industry"),
/**
* Sub-industry
*/
SUB_INDUSTRY("Sub-industry");
private final String _displayName;
private Level(String displayName) {
_displayName = displayName;
}
public String getDisplayName() {
return _displayName;
}
public int getNumber() {
return ordinal() + 1;
}
}
private Level _level;
private SecuritySource _secSource;
private boolean _includeEmptyCategories;
public GICSAggregationFunction(SecuritySource secSource, LegalEntitySource legalEntitySource, String level) {
this(secSource, legalEntitySource, Enum.valueOf(Level.class, level));
}
public GICSAggregationFunction(SecuritySource secSource, LegalEntitySource legalEntitySource, Level level) {
this(secSource, legalEntitySource, level, false);
}
public GICSAggregationFunction(SecuritySource secSource,
LegalEntitySource legalEntitySource,
Level level,
boolean useAttributes) {
this(secSource, legalEntitySource, level, useAttributes, true);
}
public GICSAggregationFunction(SecuritySource secSource, String level) {
this(secSource, Enum.valueOf(Level.class, level));
}
public GICSAggregationFunction(SecuritySource secSource, Level level) {
this(secSource, level, false);
}
public GICSAggregationFunction(SecuritySource secSource,
Level level,
boolean useAttributes) {
this(secSource, level, useAttributes, true);
}
public GICSAggregationFunction(SecuritySource secSource, Level level,
boolean useAttributes,
boolean includeEmptyCategories) {
this(secSource, null, level, useAttributes, includeEmptyCategories);
}
public GICSAggregationFunction(SecuritySource secSource,
LegalEntitySource legalEntitySource, Level level,
boolean useAttributes,
boolean includeEmptyCategories) {
_secSource = secSource;
_level = level;
_useAttributes = useAttributes;
_includeEmptyCategories = includeEmptyCategories;
if (legalEntitySource == null) {
if (_level == Level.SECTOR) {
s_logger.warn("No organization source supplied - will be unable to show sectors for CDS reference entities");
}
_obligorSectorExtractor = null;
_cdsOptionSectorExtractor = null;
_cdsIndexFamilyExtractor = null;
} else {
_obligorSectorExtractor = new CdsObligorSectorExtractor(legalEntitySource);
_cdsOptionSectorExtractor = new CdsOptionObligorSectorExtractor(secSource, legalEntitySource);
_cdsIndexFamilyExtractor = new CdsIndexFamilyExtractor(secSource);
}
}
private FinancialSecurityVisitor<String> _equitySecurityVisitor = new FinancialSecurityVisitorAdapter<String>() {
@Override
public String visitEquitySecurity(EquitySecurity security) {
if (security.getGicsCode() != null) {
switch (_level) {
case SECTOR:
return security.getGicsCode().getSectorDescription();
case INDUSTRY_GROUP:
return security.getGicsCode().getIndustryGroupDescription();
case INDUSTRY:
return security.getGicsCode().getIndustryDescription();
case SUB_INDUSTRY:
return security.getGicsCode().getSubIndustryDescription();
}
}
return UNKNOWN;
}
};
private FinancialSecurityVisitor<String> _equityOptionSecurityVisitor = new FinancialSecurityVisitorAdapter<String>() {
@Override
public String visitEquityOptionSecurity(EquityOptionSecurity security) {
EquitySecurity underlying = (EquitySecurity) _secSource.getSingle(ExternalIdBundle.of(security.getUnderlyingId()));
if (underlying != null) {
if (underlying.getGicsCode() != null) {
switch (_level) {
case SECTOR:
return underlying.getGicsCode().getSectorDescription();
case INDUSTRY_GROUP:
return underlying.getGicsCode().getIndustryGroupDescription();
case INDUSTRY:
return underlying.getGicsCode().getIndustryDescription();
case SUB_INDUSTRY:
return underlying.getGicsCode().getSubIndustryDescription();
}
}
}
return UNKNOWN;
}
};
private FinancialSecurityVisitor<String> _equityIndexOptionSecurityVisitor = new FinancialSecurityVisitorAdapter<String>() {
@Override
public String visitEquityIndexOptionSecurity(EquityIndexOptionSecurity security) {
if (_level == Level.SECTOR) {
return security.getUnderlyingId().getValue();
} else {
return UNKNOWN;
}
}
};
private FinancialSecurityVisitor<String> _standardVanillaCdsSecurityVisitor = new FinancialSecurityVisitorAdapter<String>() {
@Override
public String visitStandardVanillaCDSSecurity(StandardVanillaCDSSecurity cds) {
return sectorExtractionIsValid() ? _obligorSectorExtractor.extractOrElse(cds, UNKNOWN) : UNKNOWN;
}
};
private FinancialSecurityVisitor<String> _legacyVanillaCdsSecurityVisitor = new FinancialSecurityVisitorAdapter<String>() {
@Override
public String visitLegacyVanillaCDSSecurity(LegacyVanillaCDSSecurity cds) {
return sectorExtractionIsValid() ? _obligorSectorExtractor.extractOrElse(cds, UNKNOWN) : UNKNOWN;
}
};
private FinancialSecurityVisitor<String> _cdsOptionSecurityVisitor = new FinancialSecurityVisitorAdapter<String>() {
@Override
public String visitCreditDefaultSwapOptionSecurity(CreditDefaultSwapOptionSecurity cdsOption) {
return sectorExtractionIsValid() ? _cdsOptionSectorExtractor.extractOrElse(cdsOption, UNKNOWN) : UNKNOWN;
}
};
private FinancialSecurityVisitor<String> _cdsIndexSecurityVisitor = new FinancialSecurityVisitorAdapter<String>() {
@Override
public String visitCreditDefaultSwapIndexSecurity(CreditDefaultSwapIndexSecurity cdsIndex) {
return sectorExtractionIsValid() ? _cdsIndexFamilyExtractor.extractOrElse(cdsIndex, UNKNOWN) : UNKNOWN;
}
};
private boolean sectorExtractionIsValid() {
return (_level == Level.SECTOR && _obligorSectorExtractor != null);
}
@Override
public String classifyPosition(Position position) {
if (_useAttributes) {
Map<String, String> attributes = position.getAttributes();
if (attributes.containsKey(getName())) {
return attributes.get(getName());
} else {
return UNKNOWN;
}
} else {
FinancialSecurityVisitor<String> visitorAdapter = FinancialSecurityVisitorAdapter.<String>builder()
.equitySecurityVisitor(_equitySecurityVisitor)
.equityOptionVisitor(_equityOptionSecurityVisitor)
.equityIndexOptionVisitor(_equityIndexOptionSecurityVisitor)
.standardVanillaCDSSecurityVisitor(_standardVanillaCdsSecurityVisitor)
.legacyVanillaCDSSecurityVisitor(_legacyVanillaCdsSecurityVisitor)
.creditDefaultSwapOptionSecurityVisitor(_cdsOptionSecurityVisitor)
.creditDefaultSwapIndexSecurityVisitor(_cdsIndexSecurityVisitor)
.create();
FinancialSecurity security = (FinancialSecurity) position.getSecurityLink().resolve(_secSource);
try {
String classification = security.accept(visitorAdapter);
return classification == null ? UNKNOWN : classification;
} catch (UnsupportedOperationException uoe) {
return UNKNOWN;
}
}
}
@Override
public String getName() {
return "GICS - level " + _level.getNumber() + " (" + _level.getDisplayName() + ")";
}
@Override
public Collection<String> getRequiredEntries() {
if (_includeEmptyCategories) {
Collection<String> baseList = new ArrayList<>();
switch (_level) {
case SECTOR:
baseList.addAll(GICSCode.getAllSectorDescriptions());
break;
case INDUSTRY_GROUP:
baseList.addAll(GICSCode.getAllIndustryGroupDescriptions());
break;
case INDUSTRY:
baseList.addAll(GICSCode.getAllIndustryDescriptions());
break;
case SUB_INDUSTRY:
baseList.addAll(GICSCode.getAllSubIndustryDescriptions());
break;
}
baseList.add(UNKNOWN);
return baseList;
} else {
return Collections.emptyList();
}
}
@Override
public int compare(String o1, String o2) {
if (o1.equals(UNKNOWN)) {
if (o2.equals(UNKNOWN)) {
return 0;
}
return 1;
} else if (o2.equals(UNKNOWN)) {
return -1;
}
return o1.compareTo(o2);
}
@Override
public Comparator<Position> getPositionComparator() {
return _comparator;
}
/**
* Inner class to extract the sector from an obligor on a CDS.
*/
private static class CdsObligorSectorExtractor {
private final CdsRedCodeExtractor<LegalEntity> _obligorExtractor;
public CdsObligorSectorExtractor(LegalEntitySource legalEntitySource) {
_obligorExtractor = new CdsRedCodeExtractor<>(new CdsObligorExtractor(legalEntitySource));
}
public String extractOrElse(CreditDefaultSwapSecurity cds, String alternative) {
LegalEntity legalEntity = _obligorExtractor.extract(cds);
return legalEntity != null && legalEntity.getAttributes().get("sector") != null ? legalEntity.getAttributes().get("sector") : alternative;
}
}
/**
* Inner class to extract the sector from an obligor on a CDS option.
*/
private static class CdsOptionObligorSectorExtractor {
private final CdsOptionValueExtractor<LegalEntity> _obligorExtractor;
public CdsOptionObligorSectorExtractor(final SecuritySource securitySource, final LegalEntitySource legalEntitySource) {
_obligorExtractor = new CdsOptionValueExtractor<LegalEntity>() {
@Override
public LegalEntity extract(CreditDefaultSwapOptionSecurity cdsOption) {
ExternalId underlyingId = cdsOption.getUnderlyingId();
Security underlying = securitySource.getSingle(underlyingId.toBundle());
if (underlying instanceof AbstractCreditDefaultSwapSecurity) {
String redCode = ((CreditDefaultSwapSecurity) underlying).getReferenceEntity().getValue();
return legalEntitySource.getSingle(ExternalId.of(ExternalSchemes.MARKIT_RED_CODE, redCode));
}
return null;
}
};
}
public String extractOrElse(CreditDefaultSwapOptionSecurity cdsOption, String alternative) {
LegalEntity legalEntity = _obligorExtractor.extract(cdsOption);
return legalEntity != null && legalEntity.getAttributes().get("sector") != null ? legalEntity.getAttributes().get("sector") : alternative;
}
}
/**
* Inner class to extract the family from an CDS index.
*/
private static class CdsIndexFamilyExtractor {
private final SecuritySource _securitySource;
public CdsIndexFamilyExtractor(final SecuritySource securitySource) {
_securitySource = securitySource;
}
public String extractOrElse(CreditDefaultSwapIndexSecurity cdsIndex, String alternative) {
final CreditDefaultSwapIndexDefinitionSecurity underlyingDefinition = (CreditDefaultSwapIndexDefinitionSecurity) _securitySource.getSingle(ExternalIdBundle.of(cdsIndex.getReferenceEntity()));
String family = underlyingDefinition.getFamily();
return family != null ? family : alternative;
}
}
}