Skip to main content

pmlab

CI PyPI Python License: MIT Docs

A plugin-based ML framework for Polymarket prediction markets. Build, backtest and paper-trade models for any market family (weather, sports, politics, crypto) with a backtest that cannot look ahead and a gate that no failing model gets past.

You write a small plugin that says how to find a family's markets, build its features and resolve its outcomes. pmlab does the rest: edge and cost arithmetic, walk-forward backtesting, a hard GO / NO_GO gate before a model is promoted, paper trading, settlement, drift monitoring and a cron-friendly watchdog.

Not financial advice. pmlab is research software; trading prediction markets can lose all the money you put in. No pmlab command places an order. Only a LiveBroker you build yourself, with your own Polymarket credentials, does, and dry_run=True is how you try it first.

Why it is safe to build on

These are enforced in code and checked by the test suite, not left to convention.

  • No lookahead. rolling_origin_eval trains only on rows whose label was already known when the fold decided: a decision for tomorrow's temperature is not a label until tomorrow is over. build_panel refuses a price observed at or after the decision it informs, and check_no_leakage raises when a feature gives the outcome away. How the checks work
  • A failed gate cannot ship. ChampionManifest.publish raises unless the gate says GO, and that is not configurable. Paper trading reads its allowed segments from champion.json, the record made at promotion, and a trade is never settled before the plugin says the truth is final.
  • Reproducible. Every backtest records its seeds, library versions, resolved config and a fingerprint of the data; pmlab reproduce re-runs it and verifies the metrics come back. Reproducibility
  • Stable. From 1.0 pmlab follows semantic versioning. The public API (the names, the pmlab command and its options, the file formats) is frozen and compared with a checked-in snapshot on every change; nothing public is removed before 2.0 without a deprecation warning first. What is public

Install

pip install pmlab            # Python 3.12+; or: uv add pmlab
pip install "pmlab[tune]"    # + Optuna, for TunedForecaster
pip install "pmlab[track]"   # + MLflow, for MLflowTracker

Quickstart

This runs offline on a small synthetic sample bundled in the package: 750 weather-temperature markets, no network and no account. The numbers say nothing about real markets.

from pmlab import (ChampionManifest, HoldoutGateResult, LGBMForecaster, TradePolicy,
                   build_panel, rolling_origin_eval)
from pmlab.datasets import load_sample

sample = load_sample()                       # 750 synthetic weather markets, checked against a checksum
resolver = sample.plugin()                   # the real WeatherTmaxPlugin, wired to the bundled file
found = [(day, spec) for day in sample.days for spec in sample.plugin(day).discover_markets()]

# One row per (market, bin), each at its own price. build_panel refuses a price seen at or after
# the decision, and check_leakage=True raises if a feature gives the outcome away.
panel = build_panel(
    [resolver.build_training_rows(s, "previous_evening", decision_time=f"{d}T12:00:00+00:00")
     for d, s in found],
    check_leakage=True,
)
# The forecast alone cannot tell a market's bins apart: add how far each bin sits from it.
bounds = {(s.market_id, b.label): [x for x in (b.lower, b.upper) if x is not None]
          for _, s in found for b in s.outcome_bins}
centre = [sum(bounds[k]) / len(bounds[k]) for k in zip(panel["market_id"], panel["outcome_label"])]
panel["feature_bin_dist"] = centre - panel["feature_fcst_tmax"]

# Walk-forward backtest: every fit sees only rows from before the day it predicts.
model = LGBMForecaster(random_state=7, n_jobs=1)
result = rolling_origin_eval(
    panel, model, stride=10, min_train_rows=2700,
    policy=TradePolicy.live(min_edge=0.05),  # what `pmlab record-trades` trades: best bin and side
)
gate = HoldoutGateResult.evaluate(result.trades)
print(len(result.trades), "trades | PnL", round(result.trades["realized_pnl"].sum(), 2), "|", gate.decision)
panel.to_parquet("panel.parquet")            # the command line below reads this file

