worm-sdk 0.9.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.
- worm_sdk-0.9.0/.github/workflows/release.yml +44 -0
- worm_sdk-0.9.0/.gitignore +22 -0
- worm_sdk-0.9.0/LICENSE +21 -0
- worm_sdk-0.9.0/Makefile +10 -0
- worm_sdk-0.9.0/PKG-INFO +218 -0
- worm_sdk-0.9.0/README.md +183 -0
- worm_sdk-0.9.0/examples/account.py +36 -0
- worm_sdk-0.9.0/examples/api_keys.py +24 -0
- worm_sdk-0.9.0/examples/margin.py +82 -0
- worm_sdk-0.9.0/examples/margin_settlements.py +49 -0
- worm_sdk-0.9.0/examples/markets.py +77 -0
- worm_sdk-0.9.0/examples/orders.py +48 -0
- worm_sdk-0.9.0/examples/quickstart.py +28 -0
- worm_sdk-0.9.0/examples/redeems.py +37 -0
- worm_sdk-0.9.0/examples/trades.py +30 -0
- worm_sdk-0.9.0/pyproject.toml +64 -0
- worm_sdk-0.9.0/src/worm_sdk/__init__.py +62 -0
- worm_sdk-0.9.0/src/worm_sdk/_version.py +1 -0
- worm_sdk-0.9.0/src/worm_sdk/api/__init__.py +23 -0
- worm_sdk-0.9.0/src/worm_sdk/api/account.py +76 -0
- worm_sdk-0.9.0/src/worm_sdk/api/api_keys.py +29 -0
- worm_sdk-0.9.0/src/worm_sdk/api/events.py +69 -0
- worm_sdk-0.9.0/src/worm_sdk/api/margin.py +388 -0
- worm_sdk-0.9.0/src/worm_sdk/api/markets.py +215 -0
- worm_sdk-0.9.0/src/worm_sdk/api/orders.py +185 -0
- worm_sdk-0.9.0/src/worm_sdk/api/redeems.py +102 -0
- worm_sdk-0.9.0/src/worm_sdk/api/search.py +93 -0
- worm_sdk-0.9.0/src/worm_sdk/api/sports.py +24 -0
- worm_sdk-0.9.0/src/worm_sdk/api/trades.py +44 -0
- worm_sdk-0.9.0/src/worm_sdk/auth/__init__.py +4 -0
- worm_sdk-0.9.0/src/worm_sdk/auth/bootstrap.py +80 -0
- worm_sdk-0.9.0/src/worm_sdk/auth/signer.py +103 -0
- worm_sdk-0.9.0/src/worm_sdk/client.py +116 -0
- worm_sdk-0.9.0/src/worm_sdk/constants.py +64 -0
- worm_sdk-0.9.0/src/worm_sdk/exceptions.py +53 -0
- worm_sdk-0.9.0/src/worm_sdk/http.py +236 -0
- worm_sdk-0.9.0/src/worm_sdk/py.typed +0 -0
- worm_sdk-0.9.0/src/worm_sdk/types/__init__.py +149 -0
- worm_sdk-0.9.0/src/worm_sdk/types/account.py +77 -0
- worm_sdk-0.9.0/src/worm_sdk/types/auth.py +34 -0
- worm_sdk-0.9.0/src/worm_sdk/types/common.py +43 -0
- worm_sdk-0.9.0/src/worm_sdk/types/enums.py +165 -0
- worm_sdk-0.9.0/src/worm_sdk/types/event.py +29 -0
- worm_sdk-0.9.0/src/worm_sdk/types/margin.py +117 -0
- worm_sdk-0.9.0/src/worm_sdk/types/market.py +219 -0
- worm_sdk-0.9.0/src/worm_sdk/types/order.py +60 -0
- worm_sdk-0.9.0/src/worm_sdk/types/redeem.py +30 -0
- worm_sdk-0.9.0/src/worm_sdk/types/search.py +30 -0
- worm_sdk-0.9.0/src/worm_sdk/types/sports.py +24 -0
- worm_sdk-0.9.0/src/worm_sdk/types/trade.py +23 -0
- worm_sdk-0.9.0/src/worm_sdk/wire.py +31 -0
- worm_sdk-0.9.0/tests/conftest.py +74 -0
- worm_sdk-0.9.0/tests/test_account.py +96 -0
- worm_sdk-0.9.0/tests/test_auth.py +118 -0
- worm_sdk-0.9.0/tests/test_events.py +78 -0
- worm_sdk-0.9.0/tests/test_http.py +227 -0
- worm_sdk-0.9.0/tests/test_margin.py +260 -0
- worm_sdk-0.9.0/tests/test_markets.py +318 -0
- worm_sdk-0.9.0/tests/test_orders.py +127 -0
- worm_sdk-0.9.0/tests/test_redeems.py +76 -0
- worm_sdk-0.9.0/tests/test_search.py +130 -0
- worm_sdk-0.9.0/tests/test_sports.py +34 -0
- worm_sdk-0.9.0/tests/test_trades.py +56 -0
- worm_sdk-0.9.0/tests/test_wire.py +41 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build:
|
|
9
|
+
name: Build distribution
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.x"
|
|
16
|
+
- name: Build sdist and wheel
|
|
17
|
+
run: |
|
|
18
|
+
python -m pip install --upgrade build
|
|
19
|
+
python -m build
|
|
20
|
+
- name: Validate metadata
|
|
21
|
+
run: |
|
|
22
|
+
python -m pip install --upgrade twine
|
|
23
|
+
python -m twine check dist/*
|
|
24
|
+
- uses: actions/upload-artifact@v4
|
|
25
|
+
with:
|
|
26
|
+
name: dist
|
|
27
|
+
path: dist/
|
|
28
|
+
|
|
29
|
+
publish:
|
|
30
|
+
name: Publish to PyPI
|
|
31
|
+
needs: build
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
environment:
|
|
34
|
+
name: pypi
|
|
35
|
+
url: https://pypi.org/p/worm-sdk
|
|
36
|
+
permissions:
|
|
37
|
+
id-token: write
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/download-artifact@v4
|
|
40
|
+
with:
|
|
41
|
+
name: dist
|
|
42
|
+
path: dist/
|
|
43
|
+
- name: Publish
|
|
44
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
.venv/
|
|
2
|
+
venv/
|
|
3
|
+
.pip-cache/
|
|
4
|
+
__pycache__
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
*.egg-info/
|
|
8
|
+
.pypirc
|
|
9
|
+
__MACOSX/
|
|
10
|
+
.idea/
|
|
11
|
+
.vscode/
|
|
12
|
+
.pytest_cache/
|
|
13
|
+
.ruff_cache/
|
|
14
|
+
.mypy_cache/
|
|
15
|
+
.DS_Store
|
|
16
|
+
.env
|
|
17
|
+
.env.local
|
|
18
|
+
.env.development.local
|
|
19
|
+
.env.test.local
|
|
20
|
+
.env.production.local
|
|
21
|
+
|
|
22
|
+
local
|
worm_sdk-0.9.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Worm
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
worm_sdk-0.9.0/Makefile
ADDED
worm_sdk-0.9.0/PKG-INFO
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: worm-sdk
|
|
3
|
+
Version: 0.9.0
|
|
4
|
+
Summary: Python SDK for the Worm prediction markets platform
|
|
5
|
+
Project-URL: Homepage, https://worm.wtf
|
|
6
|
+
Project-URL: Documentation, https://docs.worm.wtf
|
|
7
|
+
Project-URL: Repository, https://github.com/wormwtf/worm-sdk
|
|
8
|
+
Project-URL: Issues, https://github.com/wormwtf/worm-sdk/issues
|
|
9
|
+
Author-email: Worm <support@worm.wtf>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: api-client,prediction-markets,sdk,solana,worm
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: httpx[http2]>=0.25
|
|
25
|
+
Requires-Dist: pydantic>=2.4.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
30
|
+
Requires-Dist: ruff>=0.1; extra == 'dev'
|
|
31
|
+
Provides-Extra: signing
|
|
32
|
+
Requires-Dist: base58>=2.1; extra == 'signing'
|
|
33
|
+
Requires-Dist: solders>=0.21; extra == 'signing'
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# worm-sdk
|
|
37
|
+
|
|
38
|
+
Official Python client for the [Worm](https://worm.wtf) prediction markets API.
|
|
39
|
+
|
|
40
|
+
[](https://github.com/wormwtf/worm-sdk/actions/workflows/ci.yml)
|
|
41
|
+
[](https://pypi.org/project/worm-sdk/)
|
|
42
|
+
[](https://www.python.org/downloads/)
|
|
43
|
+
[](LICENSE)
|
|
44
|
+
|
|
45
|
+
## Features
|
|
46
|
+
|
|
47
|
+
- Full coverage of the Worm REST API — markets, events, search, spot orders, margin, redeems, and account data
|
|
48
|
+
- Typed responses with [Pydantic](https://docs.pydantic.dev/) models
|
|
49
|
+
- HMAC authentication for private endpoints
|
|
50
|
+
- Optional Solana wallet signing for order, margin, and redeem flows
|
|
51
|
+
- Cursor-based pagination helpers
|
|
52
|
+
- HTTP/2 via [httpx](https://www.python-httpx.org/)
|
|
53
|
+
|
|
54
|
+
## Requirements
|
|
55
|
+
|
|
56
|
+
- Python 3.10 or later
|
|
57
|
+
|
|
58
|
+
## Installation
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install worm-sdk
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Install from source:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
git clone https://github.com/wormwtf/worm-sdk.git
|
|
68
|
+
cd worm-sdk
|
|
69
|
+
pip install -e .
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
For wallet signing (orders, margin, redeems):
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pip install "worm-sdk[signing]"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Quickstart
|
|
79
|
+
|
|
80
|
+
Fetch public market data — no credentials required:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from worm_sdk import WormClient
|
|
84
|
+
|
|
85
|
+
with WormClient() as client:
|
|
86
|
+
page = client.markets.list(sort="trending", limit=5)
|
|
87
|
+
for market in page.items:
|
|
88
|
+
print(market.title, market.condition_id)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Authentication
|
|
92
|
+
|
|
93
|
+
Worm uses HMAC-signed API keys. Create a key once with a Solana wallet, then reuse the key and secret for all authenticated calls.
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from worm_sdk import WormClient
|
|
97
|
+
from worm_sdk.auth import SolanaWalletSigner
|
|
98
|
+
|
|
99
|
+
# One-time setup — store the returned credentials securely
|
|
100
|
+
signer = SolanaWalletSigner("YOUR_SOLANA_PRIVATE_KEY")
|
|
101
|
+
credentials = WormClient.create_api_key(signer)
|
|
102
|
+
|
|
103
|
+
# Authenticated client
|
|
104
|
+
client = WormClient(
|
|
105
|
+
api_key=credentials.api_key,
|
|
106
|
+
api_secret=credentials.secret,
|
|
107
|
+
signer=signer, # optional; needed for place/redeem/open_position helpers
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
profile = client.account.get_summary()
|
|
111
|
+
print(profile.username)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
`SolanaWalletSigner` accepts a base58 string, 128-character hex keypair, or raw 64-byte keypair bytes. You can also provide any object that implements `SignerProtocol` (Ledger, KMS, etc.).
|
|
115
|
+
|
|
116
|
+
Set credentials via environment variables when running the examples:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
export WORM_API_KEY="..."
|
|
120
|
+
export WORM_API_SECRET="..."
|
|
121
|
+
export WORM_PRIVATE_KEY="..." # only for signing flows
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Usage
|
|
125
|
+
|
|
126
|
+
`WormClient` exposes namespaced APIs:
|
|
127
|
+
|
|
128
|
+
| Namespace | Auth | Description |
|
|
129
|
+
|-----------|------|-------------|
|
|
130
|
+
| `client.markets` | — | Market listings, order books, candles, prices, trades |
|
|
131
|
+
| `client.events` | — | Event listings and detail |
|
|
132
|
+
| `client.search` | — | Search markets and events |
|
|
133
|
+
| `client.sports` | — | Sports and league catalog |
|
|
134
|
+
| `client.orders` | ✓ | Spot order lifecycle |
|
|
135
|
+
| `client.trades` | ✓ | Your trade history |
|
|
136
|
+
| `client.account` | ✓ | Profile, portfolio, P&L |
|
|
137
|
+
| `client.margin` | ✓ / — | Margin estimate (public); positions, requests, settlements |
|
|
138
|
+
| `client.redeems` | ✓ | Redeem resolved positions |
|
|
139
|
+
| `client.api_keys` | ✓ | List and revoke API keys |
|
|
140
|
+
|
|
141
|
+
List endpoints return a `CursorPage[T]` with `items`, `next_cursor`, and `limit`:
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
page = client.markets.list(limit=20)
|
|
145
|
+
while page.next_cursor:
|
|
146
|
+
page = client.markets.list(limit=20, cursor=page.next_cursor)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Convenience methods combine draft, sign, and submit in one call when a signer is configured:
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from worm_sdk import OrderSide, OrderType
|
|
153
|
+
|
|
154
|
+
client.orders.place(
|
|
155
|
+
market_condition_id="...",
|
|
156
|
+
is_yes=True,
|
|
157
|
+
side=OrderSide.BUY,
|
|
158
|
+
order_type=OrderType.LIMIT,
|
|
159
|
+
amount="10",
|
|
160
|
+
price="0.55",
|
|
161
|
+
)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
See the [`examples/`](examples/) directory for complete scripts covering markets, orders, margin, redeems, and more.
|
|
165
|
+
|
|
166
|
+
## Error handling
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from worm_sdk import AuthenticationRequired, WormAPIError
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
client.orders.list(limit=1)
|
|
173
|
+
except WormAPIError as exc:
|
|
174
|
+
print(exc.error_code, exc.slug, exc.message)
|
|
175
|
+
except AuthenticationRequired:
|
|
176
|
+
print("Pass api_key and api_secret to WormClient")
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Configuration
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
client = WormClient(
|
|
183
|
+
api_key="...",
|
|
184
|
+
api_secret="...",
|
|
185
|
+
base_url="https://api.worm.wtf", # default
|
|
186
|
+
timeout=30.0,
|
|
187
|
+
signer=signer,
|
|
188
|
+
)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Inspect rate limits and response metadata on `client.last_response` after each request.
|
|
192
|
+
|
|
193
|
+
## Development
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
pip install -e ".[dev,signing]"
|
|
197
|
+
pytest
|
|
198
|
+
ruff check .
|
|
199
|
+
mypy src
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Build locally:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
python -m build
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Releases are published to PyPI automatically when you push a version tag (e.g. `v0.9.0`) or publish a GitHub Release. Set the `PYPI_API_TOKEN` repository secret first.
|
|
209
|
+
|
|
210
|
+
## Links
|
|
211
|
+
|
|
212
|
+
- [Worm](https://worm.wtf)
|
|
213
|
+
- [API documentation](https://docs.worm.wtf)
|
|
214
|
+
- [Issue tracker](https://github.com/wormwtf/worm-sdk/issues)
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
MIT — see [LICENSE](LICENSE).
|
worm_sdk-0.9.0/README.md
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# worm-sdk
|
|
2
|
+
|
|
3
|
+
Official Python client for the [Worm](https://worm.wtf) prediction markets API.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/wormwtf/worm-sdk/actions/workflows/ci.yml)
|
|
6
|
+
[](https://pypi.org/project/worm-sdk/)
|
|
7
|
+
[](https://www.python.org/downloads/)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- Full coverage of the Worm REST API — markets, events, search, spot orders, margin, redeems, and account data
|
|
13
|
+
- Typed responses with [Pydantic](https://docs.pydantic.dev/) models
|
|
14
|
+
- HMAC authentication for private endpoints
|
|
15
|
+
- Optional Solana wallet signing for order, margin, and redeem flows
|
|
16
|
+
- Cursor-based pagination helpers
|
|
17
|
+
- HTTP/2 via [httpx](https://www.python-httpx.org/)
|
|
18
|
+
|
|
19
|
+
## Requirements
|
|
20
|
+
|
|
21
|
+
- Python 3.10 or later
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install worm-sdk
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Install from source:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
git clone https://github.com/wormwtf/worm-sdk.git
|
|
33
|
+
cd worm-sdk
|
|
34
|
+
pip install -e .
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
For wallet signing (orders, margin, redeems):
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install "worm-sdk[signing]"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Quickstart
|
|
44
|
+
|
|
45
|
+
Fetch public market data — no credentials required:
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from worm_sdk import WormClient
|
|
49
|
+
|
|
50
|
+
with WormClient() as client:
|
|
51
|
+
page = client.markets.list(sort="trending", limit=5)
|
|
52
|
+
for market in page.items:
|
|
53
|
+
print(market.title, market.condition_id)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Authentication
|
|
57
|
+
|
|
58
|
+
Worm uses HMAC-signed API keys. Create a key once with a Solana wallet, then reuse the key and secret for all authenticated calls.
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from worm_sdk import WormClient
|
|
62
|
+
from worm_sdk.auth import SolanaWalletSigner
|
|
63
|
+
|
|
64
|
+
# One-time setup — store the returned credentials securely
|
|
65
|
+
signer = SolanaWalletSigner("YOUR_SOLANA_PRIVATE_KEY")
|
|
66
|
+
credentials = WormClient.create_api_key(signer)
|
|
67
|
+
|
|
68
|
+
# Authenticated client
|
|
69
|
+
client = WormClient(
|
|
70
|
+
api_key=credentials.api_key,
|
|
71
|
+
api_secret=credentials.secret,
|
|
72
|
+
signer=signer, # optional; needed for place/redeem/open_position helpers
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
profile = client.account.get_summary()
|
|
76
|
+
print(profile.username)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`SolanaWalletSigner` accepts a base58 string, 128-character hex keypair, or raw 64-byte keypair bytes. You can also provide any object that implements `SignerProtocol` (Ledger, KMS, etc.).
|
|
80
|
+
|
|
81
|
+
Set credentials via environment variables when running the examples:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
export WORM_API_KEY="..."
|
|
85
|
+
export WORM_API_SECRET="..."
|
|
86
|
+
export WORM_PRIVATE_KEY="..." # only for signing flows
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Usage
|
|
90
|
+
|
|
91
|
+
`WormClient` exposes namespaced APIs:
|
|
92
|
+
|
|
93
|
+
| Namespace | Auth | Description |
|
|
94
|
+
|-----------|------|-------------|
|
|
95
|
+
| `client.markets` | — | Market listings, order books, candles, prices, trades |
|
|
96
|
+
| `client.events` | — | Event listings and detail |
|
|
97
|
+
| `client.search` | — | Search markets and events |
|
|
98
|
+
| `client.sports` | — | Sports and league catalog |
|
|
99
|
+
| `client.orders` | ✓ | Spot order lifecycle |
|
|
100
|
+
| `client.trades` | ✓ | Your trade history |
|
|
101
|
+
| `client.account` | ✓ | Profile, portfolio, P&L |
|
|
102
|
+
| `client.margin` | ✓ / — | Margin estimate (public); positions, requests, settlements |
|
|
103
|
+
| `client.redeems` | ✓ | Redeem resolved positions |
|
|
104
|
+
| `client.api_keys` | ✓ | List and revoke API keys |
|
|
105
|
+
|
|
106
|
+
List endpoints return a `CursorPage[T]` with `items`, `next_cursor`, and `limit`:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
page = client.markets.list(limit=20)
|
|
110
|
+
while page.next_cursor:
|
|
111
|
+
page = client.markets.list(limit=20, cursor=page.next_cursor)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Convenience methods combine draft, sign, and submit in one call when a signer is configured:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from worm_sdk import OrderSide, OrderType
|
|
118
|
+
|
|
119
|
+
client.orders.place(
|
|
120
|
+
market_condition_id="...",
|
|
121
|
+
is_yes=True,
|
|
122
|
+
side=OrderSide.BUY,
|
|
123
|
+
order_type=OrderType.LIMIT,
|
|
124
|
+
amount="10",
|
|
125
|
+
price="0.55",
|
|
126
|
+
)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
See the [`examples/`](examples/) directory for complete scripts covering markets, orders, margin, redeems, and more.
|
|
130
|
+
|
|
131
|
+
## Error handling
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from worm_sdk import AuthenticationRequired, WormAPIError
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
client.orders.list(limit=1)
|
|
138
|
+
except WormAPIError as exc:
|
|
139
|
+
print(exc.error_code, exc.slug, exc.message)
|
|
140
|
+
except AuthenticationRequired:
|
|
141
|
+
print("Pass api_key and api_secret to WormClient")
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Configuration
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
client = WormClient(
|
|
148
|
+
api_key="...",
|
|
149
|
+
api_secret="...",
|
|
150
|
+
base_url="https://api.worm.wtf", # default
|
|
151
|
+
timeout=30.0,
|
|
152
|
+
signer=signer,
|
|
153
|
+
)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Inspect rate limits and response metadata on `client.last_response` after each request.
|
|
157
|
+
|
|
158
|
+
## Development
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
pip install -e ".[dev,signing]"
|
|
162
|
+
pytest
|
|
163
|
+
ruff check .
|
|
164
|
+
mypy src
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Build locally:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
python -m build
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Releases are published to PyPI automatically when you push a version tag (e.g. `v0.9.0`) or publish a GitHub Release. Set the `PYPI_API_TOKEN` repository secret first.
|
|
174
|
+
|
|
175
|
+
## Links
|
|
176
|
+
|
|
177
|
+
- [Worm](https://worm.wtf)
|
|
178
|
+
- [API documentation](https://docs.worm.wtf)
|
|
179
|
+
- [Issue tracker](https://github.com/wormwtf/worm-sdk/issues)
|
|
180
|
+
|
|
181
|
+
## License
|
|
182
|
+
|
|
183
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""View account summary, assets, and P&L."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from worm_sdk import WormClient
|
|
6
|
+
|
|
7
|
+
API_KEY = os.getenv("WORM_API_KEY")
|
|
8
|
+
API_SECRET = os.getenv("WORM_API_SECRET")
|
|
9
|
+
|
|
10
|
+
client = WormClient(api_key=API_KEY, api_secret=API_SECRET)
|
|
11
|
+
|
|
12
|
+
# --- Account summary ---
|
|
13
|
+
summary = client.account.get_summary()
|
|
14
|
+
print(f"Username: {summary.username}")
|
|
15
|
+
print(f"Twitter: {summary.twitter_username}")
|
|
16
|
+
print(f"Joined at: {summary.joined_at}")
|
|
17
|
+
|
|
18
|
+
# --- Market share balances ---
|
|
19
|
+
assets = client.account.get_assets(limit=10)
|
|
20
|
+
for a in assets.items:
|
|
21
|
+
print(
|
|
22
|
+
f" {a.position.outcome_text} ({a.token.symbol}) "
|
|
23
|
+
f"total={a.amounts.total} available={a.amounts.available} "
|
|
24
|
+
f"value_usdt={a.value.usdt}"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# --- P&L ---
|
|
28
|
+
pnl = client.account.get_pnl()
|
|
29
|
+
print(f"\nMargin settlements P&L: {pnl.margin_position_settlements_pnl}")
|
|
30
|
+
print(f"Margin unrealized P&L: {pnl.margin_positions_unrealized_pnl}")
|
|
31
|
+
print(f"Redeems funds: {pnl.redeems_funds}")
|
|
32
|
+
print(f"Active assets value: {pnl.active_assets_value}")
|
|
33
|
+
print(f"Creator fees: {pnl.creator_fees}")
|
|
34
|
+
print(f"Total P&L: {pnl.total_pnl}")
|
|
35
|
+
|
|
36
|
+
client.close()
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""List and revoke API keys for the authenticated account."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from worm_sdk import WormClient
|
|
6
|
+
|
|
7
|
+
API_KEY = os.getenv("WORM_API_KEY")
|
|
8
|
+
API_SECRET = os.getenv("WORM_API_SECRET")
|
|
9
|
+
|
|
10
|
+
client = WormClient(api_key=API_KEY, api_secret=API_SECRET)
|
|
11
|
+
|
|
12
|
+
# --- List keys (each entry includes an id / key identifier for revocation) ---
|
|
13
|
+
keys = client.api_keys.list()
|
|
14
|
+
for k in keys:
|
|
15
|
+
print(
|
|
16
|
+
f"key_id={k.key_id} "
|
|
17
|
+
f"created={k.created} revoked_at={k.revoked_at}"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# --- Revoke a key by id (use `key_id` from list(); not the secret) ---
|
|
21
|
+
# KEY_ID_TO_REVOKE = "key-id-from-list-response"
|
|
22
|
+
# client.api_keys.revoke(KEY_ID_TO_REVOKE)
|
|
23
|
+
|
|
24
|
+
client.close()
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Margin trading: estimate, open positions, set TP/SL, close, and claim."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from worm_sdk import WormClient
|
|
6
|
+
from worm_sdk.auth import SolanaWalletSigner
|
|
7
|
+
from worm_sdk.types.enums import MarginPositionRequestType
|
|
8
|
+
|
|
9
|
+
API_KEY = os.getenv("WORM_API_KEY")
|
|
10
|
+
API_SECRET = os.getenv("WORM_API_SECRET")
|
|
11
|
+
PRIVATE_KEY = os.getenv("WORM_PRIVATE_KEY")
|
|
12
|
+
|
|
13
|
+
signer = SolanaWalletSigner(PRIVATE_KEY)
|
|
14
|
+
client = WormClient(api_key=API_KEY, api_secret=API_SECRET, signer=signer)
|
|
15
|
+
|
|
16
|
+
MARKET_CONDITION_ID = "your-market-condition-id"
|
|
17
|
+
|
|
18
|
+
# --- Estimate a position (public, no auth) ---
|
|
19
|
+
estimate = client.margin.estimate(
|
|
20
|
+
market_condition_id=MARKET_CONDITION_ID,
|
|
21
|
+
funds="100",
|
|
22
|
+
leverage="5",
|
|
23
|
+
is_yes=True,
|
|
24
|
+
)
|
|
25
|
+
print(f"Estimated shares: {estimate}")
|
|
26
|
+
|
|
27
|
+
# --- Position request lifecycle (same paths as API reference under /margin/positions/requests/) ---
|
|
28
|
+
page = client.margin.list_requests(market_condition_id=MARKET_CONDITION_ID, limit=10)
|
|
29
|
+
print(f"Open requests: {len(page.items)}")
|
|
30
|
+
# draft = client.margin.create_request(...)
|
|
31
|
+
# client.margin.submit_request(draft.pubkey, signature=your_signature)
|
|
32
|
+
# client.margin.cancel_request(draft.pubkey)
|
|
33
|
+
# row = client.margin.get_request("request-pubkey")
|
|
34
|
+
|
|
35
|
+
# --- Open a market position (funds-based; convenience: create + sign + submit) ---
|
|
36
|
+
market_result = client.margin.open_position(
|
|
37
|
+
market_condition_id=MARKET_CONDITION_ID,
|
|
38
|
+
position_request_type=MarginPositionRequestType.MARKET,
|
|
39
|
+
funds="100",
|
|
40
|
+
leverage="5",
|
|
41
|
+
is_yes=True,
|
|
42
|
+
take_profit_price="0.95",
|
|
43
|
+
stop_loss_price="0.40",
|
|
44
|
+
)
|
|
45
|
+
print(f"Market position request submitted: {market_result.pubkey}")
|
|
46
|
+
|
|
47
|
+
# --- Open a limit position (price + shares; resting limit order) ---
|
|
48
|
+
limit_result = client.margin.open_position(
|
|
49
|
+
market_condition_id=MARKET_CONDITION_ID,
|
|
50
|
+
position_request_type=MarginPositionRequestType.LIMIT,
|
|
51
|
+
price="0.55",
|
|
52
|
+
shares="200",
|
|
53
|
+
leverage="3",
|
|
54
|
+
is_yes=True,
|
|
55
|
+
take_profit_price="0.90",
|
|
56
|
+
stop_loss_price="0.35",
|
|
57
|
+
)
|
|
58
|
+
print(f"Limit position request submitted: {limit_result.pubkey}")
|
|
59
|
+
|
|
60
|
+
# --- List positions ---
|
|
61
|
+
positions = client.margin.list_positions(limit=5)
|
|
62
|
+
for p in positions.items:
|
|
63
|
+
print(f" Position: {p.pubkey} — closed={p.is_closed}")
|
|
64
|
+
|
|
65
|
+
pubkey = input("Enter position pubkey to set TP/SL / close: ")
|
|
66
|
+
|
|
67
|
+
# --- Update TP/SL ---
|
|
68
|
+
if pubkey:
|
|
69
|
+
tp_sl = client.margin.set_tp_sl(pubkey, take_profit_price="0.90", stop_loss_price="0.35")
|
|
70
|
+
print(f"TP/SL updated: {tp_sl}")
|
|
71
|
+
|
|
72
|
+
# --- Close a position ---
|
|
73
|
+
if pubkey:
|
|
74
|
+
close_result = client.margin.close_position(pubkey)
|
|
75
|
+
print(f"Close result: {close_result}")
|
|
76
|
+
|
|
77
|
+
# --- Claim settlement ---
|
|
78
|
+
if pubkey:
|
|
79
|
+
claim = client.margin.claim_settlement(pubkey)
|
|
80
|
+
print(f"Settlement claimed: {claim}")
|
|
81
|
+
|
|
82
|
+
client.close()
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Margin: inspect a single position and list settlements across positions."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from worm_sdk import WormClient
|
|
6
|
+
from worm_sdk.auth import SolanaWalletSigner
|
|
7
|
+
|
|
8
|
+
API_KEY = os.getenv("WORM_API_KEY")
|
|
9
|
+
API_SECRET = os.getenv("WORM_API_SECRET")
|
|
10
|
+
PRIVATE_KEY = os.getenv("WORM_PRIVATE_KEY")
|
|
11
|
+
|
|
12
|
+
signer = SolanaWalletSigner(PRIVATE_KEY)
|
|
13
|
+
client = WormClient(api_key=API_KEY, api_secret=API_SECRET, signer=signer)
|
|
14
|
+
|
|
15
|
+
# Set this to a known position pubkey, or leave as placeholder to use the first listed position.
|
|
16
|
+
POSITION_PUBKEY = "your-position-pubkey"
|
|
17
|
+
MARKET_CONDITION_ID = "your-market-condition-id"
|
|
18
|
+
|
|
19
|
+
positions = client.margin.list_positions(limit=5)
|
|
20
|
+
for p in positions.items:
|
|
21
|
+
print(f" {p.pubkey} — closed={p.is_closed}")
|
|
22
|
+
|
|
23
|
+
pubkey = (
|
|
24
|
+
POSITION_PUBKEY
|
|
25
|
+
if POSITION_PUBKEY != "your-position-pubkey"
|
|
26
|
+
else (positions.items[0].pubkey if positions.items else None)
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
if pubkey:
|
|
30
|
+
detail = client.margin.get_position(pubkey)
|
|
31
|
+
print(f"\nDetail for {pubkey}: {detail}")
|
|
32
|
+
|
|
33
|
+
# --- Settlements list (cursor pagination) ---
|
|
34
|
+
page = client.margin.list_settlements(limit=20)
|
|
35
|
+
for s in page.items:
|
|
36
|
+
print(f" settlement: {s}")
|
|
37
|
+
|
|
38
|
+
if page.next_cursor:
|
|
39
|
+
more = client.margin.list_settlements(limit=20, cursor=page.next_cursor)
|
|
40
|
+
print(f"Next page: {len(more.items)} settlements")
|
|
41
|
+
|
|
42
|
+
# --- Optional filters ---
|
|
43
|
+
filtered = client.margin.list_settlements(
|
|
44
|
+
market_condition_id=MARKET_CONDITION_ID,
|
|
45
|
+
limit=10,
|
|
46
|
+
)
|
|
47
|
+
print(f"Filtered by market: {len(filtered.items)}")
|
|
48
|
+
|
|
49
|
+
client.close()
|