polymach 0.1.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.
- polymach-0.1.0/DOCUMENTATION.md +875 -0
- polymach-0.1.0/LEGAL.md +213 -0
- polymach-0.1.0/LICENSE +21 -0
- polymach-0.1.0/PKG-INFO +320 -0
- polymach-0.1.0/PRIVACY.md +150 -0
- polymach-0.1.0/README.md +273 -0
- polymach-0.1.0/TERMS.md +267 -0
- polymach-0.1.0/assets/polymach-banner.png +0 -0
- polymach-0.1.0/pyproject.toml +73 -0
- polymach-0.1.0/python/polymach/__init__.py +95 -0
- polymach-0.1.0/python/polymach/bootstrap.py +375 -0
- polymach-0.1.0/python/polymach/client.py +561 -0
- polymach-0.1.0/python/polymach/domains.py +341 -0
- polymach-0.1.0/python/polymach/errors.py +47 -0
- polymach-0.1.0/python/polymach/models.py +656 -0
- polymach-0.1.0/python/polymach/onboarding.py +135 -0
- polymach-0.1.0/python/polymach/payments.py +543 -0
- polymach-0.1.0/python/polymach/py.typed +1 -0
- polymach-0.1.0/python/polymach/release_key.py +4 -0
- polymach-0.1.0/python/polymach/runtime.py +499 -0
- polymach-0.1.0/tests/__init__.py +1 -0
- polymach-0.1.0/tests/conftest.py +74 -0
- polymach-0.1.0/tests/live_support.py +479 -0
- polymach-0.1.0/tests/test_bootstrap.py +27 -0
- polymach-0.1.0/tests/test_onboarding.py +431 -0
- polymach-0.1.0/tests/test_payments.py +262 -0
- polymach-0.1.0/tests/test_runtime_security.py +100 -0
- polymach-0.1.0/tests/test_vs_official_live.py +330 -0
|
@@ -0,0 +1,875 @@
|
|
|
1
|
+
# PolyMach Documentation
|
|
2
|
+
|
|
3
|
+
This file is the single-file operational reference for the public `polymach` SDK.
|
|
4
|
+
|
|
5
|
+
It is written for agentic ingestion:
|
|
6
|
+
|
|
7
|
+
- it focuses on the actual public API surface
|
|
8
|
+
- it describes the startup and payment state machine
|
|
9
|
+
- it includes concrete examples
|
|
10
|
+
- it distinguishes payment-wallet concerns from trading-wallet concerns
|
|
11
|
+
|
|
12
|
+
## Purpose
|
|
13
|
+
|
|
14
|
+
`polymach` is the public Python SDK for the private PolyMach execution engine on Polymarket.
|
|
15
|
+
|
|
16
|
+
What happens at runtime:
|
|
17
|
+
|
|
18
|
+
1. the Python package is installed with `uv add polymach`
|
|
19
|
+
2. on first use, the SDK resolves or downloads the correct signed runtime binary
|
|
20
|
+
3. the SDK verifies the signed release manifest and artifact hash
|
|
21
|
+
4. the runtime is launched locally
|
|
22
|
+
5. the SDK checks for a machine-bound license
|
|
23
|
+
6. if needed, the SDK returns payment instructions or submits the payment directly
|
|
24
|
+
7. after license activation, the runtime is used for low-latency discovery, quotes, streaming, and trading
|
|
25
|
+
|
|
26
|
+
Important payment model:
|
|
27
|
+
|
|
28
|
+
- the SDK returns the exact payment instructions for the current machine when payment is needed
|
|
29
|
+
- the user or agent supplies the payer wallet or signer if payment is automated
|
|
30
|
+
|
|
31
|
+
The SDK is public. The Rust engine source is not in this repo.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
uv add polymach
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Default runtime bootstrap URL:
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
https://github.com/polymach-dev/build/releases/latest/download/index.json
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
In the normal case, users do not need a local Rust toolchain or a local checkout of the private engine repo.
|
|
46
|
+
|
|
47
|
+
## Supported Machines And Builds
|
|
48
|
+
|
|
49
|
+
The SDK bootstrap selects a runtime artifact from the signed release catalog based on:
|
|
50
|
+
|
|
51
|
+
- host platform
|
|
52
|
+
- host architecture
|
|
53
|
+
- Linux libc family
|
|
54
|
+
- x86_64 CPU level
|
|
55
|
+
|
|
56
|
+
PolyMach ships native runtime builds for each supported target class. The release process produces target-specific artifacts instead of relying on one generic cross-platform binary.
|
|
57
|
+
|
|
58
|
+
Current release targets:
|
|
59
|
+
|
|
60
|
+
- Linux `x86_64` glibc `v2`
|
|
61
|
+
Artifact id: `x86_64-unknown-linux-gnu-v2`
|
|
62
|
+
- Linux `x86_64` glibc `v3`
|
|
63
|
+
Artifact id: `x86_64-unknown-linux-gnu-v3`
|
|
64
|
+
- Linux `x86_64` glibc `v4`
|
|
65
|
+
Artifact id: `x86_64-unknown-linux-gnu-v4`
|
|
66
|
+
- Linux `aarch64` glibc generic ARM64
|
|
67
|
+
Artifact id: `aarch64-unknown-linux-gnu`
|
|
68
|
+
- macOS `aarch64` native Apple Silicon
|
|
69
|
+
Artifact id: `aarch64-apple-darwin`
|
|
70
|
+
|
|
71
|
+
Why there are multiple Linux `x86_64` artifacts:
|
|
72
|
+
|
|
73
|
+
- `v2` is the broad baseline build
|
|
74
|
+
- `v3` is compiled for AVX2-class machines
|
|
75
|
+
- `v4` is compiled for AVX-512-capable machines
|
|
76
|
+
- the SDK chooses the fastest compatible build for the current host instead of forcing every machine onto the same binary
|
|
77
|
+
|
|
78
|
+
Selection behavior:
|
|
79
|
+
|
|
80
|
+
- `v4` hosts prefer `v4`
|
|
81
|
+
- `v3` hosts prefer `v3`
|
|
82
|
+
- `v2` hosts use `v2`
|
|
83
|
+
- the SDK will not select an artifact that requires a higher CPU level than the current machine
|
|
84
|
+
- Linux `aarch64` hosts use `aarch64-unknown-linux-gnu`
|
|
85
|
+
- macOS Apple Silicon hosts use `aarch64-apple-darwin`
|
|
86
|
+
|
|
87
|
+
Operationally, that means the public release channel is intended to contain individually built native artifacts for:
|
|
88
|
+
|
|
89
|
+
- modern Linux x86_64 fleets at multiple CPU tiers
|
|
90
|
+
- Linux ARM64 systems such as Graviton
|
|
91
|
+
- Apple Silicon macOS systems
|
|
92
|
+
|
|
93
|
+
If you need to pin a specific published build instead of letting the SDK choose automatically, set:
|
|
94
|
+
|
|
95
|
+
- `POLYMACH_ARTIFACT_ID`
|
|
96
|
+
|
|
97
|
+
Example:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
export POLYMACH_ARTIFACT_ID=x86_64-unknown-linux-gnu-v2
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
If you run on a target outside the published release set, provide a compatible local runtime binary with `POLYMACH_BIN`.
|
|
104
|
+
|
|
105
|
+
## Primary Entry Point
|
|
106
|
+
|
|
107
|
+
The main entry point is `PolyMach`.
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
from polymach import PolyMach
|
|
111
|
+
|
|
112
|
+
engine = PolyMach()
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Constructor options:
|
|
116
|
+
|
|
117
|
+
- `binary`: optional explicit path to a local `polymach` runtime binary
|
|
118
|
+
- `license_path`: optional explicit path to the local machine license file
|
|
119
|
+
- `env`: optional mapping of environment variables used by the runtime and SDK
|
|
120
|
+
- `persistent`: optional override for persistent-session behavior
|
|
121
|
+
|
|
122
|
+
The instance exposes these domain clients:
|
|
123
|
+
|
|
124
|
+
- `engine.system`
|
|
125
|
+
- `engine.session`
|
|
126
|
+
- `engine.license`
|
|
127
|
+
- `engine.account`
|
|
128
|
+
- `engine.discover`
|
|
129
|
+
- `engine.market`
|
|
130
|
+
- `engine.orders`
|
|
131
|
+
- `engine.trades`
|
|
132
|
+
|
|
133
|
+
It also exposes convenience wrappers around the most important flows.
|
|
134
|
+
|
|
135
|
+
## Public State Machine
|
|
136
|
+
|
|
137
|
+
The important startup states are:
|
|
138
|
+
|
|
139
|
+
1. runtime missing
|
|
140
|
+
2. runtime available but no valid local license
|
|
141
|
+
3. payment required
|
|
142
|
+
4. payment submitted and waiting for confirmation
|
|
143
|
+
5. license activated
|
|
144
|
+
6. runtime ready for trading
|
|
145
|
+
|
|
146
|
+
The SDK presents that state machine through `start()`, `ensure_ready()`, `pay()`, and `pay_and_start()`.
|
|
147
|
+
|
|
148
|
+
### `start()`
|
|
149
|
+
|
|
150
|
+
`start()` is the recommended first-run flow.
|
|
151
|
+
|
|
152
|
+
Behavior:
|
|
153
|
+
|
|
154
|
+
- if a valid local license already exists, it returns `StartResult(ready=True, ...)`
|
|
155
|
+
- if no valid license exists and no `tx_hash` is provided, it returns `StartResult(ready=False, payment=...)`
|
|
156
|
+
- if `tx_hash` is provided, it activates the machine license and returns `StartResult(ready=True, activation=...)`
|
|
157
|
+
|
|
158
|
+
### `ensure_ready()`
|
|
159
|
+
|
|
160
|
+
`ensure_ready()` is the same as `start()`, except it raises `PaymentRequiredError` instead of returning `ready=False`.
|
|
161
|
+
|
|
162
|
+
Use it when your agent wants exception-driven control flow.
|
|
163
|
+
|
|
164
|
+
### `pay()`
|
|
165
|
+
|
|
166
|
+
`pay()` fetches a machine-specific payment request and submits the on-chain Polygon payment.
|
|
167
|
+
|
|
168
|
+
It returns `SubmittedPayment`.
|
|
169
|
+
|
|
170
|
+
### `pay_and_start()`
|
|
171
|
+
|
|
172
|
+
`pay_and_start()` is the one-shot path.
|
|
173
|
+
|
|
174
|
+
Behavior:
|
|
175
|
+
|
|
176
|
+
- if a valid license already exists, it starts normally
|
|
177
|
+
- if `tx_hash` is provided, it resumes activation without submitting a new payment
|
|
178
|
+
- otherwise it submits the payment, activates the license, and optionally prewarms the runtime
|
|
179
|
+
|
|
180
|
+
This is the shortest path for fully automated agents.
|
|
181
|
+
|
|
182
|
+
## Core Startup Flows
|
|
183
|
+
|
|
184
|
+
## Flow 1: Manual Payment Then Activate
|
|
185
|
+
|
|
186
|
+
This is the safest and most explicit first-run path.
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
from polymach import PolyMach
|
|
190
|
+
|
|
191
|
+
engine = PolyMach()
|
|
192
|
+
|
|
193
|
+
start = engine.start()
|
|
194
|
+
if not start.ready:
|
|
195
|
+
payment = start.payment
|
|
196
|
+
print(payment.amount_display)
|
|
197
|
+
print(payment.token_symbol)
|
|
198
|
+
print(payment.token_address)
|
|
199
|
+
print(payment.chain_id)
|
|
200
|
+
raise SystemExit("Send that exact Polygon payment, then call start(tx_hash=...)")
|
|
201
|
+
|
|
202
|
+
start = engine.start(tx_hash="0xYOUR_PAYMENT_TX_HASH")
|
|
203
|
+
print(start.ready)
|
|
204
|
+
print(start.license_status.valid)
|
|
205
|
+
print(start.activation.tx_hash if start.activation else None)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
What to inspect when `ready=False`:
|
|
209
|
+
|
|
210
|
+
- `start.payment.amount_display`
|
|
211
|
+
- `start.payment.token_symbol`
|
|
212
|
+
- `start.payment.token_address`
|
|
213
|
+
- `start.payment.chain_id`
|
|
214
|
+
- `start.payment.min_confirmations`
|
|
215
|
+
|
|
216
|
+
Important:
|
|
217
|
+
|
|
218
|
+
- the payment request is machine-specific
|
|
219
|
+
- do not reuse a payment request from another machine
|
|
220
|
+
- follow the exact payment instructions returned by the SDK
|
|
221
|
+
|
|
222
|
+
## Flow 2: Exception-Driven Manual Payment
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
from polymach import PaymentRequiredError, PolyMach
|
|
226
|
+
|
|
227
|
+
engine = PolyMach()
|
|
228
|
+
|
|
229
|
+
try:
|
|
230
|
+
engine.ensure_ready()
|
|
231
|
+
except PaymentRequiredError as exc:
|
|
232
|
+
payment = exc.payment
|
|
233
|
+
print(exc.message)
|
|
234
|
+
print(payment.amount_display, payment.token_symbol)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
This is useful when your orchestrator already handles retries and exceptions centrally.
|
|
238
|
+
|
|
239
|
+
## Flow 3: Automated Payment
|
|
240
|
+
|
|
241
|
+
`pay()` submits the Polygon payment directly.
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
from polymach import LocalAccountSigner, PolyMach
|
|
245
|
+
|
|
246
|
+
engine = PolyMach()
|
|
247
|
+
signer = LocalAccountSigner.from_private_key("0xYOUR_PAYMENT_WALLET_PRIVATE_KEY")
|
|
248
|
+
|
|
249
|
+
submitted = engine.pay(
|
|
250
|
+
signer=signer,
|
|
251
|
+
cycles=1,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
print(submitted.tx_hash)
|
|
255
|
+
print(submitted.sender_address)
|
|
256
|
+
print(submitted.amount_display)
|
|
257
|
+
print(submitted.confirmations)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
The payment wallet must hold:
|
|
261
|
+
|
|
262
|
+
- the required payment token on Polygon
|
|
263
|
+
- a small amount of POL for gas
|
|
264
|
+
|
|
265
|
+
## Flow 4: Automated Payment And Start
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
from polymach import LocalAccountSigner, PaymentPendingError, PolyMach
|
|
269
|
+
|
|
270
|
+
engine = PolyMach()
|
|
271
|
+
signer = LocalAccountSigner.from_private_key("0xYOUR_PAYMENT_WALLET_PRIVATE_KEY")
|
|
272
|
+
|
|
273
|
+
try:
|
|
274
|
+
start = engine.pay_and_start(
|
|
275
|
+
signer=signer,
|
|
276
|
+
prewarm_tokens=["TOKEN_ID"],
|
|
277
|
+
)
|
|
278
|
+
except PaymentPendingError as exc:
|
|
279
|
+
start = engine.start(tx_hash=exc.tx_hash)
|
|
280
|
+
|
|
281
|
+
print(start.ready)
|
|
282
|
+
print(start.license_status.valid)
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
This is the preferred path for agents that are allowed to control the payment wallet directly.
|
|
286
|
+
|
|
287
|
+
## Flow 5: Resume After A Broadcast But Slow Confirmation
|
|
288
|
+
|
|
289
|
+
If the payment transaction is broadcast but confirmation polling times out, the SDK raises `PaymentPendingError`.
|
|
290
|
+
|
|
291
|
+
Useful fields:
|
|
292
|
+
|
|
293
|
+
- `exc.tx_hash`
|
|
294
|
+
- `exc.confirmations`
|
|
295
|
+
- `exc.required_confirmations`
|
|
296
|
+
- `exc.attempted_tx_hashes`
|
|
297
|
+
|
|
298
|
+
Recovery path:
|
|
299
|
+
|
|
300
|
+
```python
|
|
301
|
+
from polymach import LocalAccountSigner, PaymentPendingError, PolyMach
|
|
302
|
+
|
|
303
|
+
engine = PolyMach()
|
|
304
|
+
signer = LocalAccountSigner.from_private_key("0xYOUR_PAYMENT_WALLET_PRIVATE_KEY")
|
|
305
|
+
|
|
306
|
+
try:
|
|
307
|
+
start = engine.pay_and_start(signer=signer)
|
|
308
|
+
except PaymentPendingError as exc:
|
|
309
|
+
# wait, monitor, or persist the tx hash externally
|
|
310
|
+
start = engine.pay_and_start(tx_hash=exc.tx_hash)
|
|
311
|
+
|
|
312
|
+
print(start.ready)
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## Cold Start, Warm Start, And Bootstrap
|
|
316
|
+
|
|
317
|
+
On `PolyMach()` creation, the runtime is not necessarily started yet.
|
|
318
|
+
|
|
319
|
+
The first meaningful command will:
|
|
320
|
+
|
|
321
|
+
- look for an explicitly configured binary
|
|
322
|
+
- look for package, repo, or `PATH` binaries unless those paths are disabled
|
|
323
|
+
- if no local binary is suitable, download the correct artifact from the signed release catalog
|
|
324
|
+
- cache the runtime locally
|
|
325
|
+
|
|
326
|
+
Relevant bootstrap environment variables:
|
|
327
|
+
|
|
328
|
+
- `POLYMACH_RELEASE_INDEX_URL`
|
|
329
|
+
- `POLYMACH_RELEASE_BASE_URL`
|
|
330
|
+
- `POLYMACH_RELEASE_PUBLIC_KEY`
|
|
331
|
+
- `POLYMACH_RELEASE_TOKEN`
|
|
332
|
+
- `POLYMACH_ARTIFACT_ID`
|
|
333
|
+
- `POLYMACH_BIN`
|
|
334
|
+
- `POLYMACH_CACHE_DIR`
|
|
335
|
+
|
|
336
|
+
Most users do not need to set any of these.
|
|
337
|
+
|
|
338
|
+
## Prewarming And Session Management
|
|
339
|
+
|
|
340
|
+
The runtime can prewarm token-specific state for lower latency.
|
|
341
|
+
|
|
342
|
+
```python
|
|
343
|
+
from polymach import PolyMach
|
|
344
|
+
|
|
345
|
+
engine = PolyMach()
|
|
346
|
+
engine.ensure_ready(tx_hash="0xYOUR_PAYMENT_TX_HASH")
|
|
347
|
+
|
|
348
|
+
prewarm = engine.prewarm(
|
|
349
|
+
["TOKEN_ID_1", "TOKEN_ID_2"],
|
|
350
|
+
quotes=True,
|
|
351
|
+
order_metadata=True,
|
|
352
|
+
last_trade=True,
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
print(prewarm)
|
|
356
|
+
print(engine.session_status())
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Why prewarm:
|
|
360
|
+
|
|
361
|
+
- hot quote path
|
|
362
|
+
- hot midpoint path
|
|
363
|
+
- cached order metadata
|
|
364
|
+
- lower latency before the first live order
|
|
365
|
+
|
|
366
|
+
If your agent targets a small set of active markets, prewarm them before entering the main trading loop.
|
|
367
|
+
|
|
368
|
+
## Market Data
|
|
369
|
+
|
|
370
|
+
### Midpoint
|
|
371
|
+
|
|
372
|
+
```python
|
|
373
|
+
mid = engine.midpoint("TOKEN_ID")
|
|
374
|
+
print(mid.token_id, mid.midpoint)
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
Returned type:
|
|
378
|
+
|
|
379
|
+
- `MidpointResponse`
|
|
380
|
+
|
|
381
|
+
Fields:
|
|
382
|
+
|
|
383
|
+
- `token_id`
|
|
384
|
+
- `midpoint`
|
|
385
|
+
|
|
386
|
+
### Quote
|
|
387
|
+
|
|
388
|
+
```python
|
|
389
|
+
quote = engine.quote("TOKEN_ID")
|
|
390
|
+
print(quote.midpoint, quote.bid, quote.ask)
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
Returned type:
|
|
394
|
+
|
|
395
|
+
- `MarketQuote`
|
|
396
|
+
|
|
397
|
+
Fields:
|
|
398
|
+
|
|
399
|
+
- `midpoint`
|
|
400
|
+
- `bid`
|
|
401
|
+
- `ask`
|
|
402
|
+
|
|
403
|
+
### Streaming Quotes
|
|
404
|
+
|
|
405
|
+
```python
|
|
406
|
+
for snapshot in engine.stream_quotes(
|
|
407
|
+
["TOKEN_ID"],
|
|
408
|
+
interval_ms=100,
|
|
409
|
+
include_last_trade=True,
|
|
410
|
+
count=5,
|
|
411
|
+
):
|
|
412
|
+
print(snapshot.timestamp)
|
|
413
|
+
for q in snapshot.quotes:
|
|
414
|
+
print(q.token_id, q.midpoint, q.bid, q.ask, q.last_trade)
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Returned stream item type:
|
|
418
|
+
|
|
419
|
+
- `QuoteStreamSnapshot`
|
|
420
|
+
|
|
421
|
+
## Discovery
|
|
422
|
+
|
|
423
|
+
### Search Markets
|
|
424
|
+
|
|
425
|
+
```python
|
|
426
|
+
markets = engine.discover_markets(
|
|
427
|
+
query="btc 5m",
|
|
428
|
+
limit=10,
|
|
429
|
+
with_live=True,
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
for market in markets:
|
|
433
|
+
print(market.slug, market.question)
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### Get A Single Market
|
|
437
|
+
|
|
438
|
+
```python
|
|
439
|
+
market = engine.discover_market(slug="btc-updown-5m-...")
|
|
440
|
+
print(market.slug)
|
|
441
|
+
print(market.question)
|
|
442
|
+
for outcome in market.outcomes:
|
|
443
|
+
print(outcome.outcome, outcome.token_id)
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### Search Across Entities
|
|
447
|
+
|
|
448
|
+
```python
|
|
449
|
+
results = engine.search(
|
|
450
|
+
"btc",
|
|
451
|
+
limit=10,
|
|
452
|
+
include_profiles=False,
|
|
453
|
+
with_live=True,
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
print(len(results.markets), len(results.events))
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
Relevant return types:
|
|
460
|
+
|
|
461
|
+
- `DiscoveryMarket`
|
|
462
|
+
- `DiscoveryEvent`
|
|
463
|
+
- `DiscoverySearchResults`
|
|
464
|
+
|
|
465
|
+
Use `with_live=True` when you want live quote fields embedded into discovery results.
|
|
466
|
+
|
|
467
|
+
## Account And Portfolio
|
|
468
|
+
|
|
469
|
+
### Healthcheck
|
|
470
|
+
|
|
471
|
+
```python
|
|
472
|
+
health = engine.healthcheck(token_id="TOKEN_ID")
|
|
473
|
+
print(health.collateral_balance_usdc)
|
|
474
|
+
print(health.token_balance)
|
|
475
|
+
print(health.midpoint)
|
|
476
|
+
print(health.bid, health.ask)
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
This is the best compact account-state call when you want balances plus current market context.
|
|
480
|
+
|
|
481
|
+
### Portfolio
|
|
482
|
+
|
|
483
|
+
```python
|
|
484
|
+
portfolio = engine.portfolio("TOKEN_ID")
|
|
485
|
+
print(portfolio.token_id)
|
|
486
|
+
print(portfolio.position_qty)
|
|
487
|
+
print(portfolio.avg_entry_price)
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
## Orders
|
|
491
|
+
|
|
492
|
+
Live order methods require the live-trading environment to be configured.
|
|
493
|
+
|
|
494
|
+
### Limit Order
|
|
495
|
+
|
|
496
|
+
```python
|
|
497
|
+
receipt = engine.post_limit(
|
|
498
|
+
"buy",
|
|
499
|
+
"TOKEN_ID",
|
|
500
|
+
5.0,
|
|
501
|
+
0.01,
|
|
502
|
+
order_type="GTC",
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
print(receipt.order_id)
|
|
506
|
+
print(receipt.qty, receipt.limit_price)
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
Returned type:
|
|
510
|
+
|
|
511
|
+
- `LimitOrderReceipt`
|
|
512
|
+
|
|
513
|
+
### Timed Limit Order
|
|
514
|
+
|
|
515
|
+
```python
|
|
516
|
+
timed = engine.post_limit(
|
|
517
|
+
"buy",
|
|
518
|
+
"TOKEN_ID",
|
|
519
|
+
5.0,
|
|
520
|
+
0.01,
|
|
521
|
+
order_type="GTC",
|
|
522
|
+
timed=True,
|
|
523
|
+
)
|
|
524
|
+
|
|
525
|
+
print(timed.receipt.order_id)
|
|
526
|
+
print(timed.timing.total_seconds)
|
|
527
|
+
print(timed.timing.http_seconds)
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
Returned type:
|
|
531
|
+
|
|
532
|
+
- `TimedLimitResponse`
|
|
533
|
+
|
|
534
|
+
Use `timed=True` when the agent wants latency breakdowns for submissions.
|
|
535
|
+
|
|
536
|
+
### Market Buy
|
|
537
|
+
|
|
538
|
+
```python
|
|
539
|
+
fill = engine.market_buy("TOKEN_ID", 25.0)
|
|
540
|
+
print(fill.order_id, fill.qty, fill.fill_price)
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
### Market Sell
|
|
544
|
+
|
|
545
|
+
```python
|
|
546
|
+
fill = engine.market_sell("TOKEN_ID", 5.0)
|
|
547
|
+
print(fill.order_id, fill.qty, fill.fill_price)
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### Inspect And Cancel Orders
|
|
551
|
+
|
|
552
|
+
```python
|
|
553
|
+
orders = engine.list_orders()
|
|
554
|
+
for order in orders:
|
|
555
|
+
print(order.order_id, order.status, order.remaining_size)
|
|
556
|
+
|
|
557
|
+
if orders:
|
|
558
|
+
result = engine.cancel_order(orders[0].order_id)
|
|
559
|
+
print(result.order_id, result.canceled)
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
Also available:
|
|
563
|
+
|
|
564
|
+
- `engine.get_order(order_id)`
|
|
565
|
+
- `engine.cancel_open_orders(token_id)`
|
|
566
|
+
- `engine.cancel_all_open_orders()`
|
|
567
|
+
|
|
568
|
+
## Trades
|
|
569
|
+
|
|
570
|
+
```python
|
|
571
|
+
page = engine.list_trades()
|
|
572
|
+
for trade in page.trades:
|
|
573
|
+
print(trade.trade_id, trade.side, trade.price, trade.size)
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
You can also fetch a single trade:
|
|
577
|
+
|
|
578
|
+
```python
|
|
579
|
+
trade = engine.get_trade("TRADE_ID")
|
|
580
|
+
print(trade)
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
## License And Machine Identity
|
|
584
|
+
|
|
585
|
+
### Machine Fingerprint
|
|
586
|
+
|
|
587
|
+
```python
|
|
588
|
+
fingerprint = engine.fingerprint()
|
|
589
|
+
print(fingerprint.fingerprint)
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
Use this if you need to reason explicitly about machine-bound licensing.
|
|
593
|
+
|
|
594
|
+
### License Status
|
|
595
|
+
|
|
596
|
+
```python
|
|
597
|
+
status = engine.license_status()
|
|
598
|
+
print(status.valid, status.message)
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
### Verify The Installed License
|
|
602
|
+
|
|
603
|
+
```python
|
|
604
|
+
claims = engine.verify_license()
|
|
605
|
+
print(claims.license_id)
|
|
606
|
+
print(claims.machine_fingerprint)
|
|
607
|
+
print(claims.expires_at)
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
### Install An Existing License File
|
|
611
|
+
|
|
612
|
+
```python
|
|
613
|
+
installed_path = engine.install_license("/path/to/license.json")
|
|
614
|
+
print(installed_path)
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
## Pricing And Payment Requests
|
|
618
|
+
|
|
619
|
+
### Read Catalog Pricing
|
|
620
|
+
|
|
621
|
+
```python
|
|
622
|
+
catalog = engine.pricing()
|
|
623
|
+
print(catalog.cycle_price_usdc)
|
|
624
|
+
print(catalog.token_address)
|
|
625
|
+
print(catalog.min_confirmations)
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
### Get The Exact Machine-Specific Payment Request
|
|
629
|
+
|
|
630
|
+
```python
|
|
631
|
+
payment = engine.payment_request(cycles=2)
|
|
632
|
+
print(payment.machine_fingerprint)
|
|
633
|
+
print(payment.amount_display)
|
|
634
|
+
print(payment.amount_raw)
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
This is the object that should drive manual or automated payment execution.
|
|
638
|
+
|
|
639
|
+
## Live Trading Environment
|
|
640
|
+
|
|
641
|
+
The payment wallet and the trading wallet are different concerns.
|
|
642
|
+
|
|
643
|
+
Venue availability is a separate concern from SDK install, runtime bootstrap,
|
|
644
|
+
payment, and license activation. Those steps can succeed while Polymarket still
|
|
645
|
+
refuses live trading from the current IP address, network, or jurisdiction.
|
|
646
|
+
Before depending on live order flow, confirm venue access from the actual host
|
|
647
|
+
that will run the strategy.
|
|
648
|
+
|
|
649
|
+
### Payment Wallet
|
|
650
|
+
|
|
651
|
+
Used by:
|
|
652
|
+
|
|
653
|
+
- `pay()`
|
|
654
|
+
- `pay_and_start()` when the SDK submits the payment
|
|
655
|
+
|
|
656
|
+
Relevant inputs:
|
|
657
|
+
|
|
658
|
+
- `signer=LocalAccountSigner.from_private_key(...)`
|
|
659
|
+
- `private_key=...`
|
|
660
|
+
- `POLYMACH_WALLET_PRIVATE_KEY` as a fallback
|
|
661
|
+
|
|
662
|
+
### Trading Wallet
|
|
663
|
+
|
|
664
|
+
Used by the runtime for actual Polymarket trading.
|
|
665
|
+
|
|
666
|
+
Relevant environment variables:
|
|
667
|
+
|
|
668
|
+
- `POLYMACH_LIVE_ENABLED=true`
|
|
669
|
+
- `POLYMACH_LIVE_ACK=I_UNDERSTAND_REAL_MONEY_RISK`
|
|
670
|
+
- `POLYMACH_POLYMARKET_PRIVATE_KEY=0x...`
|
|
671
|
+
- `POLYMACH_POLYMARKET_SIGNATURE_TYPE=0`
|
|
672
|
+
|
|
673
|
+
`POLYMACH_POLYMARKET_SIGNATURE_TYPE` values:
|
|
674
|
+
|
|
675
|
+
- `0` = EOA
|
|
676
|
+
- `1` = proxy
|
|
677
|
+
- `2` = Gnosis Safe
|
|
678
|
+
|
|
679
|
+
If the signer and funded trading address differ, also set:
|
|
680
|
+
|
|
681
|
+
- `POLYMACH_POLYMARKET_FUNDER`
|
|
682
|
+
|
|
683
|
+
If you use Polymarket API credentials, set all three:
|
|
684
|
+
|
|
685
|
+
- `POLYMACH_POLYMARKET_API_KEY`
|
|
686
|
+
- `POLYMACH_POLYMARKET_API_SECRET`
|
|
687
|
+
- `POLYMACH_POLYMARKET_API_PASSPHRASE`
|
|
688
|
+
|
|
689
|
+
Minimal live-trading shell setup:
|
|
690
|
+
|
|
691
|
+
```bash
|
|
692
|
+
export POLYMACH_LIVE_ENABLED=true
|
|
693
|
+
export POLYMACH_LIVE_ACK=I_UNDERSTAND_REAL_MONEY_RISK
|
|
694
|
+
export POLYMACH_POLYMARKET_PRIVATE_KEY=0xYOUR_TRADING_WALLET_PRIVATE_KEY
|
|
695
|
+
export POLYMACH_POLYMARKET_SIGNATURE_TYPE=0
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
## Payment Configuration
|
|
699
|
+
|
|
700
|
+
Relevant payment environment variables:
|
|
701
|
+
|
|
702
|
+
- `POLYMACH_LICENSE_BASE_URL`
|
|
703
|
+
- `POLYMACH_LICENSE_TOKEN`
|
|
704
|
+
- `POLYMACH_POLYGON_RPC_URL`
|
|
705
|
+
- `POLYMACH_POLYGON_RPC_URLS`
|
|
706
|
+
- `POLYMACH_WALLET_PRIVATE_KEY`
|
|
707
|
+
- `POLYMACH_PAYMENT_GAS_LIMIT`
|
|
708
|
+
- `POLYMACH_PAYMENT_GAS_PRICE_WEI`
|
|
709
|
+
- `POLYMACH_PAYMENT_REPLACEMENT_ATTEMPTS`
|
|
710
|
+
- `POLYMACH_PAYMENT_REPLACEMENT_GAS_BUMP_PERCENT`
|
|
711
|
+
|
|
712
|
+
Default Polygon RPC fallback order:
|
|
713
|
+
|
|
714
|
+
```text
|
|
715
|
+
https://polygon.publicnode.com
|
|
716
|
+
https://1rpc.io/matic
|
|
717
|
+
https://polygon-rpc.com
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
### Automated Payment With Replacement Controls
|
|
721
|
+
|
|
722
|
+
```python
|
|
723
|
+
from polymach import LocalAccountSigner, PolyMach
|
|
724
|
+
|
|
725
|
+
engine = PolyMach()
|
|
726
|
+
signer = LocalAccountSigner.from_private_key("0xYOUR_PAYMENT_WALLET_PRIVATE_KEY")
|
|
727
|
+
|
|
728
|
+
submitted = engine.pay(
|
|
729
|
+
signer=signer,
|
|
730
|
+
replacement_attempts=2,
|
|
731
|
+
replacement_gas_bump_percent=25,
|
|
732
|
+
timeout_seconds=180.0,
|
|
733
|
+
)
|
|
734
|
+
|
|
735
|
+
print(submitted.tx_hash)
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
Use this if you want limited same-nonce replacement attempts for pending payment transactions.
|
|
739
|
+
|
|
740
|
+
## Recommended Agent Recipes
|
|
741
|
+
|
|
742
|
+
## Recipe 1: Minimal Install Then Manual Payment
|
|
743
|
+
|
|
744
|
+
```python
|
|
745
|
+
from polymach import PolyMach
|
|
746
|
+
|
|
747
|
+
engine = PolyMach()
|
|
748
|
+
result = engine.start()
|
|
749
|
+
|
|
750
|
+
if result.ready:
|
|
751
|
+
print("ready")
|
|
752
|
+
else:
|
|
753
|
+
print(result.payment.token_address)
|
|
754
|
+
print(result.payment.amount_display)
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
## Recipe 2: Automated Agent Startup
|
|
758
|
+
|
|
759
|
+
```python
|
|
760
|
+
from polymach import LocalAccountSigner, PaymentPendingError, PolyMach
|
|
761
|
+
|
|
762
|
+
engine = PolyMach()
|
|
763
|
+
payment_signer = LocalAccountSigner.from_private_key("0xPAYMENT_WALLET_KEY")
|
|
764
|
+
|
|
765
|
+
try:
|
|
766
|
+
start = engine.pay_and_start(
|
|
767
|
+
signer=payment_signer,
|
|
768
|
+
prewarm_tokens=["TOKEN_ID"],
|
|
769
|
+
prewarm_last_trade=True,
|
|
770
|
+
)
|
|
771
|
+
except PaymentPendingError as exc:
|
|
772
|
+
start = engine.start(tx_hash=exc.tx_hash)
|
|
773
|
+
|
|
774
|
+
quote = engine.quote("TOKEN_ID")
|
|
775
|
+
print(quote.midpoint)
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
## Recipe 3: Discover Then Trade
|
|
779
|
+
|
|
780
|
+
```python
|
|
781
|
+
from polymach import PolyMach
|
|
782
|
+
|
|
783
|
+
engine = PolyMach()
|
|
784
|
+
engine.ensure_ready(tx_hash="0xYOUR_PAYMENT_TX_HASH")
|
|
785
|
+
|
|
786
|
+
market = engine.discover_market(slug="btc-updown-5m-...")
|
|
787
|
+
token_id = market.outcomes[0].token_id
|
|
788
|
+
|
|
789
|
+
engine.prewarm([token_id], last_trade=True)
|
|
790
|
+
quote = engine.quote(token_id)
|
|
791
|
+
|
|
792
|
+
if quote.ask is not None and quote.ask <= 0.10:
|
|
793
|
+
receipt = engine.post_limit("buy", token_id, 5.0, 0.10)
|
|
794
|
+
print(receipt.order_id)
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
## Recipe 4: Measure Order Submission Time
|
|
798
|
+
|
|
799
|
+
```python
|
|
800
|
+
timed = engine.post_limit(
|
|
801
|
+
"buy",
|
|
802
|
+
"TOKEN_ID",
|
|
803
|
+
5.0,
|
|
804
|
+
0.01,
|
|
805
|
+
timed=True,
|
|
806
|
+
)
|
|
807
|
+
|
|
808
|
+
print(timed.timing.total_seconds)
|
|
809
|
+
print(timed.timing.http_seconds)
|
|
810
|
+
print(timed.timing.build_sign_seconds)
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
## Common Errors
|
|
814
|
+
|
|
815
|
+
### `PaymentRequiredError`
|
|
816
|
+
|
|
817
|
+
Raised by:
|
|
818
|
+
|
|
819
|
+
- `ensure_ready()`
|
|
820
|
+
|
|
821
|
+
Meaning:
|
|
822
|
+
|
|
823
|
+
- no valid local license exists yet
|
|
824
|
+
- payment instructions are attached on `.payment`
|
|
825
|
+
|
|
826
|
+
### `PaymentPendingError`
|
|
827
|
+
|
|
828
|
+
Raised by:
|
|
829
|
+
|
|
830
|
+
- `pay()`
|
|
831
|
+
- `pay_and_start()`
|
|
832
|
+
|
|
833
|
+
Meaning:
|
|
834
|
+
|
|
835
|
+
- a payment transaction was broadcast or attempted
|
|
836
|
+
- confirmation polling did not finish cleanly before timeout
|
|
837
|
+
- the caller should inspect `.tx_hash` and possibly `.attempted_tx_hashes`
|
|
838
|
+
|
|
839
|
+
### `BinaryNotFoundError`
|
|
840
|
+
|
|
841
|
+
Meaning:
|
|
842
|
+
|
|
843
|
+
- the SDK could not find a usable local runtime binary
|
|
844
|
+
- and auto-download was disabled or failed before a valid binary was installed
|
|
845
|
+
|
|
846
|
+
### `CommandError`
|
|
847
|
+
|
|
848
|
+
Meaning:
|
|
849
|
+
|
|
850
|
+
- the local runtime command failed
|
|
851
|
+
|
|
852
|
+
Useful fields:
|
|
853
|
+
|
|
854
|
+
- `.command`
|
|
855
|
+
- `.exit_code`
|
|
856
|
+
- `.stdout`
|
|
857
|
+
- `.stderr`
|
|
858
|
+
|
|
859
|
+
### `PolyMachError`
|
|
860
|
+
|
|
861
|
+
Base runtime error class for SDK-level failures.
|
|
862
|
+
|
|
863
|
+
## Security And Operational Notes
|
|
864
|
+
|
|
865
|
+
- Prefer a dedicated low-balance payment wallet for automated subscription payments.
|
|
866
|
+
- Keep payment-wallet and trading-wallet responsibilities separate unless you have a reason not to.
|
|
867
|
+
- Keep private keys out of shell history, logs, prompts, and chat transcripts.
|
|
868
|
+
- Prewarm active token ids before latency-sensitive trading.
|
|
869
|
+
- Persist `PaymentPendingError.tx_hash` externally if your agent needs reliable restart recovery.
|
|
870
|
+
- Use `timed=True` on limit orders when you want latency telemetry for benchmarking or control logic.
|
|
871
|
+
|
|
872
|
+
## Legal
|
|
873
|
+
|
|
874
|
+
- MIT license: [`LICENSE`](LICENSE)
|
|
875
|
+
- legal disclaimer: [`LEGAL.md`](LEGAL.md)
|