Skip to content

mostlyright.economy

mostlyright.economy — economic-indicator data for prediction-market settlement.

Macro releases — CPI (headline / core / YoY), PPI, nonfarm payrolls (NFP) and revisions, U3 unemployment, initial jobless claims, GDP, and Fed funds decisions — sourced from FRED/ALFRED + BLS/BEA/DOL/Federal Reserve and joined to Kalshi and Polymarket economy markets for leakage-free settlement pairs.

This package is deliberately isolated from the four weather parity files (research(), _internal/merge/observations.py’s SOURCE_PRIORITY, _internal/merge/climate.py’s policies, _live/_sources.py) — the same firewall CWOP sits behind. Economy carries its own schema.economy.observations.v1, its own per-release cache namespace (~/.mostlyright/cache/v1/economy/…), and its own error types (IndicatorNotYetReleasedError). A macro release must never be routed through the weather settlement join, and the weather merge code must never see an economy row.

Load-bearing invariant: Kalshi settlements contractually exclude post-expiration revisions, so these functions return as-released vintages, never the revised series. Vintage selection happens at read time via the vintages keyword.

These functions follow the source-identity contract documented in docs/source-identity.md: source= is always provenance (which agency produced the row) and delivery= is always where the computation runs ("live" locally, "hosted" reserved). Both are validated before any network call.

Public surface:

  • series() — observation rows for an indicator across a date range (canonical read; source=/delivery= contract kwargs).
  • snapshot() — the settlement-target state as-of a cutoff (the latest settlement-grade vintage knowable at as_of).
  • release_calendar() — the point-in-time release schedule: when agencies said releases would happen, as known at any past moment (as_of=). A reschedule is a new row rather than an overwrite, so a past view stays reconstructable. Local only: no delivery= keyword, no hosted call.
  • releases() — the release calendar / schedule for an indicator.
  • training_table() — a small, capped settlement-pair example.
  • attribution() — the licence attribution statements the rows in a frame require, derived from its indicator and agency columns so the answer survives a join, a concat and a JSON row round-trip.
  • __version__ — the installed mostlyrightmd-economy distribution version (parity with the weather / markets siblings).

exception mostlyright.economy.IndicatorNotYetReleasedError(indicator, period, , expected_release=None, remedy=None, source=None, request_id=None, error_code=None)

Section titled “exception mostlyright.economy.IndicatorNotYetReleasedError(indicator, period, , expected_release=None, remedy=None, source=None, request_id=None, error_code=None)”

Bases: MostlyrightError

A requested economic release is EXPECTED but has not been published yet.

For economic releases, “not yet released” is a normal, branchable state distinct from “unavailable”: the release calendar says a print is due, but no vintage_time exists for that period yet. The economy verbs (economy.series / economy.releases / economy.training_table) raise this instead of returning [] / None, so a caller can tell “the data is genuinely gone” (a DataAvailabilityError) apart from “come back after the 8:30 ET release” (this error) and, when the time is known, retry at expected_release.

Defined in core rather than packages/economy for the same reason as EarningsError and NoCWOPDataError: the exception taxonomy is centralized so MCP JSON-RPC serialization and Python/TypeScript lockstep apply uniformly. mostlyright.economy re-exports it.

  • Parameters:
    • indicator (str)
    • period (str)
    • expected_release (datetime | None)
    • remedy (str | None)
    • source (str | None)
    • request_id (str | None)
    • error_code (str)
  • Return type: None

The indicator id ("cpi", "nfp", "gdp", …).

The observation period requested ("2026-06", "2026Q2", an FOMC meeting date, …).

The scheduled release wall-clock when known, else None (the payload never fabricates a timestamp).

An optional actionable hint for how to unlock the value (for example, that the settlement-grade first print needs FRED_API_KEY). It has its own field — included in the message body and in _payload() — so period stays a real observation period and never carries a hint.

Subclass override — the stable string enum surfaced via error_code.

