data_updater_v2.data_clients.changes
changes.py — the symbol-change shape every client returns, and the cleanup one source makes on its own lines.
The shape (the symbol_changes table’s told columns, tables/changes.py):
change old_symbol · new_symbol (both OUR spelling) · date (the first day under the new ticker) · name (the company as the line spelled it; None when it gave none)The cleanup (sound_changes) takes out, named — never silently ({"why": ..., "row": ...},
handed on as RawResponse.dropped):
a line with no day, or one before 1971 (FMP’s 1969-12-31 line: the epoch, no date);
a side that is not a US ticker in our spelling — the speller’s own reason (a when-issued
.V line, a foreign .T suffix, FMP’s S680-style placeholders);
the same ticker on both sides once spelled ours (BRK.B -> BRK-B: a spelling, no
rename);
the same change twice (every field alike).
Measured 2026-10-09: FMP 5,511 lines (0 told twice, 216 with a dotted ACT spelling), EODHD 1,224
(0 told twice).
Functions
sound_changes
sound_changes(lines: Iterable[Mapping[str, Any]], old: str, new: str, day: str, name: str, spell: Callable[[str, str | None], str]) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]One source’s lines in our shape, the ones no rename taken out: (kept, dropped). The
keys name the source’s fields; spell(ticker, name) is the source’s speller (ours,
or ValueError with why).