Skip to content

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

Result of comparing two provider records.

match_type: MatchType | None = None class-attribute instance-attribute

matched: bool instance-attribute

name_similarity: float | None = None class-attribute instance-attribute

warning: str | None = None class-attribute instance-attribute

MatchType

Security matching result.

ISIN_NAME = 'isin_name' class-attribute instance-attribute

TICKER_AND_ISIN = 'ticker_and_isin' class-attribute instance-attribute

TICKER_NAME = 'ticker_name' class-attribute instance-attribute

SecurityMatcher(*, name_similarity_threshold: float = 85.0)

Match provider records representing the same security.

Source code in src/equities_classifier/matching/matcher.py
def __init__(self, *, name_similarity_threshold: float = 85.0,) -> None:
    """Initialize SecurityMatcher."""

    self._name_similarity_threshold = name_similarity_threshold

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)