Skip to content

mostlyright.markets.polymarket

Phase 3.3 — Polymarket discovery + settlement (US + international).

Public surface:

Security-adjacent boundary validation (all enforced before any HTTP fetch or daily-extremes call):

  • event_id MUST match the UUID4 pattern.
  • description capped at 16 KB (Polymarket descriptions are concise; oversized payloads indicate hostile input).
  • Resolution-source URLs in the description MUST resolve to a netloc in RESOLUTION_SOURCE_ALLOWLIST (Wunderground or weather.gov).

Taipei + Hong Kong-lowest markets raise mostlyright.international.DeferredMarketError — v0.2 wires CWA + HKO clients.

KNOWN_WRONG_STATIONSstations that MUST NEVER resolve a Polymarket event for that city.
RESOLUTION_SOURCE_ALLOWLISTNetloc allowlist for Polymarket resolution-source URLs.
POLYMARKET_RESOLUTION_SOURCE_TYPESEnum values for the resolution_source_type column on settlement records.
dataset(event_id, from_date, to_date, *[, …])Polymarket convenience wrapper over core dataset(label="daily_extremes").
polymarket_discover(*[, client, …])Discover active Polymarket weather markets via the Gamma API.
polymarket_settle(event_id, *[, …])Resolve a Polymarket weather event to its settlement payload.
PolymarketEventError([message, error_code, …])Polymarket event payload is malformed (bad UUID, bad URL, oversized).
PolymarketSettlementError([message, …])Settlement engine couldn’t resolve an event to a bucket.
PolymarketStrikeError([message, error_code, …])A Polymarket event’s contract strike could not be parsed from slug/title.
TooEarlyToSettleError([message, request_id, …])Settlement attempted before the resolution-source’s finalization delay.

mostlyright.markets.polymarket.KNOWN_WRONG_STATIONS : Final[Mapping[str, frozenset[str]]] = mappingproxy({‘nyc’: frozenset({‘KNYC’, ‘KEWR’, ‘KJFK’}), ‘chicago’: frozenset({‘KMDW’}), ‘houston’: frozenset({‘KIAH’}), ‘dallas’: frozenset({‘KDFW’}), ‘san_francisco’: frozenset({‘KOAK’})})

Section titled “mostlyright.markets.polymarket.KNOWN_WRONG_STATIONS : Final[Mapping[str, frozenset[str]]] = mappingproxy({‘nyc’: frozenset({‘KNYC’, ‘KEWR’, ‘KJFK’}), ‘chicago’: frozenset({‘KMDW’}), ‘houston’: frozenset({‘KIAH’}), ‘dallas’: frozenset({‘KDFW’}), ‘san_francisco’: frozenset({‘KOAK’})})”

stations that MUST NEVER resolve a Polymarket event for that city. Namespace-isolated from Kalshi’s kalshi_stations.KNOWN_WRONG_STATIONS because the two issuers disagree on station identity for the same city (Polymarket NYC = KLGA, Kalshi NYC = KNYC; KNYC is correct for Kalshi and wrong for Polymarket, KLGA is correct for Polymarket and wrong for Kalshi).

Contract test in tests/test_cross_issuer_station_identity.py asserts no Polymarket catalog entry resolves to its own per-city denylist value AND that the denylists are inverse-correct across issuers for the disagreement cities (nyc, chicago).

Per-city Mapping (NOT a flat set like Kalshi’s) because Polymarket’s catalog is international + multi-city and the “wrong” station depends on which city the event is for — KLGA is wrong for chicago but right for nyc.

  • Type: Per-city Polymarket denylist

mostlyright.markets.polymarket.POLYMARKET_RESOLUTION_SOURCE_TYPES : tuple[str, …] = (‘wunderground’, ‘noaa_wrh’, ‘hko’, ‘cwa’, ‘other’)

Section titled “mostlyright.markets.polymarket.POLYMARKET_RESOLUTION_SOURCE_TYPES : tuple[str, …] = (‘wunderground’, ‘noaa_wrh’, ‘hko’, ‘cwa’, ‘other’)”

Enum values for the resolution_source_type column on settlement records. hko, cwa predeclared for v0.2 (HKO/CWA clients).

exception mostlyright.markets.polymarket.PolymarketEventError(message=”, , error_code=None, source=None, request_id=None)

Section titled “exception mostlyright.markets.polymarket.PolymarketEventError(message=”, , error_code=None, source=None, request_id=None)”

Bases: MostlyRightError

