Skip to content

data_updater_v2.data_clients.actions

actions.py — the corporate-action shape every client returns, and the cleanups one source makes on its own rows.

The shape (the splits / dividends tables’ columns, tables/actions.py):

split symbol · date (the first day on the new share count) · split_to · split_from
· split_type (the vendor's label, None when unserved)
dividend symbol · ex_date · amount (cash as paid) · period (our words: ``period_word``)
· currency (None when the source states none) · declaration_date ·
record_date · payment_date

A cleanup never drops a row silently: it returns the rows kept and, apart, the rows it took out ({"why": ..., "row": ...}), which the client hands on as RawResponse.dropped.

The cleanups (FMP’s rows, probed 2026-10-05/07 over every US dividend in 5 years and 373 liquid ETFs and stocks):

clean_dividends the same payout twice under two labels (same symbol, ex-date and
amount: FDGFX Monthly + Quarterly, EWA Semi-Annual + Special, O
Irregular + Monthly — 20 in 373 symbols, and EODHD carries each once)
is one payout; on one ex-date two DIFFERENT amounts where exactly one
row was declared is a correction beside the old row (OPRA 2025-07-08:
0.40 declared, 0.39 not; EODHD says 0.40) — the declared row stays.
Two declared (or two undeclared) amounts on one day pass untouched: a
regular and a special, or a doubt a second source settles.
sound_splits any source's rows: a side zero or unread (no factor at all), a ratio of
exactly one (ABEO 1998, FMP: no factor), the same split twice (every
field alike) — taken out. 1,923 US names probed 2026-10-08: FMP 1 ratio
of one, 0 twins, 0 zero sides; EODHD none. A fractional side (an ADR
ratio change, 1.642 for 1) is a factor like any other.

Functions

action_day

action_day(row: Mapping[str, Any]) -> dt.date

An action row’s day: a dividend’s ex-date, a split’s date.

clean_dividends

clean_dividends(rows: Iterable[Mapping[str, Any]]) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]

One source’s dividend rows with its twins and its undeclared corrections taken out: (the rows kept, in their input order per day; the rows taken out, each with why).

period_word

period_word(value: Any) -> str

A source’s period word in ours (‘unknown’ for none).

same_amount

same_amount(a: float | None, b: float | None) -> bool

Whether two dividend amounts are the same cash, rounding apart.

sound_splits

sound_splits(rows: Iterable[Mapping[str, Any]]) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]

Any source’s split rows with the ones that carry no factor taken out: a side zero or unread, a ratio of exactly one, the same split twice (every field alike, the first kept).

split_ratio

split_ratio(row: Mapping[str, Any]) -> float | None

A split row’s price factor, shares after per share before (2.0 for 2-for-1); None when a side is missing or zero.