polysim-sdk 0.2.0__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- polysim_sdk-0.2.1/PKG-INFO +41 -0
- polysim_sdk-0.2.1/README.md +21 -0
- polysim_sdk-0.2.1/pyproject.toml +32 -0
- polysim_sdk-0.2.0/.gitignore +0 -18
- polysim_sdk-0.2.0/PKG-INFO +0 -503
- polysim_sdk-0.2.0/README.md +0 -471
- polysim_sdk-0.2.0/polysim_clob_client/__init__.py +0 -51
- polysim_sdk-0.2.0/polysim_clob_client/client.py +0 -939
- polysim_sdk-0.2.0/polysim_clob_client/clob_types.py +0 -174
- polysim_sdk-0.2.0/polysim_clob_client/constants.py +0 -52
- polysim_sdk-0.2.0/polysim_clob_client/exceptions.py +0 -31
- polysim_sdk-0.2.0/polysim_clob_client/order_builder/__init__.py +0 -5
- polysim_sdk-0.2.0/polysim_clob_client/order_builder/constants.py +0 -12
- polysim_sdk-0.2.0/polysim_sdk/__init__.py +0 -52
- polysim_sdk-0.2.0/polysim_sdk/_http.py +0 -413
- polysim_sdk-0.2.0/polysim_sdk/aio.py +0 -705
- polysim_sdk-0.2.0/polysim_sdk/client.py +0 -819
- polysim_sdk-0.2.0/polysim_sdk/constants.py +0 -44
- polysim_sdk-0.2.0/polysim_sdk/exceptions.py +0 -90
- polysim_sdk-0.2.0/polysim_sdk/pagination.py +0 -126
- polysim_sdk-0.2.0/polysim_sdk/sse.py +0 -282
- polysim_sdk-0.2.0/polysim_sdk/updown.py +0 -167
- polysim_sdk-0.2.0/polysim_sdk/ws.py +0 -211
- polysim_sdk-0.2.0/pyproject.toml +0 -70
- {polysim_sdk-0.2.0 → polysim_sdk-0.2.1}/LICENSE +0 -0
- {polysim_sdk-0.2.0 → polysim_sdk-0.2.1}/NOTICE +0 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: polysim-sdk
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Deprecated alias — the PolySimulator Python SDK is now published as 'polysimulator'. Installs it for you.
|
|
5
|
+
Project-URL: Homepage, https://polysimulator.com
|
|
6
|
+
Project-URL: Documentation, https://docs.polysimulator.com
|
|
7
|
+
Project-URL: Repository, https://github.com/Bavariance/polysimulator
|
|
8
|
+
Author: Bavariance
|
|
9
|
+
License-Expression: Apache-2.0
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
License-File: NOTICE
|
|
12
|
+
Keywords: api-sdk,paper-trading,polymarket,polysimulator,trading
|
|
13
|
+
Classifier: Development Status :: 7 - Inactive
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Requires-Dist: polysimulator>=0.2.0
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# polysim-sdk → renamed to `polysimulator`
|
|
22
|
+
|
|
23
|
+
**This package has been renamed.** The PolySimulator Python SDK is now published as
|
|
24
|
+
**[`polysimulator`](https://pypi.org/project/polysimulator/)**.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install polysimulator
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`polysim-sdk` is now a thin alias that pulls in `polysimulator` automatically, so
|
|
31
|
+
existing installs keep working. **The import paths are unchanged:**
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from polysim_sdk import PolySimClient # native client (sync + async)
|
|
35
|
+
from polysim_clob_client.client import ClobClient # py-clob-client drop-in
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Please switch your dependency from `polysim-sdk` to `polysimulator`.
|
|
39
|
+
|
|
40
|
+
- Docs: https://docs.polysimulator.com
|
|
41
|
+
- Source: https://github.com/Bavariance/polysimulator
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# polysim-sdk → renamed to `polysimulator`
|
|
2
|
+
|
|
3
|
+
**This package has been renamed.** The PolySimulator Python SDK is now published as
|
|
4
|
+
**[`polysimulator`](https://pypi.org/project/polysimulator/)**.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
pip install polysimulator
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
`polysim-sdk` is now a thin alias that pulls in `polysimulator` automatically, so
|
|
11
|
+
existing installs keep working. **The import paths are unchanged:**
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from polysim_sdk import PolySimClient # native client (sync + async)
|
|
15
|
+
from polysim_clob_client.client import ClobClient # py-clob-client drop-in
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Please switch your dependency from `polysim-sdk` to `polysimulator`.
|
|
19
|
+
|
|
20
|
+
- Docs: https://docs.polysimulator.com
|
|
21
|
+
- Source: https://github.com/Bavariance/polysimulator
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "polysim-sdk"
|
|
7
|
+
version = "0.2.1"
|
|
8
|
+
description = "Deprecated alias — the PolySimulator Python SDK is now published as 'polysimulator'. Installs it for you."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "Apache-2.0"
|
|
12
|
+
license-files = ["LICENSE", "NOTICE"]
|
|
13
|
+
authors = [{ name = "Bavariance" }]
|
|
14
|
+
keywords = ["polymarket", "polysimulator", "trading", "paper-trading", "api-sdk"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 7 - Inactive",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Topic :: Office/Business :: Financial",
|
|
20
|
+
]
|
|
21
|
+
dependencies = ["polysimulator>=0.2.0"]
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Homepage = "https://polysimulator.com"
|
|
25
|
+
Documentation = "https://docs.polysimulator.com"
|
|
26
|
+
Repository = "https://github.com/Bavariance/polysimulator"
|
|
27
|
+
|
|
28
|
+
[tool.hatch.build.targets.wheel]
|
|
29
|
+
bypass-selection = true
|
|
30
|
+
|
|
31
|
+
[tool.hatch.build.targets.sdist]
|
|
32
|
+
include = ["README.md", "LICENSE", "NOTICE", "pyproject.toml"]
|
polysim_sdk-0.2.0/.gitignore
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# Python build / packaging artifacts
|
|
2
|
-
dist/
|
|
3
|
-
build/
|
|
4
|
-
*.egg-info/
|
|
5
|
-
__pycache__/
|
|
6
|
-
*.py[cod]
|
|
7
|
-
|
|
8
|
-
# Virtualenv
|
|
9
|
-
.venv/
|
|
10
|
-
venv/
|
|
11
|
-
|
|
12
|
-
# Tooling caches
|
|
13
|
-
.mypy_cache/
|
|
14
|
-
.pytest_cache/
|
|
15
|
-
.ruff_cache/
|
|
16
|
-
|
|
17
|
-
# Local throwaway smoke scripts (never committed — may contain wiring to live keys)
|
|
18
|
-
_smoke_*.py
|
polysim_sdk-0.2.0/PKG-INFO
DELETED
|
@@ -1,503 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: polysim-sdk
|
|
3
|
-
Version: 0.2.0
|
|
4
|
-
Summary: Python SDK for the PolySimulator paper-trading API, with py-clob-client drop-in parity
|
|
5
|
-
Project-URL: Homepage, https://polysimulator.com
|
|
6
|
-
Project-URL: Documentation, https://api.polysimulator.com/docs
|
|
7
|
-
Project-URL: Repository, https://github.com/Bavariance/polysimulator
|
|
8
|
-
Author: Bavariance
|
|
9
|
-
License-Expression: Apache-2.0
|
|
10
|
-
License-File: LICENSE
|
|
11
|
-
License-File: NOTICE
|
|
12
|
-
Keywords: api-sdk,paper-trading,polymarket,polysimulator,trading
|
|
13
|
-
Classifier: Development Status :: 4 - Beta
|
|
14
|
-
Classifier: Intended Audience :: Developers
|
|
15
|
-
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
-
Classifier: Topic :: Office/Business :: Financial
|
|
20
|
-
Requires-Python: >=3.10
|
|
21
|
-
Requires-Dist: httpx<1,>=0.27
|
|
22
|
-
Requires-Dist: websockets<17,>=12
|
|
23
|
-
Provides-Extra: dev
|
|
24
|
-
Requires-Dist: build>=1.0; extra == 'dev'
|
|
25
|
-
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
26
|
-
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
27
|
-
Requires-Dist: pytest>=8; extra == 'dev'
|
|
28
|
-
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
29
|
-
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
30
|
-
Requires-Dist: twine>=5.0; extra == 'dev'
|
|
31
|
-
Description-Content-Type: text/markdown
|
|
32
|
-
|
|
33
|
-
# PolySimulator Python SDK
|
|
34
|
-
|
|
35
|
-
The official Python client for the [PolySimulator](https://polysimulator.com)
|
|
36
|
-
paper-trading API. **One package, two import surfaces:**
|
|
37
|
-
|
|
38
|
-
| Import surface | Use it when |
|
|
39
|
-
|---|---|
|
|
40
|
-
| `polysim_sdk` | You're starting fresh. A clean, modern client — sync **and** async, WebSocket streaming, pagination iterators, typed exceptions. |
|
|
41
|
-
| `polysim_clob_client` | You already have a bot written against Polymarket's [`py-clob-client`](https://github.com/Polymarket/py-clob-client). This is a **drop-in mirror** — port by changing the import path, the host, and the auth call. |
|
|
42
|
-
|
|
43
|
-
PolySimulator is paper trading, so there is **no on-chain anything**: no private
|
|
44
|
-
key, no `chain_id`, no `funder`, no `signature_type`, no EIP-712 signing, no USDC
|
|
45
|
-
allowance/approval transactions, no web3/Polygon RPC. The SDK depends only on
|
|
46
|
-
`httpx` and `websockets`. Authentication is a single `ps_live_*` API key sent as
|
|
47
|
-
the `X-API-Key` header.
|
|
48
|
-
|
|
49
|
-
---
|
|
50
|
-
|
|
51
|
-
## Install
|
|
52
|
-
|
|
53
|
-
Targets Python 3.10+.
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
pip install polysim-sdk
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
From a checkout of this directory (for development):
|
|
60
|
-
|
|
61
|
-
```bash
|
|
62
|
-
python -m venv .venv && source .venv/bin/activate
|
|
63
|
-
pip install -e ".[dev]" # editable install + pytest/ruff/mypy/build/twine
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
Provide your key via the `POLYSIM_API_KEY` environment variable (recommended) or
|
|
67
|
-
pass it to the constructor. To target staging instead of production:
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
export POLYSIM_API_KEY="ps_live_…"
|
|
71
|
-
export POLYSIM_BASE_URL="https://staging-api.polysimulator.com"
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
---
|
|
75
|
-
|
|
76
|
-
## Quickstart — native `polysim_sdk`
|
|
77
|
-
|
|
78
|
-
```python
|
|
79
|
-
from polysim_sdk import PolySimClient
|
|
80
|
-
|
|
81
|
-
with PolySimClient() as client: # POLYSIM_API_KEY from env
|
|
82
|
-
me = client.me()
|
|
83
|
-
print(me["balance"]) # your trading balance (a JSON string)
|
|
84
|
-
|
|
85
|
-
market = client.list_markets(limit=1, hot_only=True)[0]
|
|
86
|
-
cid = market["condition_id"]
|
|
87
|
-
|
|
88
|
-
book = client.get_book(cid) # by condition id (+ outcome=/depth=)
|
|
89
|
-
asks = book.get("asks") or [] # levels: [{"price","size"}, ...]
|
|
90
|
-
best_ask = min((float(a["price"]) for a in asks), default=None)
|
|
91
|
-
print("best ask:", best_ask) # lowest ask = best price to buy at
|
|
92
|
-
|
|
93
|
-
# …or by outcome-token id, the Polymarket-native way:
|
|
94
|
-
# token_book = client.get_book_by_token("<outcome-token-id>")
|
|
95
|
-
|
|
96
|
-
fill = client.place_order(
|
|
97
|
-
market_id=cid,
|
|
98
|
-
side="BUY",
|
|
99
|
-
outcome="YES",
|
|
100
|
-
quantity=10,
|
|
101
|
-
order_type="market",
|
|
102
|
-
price="0.99", # worst-acceptable fill (YES < $1 → "any fill")
|
|
103
|
-
)
|
|
104
|
-
print(f"filled {fill['status']} @ {fill.get('price')}")
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
Async is the same API with `await`:
|
|
108
|
-
|
|
109
|
-
```python
|
|
110
|
-
import asyncio
|
|
111
|
-
from polysim_sdk import AsyncPolySimClient
|
|
112
|
-
|
|
113
|
-
async def main():
|
|
114
|
-
async with AsyncPolySimClient() as client:
|
|
115
|
-
me = await client.me()
|
|
116
|
-
print(me["balance"])
|
|
117
|
-
|
|
118
|
-
asyncio.run(main())
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
## Finding BTC Up/Down markets
|
|
124
|
-
|
|
125
|
-
Short-horizon **crypto Up/Down** markets — "is BTC/ETH/SOL/… up or down over
|
|
126
|
-
this window" resolving on a 5-minute … daily horizon — are the platform's
|
|
127
|
-
most-traded product. They have a dedicated endpoint and do **not** show up
|
|
128
|
-
through `list_markets(q="btc")` (free-text search is the `q` filter). Use
|
|
129
|
-
`list_updown` / `get_updown`:
|
|
130
|
-
|
|
131
|
-
```python
|
|
132
|
-
with PolySimClient() as client:
|
|
133
|
-
# Currently-tradeable BTC 5-minute windows, filtered server-side:
|
|
134
|
-
live = client.list_updown(asset="BTC", interval="5M", live=True)
|
|
135
|
-
window = live[0]
|
|
136
|
-
print(window["slug"]) # btc-updown-5m-1781400900
|
|
137
|
-
print(window["time_range"]) # "9:35PM-9:40PM ET"
|
|
138
|
-
print(window["group_item_threshold"]) # resolution strike (None until the window opens)
|
|
139
|
-
|
|
140
|
-
# Outcome tokens live in the nested markets[] array (JSON-string fields):
|
|
141
|
-
nested = window["markets"][0]
|
|
142
|
-
print(nested["outcomes"]) # '["Up", "Down"]'
|
|
143
|
-
print(nested["outcome_prices"]) # '["0.595", "0.405"]'
|
|
144
|
-
cid = window["condition_id"]
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
`interval` is one of `5M` / `15M` / `1H` / `4H` / `daily` (**uppercase**).
|
|
148
|
-
`live=True` keeps only `active and not closed and not resolved` windows.
|
|
149
|
-
|
|
150
|
-
The **live underlying spot price** plus the available assets/intervals come from
|
|
151
|
-
the full payload via `get_updown`:
|
|
152
|
-
|
|
153
|
-
```python
|
|
154
|
-
payload = client.get_updown(asset="BTC")
|
|
155
|
-
btc = (payload.get("crypto_prices") or {}).get("BTC") or {}
|
|
156
|
-
print(btc.get("price")) # 64588.0 — live BTC/USD (coingecko)
|
|
157
|
-
print(payload["available_intervals"]) # ['5M', '15M', '1H', '4H', 'daily']
|
|
158
|
-
print(payload["interval_counts"]) # {'5M': 169, '15M': 402, ...}
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
> The spot price can be momentarily absent right at a window boundary (the
|
|
162
|
-
> payload is cached and the feed can lag a second or two), so read it
|
|
163
|
-
> defensively with `.get(...)` rather than chained `[...]` indexing.
|
|
164
|
-
|
|
165
|
-
### Pricing an Up/Down market
|
|
166
|
-
|
|
167
|
-
The raw order book (`get_book`) on an Up/Down market is a **synthetic
|
|
168
|
-
placeholder** — its `asks[0]` sits near ~0.99 and is not a real resting quote.
|
|
169
|
-
For price, use the market's own fields instead:
|
|
170
|
-
|
|
171
|
-
- `window["live_price"]` → `{"buy": 0.595, "sell": 0.405, ...}` (current marketable prices)
|
|
172
|
-
- `nested["outcome_prices"]` → the latest Up/Down implied probabilities
|
|
173
|
-
- the realised `price` returned by `place_order` — the SDK fills against the real
|
|
174
|
-
internal book and reports the true fill price, `fee`, and `book_walk_levels`
|
|
175
|
-
|
|
176
|
-
A market order with a worst-price cap still fills correctly regardless; just
|
|
177
|
-
don't read `get_book(...)["asks"][0]` as a tradeable quote on these markets.
|
|
178
|
-
|
|
179
|
-
> **Drop-in `polysim_clob_client` users:** `get_midpoint`, `get_price`,
|
|
180
|
-
> `get_spread` and `calculate_market_price` read the outcome token's **synthetic
|
|
181
|
-
> ~0.99 ladder** on Up/Down markets — they are *not* the underlying asset price.
|
|
182
|
-
> For the asset, use the native reads below.
|
|
183
|
-
|
|
184
|
-
### Up/Down for HFT: strike, spot, and a push feed
|
|
185
|
-
|
|
186
|
-
The two numbers an Up/Down strategy actually trades on — the **strike**
|
|
187
|
-
("price to beat") and the **live underlying spot** — have dedicated reads on the
|
|
188
|
-
native client, plus a push stream and a few pure helpers:
|
|
189
|
-
|
|
190
|
-
```python
|
|
191
|
-
from polysim_sdk import PolySimClient
|
|
192
|
-
from polysim_sdk import updown
|
|
193
|
-
|
|
194
|
-
with PolySimClient() as client:
|
|
195
|
-
live = client.list_updown(asset="BTC", interval="5M", live=True)
|
|
196
|
-
window = updown.next_to_expire(live) or live[0] # the contract closing soonest
|
|
197
|
-
cid = window["condition_id"]
|
|
198
|
-
|
|
199
|
-
strike = client.get_price_to_beat(cid)["price"] # GET /prices/ptb/{cid}; 404 = not set yet
|
|
200
|
-
spot = client.get_spot("BTC")["price"] # GET /prices/live/BTC — live underlying
|
|
201
|
-
# all spots in one call: client.get_spots()["prices"]
|
|
202
|
-
|
|
203
|
-
print(updown.ptb_distance(spot, strike)) # signed price units; >0 ⇒ "Up" in the money
|
|
204
|
-
print(updown.ptb_distance_bps(spot, strike)) # same, in bps of the strike
|
|
205
|
-
print(updown.seconds_to_expiry(window)) # time left on the window
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
`get_price_to_beat` raises `ApiError` with `status_code == 404` while a window's
|
|
209
|
-
strike is still pending — that is "not set yet", not "market absent". It accepts
|
|
210
|
-
a `condition_id` **or** an outcome-token id. Its `source` field tells you the
|
|
211
|
-
provenance: `polymarket_open_price` / `polymarket_scrape` / `gamma_event_metadata`
|
|
212
|
-
(Polymarket's own reported strike) and `chainlink_onchain` / `chainlink_timeline`
|
|
213
|
-
(the resolution oracle) are the settlement strike; `gamma_api` / `cryptocompare`
|
|
214
|
-
are best-effort fallbacks — treat them as indicative.
|
|
215
|
-
|
|
216
|
-
The `polysim_sdk.updown` helpers are pure functions over the row dicts (no
|
|
217
|
-
network, tolerant of missing fields): `seconds_to_expiry`, `is_window_open`,
|
|
218
|
-
`price_to_beat` (reads the strike off a row you already have), `ptb_distance` /
|
|
219
|
-
`ptb_distance_bps`, `open_windows`, `next_to_expire`, plus the `ASSETS` /
|
|
220
|
-
`INTERVALS` vocab tuples. For a **streaming** tap on the underlying, see
|
|
221
|
-
[SSE: live underlying spot](#sse-live-underlying-spot) below.
|
|
222
|
-
|
|
223
|
-
---
|
|
224
|
-
|
|
225
|
-
## Migrating from `py-clob-client`
|
|
226
|
-
|
|
227
|
-
If you have a Polymarket bot, you change **three things** and delete the on-chain
|
|
228
|
-
prelude. Everything else — method names, argument shapes, return shapes — stays.
|
|
229
|
-
|
|
230
|
-
```diff
|
|
231
|
-
- from py_clob_client.client import ClobClient
|
|
232
|
-
- from py_clob_client.clob_types import OrderArgs, OrderType
|
|
233
|
-
+ from polysim_clob_client.client import ClobClient
|
|
234
|
-
+ from polysim_clob_client.clob_types import OrderArgs, OrderType
|
|
235
|
-
|
|
236
|
-
- client = ClobClient(
|
|
237
|
-
- host="https://clob.polymarket.com",
|
|
238
|
-
- key=PRIVATE_KEY, # your wallet's private key
|
|
239
|
-
- chain_id=POLYGON,
|
|
240
|
-
- signature_type=1,
|
|
241
|
-
- funder=PROXY_WALLET_ADDRESS,
|
|
242
|
-
- )
|
|
243
|
-
- client.set_api_creds(client.create_or_derive_api_creds())
|
|
244
|
-
- # ...plus USDC allowance/approval txns, web3 setup, etc.
|
|
245
|
-
+ client = ClobClient(
|
|
246
|
-
+ host="https://api.polysimulator.com",
|
|
247
|
-
+ key="ps_live_…", # your PolySimulator API key
|
|
248
|
-
+ )
|
|
249
|
-
|
|
250
|
-
# unchanged from here on:
|
|
251
|
-
order = client.create_order(OrderArgs(token_id=tid, price=0.55, size=10, side="BUY"))
|
|
252
|
-
resp = client.post_order(order)
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
### What maps how
|
|
256
|
-
|
|
257
|
-
Every method maps by one of three strategies:
|
|
258
|
-
|
|
259
|
-
- **mirror** — identical behaviour, delegated to the HTTP core.
|
|
260
|
-
- **adapt** — translated onto the PolySim REST surface (e.g. `get_midpoint`,
|
|
261
|
-
`get_price`, `get_spread` are computed from `GET /v1/markets/{id}/book`).
|
|
262
|
-
- **stub-noop** — on-chain machinery with no analog (allowances, signing,
|
|
263
|
-
scoring, builder auth). These return a benign canned value and **make no
|
|
264
|
-
network call**; each says so in its docstring so nothing breaks silently.
|
|
265
|
-
|
|
266
|
-
### `token_id` ↔ `(market_id, outcome)`
|
|
267
|
-
|
|
268
|
-
`py-clob-client` addresses a single outcome token by `token_id`; PolySimulator
|
|
269
|
-
addresses a **market plus an outcome**. The seam: a bare `token_id` is treated as
|
|
270
|
-
the market id with outcome `YES`. Append `":NO"` or `":YES"` to target the other
|
|
271
|
-
side explicitly.
|
|
272
|
-
|
|
273
|
-
```python
|
|
274
|
-
client.create_order(OrderArgs(token_id="0xMARKET", ...)) # → market 0xMARKET, YES
|
|
275
|
-
client.create_order(OrderArgs(token_id="0xMARKET:NO", ...)) # → market 0xMARKET, NO
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
**Reads are true-token-parity; writes use the market+outcome model.** This is an
|
|
279
|
-
intentional asymmetry:
|
|
280
|
-
|
|
281
|
-
- **Book/quote reads** — `get_order_book`, `get_midpoint`, `get_price`,
|
|
282
|
-
`get_spread`, `get_tick_size`, `get_neg_risk`, `get_last_trade_price`,
|
|
283
|
-
`calculate_market_price` — send a **bare** `token_id` to the token-native
|
|
284
|
-
endpoint `GET /v1/book?token_id=...`. This matches Polymarket's CLOB book
|
|
285
|
-
reads exactly: pass the real outcome-token id you already use with
|
|
286
|
-
`py-clob-client` and the quote comes back for *that* token, no `:YES`/`:NO`
|
|
287
|
-
needed. The `condition_id:OUTCOME` colon form still works and routes to the
|
|
288
|
-
condition-id book endpoint (threading the outcome through), but it is a
|
|
289
|
-
PolySimulator convenience extension, not the parity path.
|
|
290
|
-
- **Order writes** — `create_order` / `create_market_order` / `post_order` —
|
|
291
|
-
resolve the `token_id` through the market+outcome seam above and submit to the
|
|
292
|
-
rich `POST /v1/orders` endpoint (with slippage/impact/position telemetry in the
|
|
293
|
-
response). A *bare* token id on the write path is read as a `market_id` with
|
|
294
|
-
the `YES` outcome; if that id isn't a valid market, the server rejects the
|
|
295
|
-
order loudly — it never silently places a different one.
|
|
296
|
-
|
|
297
|
-
If you only ever pass real outcome-token ids (the `py-clob-client` norm), reads
|
|
298
|
-
"just work" with full parity. The `:YES`/`:NO` suffix exists for callers who
|
|
299
|
-
prefer to address PolySimulator markets by condition id directly.
|
|
300
|
-
|
|
301
|
-
### Orders are never signed
|
|
302
|
-
|
|
303
|
-
`create_order` / `create_market_order` return a plain `dict` with **no
|
|
304
|
-
`signature` field** — there is nothing to sign. `post_order` serialises it
|
|
305
|
-
straight to `POST /v1/orders`. The recommended one-call path is
|
|
306
|
-
`create_and_post_order(...)`.
|
|
307
|
-
|
|
308
|
-
### Auth assertions
|
|
309
|
-
|
|
310
|
-
The three `py-clob-client` auth levels collapse into one. `assert_level_1_auth()`
|
|
311
|
-
and `assert_builder_auth()` are no-ops; `assert_level_2_auth()` raises
|
|
312
|
-
`PolyApiException` only if no API key is configured.
|
|
313
|
-
|
|
314
|
-
---
|
|
315
|
-
|
|
316
|
-
## Pagination
|
|
317
|
-
|
|
318
|
-
The native client exposes Python iterators that page transparently:
|
|
319
|
-
|
|
320
|
-
```python
|
|
321
|
-
from polysim_sdk import PolySimClient
|
|
322
|
-
from polysim_sdk.pagination import iter_markets, iter_orders
|
|
323
|
-
|
|
324
|
-
with PolySimClient() as client:
|
|
325
|
-
for market in iter_markets(client, hot_only=True):
|
|
326
|
-
...
|
|
327
|
-
for order in iter_orders(client, status="OPEN"):
|
|
328
|
-
...
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
The drop-in surface preserves `py-clob-client`'s base64 cursor protocol, so the
|
|
332
|
-
classic loop terminates correctly:
|
|
333
|
-
|
|
334
|
-
```python
|
|
335
|
-
from polysim_clob_client.constants import END_CURSOR
|
|
336
|
-
|
|
337
|
-
cursor = ""
|
|
338
|
-
while cursor != END_CURSOR:
|
|
339
|
-
page = client.get_markets(cursor)
|
|
340
|
-
handle(page["data"])
|
|
341
|
-
cursor = page["next_cursor"]
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
---
|
|
345
|
-
|
|
346
|
-
## WebSocket streaming
|
|
347
|
-
|
|
348
|
-
```python
|
|
349
|
-
from polysim_sdk import PolySimClient
|
|
350
|
-
from polysim_sdk.ws import prices_stream, executions_stream
|
|
351
|
-
|
|
352
|
-
with PolySimClient() as client:
|
|
353
|
-
for event in prices_stream(client, ["0xMARKET_A", "0xMARKET_B"]):
|
|
354
|
-
print(event) # blocks; reconnects with a fresh token + backoff
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
Async generators (`aprices_stream`, `aexecutions_stream`) are available for
|
|
358
|
-
`async for`. The SDK mints a short-lived WS JWT per connection automatically.
|
|
359
|
-
|
|
360
|
-
### SSE: live underlying spot
|
|
361
|
-
|
|
362
|
-
The WS channel above carries **market** prices. The live **underlying spot**
|
|
363
|
-
(BTC/ETH/SOL/… ticks — the number an Up/Down bet resolves against) comes from the
|
|
364
|
-
public, unauthenticated `/prices/stream` Server-Sent-Events firehose instead:
|
|
365
|
-
|
|
366
|
-
```python
|
|
367
|
-
from polysim_sdk import PolySimClient
|
|
368
|
-
from polysim_sdk.sse import spot_stream
|
|
369
|
-
|
|
370
|
-
with PolySimClient() as client:
|
|
371
|
-
for event in spot_stream(client, ["BTC", "ETH"], crypto_source="chainlink"):
|
|
372
|
-
if event["event"] == "crypto_price":
|
|
373
|
-
tick = event["data"]
|
|
374
|
-
print(tick["symbol"], tick["price"], tick["source"])
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
Each event is `{"event": <type>, "data": <payload>}`. The spot-bearing types are
|
|
378
|
-
`crypto_price` and `crypto_price_batch`; `keepalive` arrives every ~5 s. Pass
|
|
379
|
-
`crypto_source="chainlink"` (authoritative for 5m/15m settlement) or `"binance"`
|
|
380
|
-
(denser ~10 Hz ticks); omit for both. With `condition_ids=[...]` the same stream
|
|
381
|
-
also delivers `snapshot` / `market_price` / `orderbook` events. The async twin
|
|
382
|
-
`aspot_stream` is available for `async for`. Both reconnect with exponential
|
|
383
|
-
backoff and a per-connection cache-buster (so a CDN can't coalesce subscribers).
|
|
384
|
-
|
|
385
|
-
---
|
|
386
|
-
|
|
387
|
-
## Errors
|
|
388
|
-
|
|
389
|
-
Catch `PolySimError` to handle every SDK-raised error; `PolyApiException` (the
|
|
390
|
-
drop-in alias) is the same class.
|
|
391
|
-
|
|
392
|
-
```python
|
|
393
|
-
from polysim_sdk.exceptions import (
|
|
394
|
-
EdgeBlockedError,
|
|
395
|
-
PolySimError,
|
|
396
|
-
RateLimitError,
|
|
397
|
-
ValidationError,
|
|
398
|
-
)
|
|
399
|
-
|
|
400
|
-
try:
|
|
401
|
-
fill = client.place_order(...)
|
|
402
|
-
except RateLimitError as exc:
|
|
403
|
-
print(f"backing off for {exc.retry_after}s")
|
|
404
|
-
except ValidationError as exc:
|
|
405
|
-
print(f"bad request: {exc.code} → {exc}")
|
|
406
|
-
except EdgeBlockedError as exc:
|
|
407
|
-
print(f"blocked at the CDN edge — check your User-Agent: {exc}")
|
|
408
|
-
except PolySimError as exc:
|
|
409
|
-
print(f"other API error ({exc.status_code}): {exc}")
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
The client paces itself (a 50 ms floor between requests) and backs off on
|
|
413
|
-
`429`/`425`/`5xx` using `Retry-After`. Opt out with `floor_interval=0.0` and
|
|
414
|
-
`max_retries=0` to handle pacing yourself.
|
|
415
|
-
|
|
416
|
-
### Troubleshooting: `error code: 1010` / blocked User-Agent
|
|
417
|
-
|
|
418
|
-
If a raw HTTP call returns **HTTP 403** with the body `error code: 1010`, the
|
|
419
|
-
request was blocked at the CDN edge — **not** by the API. The edge rejects
|
|
420
|
-
Python's stdlib `urllib` default User-Agent (`Python-urllib/x.y`). `requests`,
|
|
421
|
-
`httpx`, `aiohttp`, and **this SDK** all send User-Agents that pass, so you only
|
|
422
|
-
hit this with raw `urllib` or a custom client that forwards the stdlib UA.
|
|
423
|
-
|
|
424
|
-
This SDK sends a branded `User-Agent: polysim-sdk/<version>` and turns any edge
|
|
425
|
-
block into a clear `EdgeBlockedError` (an `ApiError`, `code="EDGE_BLOCKED"`). If
|
|
426
|
-
you must call the API without the SDK, set your own header:
|
|
427
|
-
|
|
428
|
-
```python
|
|
429
|
-
# raw urllib — set a User-Agent so the edge lets you through
|
|
430
|
-
import urllib.request, json
|
|
431
|
-
req = urllib.request.Request(
|
|
432
|
-
"https://api.polysimulator.com/v1/markets?limit=1",
|
|
433
|
-
headers={"User-Agent": "my-app/1.0", "X-API-Key": "ps_live_..."},
|
|
434
|
-
)
|
|
435
|
-
data = json.load(urllib.request.urlopen(req))
|
|
436
|
-
```
|
|
437
|
-
|
|
438
|
-
---
|
|
439
|
-
|
|
440
|
-
## Rate limits
|
|
441
|
-
|
|
442
|
-
Authoritative values are returned live by `GET /v1/keys/tiers` (`client.tiers()`).
|
|
443
|
-
At the time of writing:
|
|
444
|
-
|
|
445
|
-
| Tier | rps | rpm | WS conns | Batch | API balance |
|
|
446
|
-
|---|---|---|---|---|---|
|
|
447
|
-
| Free | 2 | 120 | 1 | 1 | none — read only |
|
|
448
|
-
| Pro | 10 | 600 | 3 | 5 | $10K |
|
|
449
|
-
| Pro+ | 30 | 1800 | 10 | 10 | $25K |
|
|
450
|
-
| Enterprise | 100 | 6000 | 50 | 25 | custom |
|
|
451
|
-
|
|
452
|
-
`free` allows 2 requests/second up to 120/minute and is read-only (no API
|
|
453
|
-
balance). Use `pro` or above for any continuously-running bot. The table is
|
|
454
|
-
illustrative — always trust `client.tiers()` for the live values.
|
|
455
|
-
|
|
456
|
-
---
|
|
457
|
-
|
|
458
|
-
## Beta caveats
|
|
459
|
-
|
|
460
|
-
During the closed-cohort phase, a few sim behaviours differ from Polymarket.
|
|
461
|
-
These are known limitations, not bugs:
|
|
462
|
-
|
|
463
|
-
1. **Shared API balance across keys.** Every key a user owns draws on the same
|
|
464
|
-
balance pool today (the `balance` field returned by `me()`). Treat it as
|
|
465
|
-
user-scoped, not key-scoped.
|
|
466
|
-
2. **Maker/taker heuristic.** `is_maker = (time_in_force == "GTC")`. A GTC limit
|
|
467
|
-
that crosses on submit is booked as a $0-fee maker in the sim; on Polymarket
|
|
468
|
-
it would be a taker. We over-count makers by design until the classifier is
|
|
469
|
-
tightened.
|
|
470
|
-
3. **Orderbook freshness.** The book cache TTL is 300 s. For markets Polymarket
|
|
471
|
-
updates less often than that, a fill may revert to displayed midpoint; a ±15%
|
|
472
|
-
sanity guard bounds the drift. High-volume markets always fill at real ask/bid.
|
|
473
|
-
|
|
474
|
-
---
|
|
475
|
-
|
|
476
|
-
## What's included
|
|
477
|
-
|
|
478
|
-
| Path | Purpose |
|
|
479
|
-
|---|---|
|
|
480
|
-
| `polysim_sdk/` | Native client: `PolySimClient` (sync) + `AsyncPolySimClient` (async), `ws`, `sse` (underlying-spot stream), `updown` (Up/Down row helpers), `pagination`, `exceptions`, `constants`. |
|
|
481
|
-
| `polysim_clob_client/` | `py-clob-client` drop-in: `ClobClient` + matching `clob_types`, `constants`, `order_builder`, `exceptions`. |
|
|
482
|
-
| `scripts/01_balance_and_market.py` | Smallest end-to-end native demo. |
|
|
483
|
-
| `scripts/02_clob_dropin_demo.py` | The `py-clob-client` drop-in surface end-to-end. |
|
|
484
|
-
| `scripts/03_async_concurrent.py` | Concurrent reads via the async client. |
|
|
485
|
-
| `scripts/04_btc_updown.py` | Discover the live BTC Up/Down window: spot, strike, prices, tokens. |
|
|
486
|
-
| `tests/` | respx-mocked unit tests for both surfaces (no creds, no network). |
|
|
487
|
-
|
|
488
|
-
Run the test suite with `pytest`. Lint/type with `ruff check . && mypy polysim_sdk polysim_clob_client`.
|
|
489
|
-
|
|
490
|
-
---
|
|
491
|
-
|
|
492
|
-
## License
|
|
493
|
-
|
|
494
|
-
[Apache License 2.0](./LICENSE). You may use, modify, and redistribute this
|
|
495
|
-
SDK — including in commercial and closed-source projects — provided you keep
|
|
496
|
-
the license and attribution notices (see [`NOTICE`](./NOTICE)). Apache-2.0 also
|
|
497
|
-
grants an explicit patent license, which is why many companies prefer it over
|
|
498
|
-
MIT for dependencies.
|
|
499
|
-
|
|
500
|
-
The `polysim_clob_client` package mirrors the *public surface* of
|
|
501
|
-
[`py-clob-client`](https://github.com/Polymarket/py-clob-client) (MIT) for
|
|
502
|
-
drop-in compatibility; no upstream source is bundled. This project is not
|
|
503
|
-
affiliated with or endorsed by Polymarket.
|