Polymarket event payload is malformed (bad UUID, bad URL, oversized).

  • Parameters:
    • message (str)
    • error_code (str)
    • source (str | None)
    • request_id (str | None)
  • Return type: None

default_error_code : str = ‘POLYMARKET_EVENT_INVALID’

Section titled “default_error_code : str = ‘POLYMARKET_EVENT_INVALID’”

Subclass override — the stable string enum surfaced via error_code.

exception mostlyright.markets.polymarket.PolymarketSettlementError(message=”, , error_code=None, source=None, request_id=None)

Section titled “exception mostlyright.markets.polymarket.PolymarketSettlementError(message=”, , error_code=None, source=None, request_id=None)”

Bases: MostlyRightError

Settlement engine couldn’t resolve an event to a bucket.

Reasons: no buckets parseable from description, daily_extremes returned no rows for the resolution station/date, or the parsed value didn’t match any bucket.

  • Parameters:
    • message (str)
    • error_code (str)
    • source (str | None)
    • request_id (str | None)
  • Return type: None

default_error_code : str = ‘POLYMARKET_SETTLEMENT_FAILED’

Section titled “default_error_code : str = ‘POLYMARKET_SETTLEMENT_FAILED’”

Subclass override — the stable string enum surfaced via error_code.

exception mostlyright.markets.polymarket.PolymarketStrikeError(message=”, , error_code=None, source=None, request_id=None)

Section titled “exception mostlyright.markets.polymarket.PolymarketStrikeError(message=”, , error_code=None, source=None, request_id=None)”

Bases: MostlyRightError

A Polymarket event’s contract strike could not be parsed from slug/title.

outcome=True needs the strike to binarize the observed value; a market with no parseable above/below/between <n><C|F> clause cannot be settled to 0/1 and this error is raised loudly (never a silent 0/1).

  • Parameters:
    • message (str)
    • error_code (str)
    • source (str | None)
    • request_id (str | None)
  • Return type: None

default_error_code : str = ‘POLYMARKET_STRIKE_UNPARSEABLE’

Section titled “default_error_code : str = ‘POLYMARKET_STRIKE_UNPARSEABLE’”

Subclass override — the stable string enum surfaced via error_code.

Netloc allowlist for Polymarket resolution-source URLs. Anything else raises PolymarketEventError to prevent silent settlement against an unknown / hostile source.

exception mostlyright.markets.polymarket.TooEarlyToSettleError(message=”, , wait_hours, resolution_source_type, request_id=None, error_code=None)

Section titled “exception mostlyright.markets.polymarket.TooEarlyToSettleError(message=”, , wait_hours, resolution_source_type, request_id=None, error_code=None)”

Bases: MostlyRightError

Settlement attempted before the resolution-source’s finalization delay.

Carries the wait time so the caller can schedule a retry.

  • Parameters:
    • message (str)
    • wait_hours (float)
    • resolution_source_type (str)
    • request_id (str | None)
    • error_code (str)
  • Return type: None

Subclass override — the stable string enum surfaced via error_code.

mostlyright.markets.polymarket.dataset(event_id, from_date, to_date, , outcome=False, event=None, client=None, now=None, features=None, **core_kwargs)

Section titled “mostlyright.markets.polymarket.dataset(event_id, from_date, to_date, , outcome=False, event=None, client=None, now=None, features=None, **core_kwargs)”

Polymarket convenience wrapper over core dataset(label="daily_extremes").

THIN DELEGATOR — zero duplicated join logic. Resolves the event’s settlement station from the Gamma payload, then forwards the whole feature-composition + join to core mostlyright.research.dataset() with label="daily_extremes" (Polymarket weather markets resolve on the WU/NOAA-WRH daily extremes, NOT CLI — the venue-correct routing that motivated Phase 32).