exception mostlyright.economy.ReleaseScheduleUnavailableError(message=”, , source=None, http_status=None, retryable=False, retry_after_s=None, underlying=”, url=None, request_id=None, error_code=None)

Section titled “exception mostlyright.economy.ReleaseScheduleUnavailableError(message=”, , source=None, http_status=None, retryable=False, retry_after_s=None, underlying=”, url=None, request_id=None, error_code=None)”

Bases: SourceUnavailableError

A release-schedule source answered, but the body carried no usable schedule.

Raised when a schedule leg returns a body that parses to zero usable events, or an event whose release-type token is outside the mapped vocabulary. An empty schedule is an explicit error, never a silently-empty return: a caller that receives an empty calendar reads it as “every future release is cancelled”, which is the wrong answer to a bot-wall page, an HTML landing page served at HTTP 200, or an unfolded line the parser dropped.

Subclasses SourceUnavailableError so an existing except SourceUnavailableError block still catches it, and inherits that constructor unchanged — source= / url= / http_status= carry the same meaning. The distinct error_code (RELEASE_SCHEDULE_UNAVAILABLE, shared byte-for-byte with the TypeScript mirror) is what lets a caller that cares tell “the endpoint answered with nothing usable” apart from “the endpoint was unreachable”.

  • Parameters:
    • message (str)
    • source (str | None)
    • http_status (int | None)
    • retryable (bool)
    • retry_after_s (float | None)
    • underlying (str)
    • url (str | None)
    • request_id (str | None)
    • error_code (str)
  • Return type: None

Subclass override — the stable string enum surfaced via error_code.

Return every attribution statement the rows in df require.

Reads the indicator column (an observation frame) and the agency column (a release-schedule frame), maps each distinct value onto the statements its licence requires, and returns the union with duplicates removed, sorted so the result does not depend on row order. A frame carrying both columns is read on both.

An indicator resolves through ATTRIBUTION_BY_INDICATOR first and through ATTRIBUTION_BY_AREA only if it has no entry there. Statements that name a reference date — the Statistics Canada acknowledgment is the one today — read it from the LATEST period in that id’s own rows, so a narrower frame produces a narrower claim.

Publishing results derived from ONS rows requires reproducing the returned statement. US rows return an empty tuple: those bytes are public domain.

df is never modified, and df.attrs is never consulted — the whole point is that the answer comes from data that survives a join and a JSON row round-trip, which frame-level metadata does not.

  • Parameters: df (DataFrame) – Any DataFrame carrying an indicator column, an agency column, or both. Null entries in either column are skipped: indicator is nullable on the release-schedule schema, and an absent value is not an id.
  • Return type: tuple[str, ...]
  • Returns: The sorted, de-duplicated statements. Empty when every row’s licence requires nothing.
  • Raises:
    • ValueErrordf carries neither column, so there is nothing to derive the obligation from; or a value names a reference area or an agency with no entry in the maps above; or an id’s statement names a reference date and the frame has no period value for its rows. All three are loud on purpose — silently answering “no obligation” for data this cannot read, or an acknowledgment with a required field missing, are the two outcomes that would put a caller in breach.
    • TypeError – an indicator entry is not a string (raised by the id grammar, which is where what an id may be is decided).

mostlyright.economy.release_calendar(indicator=None, , agency=None, as_of=None, from_date=None, to_date=None, refresh=False, client=None)

Section titled “mostlyright.economy.release_calendar(indicator=None, , agency=None, as_of=None, from_date=None, to_date=None, refresh=False, client=None)”

Return the agency release schedule as a schema.economy.releases.v1 frame.

On a cold cache this captures from every available tier, persists what the licensing gate permits, and answers from the store plus the rows the gate refused to write. On a warm cache it reads only — refresh=True forces a new capture.