# A gate that fails cannot publish a champion.
failed = HoldoutGateResult.evaluate(result.trades, required_segments=["Atlantis"])
try:
    ChampionManifest.publish(model, failed, "champion", plugin_family="weather_tmax")
except ValueError as err:
    print("refused:", err)

It prints the number of trades, the total PnL and the gate's decision, then the refusal. The same panel runs from the command line:

pmlab backtest --plugin weather_tmax --panel panel.parquet \
    --stride 10 --min-train-rows 2700 --min-edge 0.05 --output metrics.json
pmlab reproduce metrics.json     # re-runs the backtest and verifies the same metrics come back

The tutorial continues from here: tuning, a per-city gate, a published champion, paper trading, settlement and an HTML report, in one notebook that CI executes.

The command line

Command What it does
pmlab backtest Walk-forward backtest of a panel, recorded so it can be reproduced
pmlab reproduce Re-run a recorded backtest and verify the metrics
pmlab promote-champion Publish a model as champion, only if its gate is GO
pmlab scan-markets List a family's open markets from the Gamma API
pmlab record-trades Paper-trade the scan's signals that clear --min-edge
pmlab status · report · resettle Inspect paper trades, render an HTML report, repair old settlements
pmlab monitor The watchdog: drift, live calibration and live PnL; silent when healthy
pmlab settle-trades A placeholder that exits 1: it settles nothing and prints the SettlementEngine call that does
pmlab version Print the version

pmlab --help and pmlab <command> --help list every option. Command line reference

Bring your own market family

A plugin is one class with four methods. Everything else is shared.

from pmlab import MarketPlugin

class MyPlugin(MarketPlugin):
    family = "my_markets"
    def discover_markets(self, **kwargs): ...
    def fetch_features(self, spec, horizon, **kwargs): ...
    def fetch_truth(self, spec, **kwargs): ...
    def build_training_row(self, spec, horizon, **kwargs): ...

Plugin authoring guide

Documentation

Everything else lives on the documentation site.

Start here Tutorial · Architecture · Command line · Plugin authoring
Build models Data layer · Features · Modeling · Tuning · Costs · Position sizing
Run it Live trading · Drift monitoring · Watchdog · Champion registry
Reference API reference · Public API and stability · Deprecation policy
Project Changelog · Roadmap · Contributing · Security

Development

git clone https://github.com/ArtBreguez/polymarket-lab && cd polymarket-lab
uv sync --extra dev
uv run pytest                  # the suite; the coverage gate is in pyproject.toml

CONTRIBUTING.md has the rest: the test-first rule, the end-to-end, performance and notebook suites, and how the public API is changed.

License

MIT. See LICENSE.

Metadata

Release files for pmlab 1.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pmlab 1.1.1
File Size Uploaded Provenance Actions
pmlab-1.1.1.tar.gz 1.1 MB With provenance Details for pmlab-1.1.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for pmlab 1.1.1
File Interpreter ABI Platform Actions
pmlab-1.1.1-py3-none-any.whl Python 3 none any Details for pmlab-1.1.1-py3-none-any.whl

Total release size: 1.4 MB

Release files / pmlab-1.1.1.tar.gz

Download URL pmlab-1.1.1.tar.gz
Size 1.1 MB
Tags Source
SHA-256 checksum
How to use checksums
25fabb93f430d5571ed3ae7a2bd984b3be0365e2188dc151bc71da97d7d06430
BLAKE2b-256 checksum
How to use checksums
8b077071e4b48a8616f9c08575155d665627c31a23c89488aaf3c7699f98c5ed
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.

Transparency log

Release files / pmlab-1.1.1-py3-none-any.whl

Download URL pmlab-1.1.1-py3-none-any.whl
Size 262.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cc5ac258607b76eedcf48c7d06e2425ea231738dad3eb573aff8be8a5cf43a66
BLAKE2b-256 checksum
How to use checksums
e5582f72de9f735e801c84498d4f3b3c3d60c4e0f5022f088837556306821f17
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.

Transparency log

Security for pmlab 1.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for pmlab-1.1.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.1.1 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page