Matcher
matcher ¶
match SecurityProviderRecords.
SecurityMatchSource: TypeAlias = SecurityProviderRecord | Security module-attribute ¶
MatchResult(matched: bool, match_type: MatchType | None = None, warning: str | None = None, name_similarity: float | None = None) dataclass ¶
MatchType ¶
SecurityMatcher(*, name_similarity_threshold: float = 85.0) ¶
Match provider records representing the same security.
Source code in src/equities_classifier/matching/matcher.py
match(left: SecurityMatchSource, right: SecurityProviderRecord) -> MatchResult ¶
Match SecurityProviderRecords according to matching hierarchy.
Source code in src/equities_classifier/matching/matcher.py
def match(
self,
left: SecurityMatchSource,
right: SecurityProviderRecord,
) -> MatchResult:
"""Match SecurityProviderRecords according to matching hierarchy."""
left_ticker = left.identifier_value(SecurityIdentifierType.TICKER,)
right_ticker = right.identifier_value(SecurityIdentifierType.TICKER,)
left_isin = left.identifier_value(SecurityIdentifierType.ISIN,)
right_isin = right.identifier_value(SecurityIdentifierType.ISIN,)
# 1. Ticker + ISIN
if (
left_ticker
and right_ticker
and left_isin
and right_isin
and left_ticker == right_ticker
and left_isin == right_isin
):
return MatchResult(matched=True, match_type=MatchType.TICKER_AND_ISIN,)
# 2. ISIN + similar name
if (
left_isin
and right_isin
and left_isin == right_isin
and left_ticker != right_ticker
):
similarity = name_similarity(left.name, right.name)
if similarity >= self._name_similarity_threshold:
return MatchResult(
matched=True,
match_type=MatchType.ISIN_NAME,
warning=(f"ISIN matches but ticker differs: {left_ticker!r} != {right_ticker!r}"),
name_similarity=similarity,
)
# 3. Ticker + similar name
if (
left_ticker
and right_ticker
and left_ticker == right_ticker
):
similarity = name_similarity(left.name, right.name)
if similarity >= self._name_similarity_threshold:
return MatchResult(
matched=True,
match_type=MatchType.TICKER_NAME,
name_similarity=similarity,
)
return MatchResult(matched=False)