From e9798bf021847689084f951de3d0111d97167ea5 Mon Sep 17 00:00:00 2001 From: bozarnr <97791974+bozarnr@users.noreply.github.com> Date: Fri, 14 Aug 2026 21:05:43 +0800 Subject: [PATCH 1/3] Fix shared-prefix ticker hint extraction --- quantmind/preprocess/news.py | 37 +++++++++++++++++++++++++++++------ tests/preprocess/test_news.py | 33 +++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/quantmind/preprocess/news.py b/quantmind/preprocess/news.py index 12e7706..31d3695 100644 --- a/quantmind/preprocess/news.py +++ b/quantmind/preprocess/news.py @@ -56,6 +56,10 @@ r"(? tuple[NewsTickerHint, ...]: scan_text = _MARKDOWN_EMPHASIS_RE.sub(r"\1", scan_text) hints: list[NewsTickerHint] = [] seen: set[tuple[str, str | None]] = set() - for match in _EXCHANGE_TICKER_RE.finditer(scan_text): - raw_exchange = " ".join(match.group(1).upper().split()) - exchange = _EXCHANGE_NAMES.get(raw_exchange, raw_exchange) - symbol = match.group(2).upper() + + def append_hint( + *, + symbol: str, + exchange: str, + raw: str, + ) -> None: key = (symbol, exchange) if key in seen: - continue + return seen.add(key) hints.append( NewsTickerHint( symbol=symbol, exchange=exchange, - raw=match.group(0).strip(), + raw=raw, ) ) + + for match in _EXCHANGE_TICKER_RE.finditer(scan_text): + raw_exchange = " ".join(match.group(1).upper().split()) + exchange = _EXCHANGE_NAMES.get(raw_exchange, raw_exchange) + symbol = match.group(2).upper() + append_hint( + symbol=symbol, + exchange=exchange, + raw=match.group(0).strip(), + ) + + tail = re.split(r"[;)]", scan_text[match.end() :], maxsplit=1)[0] + for list_match in _EXCHANGE_TICKER_LIST_MEMBER_RE.finditer(tail): + append_hint( + symbol=list_match.group(1).upper(), + exchange=exchange, + raw=f"{raw_exchange}: {list_match.group(1).upper()}", + ) return tuple(hints) diff --git a/tests/preprocess/test_news.py b/tests/preprocess/test_news.py index 7a0b634..c4db38c 100644 --- a/tests/preprocess/test_news.py +++ b/tests/preprocess/test_news.py @@ -117,6 +117,39 @@ def test_exchange_ticker_hints_ignore_markdown_decoration(self): expected, ) + def test_exchange_ticker_hints_capture_shared_prefix_lists(self): + cases = ( + ( + "(OTCID: QVCAQ, QVCGQ, QVCPQ)", + [], + ), + ( + "(NYSE: EVEX, EVEXW; B3: EVEB31)", + [("EVEX", "NYSE"), ("EVEXW", "NYSE")], + ), + ( + "(NYSE: TME and HKEX: 1698)", + [("TME", "NYSE")], + ), + ( + "(NASDAQ: VMAR; TSXV: VMAR)", + [("VMAR", "NASDAQ")], + ), + ( + "(NYSE: ASR; BMV: ASUR)", + [("ASR", "NYSE")], + ), + ) + + for text, expected in cases: + with self.subTest(text=text): + hints = extract_exchange_ticker_hints(text) + + self.assertEqual( + [(hint.symbol, hint.exchange) for hint in hints], + expected, + ) + def test_build_sec_news_identity(self): self.assertEqual( build_sec_news_identity( From 76ece5b9d4c15a09b8bc79783edd3c3ea1fda6c2 Mon Sep 17 00:00:00 2001 From: Newman Gao Date: Sat, 15 Aug 2026 15:09:02 +0800 Subject: [PATCH 2/3] Keep shared-prefix ticker members uppercase --- quantmind/preprocess/news.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/quantmind/preprocess/news.py b/quantmind/preprocess/news.py index 31d3695..33427ce 100644 --- a/quantmind/preprocess/news.py +++ b/quantmind/preprocess/news.py @@ -57,8 +57,7 @@ re.IGNORECASE, ) _EXCHANGE_TICKER_LIST_MEMBER_RE = re.compile( - r"\s*,\s*([A-Z][A-Z0-9.-]{0,9})\b", - re.IGNORECASE, + r"\s*,\s*([A-Z][A-Z0-9.-]{0,9})\b" ) _EMAIL_PROTECTION_LINK_RE = re.compile( r"\[\[email protected]\]\(/cdn-cgi/l/email-protection#[^)]+\)" From c89b53259a5884afa1aae9a723d69caa7e93e26b Mon Sep 17 00:00:00 2001 From: Newman Gao Date: Sat, 15 Aug 2026 15:09:06 +0800 Subject: [PATCH 3/3] Test lowercase prose after ticker lists --- tests/preprocess/test_news.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/preprocess/test_news.py b/tests/preprocess/test_news.py index c4db38c..64d9cbd 100644 --- a/tests/preprocess/test_news.py +++ b/tests/preprocess/test_news.py @@ -127,6 +127,10 @@ def test_exchange_ticker_hints_capture_shared_prefix_lists(self): "(NYSE: EVEX, EVEXW; B3: EVEB31)", [("EVEX", "NYSE"), ("EVEXW", "NYSE")], ), + ( + "(NASDAQ: ABC, a leading provider announced results)", + [("ABC", "NASDAQ")], + ), ( "(NYSE: TME and HKEX: 1698)", [("TME", "NYSE")],