data_updater_v2.jobs.events
events.py — the corporate actions, one class a table: Splits and Dividends; and
EventChecks, the filers’ own word on both, weekly.
splits_build / dividends_build by hand: every active ETF's and equity's whole record from every source, a family at a time in batches, each batch written before the next is fetchedsplits_update / dividends_update once the day's listed bars have landed: the window (``lookback_days`` back through today, ``ahead_days`` ahead) from every source's calendar, EODHD's files through today only, combined, the window made exactsplits_repair / dividends_repair 02:00-04:00 New York while an issue is open: the window again, then the listings left to retry
build_splits (one step: fetch and write a batch at a time) fetch_split_window ─▶ write_split_window retry_splits
events_sec_check Sundays 22:00 New York: every equity the SEC holds (a CIK), its filed dividends per share against our payouts by fiscal quarter, its filed split ratios against our splits — ``sec_disagrees`` (a quarter whose payouts do not sum to what the filer declared) and ``sec_only`` (a split the filer reported that we lack) opened as notices for a person, closed when the next check no longer finds themSources: FMP (per-listing history and its five-year calendar on a build; its split and
dividend calendars on an update), EODHD (per-listing history; its split and dividend bulks,
one call a calendar day, on an update), Nasdaq (its calendars: the exchange’s own record for
its listings). A split the sources tell differently is judged by the listing’s raw daily bars
(daily=); a dividend’s cash is settled by Nasdaq where it lists the name.
The write law: a complete answer makes its window exact — reconcile over the listings
answered for by the window, a cancelled announcement archived. A calendar that did not answer
means the night writes upsert only (nothing retired) and opens an unread issue on the
table; the repair re-sweeps the window. The window is the safety net: every night asks the
last lookback_days again, so a late or corrected line, or a missed night, lands the next
pass. Stored splits against the fetched → split_changed issues, the daily job’s refill.
Classes
Dividends
Bases: DataJob
The dividends table: built from every source’s record, updated a window at a time
after the day’s bars land, retried from the issues. Carried state: the base’s, with
params this job’s DividendsParams.
build_dividends
build_dividends(cfg: RunConfig) -> NoneEvery active ETF’s and equity’s dividend record — FMP’s, EODHD’s, Nasdaq’s for its own listings — combined one row a payout, a family at a time in batches, each batch written before the next is fetched: one receipt a batch, carrying the gaps; the fetch failures opened as issues.
dividends_repair_due
dividends_repair_due(context: Any) -> AnyAs Splits.splits_repair_due, for the dividends table.
fetch_dividend_window
fetch_dividend_window(cfg: RunConfig) -> dict[str, Any]The window’s payouts (lookback_days back, ahead_days ahead) for every
active ETF and equity: FMP’s dividend calendar, EODHD’s dividend bulk one call a day,
Nasdaq’s calendar one call a day, combined, Nasdaq settling the cash where it lists
the name; the calendars that did not answer named. Writes nothing.
retry_dividends
retry_dividends(cfg: RunConfig) -> NoneThe listings an earlier fetch left to retry (dividend_backfill): their whole
record fetched again, written; the ones that answered have their issues closed, the
others are seen again.
write_dividend_window
write_dividend_window(cfg: RunConfig, fetch_dividend_window: dict[str, Any]) -> NoneThe window landed: every calendar answered → the window made exact for the
listings answered for (a cancelled announcement moved aside); a calendar unanswered →
upsert only, nothing retired, and an unread issue the repair acts on. Receipt with
the gaps; the fetch failures opened; the unread closed once every calendar answers.
DividendsParams
Bases: JobParams
A dividends job’s row params, as SplitsParams: which listings, the build’s
window and batch, the update’s window, and how the table is written.
EventCheckParams
Bases: JobParams
The weekly SEC check’s knobs (the row’s params).
EventChecks
Bases: DataJob
The SEC’s word on our splits and dividends, a week at a time (DataClient .check_dividends_sec / check_splits_sec): what the filer filed that our record does
not show is opened as a notice (no job acts on it: a person looks), each closed when the
next check no longer finds it. Funds and most ADRs file no dividend per share: they are
not checked. Nothing is written. Carried state: the base’s, with params this job’s
EventCheckParams.
check_dividends_sec
check_dividends_sec(cfg: RunConfig) -> NoneEach equity’s payouts summed into its fiscal quarters against the dividends per
share it declared: a quarter that disagrees opens sec_disagrees.
check_splits_sec
check_splits_sec(cfg: RunConfig) -> NoneEach equity’s filed split ratios against our splits (the filer’s rounding allowed,
within SPLIT_SEC_DAYS): a ratio the filer reported that no split of ours matches
opens sec_only.
Splits
Bases: DataJob
The splits table: built from every source’s record, updated a window at a time after
the day’s bars land, retried from the issues. Carried state: the base’s, with params
this job’s SplitsParams.
build_splits
build_splits(cfg: RunConfig) -> NoneEvery active ETF’s and equity’s split record — FMP’s history and five-year calendar, EODHD’s history, Nasdaq’s calendar — judged on the listing’s raw daily bars, a family at a time in batches, each batch written before the next is fetched: one receipt a batch, carrying the gaps; the fetch failures opened as issues.
fetch_split_window
fetch_split_window(cfg: RunConfig) -> dict[str, Any]The window’s splits (lookback_days back, ahead_days ahead) for every active
ETF and equity: FMP’s split calendar, EODHD’s split bulk one call a day, Nasdaq’s
calendar, combined and judged on the listings’ raw daily bars from JUDGE_DAYS
before the window; the calendars that did not answer named. Writes nothing.
retry_splits
retry_splits(cfg: RunConfig) -> NoneThe listings an earlier fetch left to retry (split_backfill): their whole
record fetched again, judged on their daily, written; the ones that answered have
their issues closed, the others are seen again.
splits_repair_due
splits_repair_due(context: Any) -> AnyOnce a night, 02:00-04:00 New York (two hours: the night the clocks go forward has no 02:00), only while a splits issue the repair acts on is open: a calendar unread, a listing to retry.
write_split_window
write_split_window(cfg: RunConfig, fetch_split_window: dict[str, Any]) -> NoneThe window landed. First the stored splits against the fetched, for the listings
answered for: split_changed issues (the daily job’s refill). Then the write: every
calendar answered → the window made exact for those listings (a cancelled
announcement moved aside); a calendar unanswered → upsert only, nothing retired, and
an unread issue the repair acts on. Receipt with the gaps; the fetch failures
opened; the unread closed once every calendar answers.
SplitsParams
Bases: JobParams
A splits job’s row params: which listings, the build’s window and batch, the
update’s window, and how the table is written (build_splits / update_splits /
write_splits’ own).
Functions
unanswered
unanswered(section: str, model: Any, calendars: list[str]) -> list[dict[str, Any]]The unread issue each calendar that did not answer opens on its table: the night wrote without retiring anything, and the repair re-sweeps the window.