mostlyright.markets.polymarket
Polymarket discovery + settlement (US + international).
Public surface:
discover()— Gamma API discovery, no auth.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 boundary id pattern ([A-Za-z0-9_-]{1,128}).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. |
label | The polymarket.label namespace singleton (polymarket.label.settlement(...)). |
Functions
Section titled “Functions”discover(*[, client, sleep_between, …]) | Discover active Polymarket weather markets via the Gamma API. |
|---|---|
settle(event_id, *[, description, event, …]) | Resolve a Polymarket weather event to its settlement payload. |
settlement_days(entity, from_date, to_date, *) | The bare Polymarket settlement-day grid (train == live — the skew guard). |
training_table(entity, from_date, to_date, *) | Polymarket leakage-free supervised table over the label="daily_extremes" target. |
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({‘KJFK’, ‘KNYC’, ‘KEWR’}), ‘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({‘KJFK’, ‘KNYC’, ‘KEWR’}), ‘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
Section titled “default_error_code: str”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
Section titled “default_error_code: str”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
Section titled “default_error_code: str”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
Section titled “default_error_code: str”Subclass override — the stable string enum surfaced via error_code.
mostlyright.markets.polymarket.discover(, client=None, sleep_between=None, backend=‘pandas’, return_type=‘dataframe’)
Section titled “mostlyright.markets.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.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.temperature_extreme(str | None):"high"/"low"/"ambiguous"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.label = <mostlyright.markets.polymarket._PolymarketLabel object>
Section titled “mostlyright.markets.polymarket.label = <mostlyright.markets.polymarket._PolymarketLabel object>”The polymarket.label namespace singleton (polymarket.label.settlement(...)).
mostlyright.markets.polymarket.settle(event_id, , description=None, event=None, client=None, now=None)
Section titled “mostlyright.markets.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) – Polymarket 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),temperature_extreme(str): resolution station + high/low.resolution_source_type(str)observed_value_c(float): the relevant daily low or high fromdaily_extremes().observation_count(int): hourly observation count backing the value.observed_source(str): provenance of the observed extreme, from the daily_extremes row.country: pass-through 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.
mostlyright.markets.polymarket.settlement_days(entity, from_date, to_date, , station_override=None)
Section titled “mostlyright.markets.polymarket.settlement_days(entity, from_date, to_date, , station_override=None)”The bare Polymarket settlement-day grid (train == live — the skew guard).
TWO POSITIONAL DATES (inclusive ends). entity is the Polymarket event id (a
UUID/slug — NOT a station). station_override= pins the settlement station
directly (the denylist backstop’s home), bypassing event resolution AND the
network; otherwise the event’s station is resolved via the shipped resolver. The
grid is one row per LST settlement day with NO y columns.
IDENTITY RETENTION: every returned row RETAINS its originating entity (the
event id) as the LEADING column (before station and the date day field),
so concatenating several events never collapses provenance — each day stays
attributable to the event that minted it.
The caller names the contract; PIT-correct rolling-feature transforms are OUT of scope (computed inside sources or by users downstream at their own risk).
- Return type:
DataFrame - Parameters:
mostlyright.markets.polymarket.training_table(entity, from_date, to_date, , outcome=False, event=None, features=None)
Section titled “mostlyright.markets.polymarket.training_table(entity, from_date, to_date, , outcome=False, event=None, features=None)”Polymarket leakage-free supervised table over the label="daily_extremes" target.
THIN DELEGATOR — zero duplicated join logic. entity is the Polymarket event
id. Resolves the event’s settlement station from the Gamma payload, then forwards
the whole feature-composition + join to core research.dataset with
label="daily_extremes" (Polymarket weather markets resolve on the WU/NOAA-WRH
daily extremes, NOT CLI — the venue-correct routing). TWO
positional dates.
This is the markets analogue of weather.training_table(); the old
polymarket.dataset() name is a deprecation shim routing here — dataset is
reserved for the catalog noun.
“outcome=True“ is NETWORK-TOUCHING. It runs a per-settlement-date loop
over settle(); for an expired event 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 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:
- entity (
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. This is the sanctioned OFFLINE seam — the transport/clock injection seams (client/now) were removed at 2.0 (DX-H9); tests mock the module-levelsettle/fetch_event_by_idinstead. - features (
list[str] |tuple[str,...] |None) – Extra core feature names.
- entity (
- 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.