Concepts
Daily windows (local standard time)
A daily high or low belongs to the station’s local calendar day, not a UTC day. Mostly Right buckets every observation by station-local date using the IANA tz from the registry — so daily aggregation is correct without any extra work on your side.
LST, not UTC
Section titled “LST, not UTC”A station’s “day” in settlement terms is the station-local calendar day in local standard time — never daylight-savings shifted. Two reasons:
- Consistency across DST transitions. A daylight-savings-aware “local day” would be 23 or 25 hours twice a year. A 24-hour day with a fixed UTC offset is unambiguous.
- Matches how daily climate records are kept. The NWS daily climate report for KNYC dated 2025-07-15 covers the high °F observed in EST 2025-07-15 00:00 → 24:00, regardless of whether NYC is observing EDT that day.
The station’s IANA tz from STATIONS carries the LST offset — America/New_York is UTC−5 (EST), even during summer.
settlement_date_for
Section titled “settlement_date_for”The canonical mapping is settlement_date_for(observed_at, station):
from mostlyright import snapshot
# Observation at UTC 2025-07-15T02:00 — that's EST 2025-07-14T21:00 (still 2025-07-14)date = snapshot.settlement_date_for("2025-07-15T02:00:00Z", "KNYC")print(date) # "2025-07-14"
# Observation at UTC 2025-07-15T07:00 — that's EST 2025-07-15T02:00 (rolled into 2025-07-15)date = snapshot.settlement_date_for("2025-07-15T07:00:00Z", "KNYC")print(date) # "2025-07-15"The function takes an ISO-8601 UTC timestamp and the station code; it returns the settlement date as a string.
Why this matters for backtests
Section titled “Why this matters for backtests”Consider a temperature high at KNYC of 95°F observed at UTC 2025-07-15T22:51:00 (EST 17:51, 5:51 PM):
| Bucketing | Buckets to | Belongs to (local day) |
|---|---|---|
| UTC day (naive) | 2025-07-15 | Same — works for this row |
| Station LST (correct) | 2025-07-15 | Same |
Now consider the same temperature observed at UTC 2025-07-16T03:51:00 (EST 22:51, 10:51 PM — still the same local day):
| Bucketing | Buckets to | Belongs to (local day) |
|---|---|---|
| UTC day (naive) | 2025-07-16 | Wrong — the high belongs to 2025-07-15 |
| Station LST (correct) | 2025-07-15 | Same |
UTC bucketing silently mis-routes the highest observations of the day for any station east of UTC; symmetric mis-routing for stations west of UTC during the early-morning hours. research() uses LST internally to avoid this.
The pre-rollup fetch widens by ±1 UTC day
Section titled “The pre-rollup fetch widens by ±1 UTC day”To guarantee no tz-edge observation is missed, obs() and research() fetch [from_date - 1 UTC day, to_date + 1 UTC day] internally. After bucketing by LST, the output is clipped back to the caller’s window:
caller: research("KNYC", "2025-07-15", "2025-07-15")fetcher: [2025-07-14T00Z, 2025-07-17T00Z) ← widened ±1 UTC daybucket: group rows by LST date = "2025-07-15"output: one row keyed "2025-07-15"The widening is invisible to the caller; the LST clip happens at the rollup boundary.
Day and timestamp columns
Section titled “Day and timestamp columns”research() output rows carry:
date— the LST day (string"YYYY-MM-DD")obs_high_at— ISO-8601 UTC timestamp of the observation that producedobs_high_fobs_low_at— same for the lowmarket_close_utc— ISO-8601 UTC market-close timestamp (populated for market-linked workflows; null otherwise)
obs_high_at and obs_low_at are UTC — they’re the actual observation time, not bucketed. The LST date is in date.
International stations
Section titled “International stations”Same logic applies. Tokyo (RJTT) uses Asia/Tokyo (UTC+9). A UTC 2025-07-14T15:00 observation is Tokyo-local 2025-07-15T00:00 — it rolls into the 2025-07-15 settlement bucket.
date = snapshot.settlement_date_for("2025-07-14T15:00:00Z", "RJTT")print(date) # "2025-07-15"Tail-of-day handling
Section titled “Tail-of-day handling”A “tail observation” is one near the local-day boundary — e.g. KNYC at UTC 2025-07-16T04:00 (EST 23:00, end of local 2025-07-15). To include these correctly:
research()widens by ±1 UTC day — done automatically.obs()withstrategy="exact_window"returns only observations within the exact requested UTC range. Manual LST grouping is your responsibility.
Most callers want research(). Reach for obs() only when you need raw rows.
See also
Section titled “See also”- Raw as reported — the obs identity invariant
- Stations — where the IANA tz comes from
- Research API — the bucketing primitive
- Markets guide — optional prediction-market resolution helpers
- API reference:
mostlyright.snapshot(Python) ·settlementDateFor(TypeScript)