polynode 0.14.1__tar.gz → 0.15.0__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.
- {polynode-0.14.1 → polynode-0.15.0}/PKG-INFO +109 -6
- {polynode-0.14.1 → polynode-0.15.0}/README.md +108 -5
- {polynode-0.14.1 → polynode-0.15.0}/core-contract-v1.json +55 -1
- polynode-0.15.0/polynode/__init__.py +161 -0
- polynode-0.15.0/polynode/_version.py +1 -0
- polynode-0.15.0/polynode/market_protocol.py +466 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/redemption_watcher.py +66 -8
- {polynode-0.14.1 → polynode-0.15.0}/polynode/short_form.py +35 -2
- {polynode-0.14.1 → polynode-0.15.0}/polynode/trading/__init__.py +17 -1
- {polynode-0.14.1 → polynode-0.15.0}/polynode/trading/clob_api.py +12 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/trading/constants.py +16 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/trading/eip712.py +93 -7
- {polynode-0.14.1 → polynode-0.15.0}/polynode/trading/onboarding.py +186 -1
- {polynode-0.14.1 → polynode-0.15.0}/polynode/trading/position_management.py +93 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/trading/sqlite_backend.py +37 -1
- {polynode-0.14.1 → polynode-0.15.0}/polynode/trading/trader.py +487 -7
- {polynode-0.14.1 → polynode-0.15.0}/polynode/trading/types.py +55 -1
- {polynode-0.14.1 → polynode-0.15.0}/polynode/types/__init__.py +1 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/types/events.py +59 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/types/perps.py +62 -1
- {polynode-0.14.1 → polynode-0.15.0}/polynode/types/short_form.py +3 -0
- polynode-0.15.0/polynode/types/v3.py +263 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/v3.py +469 -3
- {polynode-0.14.1 → polynode-0.15.0}/polynode/v3_operations.py +9 -0
- {polynode-0.14.1 → polynode-0.15.0}/pyproject.toml +1 -1
- polynode-0.14.1/polynode/__init__.py +0 -83
- polynode-0.14.1/polynode/_version.py +0 -1
- {polynode-0.14.1 → polynode-0.15.0}/.gitignore +0 -0
- {polynode-0.14.1 → polynode-0.15.0}/core-fixtures-v1.json +0 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/cache/__init__.py +0 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/client.py +0 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/engine.py +0 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/errors.py +0 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/orderbook.py +0 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/orderbook_integrity.py +0 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/orderbook_state.py +0 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/perps.py +0 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/subscription.py +0 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/testing.py +0 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/trading/cosigner.py +0 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/trading/escrow.py +0 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/trading/privy.py +0 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/trading/relayer.py +0 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/trading/signer.py +0 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/trading/user_relayer.py +0 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/types/enums.py +0 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/types/orderbook.py +0 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/types/rest.py +0 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/types/ws.py +0 -0
- {polynode-0.14.1 → polynode-0.15.0}/polynode/ws.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: polynode
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.15.0
|
|
4
4
|
Summary: Python SDK for the Polynode real-time prediction market data platform
|
|
5
5
|
Project-URL: Homepage, https://polynode.dev
|
|
6
6
|
Project-URL: Documentation, https://docs.polynode.dev
|
|
@@ -36,7 +36,9 @@ Description-Content-Type: text/markdown
|
|
|
36
36
|
|
|
37
37
|
Python SDK for the [Polynode](https://polynode.dev) real-time prediction market data platform.
|
|
38
38
|
|
|
39
|
-
**New in v0.
|
|
39
|
+
**New in v0.15.0:** Polymarket Protocol V2 markets (October 2026). Upgrade before 2026-11-02 if you trade: `order()` looks up each market's protocol and signs Protocol V2 orders for the right exchange automatically, and split, merge and the new redeem work on them too. Nothing changes for current markets or for streaming and REST users. This is not the April 2026 "V2" (CTF Exchange V2 and pUSD) described elsewhere in this README; see [Polymarket Protocol V2 markets](#polymarket-protocol-v2-markets).
|
|
40
|
+
|
|
41
|
+
**In v0.14.1:** Every prepared user-owned order exposes its canonical V2 exchange `order_hash` before submission. This is the exact CLOB/open-order ID and fill `order_hash`; deposit-wallet orders hash the standard inner `Order`, not the POLY_1271 wallet-signing wrapper. Record it before the network attempt so timeouts can be reconciled without risking a duplicate.
|
|
40
42
|
|
|
41
43
|
**In v0.14.0:** Web platforms can take a user from wallet authorization through an exact browser-signed user-owned order without exposing backend credentials. The SDK imports the shared versioned browser bundle into memory, produces a credential-free signing request with a complete order preview, supports one-time multi-worker state, validates canonical signatures and wallet identity, checks BUY collateral before prompting, and submits with exact zero builder attribution.
|
|
42
44
|
|
|
@@ -72,7 +74,6 @@ with PolyNode(api_key="pn_live_...") as pn:
|
|
|
72
74
|
status = pn.status()
|
|
73
75
|
connections = pn.connections()
|
|
74
76
|
markets = pn.markets(count=10)
|
|
75
|
-
settlements = pn.recent_settlements(count=5)
|
|
76
77
|
wallet_positions = pn.wallet_positions(
|
|
77
78
|
address, redeemable=True, condition_id=condition_id
|
|
78
79
|
)
|
|
@@ -127,7 +128,7 @@ asyncio.run(main())
|
|
|
127
128
|
|
|
128
129
|
### Complete V3 API
|
|
129
130
|
|
|
130
|
-
V3 includes wallets, combos, rewards, credits, identities, markets, builders, profiles, perps, crypto, sports, backtesting, and other current product families. `execute()` gives you access to all
|
|
131
|
+
V3 includes wallets, combos, rewards, credits, identities, markets, events, builders, profiles, perps, crypto, sports, backtesting, and other current product families. `execute()` gives you access to all 129 current V3 operations through one consistent Python interface.
|
|
131
132
|
|
|
132
133
|
```python
|
|
133
134
|
import asyncio
|
|
@@ -135,7 +136,7 @@ from polynode import AsyncPolyNode
|
|
|
135
136
|
|
|
136
137
|
async def read_v3(address: str):
|
|
137
138
|
async with AsyncPolyNode(api_key="pn_live_...") as pn:
|
|
138
|
-
print(len(pn.v3.operations)) #
|
|
139
|
+
print(len(pn.v3.operations)) # 128
|
|
139
140
|
|
|
140
141
|
combo_activity = await pn.v3.execute(
|
|
141
142
|
"GET /v3/combos/activity",
|
|
@@ -146,11 +147,76 @@ async def read_v3(address: str):
|
|
|
146
147
|
path_params={"address": address},
|
|
147
148
|
query={"limit": 100},
|
|
148
149
|
)
|
|
149
|
-
|
|
150
|
+
scopes = await pn.v3.execute(
|
|
151
|
+
"GET /v3/leaderboard/scopes",
|
|
152
|
+
query={"period": "7d", "scope": "category"},
|
|
153
|
+
)
|
|
154
|
+
markets = await pn.v3.execute(
|
|
155
|
+
"GET /v3/markets",
|
|
156
|
+
query={"status": "open", "limit": 100},
|
|
157
|
+
)
|
|
158
|
+
return combo_activity, wallet_rewards, scopes, markets
|
|
150
159
|
|
|
151
160
|
asyncio.run(read_v3("0xabc..."))
|
|
152
161
|
```
|
|
153
162
|
|
|
163
|
+
Use the typed Combo P&L helper for All Time realized, unrealized, and total
|
|
164
|
+
P&L, or realized P&L over a fixed or custom half-open window:
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
from polynode import PolyNodeV3
|
|
168
|
+
|
|
169
|
+
with PolyNodeV3("pn_live_...") as v3:
|
|
170
|
+
all_time = v3.combo_pnl_leaderboard(
|
|
171
|
+
period="all",
|
|
172
|
+
sort_by="total_pnl",
|
|
173
|
+
)
|
|
174
|
+
custom = v3.combo_pnl_leaderboard(
|
|
175
|
+
period="custom",
|
|
176
|
+
after=1_788_134_400,
|
|
177
|
+
before=1_788_220_800,
|
|
178
|
+
sort_by="realized_pnl",
|
|
179
|
+
)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Wallets with incomplete arbitrary transfer basis or without a current mark are
|
|
183
|
+
omitted rather than returned with zero P&L. Inspect `coverage` on every response
|
|
184
|
+
before presenting the ranking.
|
|
185
|
+
|
|
186
|
+
Market and event catalogs use opaque cursor pagination. Reward-market list
|
|
187
|
+
rows include nullable title, slug, image, event, category, and tag fields.
|
|
188
|
+
|
|
189
|
+
Named perps helpers accept the returned opaque cursor:
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
from polynode import PolyNodeV3
|
|
193
|
+
|
|
194
|
+
with PolyNodeV3(api_key="pn_live_...") as v3:
|
|
195
|
+
first = v3.perps_trades(
|
|
196
|
+
"BTC-USD",
|
|
197
|
+
after=1_787_875_200,
|
|
198
|
+
before=1_787_961_600,
|
|
199
|
+
limit=100,
|
|
200
|
+
)
|
|
201
|
+
if first.get("next_cursor"):
|
|
202
|
+
second = v3.perps_trades(
|
|
203
|
+
"BTC-USD",
|
|
204
|
+
after=1_787_875_200,
|
|
205
|
+
before=1_787_961_600,
|
|
206
|
+
limit=100,
|
|
207
|
+
cursor=first["next_cursor"],
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
events = v3.perps_events(address=address, limit=100)
|
|
211
|
+
liquidations = v3.perps_liquidations(min_notional=10_000)
|
|
212
|
+
flows = v3.perps_wallet_flows(address, limit=100)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Reuse the same route and filters on every cursor page. Wallet-flow totals and
|
|
216
|
+
counts remain fixed during one traversal. Cumulative volume delta remains a
|
|
217
|
+
bounded single-page series and has no cursor helper. Async clients expose the
|
|
218
|
+
same methods with `await`.
|
|
219
|
+
|
|
154
220
|
The SDK encodes path parameters for you. Read requests that are safe to repeat retry transient failures and honor `Retry-After`; requests that change data are never retried automatically. JSON decimals decode as `Decimal`, and `ApiError` exposes the status, request ID, retry details, and a request URL with credentials removed.
|
|
155
221
|
|
|
156
222
|
Current presets include `dome`, `fills`, `combos`, `redemptions`, and `deposits`. Current filters include `since()`, `combo_condition_ids()`, `leg_position_ids()`, `event_ids()`, `module_ids()`, `action()`, and `direction()`.
|
|
@@ -301,6 +367,43 @@ For the V2 order flow, required approvals, and common failure modes, see [docs.p
|
|
|
301
367
|
|
|
302
368
|
V2 fees are determined at match time and are not signed into an order, so V2 payloads omit `feeRateBps`, `nonce`, and `taker`. Explicit legacy V1 mode still signs `feeRateBps`; for that path the SDK fetches `/fee-rate` and fails closed if fee, tick-size, or neg-risk metadata is unavailable or malformed.
|
|
303
369
|
|
|
370
|
+
#### Polymarket Protocol V2 markets
|
|
371
|
+
|
|
372
|
+
> **Two different "V2"s.** Polymarket Protocol V2 (October 2026) is Polymarket's new position system: positions live in a new ledger (PositionManager), every market type trades on one new exchange (ExchangeV3, order signing domain version `"3"`), and split, merge and redeem go through a Router. It is **not** the April 2026 upgrade (CTF Exchange V2 with pUSD collateral) that `ExchangeVersion.V2` and the rest of this README call "V2". Current markets keep using the April exchanges.
|
|
373
|
+
|
|
374
|
+
**When.** Polymarket's test markets for Protocol V2 trade from 2026-10-05 to 2026-10-30. New markets move to Protocol V2 from around 2026-11-02 (tentative, per Polymarket). Existing markets stay where they are.
|
|
375
|
+
|
|
376
|
+
**What you need to do.**
|
|
377
|
+
|
|
378
|
+
- Streaming and REST only: nothing. Same methods, same response shapes. Rows and events for Protocol V2 markets carry a few optional fields: `protocol_version: "v2"`, `module` (`"binary"` or `"neg_risk"`), `exchange_version: "v3"` on fills, and `payouts_ppm` / `derived` on oracle events. A row without them is a current market.
|
|
379
|
+
- Trading: upgrade to `polynode` 0.15.0 before 2026-11-02 and place orders exactly as before. Older versions keep working on current markets, but Polymarket rejects their orders on Protocol V2 markets (no funds move).
|
|
380
|
+
- Storing ids yourself: keep outcome ids as strings (up to 78 digits). Condition ids are returned in the 66-character form; the 64-character form is accepted as input.
|
|
381
|
+
|
|
382
|
+
The first order on a Protocol V2 market grants the two approvals it needs (pUSD and PositionManager, both for ExchangeV3): one gasless batch for Safe and deposit wallets, or two transactions with a little POL gas for an EOA. To do it ahead of time, or for browser-signed user-owned orders, call `ensure_ready` once with `protocol_v2=True`. Plain `ensure_ready()` behaves exactly as before.
|
|
383
|
+
|
|
384
|
+
```python
|
|
385
|
+
from polynode.trading import MergeParams, RedeemParams, SplitParams
|
|
386
|
+
|
|
387
|
+
await trader.ensure_ready("0xYourPrivateKey...", protocol_v2=True) # optional
|
|
388
|
+
version = await trader.get_market_protocol(token_id) # "v1" (current) or "v2"
|
|
389
|
+
|
|
390
|
+
# Same call for every market; the SDK picks the exchange and signing domain.
|
|
391
|
+
await trader.order(OrderParams(token_id=token_id, side="BUY", price=0.5, size=10))
|
|
392
|
+
|
|
393
|
+
# Position operations (user-owned execution) route through the Router on V2 markets.
|
|
394
|
+
await trader.execute_split(SplitParams(condition_id=condition_id, amount=10))
|
|
395
|
+
await trader.execute_merge(MergeParams(condition_id=condition_id, amount=10))
|
|
396
|
+
await trader.execute_redeem(RedeemParams(condition_id=condition_id, outcome_index=0)) # 0 = YES, 1 = NO
|
|
397
|
+
payout = await trader.preview_payout(position_id, 10) # None until resolved
|
|
398
|
+
|
|
399
|
+
status = await trader.check_approvals(protocol_v2=True)
|
|
400
|
+
print(status.protocol_v2_ready, status.position_ops_v2_ready, status.auto_redeem_enabled)
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
The SDK learns a market's protocol from Polymarket's market data and caches it for 24 hours in a new local table (existing tables are unchanged). It never guesses from the shape of an id: if the protocol cannot be learned, the order is refused with a retryable `MarketProtocolError` instead of being signed for the wrong exchange. Pass `protocol_version="v2"` (or call `trader.hint_market_protocol(...)`) to skip the lookup; a value that contradicts what the SDK already knows is refused. Short-form streams pick `positionIds` for Protocol V2 markets and expose `market.protocol_version`, so orders on discovered markets skip the lookup.
|
|
404
|
+
|
|
405
|
+
Results of Protocol V2 markets are shares of 1,000,000 (`[1000000, 0]` = YES won, `[500000, 500000]` = 50/50); `payouts` carries the same result in smallest whole numbers, like current markets. `RedemptionWatcher` handles both. Automatic redemption by Polymarket's AutoRedeemer is opt-in only (`enable_auto_redeem()` / `disable_auto_redeem()`); proceeds always return to your wallet. Converting neg-risk positions is not supported for Protocol V2 markets yet.
|
|
406
|
+
|
|
304
407
|
#### Optional user-owned execution
|
|
305
408
|
|
|
306
409
|
Set `execution_mode=ExecutionMode.USER_OWNED` when the signing wallet should trade with zero builder attribution and use its own gasless authorization. Builder mode remains the default and existing integrations are unchanged.
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Python SDK for the [Polynode](https://polynode.dev) real-time prediction market data platform.
|
|
4
4
|
|
|
5
|
-
**New in v0.
|
|
5
|
+
**New in v0.15.0:** Polymarket Protocol V2 markets (October 2026). Upgrade before 2026-11-02 if you trade: `order()` looks up each market's protocol and signs Protocol V2 orders for the right exchange automatically, and split, merge and the new redeem work on them too. Nothing changes for current markets or for streaming and REST users. This is not the April 2026 "V2" (CTF Exchange V2 and pUSD) described elsewhere in this README; see [Polymarket Protocol V2 markets](#polymarket-protocol-v2-markets).
|
|
6
|
+
|
|
7
|
+
**In v0.14.1:** Every prepared user-owned order exposes its canonical V2 exchange `order_hash` before submission. This is the exact CLOB/open-order ID and fill `order_hash`; deposit-wallet orders hash the standard inner `Order`, not the POLY_1271 wallet-signing wrapper. Record it before the network attempt so timeouts can be reconciled without risking a duplicate.
|
|
6
8
|
|
|
7
9
|
**In v0.14.0:** Web platforms can take a user from wallet authorization through an exact browser-signed user-owned order without exposing backend credentials. The SDK imports the shared versioned browser bundle into memory, produces a credential-free signing request with a complete order preview, supports one-time multi-worker state, validates canonical signatures and wallet identity, checks BUY collateral before prompting, and submits with exact zero builder attribution.
|
|
8
10
|
|
|
@@ -38,7 +40,6 @@ with PolyNode(api_key="pn_live_...") as pn:
|
|
|
38
40
|
status = pn.status()
|
|
39
41
|
connections = pn.connections()
|
|
40
42
|
markets = pn.markets(count=10)
|
|
41
|
-
settlements = pn.recent_settlements(count=5)
|
|
42
43
|
wallet_positions = pn.wallet_positions(
|
|
43
44
|
address, redeemable=True, condition_id=condition_id
|
|
44
45
|
)
|
|
@@ -93,7 +94,7 @@ asyncio.run(main())
|
|
|
93
94
|
|
|
94
95
|
### Complete V3 API
|
|
95
96
|
|
|
96
|
-
V3 includes wallets, combos, rewards, credits, identities, markets, builders, profiles, perps, crypto, sports, backtesting, and other current product families. `execute()` gives you access to all
|
|
97
|
+
V3 includes wallets, combos, rewards, credits, identities, markets, events, builders, profiles, perps, crypto, sports, backtesting, and other current product families. `execute()` gives you access to all 129 current V3 operations through one consistent Python interface.
|
|
97
98
|
|
|
98
99
|
```python
|
|
99
100
|
import asyncio
|
|
@@ -101,7 +102,7 @@ from polynode import AsyncPolyNode
|
|
|
101
102
|
|
|
102
103
|
async def read_v3(address: str):
|
|
103
104
|
async with AsyncPolyNode(api_key="pn_live_...") as pn:
|
|
104
|
-
print(len(pn.v3.operations)) #
|
|
105
|
+
print(len(pn.v3.operations)) # 128
|
|
105
106
|
|
|
106
107
|
combo_activity = await pn.v3.execute(
|
|
107
108
|
"GET /v3/combos/activity",
|
|
@@ -112,11 +113,76 @@ async def read_v3(address: str):
|
|
|
112
113
|
path_params={"address": address},
|
|
113
114
|
query={"limit": 100},
|
|
114
115
|
)
|
|
115
|
-
|
|
116
|
+
scopes = await pn.v3.execute(
|
|
117
|
+
"GET /v3/leaderboard/scopes",
|
|
118
|
+
query={"period": "7d", "scope": "category"},
|
|
119
|
+
)
|
|
120
|
+
markets = await pn.v3.execute(
|
|
121
|
+
"GET /v3/markets",
|
|
122
|
+
query={"status": "open", "limit": 100},
|
|
123
|
+
)
|
|
124
|
+
return combo_activity, wallet_rewards, scopes, markets
|
|
116
125
|
|
|
117
126
|
asyncio.run(read_v3("0xabc..."))
|
|
118
127
|
```
|
|
119
128
|
|
|
129
|
+
Use the typed Combo P&L helper for All Time realized, unrealized, and total
|
|
130
|
+
P&L, or realized P&L over a fixed or custom half-open window:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from polynode import PolyNodeV3
|
|
134
|
+
|
|
135
|
+
with PolyNodeV3("pn_live_...") as v3:
|
|
136
|
+
all_time = v3.combo_pnl_leaderboard(
|
|
137
|
+
period="all",
|
|
138
|
+
sort_by="total_pnl",
|
|
139
|
+
)
|
|
140
|
+
custom = v3.combo_pnl_leaderboard(
|
|
141
|
+
period="custom",
|
|
142
|
+
after=1_788_134_400,
|
|
143
|
+
before=1_788_220_800,
|
|
144
|
+
sort_by="realized_pnl",
|
|
145
|
+
)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Wallets with incomplete arbitrary transfer basis or without a current mark are
|
|
149
|
+
omitted rather than returned with zero P&L. Inspect `coverage` on every response
|
|
150
|
+
before presenting the ranking.
|
|
151
|
+
|
|
152
|
+
Market and event catalogs use opaque cursor pagination. Reward-market list
|
|
153
|
+
rows include nullable title, slug, image, event, category, and tag fields.
|
|
154
|
+
|
|
155
|
+
Named perps helpers accept the returned opaque cursor:
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
from polynode import PolyNodeV3
|
|
159
|
+
|
|
160
|
+
with PolyNodeV3(api_key="pn_live_...") as v3:
|
|
161
|
+
first = v3.perps_trades(
|
|
162
|
+
"BTC-USD",
|
|
163
|
+
after=1_787_875_200,
|
|
164
|
+
before=1_787_961_600,
|
|
165
|
+
limit=100,
|
|
166
|
+
)
|
|
167
|
+
if first.get("next_cursor"):
|
|
168
|
+
second = v3.perps_trades(
|
|
169
|
+
"BTC-USD",
|
|
170
|
+
after=1_787_875_200,
|
|
171
|
+
before=1_787_961_600,
|
|
172
|
+
limit=100,
|
|
173
|
+
cursor=first["next_cursor"],
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
events = v3.perps_events(address=address, limit=100)
|
|
177
|
+
liquidations = v3.perps_liquidations(min_notional=10_000)
|
|
178
|
+
flows = v3.perps_wallet_flows(address, limit=100)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Reuse the same route and filters on every cursor page. Wallet-flow totals and
|
|
182
|
+
counts remain fixed during one traversal. Cumulative volume delta remains a
|
|
183
|
+
bounded single-page series and has no cursor helper. Async clients expose the
|
|
184
|
+
same methods with `await`.
|
|
185
|
+
|
|
120
186
|
The SDK encodes path parameters for you. Read requests that are safe to repeat retry transient failures and honor `Retry-After`; requests that change data are never retried automatically. JSON decimals decode as `Decimal`, and `ApiError` exposes the status, request ID, retry details, and a request URL with credentials removed.
|
|
121
187
|
|
|
122
188
|
Current presets include `dome`, `fills`, `combos`, `redemptions`, and `deposits`. Current filters include `since()`, `combo_condition_ids()`, `leg_position_ids()`, `event_ids()`, `module_ids()`, `action()`, and `direction()`.
|
|
@@ -267,6 +333,43 @@ For the V2 order flow, required approvals, and common failure modes, see [docs.p
|
|
|
267
333
|
|
|
268
334
|
V2 fees are determined at match time and are not signed into an order, so V2 payloads omit `feeRateBps`, `nonce`, and `taker`. Explicit legacy V1 mode still signs `feeRateBps`; for that path the SDK fetches `/fee-rate` and fails closed if fee, tick-size, or neg-risk metadata is unavailable or malformed.
|
|
269
335
|
|
|
336
|
+
#### Polymarket Protocol V2 markets
|
|
337
|
+
|
|
338
|
+
> **Two different "V2"s.** Polymarket Protocol V2 (October 2026) is Polymarket's new position system: positions live in a new ledger (PositionManager), every market type trades on one new exchange (ExchangeV3, order signing domain version `"3"`), and split, merge and redeem go through a Router. It is **not** the April 2026 upgrade (CTF Exchange V2 with pUSD collateral) that `ExchangeVersion.V2` and the rest of this README call "V2". Current markets keep using the April exchanges.
|
|
339
|
+
|
|
340
|
+
**When.** Polymarket's test markets for Protocol V2 trade from 2026-10-05 to 2026-10-30. New markets move to Protocol V2 from around 2026-11-02 (tentative, per Polymarket). Existing markets stay where they are.
|
|
341
|
+
|
|
342
|
+
**What you need to do.**
|
|
343
|
+
|
|
344
|
+
- Streaming and REST only: nothing. Same methods, same response shapes. Rows and events for Protocol V2 markets carry a few optional fields: `protocol_version: "v2"`, `module` (`"binary"` or `"neg_risk"`), `exchange_version: "v3"` on fills, and `payouts_ppm` / `derived` on oracle events. A row without them is a current market.
|
|
345
|
+
- Trading: upgrade to `polynode` 0.15.0 before 2026-11-02 and place orders exactly as before. Older versions keep working on current markets, but Polymarket rejects their orders on Protocol V2 markets (no funds move).
|
|
346
|
+
- Storing ids yourself: keep outcome ids as strings (up to 78 digits). Condition ids are returned in the 66-character form; the 64-character form is accepted as input.
|
|
347
|
+
|
|
348
|
+
The first order on a Protocol V2 market grants the two approvals it needs (pUSD and PositionManager, both for ExchangeV3): one gasless batch for Safe and deposit wallets, or two transactions with a little POL gas for an EOA. To do it ahead of time, or for browser-signed user-owned orders, call `ensure_ready` once with `protocol_v2=True`. Plain `ensure_ready()` behaves exactly as before.
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
from polynode.trading import MergeParams, RedeemParams, SplitParams
|
|
352
|
+
|
|
353
|
+
await trader.ensure_ready("0xYourPrivateKey...", protocol_v2=True) # optional
|
|
354
|
+
version = await trader.get_market_protocol(token_id) # "v1" (current) or "v2"
|
|
355
|
+
|
|
356
|
+
# Same call for every market; the SDK picks the exchange and signing domain.
|
|
357
|
+
await trader.order(OrderParams(token_id=token_id, side="BUY", price=0.5, size=10))
|
|
358
|
+
|
|
359
|
+
# Position operations (user-owned execution) route through the Router on V2 markets.
|
|
360
|
+
await trader.execute_split(SplitParams(condition_id=condition_id, amount=10))
|
|
361
|
+
await trader.execute_merge(MergeParams(condition_id=condition_id, amount=10))
|
|
362
|
+
await trader.execute_redeem(RedeemParams(condition_id=condition_id, outcome_index=0)) # 0 = YES, 1 = NO
|
|
363
|
+
payout = await trader.preview_payout(position_id, 10) # None until resolved
|
|
364
|
+
|
|
365
|
+
status = await trader.check_approvals(protocol_v2=True)
|
|
366
|
+
print(status.protocol_v2_ready, status.position_ops_v2_ready, status.auto_redeem_enabled)
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
The SDK learns a market's protocol from Polymarket's market data and caches it for 24 hours in a new local table (existing tables are unchanged). It never guesses from the shape of an id: if the protocol cannot be learned, the order is refused with a retryable `MarketProtocolError` instead of being signed for the wrong exchange. Pass `protocol_version="v2"` (or call `trader.hint_market_protocol(...)`) to skip the lookup; a value that contradicts what the SDK already knows is refused. Short-form streams pick `positionIds` for Protocol V2 markets and expose `market.protocol_version`, so orders on discovered markets skip the lookup.
|
|
370
|
+
|
|
371
|
+
Results of Protocol V2 markets are shares of 1,000,000 (`[1000000, 0]` = YES won, `[500000, 500000]` = 50/50); `payouts` carries the same result in smallest whole numbers, like current markets. `RedemptionWatcher` handles both. Automatic redemption by Polymarket's AutoRedeemer is opt-in only (`enable_auto_redeem()` / `disable_auto_redeem()`); proceeds always return to your wallet. Converting neg-risk positions is not supported for Protocol V2 markets yet.
|
|
372
|
+
|
|
270
373
|
#### Optional user-owned execution
|
|
271
374
|
|
|
272
375
|
Set `execution_mode=ExecutionMode.USER_OWNED` when the signing wallet should trade with zero builder attribution and use its own gasless authorization. Builder mode remains the default and existing integrations are unchanged.
|
|
@@ -237,7 +237,7 @@
|
|
|
237
237
|
"never_drop_silently": true
|
|
238
238
|
}
|
|
239
239
|
},
|
|
240
|
-
"operation_count":
|
|
240
|
+
"operation_count": 129,
|
|
241
241
|
"operations": [
|
|
242
242
|
{
|
|
243
243
|
"method": "POST",
|
|
@@ -371,12 +371,48 @@
|
|
|
371
371
|
"domain": "combos",
|
|
372
372
|
"retry": "safe_read"
|
|
373
373
|
},
|
|
374
|
+
{
|
|
375
|
+
"method": "GET",
|
|
376
|
+
"path": "/v3/combos/activity/daily",
|
|
377
|
+
"domain": "combos",
|
|
378
|
+
"retry": "safe_read"
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
"method": "GET",
|
|
382
|
+
"path": "/v3/combos/leaderboard",
|
|
383
|
+
"domain": "combos",
|
|
384
|
+
"retry": "safe_read"
|
|
385
|
+
},
|
|
374
386
|
{
|
|
375
387
|
"method": "GET",
|
|
376
388
|
"path": "/v3/combos/redemptions",
|
|
377
389
|
"domain": "combos",
|
|
378
390
|
"retry": "safe_read"
|
|
379
391
|
},
|
|
392
|
+
{
|
|
393
|
+
"method": "GET",
|
|
394
|
+
"path": "/v3/markets",
|
|
395
|
+
"domain": "markets",
|
|
396
|
+
"retry": "safe_read"
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
"method": "GET",
|
|
400
|
+
"path": "/v3/markets/stats",
|
|
401
|
+
"domain": "markets",
|
|
402
|
+
"retry": "safe_read"
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
"method": "GET",
|
|
406
|
+
"path": "/v3/events",
|
|
407
|
+
"domain": "events",
|
|
408
|
+
"retry": "safe_read"
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
"method": "GET",
|
|
412
|
+
"path": "/v3/events/stats",
|
|
413
|
+
"domain": "events",
|
|
414
|
+
"retry": "safe_read"
|
|
415
|
+
},
|
|
380
416
|
{
|
|
381
417
|
"method": "GET",
|
|
382
418
|
"path": "/v3/markets/search",
|
|
@@ -485,6 +521,12 @@
|
|
|
485
521
|
"domain": "global",
|
|
486
522
|
"retry": "safe_read"
|
|
487
523
|
},
|
|
524
|
+
{
|
|
525
|
+
"method": "GET",
|
|
526
|
+
"path": "/v3/leaderboard/scopes",
|
|
527
|
+
"domain": "global",
|
|
528
|
+
"retry": "safe_read"
|
|
529
|
+
},
|
|
488
530
|
{
|
|
489
531
|
"method": "GET",
|
|
490
532
|
"path": "/v3/builders",
|
|
@@ -497,6 +539,12 @@
|
|
|
497
539
|
"domain": "builders",
|
|
498
540
|
"retry": "safe_read"
|
|
499
541
|
},
|
|
542
|
+
{
|
|
543
|
+
"method": "GET",
|
|
544
|
+
"path": "/v3/builders/{code}/leaderboard",
|
|
545
|
+
"domain": "builders",
|
|
546
|
+
"retry": "safe_read"
|
|
547
|
+
},
|
|
500
548
|
{
|
|
501
549
|
"method": "GET",
|
|
502
550
|
"path": "/v3/builders/{code}/trades",
|
|
@@ -887,6 +935,12 @@
|
|
|
887
935
|
"domain": "perps",
|
|
888
936
|
"retry": "safe_read"
|
|
889
937
|
},
|
|
938
|
+
{
|
|
939
|
+
"method": "GET",
|
|
940
|
+
"path": "/v3/perps/activity/daily",
|
|
941
|
+
"domain": "perps",
|
|
942
|
+
"retry": "safe_read"
|
|
943
|
+
},
|
|
890
944
|
{
|
|
891
945
|
"method": "GET",
|
|
892
946
|
"path": "/v3/perps/funding-pnl/{instrument}",
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""PolyNode Python SDK — real-time prediction market data and trading."""
|
|
2
|
+
|
|
3
|
+
from ._version import __version__
|
|
4
|
+
from .client import AsyncPolyNode, PolyNode
|
|
5
|
+
from .engine import EngineView, OrderbookEngine
|
|
6
|
+
from .errors import ApiError, PolyNodeError, WsError
|
|
7
|
+
from .market_protocol import (
|
|
8
|
+
MarketProtocolError,
|
|
9
|
+
MarketProtocolInfo,
|
|
10
|
+
MarketProtocolResolver,
|
|
11
|
+
ProtocolVersion,
|
|
12
|
+
get_market_protocol,
|
|
13
|
+
normalize_condition_id,
|
|
14
|
+
protocol_v2_position_ids,
|
|
15
|
+
)
|
|
16
|
+
from .orderbook import OrderbookWS
|
|
17
|
+
from .orderbook_state import LocalOrderbook
|
|
18
|
+
from .perps import PerpsChannels, PerpsWS, perps_channels, perps_websocket_url
|
|
19
|
+
from .types.perps import (
|
|
20
|
+
PerpsConnected,
|
|
21
|
+
PerpsDecodeError,
|
|
22
|
+
PerpsEvent,
|
|
23
|
+
PerpsLagWarning,
|
|
24
|
+
PerpsMessage,
|
|
25
|
+
PerpsPositionEvent,
|
|
26
|
+
PerpsPositionEventsResponse,
|
|
27
|
+
PerpsPositionEventType,
|
|
28
|
+
PerpsPong,
|
|
29
|
+
PerpsQueueOverflow,
|
|
30
|
+
PerpsReconnectNotice,
|
|
31
|
+
PerpsRejectedChannel,
|
|
32
|
+
PerpsServerError,
|
|
33
|
+
PerpsSubscribed,
|
|
34
|
+
PerpsRestTrade,
|
|
35
|
+
PerpsTradesResponse,
|
|
36
|
+
PerpsUnknownMessage,
|
|
37
|
+
PerpsUnsubscribed,
|
|
38
|
+
PerpsWalletFlowEvent,
|
|
39
|
+
PerpsWalletFlowsResponse,
|
|
40
|
+
)
|
|
41
|
+
from .types.v3 import (
|
|
42
|
+
BuilderTraderLeaderboardCoverage,
|
|
43
|
+
BuilderTraderLeaderboardPeriod,
|
|
44
|
+
BuilderTraderLeaderboardResponse,
|
|
45
|
+
BuilderTraderLeaderboardRow,
|
|
46
|
+
BuilderTraderLeaderboardSort,
|
|
47
|
+
BuilderTraderLeaderboardWindow,
|
|
48
|
+
ComboActivityCountSemantics,
|
|
49
|
+
ComboActivityDailyResponse,
|
|
50
|
+
ComboActivityDay,
|
|
51
|
+
ComboPnlLeaderboardCoverage,
|
|
52
|
+
ComboPnlLeaderboardPeriod,
|
|
53
|
+
ComboPnlLeaderboardResponse,
|
|
54
|
+
ComboPnlLeaderboardRow,
|
|
55
|
+
ComboPnlLeaderboardSort,
|
|
56
|
+
ComboPnlLeaderboardWindow,
|
|
57
|
+
ComboPnlScope,
|
|
58
|
+
DailyActivityPeriod,
|
|
59
|
+
PerpsActivityDailyResponse,
|
|
60
|
+
PerpsActivityDay,
|
|
61
|
+
PerpsActivityTotal,
|
|
62
|
+
PerpsInstrumentActivityDay,
|
|
63
|
+
PerpsInstrumentActivitySeries,
|
|
64
|
+
ProtocolV2RowFields,
|
|
65
|
+
)
|
|
66
|
+
from .redemption_watcher import RedeemableAlert, RedemptionWatcher, TrackedPosition
|
|
67
|
+
from .short_form import ShortFormStream, short_form_chainlink_feed, short_form_twap_window
|
|
68
|
+
from .subscription import Subscription, SubscriptionBuilder
|
|
69
|
+
from .testing import get_active_test_wallet, get_active_test_wallets
|
|
70
|
+
from .v3 import AsyncPolyNodeV3, PolyNodeV3
|
|
71
|
+
from .v3_operations import V3_OPERATIONS, V3_OPERATION_COUNT, V3Operation
|
|
72
|
+
from .ws import PolyNodeWS
|
|
73
|
+
|
|
74
|
+
__all__ = [
|
|
75
|
+
"MarketProtocolError",
|
|
76
|
+
"MarketProtocolInfo",
|
|
77
|
+
"MarketProtocolResolver",
|
|
78
|
+
"ProtocolVersion",
|
|
79
|
+
"ProtocolV2RowFields",
|
|
80
|
+
"get_market_protocol",
|
|
81
|
+
"normalize_condition_id",
|
|
82
|
+
"protocol_v2_position_ids",
|
|
83
|
+
"__version__",
|
|
84
|
+
# Clients
|
|
85
|
+
"PolyNode",
|
|
86
|
+
"AsyncPolyNode",
|
|
87
|
+
"PolyNodeV3",
|
|
88
|
+
"AsyncPolyNodeV3",
|
|
89
|
+
"PerpsWS",
|
|
90
|
+
"PerpsChannels",
|
|
91
|
+
"perps_channels",
|
|
92
|
+
"perps_websocket_url",
|
|
93
|
+
"PerpsConnected",
|
|
94
|
+
"PerpsRejectedChannel",
|
|
95
|
+
"PerpsSubscribed",
|
|
96
|
+
"PerpsUnsubscribed",
|
|
97
|
+
"PerpsPong",
|
|
98
|
+
"PerpsEvent",
|
|
99
|
+
"PerpsLagWarning",
|
|
100
|
+
"PerpsServerError",
|
|
101
|
+
"PerpsReconnectNotice",
|
|
102
|
+
"PerpsDecodeError",
|
|
103
|
+
"PerpsUnknownMessage",
|
|
104
|
+
"PerpsQueueOverflow",
|
|
105
|
+
"PerpsMessage",
|
|
106
|
+
"PerpsPositionEventType",
|
|
107
|
+
"PerpsRestTrade",
|
|
108
|
+
"PerpsTradesResponse",
|
|
109
|
+
"PerpsPositionEvent",
|
|
110
|
+
"PerpsPositionEventsResponse",
|
|
111
|
+
"PerpsWalletFlowEvent",
|
|
112
|
+
"PerpsWalletFlowsResponse",
|
|
113
|
+
"BuilderTraderLeaderboardPeriod",
|
|
114
|
+
"BuilderTraderLeaderboardSort",
|
|
115
|
+
"BuilderTraderLeaderboardRow",
|
|
116
|
+
"BuilderTraderLeaderboardWindow",
|
|
117
|
+
"BuilderTraderLeaderboardCoverage",
|
|
118
|
+
"BuilderTraderLeaderboardResponse",
|
|
119
|
+
"ComboPnlLeaderboardPeriod",
|
|
120
|
+
"ComboPnlLeaderboardSort",
|
|
121
|
+
"ComboPnlScope",
|
|
122
|
+
"ComboPnlLeaderboardWindow",
|
|
123
|
+
"ComboPnlLeaderboardCoverage",
|
|
124
|
+
"ComboPnlLeaderboardRow",
|
|
125
|
+
"ComboPnlLeaderboardResponse",
|
|
126
|
+
"DailyActivityPeriod",
|
|
127
|
+
"ComboActivityDay",
|
|
128
|
+
"ComboActivityCountSemantics",
|
|
129
|
+
"ComboActivityDailyResponse",
|
|
130
|
+
"PerpsActivityDay",
|
|
131
|
+
"PerpsInstrumentActivityDay",
|
|
132
|
+
"PerpsInstrumentActivitySeries",
|
|
133
|
+
"PerpsActivityTotal",
|
|
134
|
+
"PerpsActivityDailyResponse",
|
|
135
|
+
"V3Operation",
|
|
136
|
+
"V3_OPERATIONS",
|
|
137
|
+
"V3_OPERATION_COUNT",
|
|
138
|
+
# WebSocket
|
|
139
|
+
"PolyNodeWS",
|
|
140
|
+
"SubscriptionBuilder",
|
|
141
|
+
"Subscription",
|
|
142
|
+
# Orderbook
|
|
143
|
+
"OrderbookWS",
|
|
144
|
+
"LocalOrderbook",
|
|
145
|
+
"OrderbookEngine",
|
|
146
|
+
"EngineView",
|
|
147
|
+
# Streams
|
|
148
|
+
"ShortFormStream",
|
|
149
|
+
"short_form_chainlink_feed",
|
|
150
|
+
"short_form_twap_window",
|
|
151
|
+
"RedemptionWatcher",
|
|
152
|
+
"RedeemableAlert",
|
|
153
|
+
"TrackedPosition",
|
|
154
|
+
# Testing
|
|
155
|
+
"get_active_test_wallet",
|
|
156
|
+
"get_active_test_wallets",
|
|
157
|
+
# Errors
|
|
158
|
+
"PolyNodeError",
|
|
159
|
+
"ApiError",
|
|
160
|
+
"WsError",
|
|
161
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.15.0"
|