From 73b03b227e9bacc4eaac4553c74f3f652facfeda Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Fri, 17 Apr 2026 19:29:23 +0000 Subject: [PATCH] Add Trading212 ticker normalisation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Context ------- Phase 1 kickoff: Trading212 tags every ticker with `_EQ`, sometimes preceded by a lowercase exchange letter ("l" = LSE) or `_US`. Raw symbols like `VUAGl_EQ` are an implementation detail that would leak into Wealthfolio and diverge from other providers (InvestEngine and Schwab emit `VUAG` / `META`). The canonical form has to match across providers so portfolio aggregation lines up. Unlike the finance/ reference code, we do NOT restrict to a SUPPORTED_TICKERS allowlist here — Wealthfolio is the source of truth, everything gets imported, and the user decides what to track. This change ----------- - broker_sync/providers/trading212.py: pure `_normalise_ticker` helper backed by a single regex that peels `(_US)?[a-z]?_EQ`. No lookup tables — the rule covers all observed shapes. - tests/providers/test_trading212_ticker.py: parametrised cases for every mapping called out in the Phase 1 plan plus pass-through of already-canonical symbols. Test plan --------- ## Automated - poetry run pytest -q → 41 passed in 0.46s - poetry run mypy broker_sync tests → Success: no issues found in 22 source files - poetry run ruff check . → All checks passed! ## Manual Verification Not applicable — pure function, no external side effects. Co-Authored-By: Claude Opus 4.7 (1M context) --- broker_sync/providers/trading212.py | 15 +++++++++++++ tests/providers/__init__.py | 0 tests/providers/test_trading212_ticker.py | 27 +++++++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 broker_sync/providers/trading212.py create mode 100644 tests/providers/__init__.py create mode 100644 tests/providers/test_trading212_ticker.py diff --git a/broker_sync/providers/trading212.py b/broker_sync/providers/trading212.py new file mode 100644 index 0000000..06da489 --- /dev/null +++ b/broker_sync/providers/trading212.py @@ -0,0 +1,15 @@ +from __future__ import annotations + +import re + +_SUFFIX_RE = re.compile(r"(?:_US)?(?:[a-z])?_EQ$") + + +def _normalise_ticker(raw: str) -> str: + """Strip T212's exchange-suffix decoration from a ticker. + + T212 tags every ticker with `_EQ`, optionally preceded by a lowercase + letter ("l" for LSE) or `_US` for a US listing. Peel those off so the + symbol matches what Schwab / InvestEngine emit. + """ + return _SUFFIX_RE.sub("", raw) diff --git a/tests/providers/__init__.py b/tests/providers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/providers/test_trading212_ticker.py b/tests/providers/test_trading212_ticker.py new file mode 100644 index 0000000..da918b6 --- /dev/null +++ b/tests/providers/test_trading212_ticker.py @@ -0,0 +1,27 @@ +from __future__ import annotations + +import pytest + +from broker_sync.providers.trading212 import _normalise_ticker + + +@pytest.mark.parametrize( + ("raw", "expected"), + [ + # T212 LSE suffix: lowercase "l" before _EQ means London listing. + ("VUAGl_EQ", "VUAG"), + ("VUSAl_EQ", "VUSA"), + ("VWRPl_EQ", "VWRP"), + ("METAl_EQ", "META"), + # T212 US suffix: _US_EQ means US listing. Strip the whole suffix. + ("META_US_EQ", "META"), + ("AAPL_US_EQ", "AAPL"), + # Plain _EQ suffix with no exchange letter. + ("FOO_EQ", "FOO"), + # Already canonical — passes through. + ("VUAG", "VUAG"), + ("META", "META"), + ], +) +def test_normalise_ticker(raw: str, expected: str) -> None: + assert _normalise_ticker(raw) == expected