Guides
Build a point-in-time research table
A point-in-time research table answers one question: what could have been known at each decision? Start with a target and cutoff, then align sources using their own availability timestamps.
01 · Start with the target and cutoff
Section titled “01 · Start with the target and cutoff”Each row needs:
- an entity, such as a station or contract
- a decision cutoff
- the outcome you want to predict
For a Kalshi daily-temperature market, the NWS CLI label provides the target:
from mostlyright import weather
target = weather.label.cli( "KNYC", "2025-01-06", "2025-01-12",)The target acts as the table’s spine. Keep its decision_time_utc visible so
every feature can be evaluated against the same cutoff.
Do not treat every timestamp as equally strong. Exact availability timestamps can support a point-in-time audit; derived or reconstructed timestamps carry a weaker guarantee. See Temporal safety for the timing model and its limits.
02 · Align sources to the cutoff
Section titled “02 · Align sources to the cutoff”Use mostlyright.experimental.align() when you want to choose which sources
are joined to the target:
from mostlyright.experimental import align
reports = weather.observations("KNYC")table = align(target, reports)Calling a supported source without dates returns a DeferredSource. align()
reads the target window, fetches the source, and applies its SourceContract
against the decision cutoff.
Add more sources explicitly:
forecasts = weather.forecasts("KNYC")table = align(target, reports, forecasts)align() can audit exact source timestamps. Reconstructed or unknown timing
does not become exact just because it passes through the helper. The composition
API is documented and usable, but semver-exempt while it remains experimental.
03 · Inspect the ingredients directly
Section titled “03 · Inspect the ingredients directly”Use date-bound domain reads when you want the source rows before composition:
reports = weather.observations("KNYC", "2025-01-06", "2025-01-12")forecasts = weather.forecasts("KNYC", "2025-01-06", "2025-01-12")target = weather.label.cli("KNYC", "2025-01-06", "2025-01-12")These stable reads are eager and require both from_date and to_date.
observations() returns per-report METAR/SPECI rows; it does not hide a daily
aggregation behind a switch.
Compose these rows yourself with pandas or Polars when you need a custom table. Keep each source’s availability timestamp and the target cutoff visible through the join.
04 · Use the built-in training-table example
Section titled “04 · Use the built-in training-table example”weather.training_table() is a small supervised-learning example. It places a
built-in settlement target beside daily observation features:
from datetime import datefrom mostlyright import weather
example = weather.training_table( "KNYC", date(2025, 1, 6), date(2025, 1, 12),)The result includes:
- identity and timing:
station,local_standard_date,decision_time_utc,label_available_time_utc,knowable_at_utc - target columns:
daily_summary_temp_high_f,daily_summary_temp_low_f,daily_summary_report_type - observation features:
observed_temp_high_f,observed_temp_low_f,observed_temp_mean_f, and the otherobserved_*aggregates
The helper is deliberately narrow: station, two inclusive dates, and label.
Use it when this recipe matches your research; use explicit sources and
composition when it does not.
Choose the built-in target
Section titled “Choose the built-in target”label= | Target | Typical use |
|---|---|---|
"cli" (default) | NWS CLI daily high/low in °F | Kalshi NHIGH/NLOW research |
"daily_extremes" | WU / NOAA-WRH daily extremes in °C | Polymarket weather research |
kalshi = weather.training_table( "KNYC", "2025-01-06", "2025-01-12", label="cli")
polymarket = weather.training_table( "EGLL", "2025-01-06", "2025-01-12", label="daily_extremes")For a station panel, pass a list or tuple. The result stays long-form, with one row per station and local-standard day:
panel = weather.training_table( ["KNYC", "KLAX", "KMDW"], "2024-11-01", "2025-04-30",)The 1.x verbs pairs(), research(), and dataset() were removed in 2.0. See
the upgrade guide for the migration map.
TypeScript
Section titled “TypeScript”TypeScript mirrors the stable domain reads and the small training-table example:
import { trainingTable, weather } from "mostlyright";
const { rows: reports } = await weather.observations( "KNYC", "2025-01-06", "2025-01-12",);
const { rows: example } = await trainingTable( "KNYC", "2025-01-06", "2025-01-12",);Both calls return a DataResult; read the data from rows. When the built-in
example does not fit, compose the explicit domain rows in your application.
Timing and errors
Section titled “Timing and errors”Split research data on time, not randomly, and recompute rolling features inside each walk-forward fold.
Common failures are typed:
ContractErrorfor an invalid station, date window, source, or labelHttpErrorfor upstream transport and HTTP failuresLeakageErrorwhen an experimental composition violates a point-in-time cutoffNoDataErrorwhen a valid request has no usable rows
See also
Section titled “See also”- Temporal safety: knowledge time, cutoffs, and source fidelity
- Weather observations: the per-report feature source
- Sources & provenance: fused and source-pinned reads
- Prediction markets: venue contracts and settlement targets
- 2.0 migration: renamed and removed APIs