“Warm” is judged against the requested scope, not the whole store. The curated Federal Reserve tier always persists, so a store-wide check reads as warm even when the leg the caller needs has never landed: after a first capture with both BEA legs down, every later indicator="gdp" call would skip the capture, filter to nothing and raise, permanently. The cost of scoping it is that an indicator with no coverage at all re-captures on each call before raising.

FRED rows are fetch-through. FRED’s Terms of Use ban store/cache/archive, so without MOSTLYRIGHT_PERSIST_FRED=1 its rows answer the call that fetched them and are then discarded, which also means a FRED-scoped call re-captures every time because nothing was kept to serve the next one. They are still recognizable in the returned frame: agency='FRED' and time_precision='date_only'. The frame-level source tag stays economy.calendar.cache for every row, because a frame carries one provenance tag and the schema validator requires the row column to match it.

  • Parameters:
    • indicator (str | None) – restrict to one economy indicator. 'gdp' reaches the BEA leg; 'fed_funds' / 'fed_decision' reach the curated Federal Reserve tier. An indicator with no calendar coverage raises rather than returning an empty frame — see the coverage split in the module docstring.
    • agency (str | None) – restrict to one publishing agency ('BEA', 'FederalReserve', 'FRED').
    • as_of (datetime | None) – return the schedule as it was known at this instant. Rows captured later are invisible, and per release the newest surviving capture wins. Default (None) is now: the current schedule.
    • to_date (datetime | None) – bound the release instants returned. A cancelled row carries no release instant, so bounding excludes tombstones.
    • refresh (bool) – re-capture even when the store already holds rows.
    • client (Client | None) – an injected httpx.Client (tests / connection reuse).
    • from_date (datetime | None)
    • to_date
  • Return type: DataFrame
  • Returns: A schema.economy.releases.v1 DataFrame stamped source='economy.calendar.cache' — the rows were read back from the local store. There is no delivery= keyword and no hosted call.
  • Raises: ReleaseScheduleUnavailableError – the requested scope holds no row. Never an empty frame: an empty schedule read back as data says every future release was cancelled.

Return the release calendar / schedule for indicator.

  • Parameters: indicator (str) – An economy indicator id (schema.economy.observations.v1 vocabulary: "cpi" / "nfp" / "gdp" / "fed_funds" / "jobless_claims" / …).
  • Return type: list[ReleaseEvent]
  • Returns: The scheduled releases for the indicator as a list of ReleaseEvent, sorted by release_datetime ascending. Sourced from the curated schedule table.
  • Raises:
    • TypeErrorindicator is not a str.
    • ValueErrorindicator has no known schedule (never returns []/None — an unschedulable indicator is an explicit error).

mostlyright.economy.series(indicator, from_date, to_date, , vintages=‘settlement’, source=None, delivery=‘live’)

Section titled “mostlyright.economy.series(indicator, from_date, to_date, , vintages=‘settlement’, source=None, delivery=‘live’)”

Return economic-indicator observation rows for indicator (canonical).

series is the canonical economy read function and follows the cross-domain source-identity keyword contract (docs/source-identity.md).

  • Parameters:
    • indicator (str) – The indicator id ("cpi" / "nfp" / "gdp" / "ppi" / "jobless_claims" / …; the schema.economy.observations.v1 vocabulary).
    • from_date (date | datetime) – Inclusive start of the requested range.
    • to_date (date | datetime) – Inclusive end of the requested range.
    • vintages (str) – "settlement" (default) returns only the settlement-grade first-print rows (the value as-of the Kalshi expiration); "all" returns every vintage for feature engineering. settlement rows are always a subset of all rows (the clean-partition property).
    • source (str | None) – Provenance pin (contract §1). None (default) uses the per-indicator default routing. A pin names the authority ("fred" ALFRED vintage store, "bls", "bea", "dol", "fed"). An unknown source, or a valid authority that cannot serve indicator (e.g. source="bea" for "cpi"), raises ValueError before any network call — never a silent fallback.
    • delivery (str) – Where the computation runs (contract §2). "live" (default) hits the public agency APIs locally; "hosted" is the reserved precomputed-API channel and raises SourceUnavailableError naming MOSTLYRIGHT_ECONOMY_HOSTED_URL + MOSTLYRIGHT_API_KEY. An unknown value raises ValueError before any network call.
  • Return type: DataFrame
  • Returns: A schema.economy.observations.v1 pandas.DataFrame (validated).
  • Raises:
    • ValueErrorvintages is not "settlement"/"all"; an invalid source/delivery (before any network call); or indicator has no dispatch entry, which never returns a silent empty frame.
    • SourceUnavailableErrordelivery="hosted", the reserved channel.
    • DataAvailabilityErrorreason="out_of_window" when from_date is below the indicator’s FEDS-2026-010 first-contract floor.
    • IndicatorNotYetReleasedError – the window has no relevant rows — a scheduled release has not landed yet. It never returns []/None.
    • ImportError – pandas is not installed (it normally arrives with mostlyrightmd-core).
