Metadata-Version: 2.5
Name: uselayer
Version: 0.1.0
Summary: Trade prediction markets with your own venue keys: one order shape, paper mode by default, guardrails, and fee math that matches Layer's API.
Project-URL: Homepage, https://uselayer.sh
Project-URL: Source, https://github.com/Dave-56/uselayer-sdk
Author: Layer
License-Expression: MIT
License-File: LICENSE
Keywords: polymarket,prediction markets,sdk,trading
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: cryptography>=42
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.7
Requires-Dist: websockets>=13
Provides-Extra: yaml
Requires-Dist: pyyaml>=6; extra == 'yaml'
Description-Content-Type: text/markdown

# 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

```bash
pip install uselayer
```

Python 3.11 or newer.

## Paper trade in five lines

```python
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

```python
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

```python
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:

```python
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

```python
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

```python
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

```python
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
