Systems
systems ¶
Classification Systems supported by equities_classifier.
GECS = ClassificationSystem(id=ClassificationSystemID.GECS, display_name='Global Equity Classification Structure', short_name='GECS', authorities=('Morningstar',), hierarchy=('Super Sector', 'Sector', 'Industry'), supports_codes=False) module-attribute ¶
GICS = ClassificationSystem(id=ClassificationSystemID.GICS, display_name='Global Industry Classification Standard', short_name='GICS', authorities=('MSCI', 'S&P Dow Jones Indices'), hierarchy=('Sector', 'Industry Group', 'Industry', 'Sub-Industry'), supports_codes=True) module-attribute ¶
GICS_DICT: dict[str, str] = {code: _normalize_gics_name(definition['name']) for code, definition in GICSDefinition('').definition.items()} module-attribute ¶
GICS_INVDICT: dict[tuple[str, int], str] = {(_normalize_gics_name(definition['name']), len(code) // 2): code for code, definition in GICSDefinition('').definition.items()} module-attribute ¶
MAP_GECS_SUPERSECTOR_FROM_SECTOR: immutabledict[str, str] = immutabledict({'Basic Materials': 'Cyclical', 'Consumer Cyclical': 'Cyclical', 'Financial Services': 'Cyclical', 'RealEstate': 'Cyclical', 'Communication Services': 'Sensitive', 'Energy': 'Sensitive', 'Industrials': 'Sensitive', 'Technology': 'Sensitive', 'Healthcare': 'Defensive', 'Consumer Defensive': 'Defensive', 'Utilities': 'Defensive'}) module-attribute ¶
resolve_gecs_supersector(nodes: dict[ClassificationLevel, ClassificationNode]) -> Mapping[ClassificationLevel, ClassificationNode] ¶
Resolve missing GECS super sector.
Source code in src/equities_classifier/classification/systems.py
def resolve_gecs_supersector(
nodes: dict[ClassificationLevel, ClassificationNode],
) -> Mapping[ClassificationLevel, ClassificationNode]:
"""Resolve missing GECS super sector."""
result = dict(nodes)
sector = result.get(ClassificationLevel.LEVEL2)
if sector is None:
return result
super_sector = MAP_GECS_SUPERSECTOR_FROM_SECTOR.get(sector.value,)
if super_sector is None:
return result
result.setdefault(
ClassificationLevel.LEVEL1,
ClassificationNode(value=super_sector,),
)
return result
resolve_gics_motleyfool(nodes: Mapping[ClassificationLevel, ClassificationNode]) -> Mapping[ClassificationLevel, ClassificationNode] ¶
Resolve and validate Motley Fool GICS classification.
Source code in src/equities_classifier/classification/systems.py
def resolve_gics_motleyfool(
nodes: Mapping[ClassificationLevel, ClassificationNode],
) -> Mapping[ClassificationLevel, ClassificationNode]:
"""Resolve and validate Motley Fool GICS classification."""
result = dict(nodes)
sector_node = result.get(ClassificationLevel.LEVEL1)
industry_node = result.get(ClassificationLevel.LEVEL3)
if industry_node is None:
return result
# Find GICS industry (level 3) from Motley Fool industry name.
industry_code: str | None = _find_gics_code(industry_node.value, 3)
if industry_code is None:
ClassificationHelper.classification_element_invalid(
DataSourceID.MOTLEYFOOL,
ClassificationSystemID.GICS,
ClassificationLevel.LEVEL3,
industry_node.value,
)
return result
# GICS level 3 must consist of six digits.
if len(industry_code) < 6:
return result
# Derive the complete hierarchy from the level-3 code.
sector_code = industry_code[:2]
industry_group_code = industry_code[:4]
# Validate Motley-Fool sector against the sector derived
# from the GICS industry code.
if sector_node is not None:
expected_sector = GICS_DICT.get(sector_code)
if expected_sector is not None:
actual_sector = _normalize_gics_name(sector_node.value,)
if actual_sector != expected_sector:
# Sector and industry disagree. Do not silently
# create an inconsistent classification.
ClassificationHelper.classification_mismatch(
DataSourceID.MOTLEYFOOL,
ClassificationSystemID.GICS,
ClassificationLevel.LEVEL1,
actual_sector,
expected_sector,
)
# Replace / enrich level 1 with the canonical GICS value.
sector_name = GICS_DICT.get(sector_code)
if sector_name is not None:
result[ClassificationLevel.LEVEL1] = ClassificationNode(
# value=sector_name,
# use uppercase name from GICS definition
value=GICSDefinition(sector_code).sector.name,
code=sector_code,
)
# Level 2 is not supplied by Motley Fool.
industry_group_name = GICS_DICT.get(industry_group_code)
if industry_group_name is not None:
result[ClassificationLevel.LEVEL2] = ClassificationNode(
# value=industry_group_name,
# use uppercase name from GICS definition
value=GICSDefinition(industry_group_code).industry_group.name,
code=industry_group_code,
)
# Level 3 comes from Motley Fool but gets the canonical GICS code/name.
industry_name = GICS_DICT.get(industry_code)
if industry_name is not None:
result[ClassificationLevel.LEVEL3] = ClassificationNode(
# value=industry_name,
# use uppercase name from GICS definition
value=GICSDefinition(industry_code).industry.name,
code=industry_code,
)
return result