>>> df = series("cpi", "2024-01-01", "2024-06-30")
>>> df.columns[:2].tolist()
['indicator', 'period']

mostlyright.economy.snapshot(indicator, , as_of=None, source=None, delivery=‘live’)

Section titled “mostlyright.economy.snapshot(indicator, , as_of=None, source=None, delivery=‘live’)”

Return the settlement-target state of indicator as knowable at as_of.

  • Parameters:
    • indicator (str) – The indicator id ("cpi" / "nfp" / "gdp" / "ppi" / "jobless_claims" / …; the schema.economy.observations.v1 vocabulary).
    • as_of (datetime | None) – The knowledge-time cutoff. Only vintages whose vintage_time is at or before as_of are considered. Defaults to now (UTC-aware) — the current settlement-target state.
    • source (str | None) – Provenance pin (contract §1), forwarded to series(). None (default) uses the per-indicator default routing; an unknown source or a valid authority that cannot serve indicator raises ValueError pre-network.
    • delivery (str) – Where the computation runs (contract §2), forwarded to series. "live" (default) | "hosted" (reserved; raises SourceUnavailableError).
  • Return type: DataFrame
  • Returns: A schema.economy.observations.v1 pandas.DataFrame — one settlement-grade row per period knowable at as_of (the latest such vintage per period).
  • Raises:

mostlyright.economy.training_table(entity, from_date, to_date, , as_of=None, source=None)

Section titled “mostlyright.economy.training_table(entity, from_date, to_date, , as_of=None, source=None)”

Leakage-guarded economy supervised table for entity (the quickstart one-liner).

A capped quickstart over the first-print settlement join: resolves the Kalshi/Polymarket economy contract (or series root) to its settlement-grade first-print agency vintage and returns training pairs. Two positional dates, inclusive ends. as_of and source forward to that join.

  • Parameters:
    • entity (str) – A Kalshi economy series root ("KXCPIYOY") or a concrete dated market ticker ("KXCPIYOY-26JUL"). Case-insensitive.
    • from_date (date | datetime) – Inclusive start of the requested range.
    • to_date (date | datetime) – Inclusive end of the requested range.
    • as_of (TimePoint | None) – Optional leakage cutoff (a TimePoint); any pair whose knowledge_time is after it raises LeakageError — a future revision can never leak into research.
    • source (str | None) – Provenance pin (source-identity contract §1), forwarded verbatim to the underlying join (None = per-indicator default routing).
  • Return type: DataFrame
  • Returns: The economy settlement-pairs pandas.DataFrame.

:raises TypeError` / ValueError / SourceUnavailableError : :raises IndicatorNotYetReleasedError` / LeakageError / ImportError — th: :raises typed errors the underlying settlement join raises.:

For anything beyond the capped quickstart, compose the pieces yourself — economy.series() / economy.snapshot() joined through mostlyright.experimental (align / spine), the documented composition path. The five-parameter cap is not widened with new keywords; every top-level domain verb keeps the same grammar.