core_data_updater.jobs.prices
prices.py — PricesFlow: all nine+four price tables, the flow of record.
Design record: PRICES_FLOW.md (historical context; executable declarations in this module are the current truth). Every recurrent job splits on its CLOCK: the market families close, crypto never does, so neither island waits on the other’s roster or calendar. UPDATE prices_daily_market market bulks + the coverage pass — close-aware, sensor-fired prices_daily_crypto the 24/7 island — 00:40 UTC daily, weekends included prices_intraday_core_market the CORE market tables (PG), 15m slots prices_intraday_core_crypto the CORE crypto table, 15m slots prices_crypto_aligned DB-derived aligned bars, fired from the canonical crypto-core product prices_intraday_full_market the FULL equity/etf roster → pq/Garage (fail-closed until layout-init) prices_intraday_full_crypto the FULL crypto roster → pq/Garage BUILD prices_build full history, replace (NO stales), manual BACKFILL prices_backfill refresh each island’s coverage FIRST, then coverage-debt ∪ recent-gap fills, upsert, NIGHTLY 09:00 UTC (≈4am ET)
Laws carried here: state-as-params (market rosters, crypto roster, and sessions are separate operations and each consumer requests exactly what it needs); configs read LIVE per step (named fallback; enabled comes back VISIBLE — a disabled row skips, named; the stamped-disabled runs default enabled=False fail-closed); every landing through CoreDataRepo.land (gates + policy op, drops counted); the same-day drop is CLOSE-AWARE (only the session whose close hasn’t passed is unfinished); fulfill-or-name; no dbt anywhere in the landing path. Aligned bars never fetch a vendor: they rehydrate the canonical landed crypto core table and aggregate it against the passed market calendar.
Sensor timings (Sam): market probes begin close+30m (close via the DOUBLE
client hit — EODHD exchange_details + FMP exchange_hours, AM/PM honored),
re-probe 15m, INDX bulk as the served canary; crypto daily begins 00:40 UTC
every day (no market close); intraday = ONE sensor PER JOB on 15m slots (NOT
one per family — see the sensor section’s comment: N sensors on one job mint
N run_keys for the same slot, so the slot fires N runs). Every sensor also
gates on its own island’s prerequisites through the requires asset-key
channel (@sensor(requires=[…]) → the body gains upstream_done): a target
is not probed until its canonical state products have been bootstrapped. The
body and launched job read current table state; requires is deliberately
an existence contract, not an age/freshness claim.
KNOWN LIMIT (documented, §4): session gates ride the NYSE calendar for every
market family until metadata lands multi-exchange calendars.
Classes
PricesFlow
Bases: CoreDataFlow
The prices flow — PRICES_FLOW.md §5, method for method.
backfill_crypto_daily
backfill_crypto_daily(cfg, fetch_crypto_roster: dict) -> Nonebackfill_crypto_intraday_core
backfill_crypto_intraday_core(cfg, fetch_crypto_roster: dict) -> Nonebackfill_equity_daily
backfill_equity_daily(cfg, fetch_market_rosters: dict, fetch_history_sessions: dict) -> Nonebackfill_equity_intraday_core
backfill_equity_intraday_core(cfg, fetch_market_rosters: dict, fetch_history_sessions: dict) -> Nonebackfill_etf_daily
backfill_etf_daily(cfg, fetch_market_rosters: dict, fetch_history_sessions: dict) -> Nonebackfill_etf_intraday_core
backfill_etf_intraday_core(cfg, fetch_market_rosters: dict, fetch_history_sessions: dict) -> Nonebackfill_index_daily
backfill_index_daily(cfg, fetch_market_rosters: dict, fetch_history_sessions: dict) -> Nonebackfill_index_intraday_core
backfill_index_intraday_core(cfg, fetch_market_rosters: dict, fetch_history_sessions: dict) -> Nonebuild_aligned
build_aligned(cfg, fetch_history_sessions: dict) -> NoneFull aligned history from OWN landed intraday + the full calendar.
build_crypto_daily
build_crypto_daily(cfg, fetch_crypto_roster: dict) -> Nonebuild_crypto_intraday_core
build_crypto_intraday_core(cfg, fetch_crypto_roster: dict) -> Nonebuild_crypto_intraday_full
build_crypto_intraday_full(cfg, fetch_crypto_roster: dict) -> Nonebuild_equity_daily
build_equity_daily(cfg, fetch_market_rosters: dict, fetch_history_sessions: dict) -> Nonebuild_equity_intraday_core
build_equity_intraday_core(cfg, fetch_market_rosters: dict, fetch_history_sessions: dict) -> Nonebuild_equity_intraday_full
build_equity_intraday_full(cfg, fetch_market_rosters: dict, fetch_history_sessions: dict) -> Nonebuild_etf_daily
build_etf_daily(cfg, fetch_market_rosters: dict, fetch_history_sessions: dict) -> Nonebuild_etf_intraday_core
build_etf_intraday_core(cfg, fetch_market_rosters: dict, fetch_history_sessions: dict) -> Nonebuild_etf_intraday_full
build_etf_intraday_full(cfg, fetch_market_rosters: dict, fetch_history_sessions: dict) -> Nonebuild_index_daily
build_index_daily(cfg, fetch_market_rosters: dict, fetch_history_sessions: dict) -> Nonebuild_index_intraday_core
build_index_intraday_core(cfg, fetch_market_rosters: dict, fetch_history_sessions: dict) -> Nonecrypto_daily_ready
crypto_daily_ready(ctx, upstream_done)The 24/7 clock: fires once per UTC day from 00:40, weekends and holidays included — the just-finished UTC day is the target. Gated on the crypto catalog: the island’s roster IS its universe, so the job cannot run before that product has been bootstrapped.
daily_ready
daily_ready(ctx, upstream_done)Fire the newest completed session missing from any daily table.
Calendar close truth comes from the canonical session table. Distinct landed table dates, not min/max coverage, expose an interior missed day. One run repairs the newest hole; the next sensor tick advances to the next one. The chosen date rides both run key and tag so retries and reexecution fetch the same vendor partition deterministically.
fetch_crypto_roster
fetch_crypto_roster(cfg) -> dictRead only the 24/7 island’s catalog; no market table is touched.
fetch_history_sessions
fetch_history_sessions(cfg) -> dictRead the complete configured calendar span for builds/backfills.
The historical jobs can fetch rows years outside the operational heartbeat. Feeding their landing gates the 30-day payload silently discarded valid old bars; this dedicated state product uses explicit start/end bounds and is selected only by historical workloads.
fetch_market_rosters
fetch_market_rosters(cfg) -> dictRead only the equity/ETF/index state used by market-clock jobs.
fetch_sessions
fetch_sessions(cfg) -> dictRead the bounded NYSE calendar used by operational heartbeats.
intraday_full_crypto_ready
intraday_full_crypto_ready(ctx, upstream_done)The island’s 15m slot probe → the FULL crypto sweep.
intraday_full_market_ready
intraday_full_market_ready(ctx, upstream_done)The market clock’s 15m slot probe → the FULL equity/etf sweep.
intraday_ready_crypto
intraday_ready_crypto(ctx, upstream_done)The island’s 15m slot probe → the core crypto intraday job.
intraday_ready_market
intraday_ready_market(ctx, upstream_done)The market clock’s 15m slot probe → the core market intraday job.
refresh_crypto_coverage
refresh_crypto_coverage(cfg, fetch_crypto_roster: dict) -> Nonerefresh_market_coverage
refresh_market_coverage(cfg, fetch_market_rosters: dict) -> Nonesweep_crypto_full
sweep_crypto_full(cfg, fetch_crypto_roster: dict) -> Nonesweep_equity_full
sweep_equity_full(cfg, fetch_market_rosters: dict, fetch_sessions: dict) -> Nonesweep_etf_full
sweep_etf_full(cfg, fetch_market_rosters: dict, fetch_sessions: dict) -> Noneupdate_crypto_aligned
update_crypto_aligned(cfg, fetch_sessions: dict) -> NoneDerive the recent aligned window from landed core bars only.
This is a separate product-triggered job: the 24/7 ingestion island never waits on the market calendar, while every successful writer of the canonical core product can request a deterministic re-derivation.
update_crypto_daily
update_crypto_daily(cfg, fetch_crypto_roster: dict) -> NoneThe 24/7 island — fired at 00:40 UTC every day; the running UTC day is ALWAYS the unfinished one.
update_crypto_intraday_core
update_crypto_intraday_core(cfg, fetch_crypto_roster: dict) -> Noneupdate_equity_daily
update_equity_daily(cfg, fetch_market_rosters: dict, fetch_sessions: dict) -> Noneupdate_equity_intraday_core
update_equity_intraday_core(cfg, fetch_market_rosters: dict, fetch_sessions: dict) -> Noneupdate_etf_daily
update_etf_daily(cfg, fetch_market_rosters: dict, fetch_sessions: dict) -> Noneupdate_etf_intraday_core
update_etf_intraday_core(cfg, fetch_market_rosters: dict, fetch_sessions: dict) -> Noneupdate_index_daily
update_index_daily(cfg, fetch_market_rosters: dict, fetch_sessions: dict) -> Noneupdate_index_intraday_core
update_index_intraday_core(cfg, fetch_market_rosters: dict, fetch_sessions: dict) -> None