Skip to main content

uselayer

A Python SDK for trading prediction markets with your own venue keys.

This release (0.1) trades Polymarket US. Paper mode (the default) fills orders against the venue's real order books with simulated money and sends nothing to the venue. Live mode sends orders with your own Polymarket US API key. Backtest mode replays books you saved.

  • One order shape for every venue and mode, published as a JSON Schema (schema/order.json).
  • Paper mode is the default. preview() shows what an order would do and sends nothing.
  • Guardrails check every order before it's sent: position size, budget, daily loss, allowed markets, approvals, stop-loss and take-profit. A price collar, an order throttle and a kill switch are always on.
  • Fees come from each venue's published schedule in force at the time of the trade. They match Layer's API (POST /v0/profit, POST /v0/size) to the millionth of a dollar.
  • Everything stays on your machine: a local SQLite file per mode, no telemetry.

Install

pip install uselayer

Python 3.11 or newer.

Paper trade in five lines

from uselayer import Client

client = Client()  # paper mode: real books, simulated fills
m = client.markets(limit=20)[0]  # open Polymarket US markets, no key needed
book = client.book(m.slug)
order = client.order(
    venue="polymarket_us", market=m.slug, side="yes", price=book.outcome("yes").best_ask.price, size=5
)
print(client.preview(order))  # fill, fees, every rule's decision
print(client.send(order))  # the order, filled against the book

client.positions(), client.fills() and client.orders() read the local store. Every fill in paper mode is a SimulatedFill with simulated=True.

Live mode

from uselayer import Client, PolymarketUS

client = Client(mode="live", polymarket_us=PolymarketUS(key_id="...", secret_key_path="~/.pmus/secret"))
client.balances()["polymarket_us"].cash
order = client.buy(venue="polymarket_us", market="<slug>", side="yes", price=0.42, size=5)
client.positions()                                     # from the venue

Create the key at polymarket.us/developer. It stays on your machine: requests are signed with it locally and only the signature is sent. Books in live mode come from the venue's WebSocket, so they aren't cached. An order whose answer never arrives raises outcome_unknown and is never sent again on its own: call client.sync() and check client.orders().

If the local store is new but your account already has open orders or positions, live mode starts with the kill switch on, until you run python -m uselayer resume --mode live.

Guardrails

client = Client(
    rules={
        "max_position": {"per_market": 200},  # $ at risk in one market
        "budget": 1000,  # $ at risk in total
        "max_daily_loss": {"amount": 150},  # stop opening positions after this loss today
        "approve_above": 100,  # ask before orders above $100
        "stop_loss": {"pct": 25},  # exits sent by client.monitor()
    }
)

Rules can also come from a YAML or JSON file: Client(rules="guardrails.yaml") (YAML needs pip install "uselayer[yaml]"). They're fixed when the client is created.

Kill switch. client.kill() cancels resting orders and blocks new ones. From another terminal: python -m uselayer kill. It stays on, even after a restart, until a person runs python -m uselayer resume. The client a strategy or agent holds can't resume.

Pairs: both sides, with the leg-risk guard

When two markets are the same bet, buying YES on one and NO on the other pays $1 per contract either way. quote() prices that after both fees; trade() places both legs:

q = client.quote(pair)                        # pair: a Match from client.matches(), or two (venue, market)
t = client.trade(pair, size=100, min_edge=0.01)
t.status                                      # "hedged" | "missed" | "unwound" | "exposed"

The thinner leg goes first, immediate-or-cancel. The other leg goes for what filled, up to its break-even price. If it can't be completed within chase_s, the first leg is sold back, never below its entry price minus max_unwind_loss (on_miss="unwind", the default), or the open contracts are reported (on_miss="hold"). Both legs pass the guardrails together before either is sent.

In this release trade() runs in paper and backtest mode.

One strategy, every mode

def strategy(client, pair, quote):
    if quote.net_profit_per_contract >= 0.02:
        client.trade(pair, size=100)

Client(mode="backtest", books=saved_books).run(strategy, [pair])   # the past
Client().run(strategy, [pair], iterations=60)                      # now, paper

Backtest on books you saved

from uselayer import Client
from uselayer.backtest import load_books, record_books

record_books(Client(), ["<slug>"], "books.jsonl")  # run on a schedule to build a history

bt = Client(mode="backtest", books=load_books("books.jsonl"))
bt.replay(lambda client, book: ...)  # place orders as each book arrives

The replay uses the same fill model, fees and rules as paper mode, on the replayed clock.

Fees by date

from datetime import UTC, datetime
from uselayer import FeeSettings, calculate_fee, rules_at

rules_at("polymarket_us", datetime.now(UTC)).source  # the schedule's page
calculate_fee(
    FeeSettings(venue="polymarket_us"), contracts=100, price=0.5, role="taker", at=datetime.now(UTC)
)

Before the earliest schedule the SDK knows, it raises no_venue_rules instead of guessing.

What paper mode can't tell you

  • Queue position. A paper order that rests fills as soon as a later book reaches its price. A real one waits in line, so paper fills look at least as good as live ones.
  • Freshness without a key. In paper mode, Polymarket US's public book is cached for up to 30 seconds. The SDK stamps each book with the venue's time and, when a copy is older than max_quote_age_s (10 s by default), waits for a fresh one before using it.

For AI agents

AGENTS.md and llms.txt ship inside the package. Every public method has a docstring with an example, every object has .to_dict(), and every error is a VenueError with code, hint and next. The examples/ folder runs in CI.

Layer

Layer (uselayer.sh) finds markets that are the same bet on different venues. With a Layer API key, client.matches(q="...") returns them. The SDK sends Layer your key, the market ids Layer gave you and your filters, and nothing else: no prices, orders, positions or venue keys.

License

MIT

Metadata

Release files for uselayer 0.1.0

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

Source distribution (sdist)

Source distribution for uselayer 0.1.0
File Size Uploaded
uselayer-0.1.0.tar.gz 97.3 kB Details

Built distribution (wheel)

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

Total release size: 180.9 kB

Release files / uselayer-0.1.0.tar.gz

Download URL uselayer-0.1.0.tar.gz
Size 97.3 kB
Tags Source
SHA-256 checksum
How to use checksums
688828be633d12f58875ac2dcab872d5a74472213b9ed204c71a8e4686d465de
BLAKE2b-256 checksum
How to use checksums
873e98a5640e9bedda7d893749f30d74c48d329ba69d244c0ada852ff568b5ee
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / uselayer-0.1.0-py3-none-any.whl

Download URL uselayer-0.1.0-py3-none-any.whl
Size 83.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
95b19ad6bf9b6a8b45dab9e9b58a0ab0f2db9dd8773c8253aa16f73be010318c
BLAKE2b-256 checksum
How to use checksums
5bf3f98317839c9bce265d4757bee3a8afba48d66c42fd7f64181bbc99f3ab35
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

0.3.0

2 release files

0.2.0

2 release files

This release

0.1.0 This release

2 release files

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