Skip to main content

uselayer

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

This release 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.

Sandbox mode (Layer's hosted sandbox)

With a Layer API key that has early access, mode="sandbox" sends practice orders to Layer's hosted sandbox instead of filling them on your machine. Layer fills them against Polymarket US's live book, with the same fill model and fees, and keeps the account: $10,000 of simulated cash to start, the same account as the web page at uselayer.sh/sandbox.

client = Client(mode="sandbox", layer_key="lyr_...")
client.buy(venue="polymarket_us", market="<slug>", side="yes", price=0.42, size=10)
client.positions(), client.balances(), client.sandbox_account()["pnl"]
client.sandbox_reset()                                 # back to $10,000

Your guardrails still run on your machine before each order is sent. Orders fill at once (ioc or fok); orders that wait in the book aren't in the sandbox. This is the only mode that sends orders to Layer, and it sends only the order's own fields.

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.2.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.2.0
File Size Uploaded
uselayer-0.2.0.tar.gz 103.0 kB Details

Built distribution (wheel)

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

Total release size: 190.5 kB

Release files / uselayer-0.2.0.tar.gz

Download URL uselayer-0.2.0.tar.gz
Size 103.0 kB
Tags Source
SHA-256 checksum
How to use checksums
0d13d3b37386b16890667e76c11a9cab3816ff578b32dc88b6046c6d839b4840
BLAKE2b-256 checksum
How to use checksums
e894e97d0a96537149d2522c3a4ed542542d063d89fc6f1f22a7c2448a789a76
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.2.0-py3-none-any.whl

Download URL uselayer-0.2.0-py3-none-any.whl
Size 87.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
18278cdc79ba05880e01614e14149ed4ecada421046a438466af63f318a1e54f
BLAKE2b-256 checksum
How to use checksums
819fb697c301282b052916c8cd1cff8b9e0af0c4f2f775c72594feeaa9c55ef1
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

This release

0.2.0 This release

2 release files

0.1.0

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