mostlyright.markets.polymarket
mostlyright.markets.polymarket
Section titled “mostlyright.markets.polymarket”Phase 3.3 — Polymarket discovery + settlement (US + international).
Public surface:
polymarket_discover()— Gamma API discovery, no auth.polymarket_settle(event_id)()— settlement engine using internalmostlyright.international.daily_extremes()as the resolution source.
Security-adjacent boundary validation (all enforced before any HTTP fetch or daily-extremes call):
event_idMUST match the UUID4 pattern.descriptioncapped 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.
Module Attributes
Section titled “Module Attributes”KNOWN_WRONG_STATIONS | stations that MUST NEVER resolve a Polymarket event for that city. |
|---|---|
RESOLUTION_SOURCE_ALLOWLIST | Netloc allowlist for Polymarket resolution-source URLs. |
POLYMARKET_RESOLUTION_SOURCE_TYPES | Enum values for the resolution_source_type column on settlement records. |
Functions
Section titled “Functions”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. |
Exceptions
Section titled “Exceptions”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:
- 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:
- 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:
- 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.
mostlyright.markets.polymarket.RESOLUTION_SOURCE_ALLOWLIST : frozenset[str] = frozenset({‘weather.gov’, ‘wunderground.com’, ‘www.weather.gov’, ‘www.wunderground.com’})
Section titled “mostlyright.markets.polymarket.RESOLUTION_SOURCE_ALLOWLIST : frozenset[str] = frozenset({‘weather.gov’, ‘wunderground.com’, ‘www.weather.gov’, ‘www.wunderground.com’})”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:
- Return type: None
default_error_code : str = ‘POLYMARKET_TOO_EARLY’
Section titled “default_error_code : str = ‘POLYMARKET_TOO_EARLY’”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-DDwindow bounds (forwarded verbatim). - to_date (
str) –YYYY-MM-DDwindow bounds (forwarded verbatim). - outcome (
bool) – WhenTrue, append a binarylabel_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 byevent_id. - client (
Client|None) – Optionalhttpx.Clientfor connection reuse. - now (
datetime|None) – Override wall-clock passed topolymarket_settle(test seam). - features (
list[str] |tuple[str,...] |None) – Extra core feature names. - **core_kwargs (
Any) – Forwarded verbatim to coredataset().
- event_id (
- Return type:
DataFrame - Returns:
The core
dataset(label="daily_extremes", ...)frame plus, whenoutcome=True, thelabel_outcomecolumn. - 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:
- Return type:
DataFrame - Returns:
pd.DataFramewith one row per active weather event. Requires the[polymarket]extra (raisesSourceUnavailableErrorotherwise). Columns:event_id(str): Polymarket event id (UUID4).slug(str): Polymarket slug.title(str): event title.city(str | None): lowercased city key frompolymarket_city_stations.jsonif 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.endDateISO 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 byevent_id. - client (
Client|None) – Optionalhttpx.Clientfor connection reuse. - now (
datetime|None) – Override wall-clock for tests. Defaults todatetime.now(UTC).
- event_id (
- Returns:
event_id(str),slug(str),title(str)settlement_date(str,YYYY-MM-DDstation-local).icao(str),measure(str): resolution station + high/low.resolution_source_type(str)observed_value_c(float): the relevant tmin or tmax fromdaily_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
eventwas provided.