“outcome=True“ is NETWORK-TOUCHING. It runs a per-settlement-date loop over polymarket_settle(); for an expired event polymarket_settle refetches the event description from the live Gamma API to validate the RESOLUTION_SOURCE_ALLOWLIST. The event payload/description is fetched ONCE here and cached per call (passed into every polymarket_settle call), so a multi-date window makes at most one Gamma fetch — but the outcome path is NOT offline. Pass event= (a preloaded Gamma payload) to skip the network entirely.

  • Parameters:
    • event_id (str) – The Polymarket event id (validated at the boundary).
    • from_date (str) – YYYY-MM-DD window bounds (forwarded verbatim).
    • to_date (str) – YYYY-MM-DD window bounds (forwarded verbatim).
    • outcome (bool) – When True, append a binary label_outcome (Int64, 0/1/NA) column — see the comparison rule in _settle_polymarket_strike(). Requires the daily_extremes label frame (the core DataFrame path) and the event’s strike (parsed from the slug/title).
    • event (dict[str, Any] | None) – Optional preloaded Gamma event payload — skips the network fetch (both for station resolution AND the outcome loop). When omitted, fetched once from Gamma by event_id.
    • client (Client | None) – Optional httpx.Client for connection reuse.
    • now (datetime | None) – Override wall-clock passed to polymarket_settle (test seam).
    • features (list[str] | tuple[str, ...] | None) – Extra core feature names.
    • **core_kwargs (Any) – Forwarded verbatim to core dataset().
  • Return type: DataFrame
  • Returns: The core dataset(label="daily_extremes", ...) frame plus, when outcome=True, the label_outcome column.
  • Raises:
    • PolymarketEventError / DeferredMarketError / PolymarketSettlementError /
    • PolymarketStrikeError – per the settlement + strike-parse contract.

mostlyright.markets.polymarket.polymarket_discover(, client=None, sleep_between=None, backend=‘pandas’, return_type=‘dataframe’)

Section titled “mostlyright.markets.polymarket.polymarket_discover(, client=None, sleep_between=None, backend=‘pandas’, return_type=‘dataframe’)”

Discover active Polymarket weather markets via the Gamma API.

  • Parameters:
    • client (Client | None) – Optional httpx.Client for connection reuse.
    • sleep_between (float | None) – Optional per-request politeness sleep. Tests should pass 0 to skip the delay. Default uses the client module’s built-in 0.2s.
    • backend (str)
    • return_type (str)
  • Return type: DataFrame
  • Returns: pd.DataFrame with one row per active weather event. Requires the [polymarket] extra (raises SourceUnavailableError otherwise). Columns:
    • event_id (str): Polymarket event id (UUID4).
    • slug (str): Polymarket slug.
    • title (str): event title.
    • city (str | None): lowercased city key from polymarket_city_stations.json if a match was found, else None.
    • icao (str | None): resolved ICAO for the city (high vs low aware), or None if no city / unknown city.
    • measure (str | None): "high" / "low" / "default" per the per-event resolver, or None if unresolved.
    • end_time (str | None): event.endDate ISO timestamp.
    • resolution_source_type (str | None): wunderground / noaa_wrh / other from the description URL.
  • Raises:
    • httpx.HTTPStatusError – Gamma API returned non-2xx.
    • SourceUnavailableError[polymarket] extra not installed.

mostlyright.markets.polymarket.polymarket_settle(event_id, , description=None, event=None, client=None, now=None)

Section titled “mostlyright.markets.polymarket.polymarket_settle(event_id, , description=None, event=None, client=None, now=None)”

Resolve a Polymarket weather event to its settlement payload.

  • Parameters:
    • event_id (str) – UUID4 event identifier (validated at the boundary).
    • description (str | None) – Optional event description. If passed, used in place of the upstream payload (lets tests skip the HTTP call). Still subject to the 16 KB cap + URL allowlist.
    • event (dict[str, Any] | None) – Optional preloaded event payload (skips the HTTP call entirely). When omitted, fetched from Gamma by event_id.
    • client (Client | None) – Optional httpx.Client for connection reuse.
    • now (datetime | None) – Override wall-clock for tests. Defaults to datetime.now(UTC).
  • Returns:
    • event_id (str), slug (str), title (str)
    • settlement_date (str, YYYY-MM-DD station-local).
    • icao (str), measure (str): resolution station + high/low.
    • resolution_source_type (str)
    • observed_value_c (float): the relevant tmin or tmax from daily_extremes().
    • n_obs (int): hourly observation count backing the value.
    • source_tmin / source_tmax (str): per-source provenance from the daily_extremes row.
  • Return type: Settlement payload dict with
  • Raises:
    • PolymarketEventError – invalid event_id format, oversized description, or resolution URL outside the allowlist.
    • DeferredMarketError – resolves to Taipei / HK-low (v0.2).
    • PolymarketSettlementError – settlement date unparseable, or daily_extremes returned no usable row.
    • TooEarlyToSettleError – not enough time has passed since the resolution date for the source to have published.
    • httpx.HTTPStatusError – Gamma API returned non-2xx when no event was provided.