omnibook 1.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.
- omnibook-1.1.0/.github/workflows/ci.yml +22 -0
- omnibook-1.1.0/.github/workflows/publish.yml +24 -0
- omnibook-1.1.0/.gitignore +16 -0
- omnibook-1.1.0/LICENSE +21 -0
- omnibook-1.1.0/PKG-INFO +132 -0
- omnibook-1.1.0/PUBLISHING.md +26 -0
- omnibook-1.1.0/README.md +101 -0
- omnibook-1.1.0/examples/place_order.py +55 -0
- omnibook-1.1.0/examples/stream_user.py +32 -0
- omnibook-1.1.0/pyproject.toml +56 -0
- omnibook-1.1.0/src/omnibook/__init__.py +34 -0
- omnibook-1.1.0/src/omnibook/_base.py +61 -0
- omnibook-1.1.0/src/omnibook/async_client.py +272 -0
- omnibook-1.1.0/src/omnibook/auth.py +90 -0
- omnibook-1.1.0/src/omnibook/client.py +285 -0
- omnibook-1.1.0/src/omnibook/errors.py +117 -0
- omnibook-1.1.0/src/omnibook/models.py +41 -0
- omnibook-1.1.0/src/omnibook/py.typed +0 -0
- omnibook-1.1.0/src/omnibook/ws.py +261 -0
- omnibook-1.1.0/tests/conftest.py +53 -0
- omnibook-1.1.0/tests/integration/test_auth_login.py +32 -0
- omnibook-1.1.0/tests/integration/test_market_data.py +48 -0
- omnibook-1.1.0/tests/integration/test_place_order.py +89 -0
- omnibook-1.1.0/tests/integration/test_ws_subscribe_channels.py +35 -0
- omnibook-1.1.0/tests/integration/test_ws_subscribe_markets.py +63 -0
- omnibook-1.1.0/tests/integration/test_ws_subscribe_multi.py +61 -0
- omnibook-1.1.0/tests/unit/test_auth_signing.py +80 -0
- omnibook-1.1.0/tests/unit/test_client_rest_mock.py +155 -0
- omnibook-1.1.0/tests/unit/test_errors.py +65 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: ${{ matrix.python-version }}
|
|
19
|
+
- name: Install
|
|
20
|
+
run: pip install -e ".[dev]"
|
|
21
|
+
- name: Unit tests
|
|
22
|
+
run: pytest -q --ignore=tests/integration
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
id-token: write
|
|
13
|
+
contents: read
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: "3.12"
|
|
19
|
+
- name: Build
|
|
20
|
+
run: |
|
|
21
|
+
pip install build
|
|
22
|
+
python -m build
|
|
23
|
+
- name: Publish
|
|
24
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
omnibook-1.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Raeth AI
|
|
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.
|
omnibook-1.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: omnibook
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: Official Python SDK for the Omnibook prediction market exchange
|
|
5
|
+
Project-URL: Homepage, https://omnibook.xyz
|
|
6
|
+
Project-URL: Documentation, https://docs.omnibook.xyz/sdk/python
|
|
7
|
+
Project-URL: Repository, https://github.com/OmniBook-HQ/python-sdk-v1
|
|
8
|
+
Project-URL: Issues, https://github.com/OmniBook-HQ/python-sdk-v1/issues
|
|
9
|
+
Author-email: Raeth AI <devs@omnibook.xyz>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: exchange,omnibook,prediction-markets,sdk,trading
|
|
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 :: Office/Business :: Financial :: Investment
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: httpx>=0.27
|
|
25
|
+
Requires-Dist: websockets>=12.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# Omnibook Python SDK
|
|
33
|
+
|
|
34
|
+
Official Python client for the [Omnibook](https://omnibook.xyz) prediction market
|
|
35
|
+
exchange. Sign up, create an API key, install this package, and trade.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install omnibook
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from omnibook import Client
|
|
43
|
+
|
|
44
|
+
client = Client(api_key="<alphanumeric-api-key>", api_secret="<64-char-hex-secret>")
|
|
45
|
+
|
|
46
|
+
status = client.get_exchange_status()
|
|
47
|
+
rounds = client.get_rounds(limit=5)
|
|
48
|
+
order = client.place_order(
|
|
49
|
+
client_order_id="42",
|
|
50
|
+
market_id=66,
|
|
51
|
+
side="buy",
|
|
52
|
+
outcome="yes",
|
|
53
|
+
type="limit",
|
|
54
|
+
tick=47,
|
|
55
|
+
qty=10,
|
|
56
|
+
tif="gtc",
|
|
57
|
+
post_only=True,
|
|
58
|
+
)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Prerequisites
|
|
62
|
+
|
|
63
|
+
1. Sign in at [omnibook.xyz](https://omnibook.xyz).
|
|
64
|
+
2. Deposit USDC (bot keys cannot withdraw).
|
|
65
|
+
3. Create a key under **Settings → API keys**. Copy the **API key** and **secret**
|
|
66
|
+
(the secret is shown once).
|
|
67
|
+
|
|
68
|
+
Docs: [docs.omnibook.xyz/sdk/python](https://docs.omnibook.xyz/sdk/python)
|
|
69
|
+
|
|
70
|
+
## Environments
|
|
71
|
+
|
|
72
|
+
| Env | REST | WebSocket |
|
|
73
|
+
| --- | --- | --- |
|
|
74
|
+
| Production (default) | `https://api.omnibook.xyz` | `wss://api.omnibook.xyz/v1/ws` |
|
|
75
|
+
| Local | `http://127.0.0.1:9104` | `ws://127.0.0.1:9105/v1/ws` |
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
client = Client(
|
|
79
|
+
api_key="...",
|
|
80
|
+
api_secret="...",
|
|
81
|
+
base_url="http://127.0.0.1:9104",
|
|
82
|
+
ws_url="ws://127.0.0.1:9105/v1/ws",
|
|
83
|
+
)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Sync and async
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from omnibook import Client, AsyncClient
|
|
90
|
+
|
|
91
|
+
# Sync
|
|
92
|
+
with Client(api_key=..., api_secret=...) as client:
|
|
93
|
+
print(client.get_balance())
|
|
94
|
+
|
|
95
|
+
# Async
|
|
96
|
+
async with AsyncClient(api_key=..., api_secret=...) as client:
|
|
97
|
+
print(await client.get_balance())
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## WebSocket
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from omnibook import Client
|
|
104
|
+
|
|
105
|
+
with Client(api_key=..., api_secret=...) as client:
|
|
106
|
+
with client.websocket() as ws:
|
|
107
|
+
ws.subscribe(channels=["rounds", "oracle", "user"])
|
|
108
|
+
ws.subscribe(channels=["orderbook_delta", "trades"], market_ids=[66])
|
|
109
|
+
for msg in ws:
|
|
110
|
+
print(msg)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Auth note
|
|
114
|
+
|
|
115
|
+
Every request is HMAC-SHA256 signed. Settings hands you a **64-character hex**
|
|
116
|
+
secret (32 raw bytes). Pass that string (or a legacy base64 secret) to
|
|
117
|
+
`api_secret` — the SDK hex-/base64-decodes it to the raw HMAC key for you.
|
|
118
|
+
|
|
119
|
+
## Tests
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
pip install -e ".[dev]"
|
|
123
|
+
pytest # unit tests (mocked)
|
|
124
|
+
OMNIBOOK_RUN_INTEGRATION=1 \
|
|
125
|
+
OMNIBOOK_API_KEY=... \
|
|
126
|
+
OMNIBOOK_API_SECRET=... \
|
|
127
|
+
pytest -m integration # live venue
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
MIT
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Publishing Omnibook Python SDK
|
|
2
|
+
|
|
3
|
+
Local package is ready at version **1.0.0** (`dist/` passes `twine check`).
|
|
4
|
+
|
|
5
|
+
Repo: https://github.com/OmniBook-HQ/python-sdk-v1
|
|
6
|
+
|
|
7
|
+
## Publish to PyPI
|
|
8
|
+
|
|
9
|
+
Preferred: Trusted Publishing (OIDC) via `.github/workflows/publish.yml`.
|
|
10
|
+
|
|
11
|
+
1. On [pypi.org](https://pypi.org): create project `omnibook` (or claim the name).
|
|
12
|
+
2. Add a trusted publisher: GitHub org `OmniBook-HQ`, repo `python-sdk-v1`,
|
|
13
|
+
workflow `publish.yml`, environment optional.
|
|
14
|
+
3. Create a GitHub Release `v1.0.0` (or run the workflow manually).
|
|
15
|
+
|
|
16
|
+
Manual upload (needs a PyPI API token):
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
twine upload dist/omnibook-1.0.0*
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Docs PR
|
|
23
|
+
|
|
24
|
+
Branch already pushed: `docs/python-sdk` on `omnibook-docs`.
|
|
25
|
+
|
|
26
|
+
Open: https://github.com/Raeth-AI/omnibook-docs/pull/new/docs/python-sdk
|
omnibook-1.1.0/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Omnibook Python SDK
|
|
2
|
+
|
|
3
|
+
Official Python client for the [Omnibook](https://omnibook.xyz) prediction market
|
|
4
|
+
exchange. Sign up, create an API key, install this package, and trade.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
pip install omnibook
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
from omnibook import Client
|
|
12
|
+
|
|
13
|
+
client = Client(api_key="<alphanumeric-api-key>", api_secret="<64-char-hex-secret>")
|
|
14
|
+
|
|
15
|
+
status = client.get_exchange_status()
|
|
16
|
+
rounds = client.get_rounds(limit=5)
|
|
17
|
+
order = client.place_order(
|
|
18
|
+
client_order_id="42",
|
|
19
|
+
market_id=66,
|
|
20
|
+
side="buy",
|
|
21
|
+
outcome="yes",
|
|
22
|
+
type="limit",
|
|
23
|
+
tick=47,
|
|
24
|
+
qty=10,
|
|
25
|
+
tif="gtc",
|
|
26
|
+
post_only=True,
|
|
27
|
+
)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Prerequisites
|
|
31
|
+
|
|
32
|
+
1. Sign in at [omnibook.xyz](https://omnibook.xyz).
|
|
33
|
+
2. Deposit USDC (bot keys cannot withdraw).
|
|
34
|
+
3. Create a key under **Settings → API keys**. Copy the **API key** and **secret**
|
|
35
|
+
(the secret is shown once).
|
|
36
|
+
|
|
37
|
+
Docs: [docs.omnibook.xyz/sdk/python](https://docs.omnibook.xyz/sdk/python)
|
|
38
|
+
|
|
39
|
+
## Environments
|
|
40
|
+
|
|
41
|
+
| Env | REST | WebSocket |
|
|
42
|
+
| --- | --- | --- |
|
|
43
|
+
| Production (default) | `https://api.omnibook.xyz` | `wss://api.omnibook.xyz/v1/ws` |
|
|
44
|
+
| Local | `http://127.0.0.1:9104` | `ws://127.0.0.1:9105/v1/ws` |
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
client = Client(
|
|
48
|
+
api_key="...",
|
|
49
|
+
api_secret="...",
|
|
50
|
+
base_url="http://127.0.0.1:9104",
|
|
51
|
+
ws_url="ws://127.0.0.1:9105/v1/ws",
|
|
52
|
+
)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Sync and async
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from omnibook import Client, AsyncClient
|
|
59
|
+
|
|
60
|
+
# Sync
|
|
61
|
+
with Client(api_key=..., api_secret=...) as client:
|
|
62
|
+
print(client.get_balance())
|
|
63
|
+
|
|
64
|
+
# Async
|
|
65
|
+
async with AsyncClient(api_key=..., api_secret=...) as client:
|
|
66
|
+
print(await client.get_balance())
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## WebSocket
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from omnibook import Client
|
|
73
|
+
|
|
74
|
+
with Client(api_key=..., api_secret=...) as client:
|
|
75
|
+
with client.websocket() as ws:
|
|
76
|
+
ws.subscribe(channels=["rounds", "oracle", "user"])
|
|
77
|
+
ws.subscribe(channels=["orderbook_delta", "trades"], market_ids=[66])
|
|
78
|
+
for msg in ws:
|
|
79
|
+
print(msg)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Auth note
|
|
83
|
+
|
|
84
|
+
Every request is HMAC-SHA256 signed. Settings hands you a **64-character hex**
|
|
85
|
+
secret (32 raw bytes). Pass that string (or a legacy base64 secret) to
|
|
86
|
+
`api_secret` — the SDK hex-/base64-decodes it to the raw HMAC key for you.
|
|
87
|
+
|
|
88
|
+
## Tests
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pip install -e ".[dev]"
|
|
92
|
+
pytest # unit tests (mocked)
|
|
93
|
+
OMNIBOOK_RUN_INTEGRATION=1 \
|
|
94
|
+
OMNIBOOK_API_KEY=... \
|
|
95
|
+
OMNIBOOK_API_SECRET=... \
|
|
96
|
+
pytest -m integration # live venue
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## License
|
|
100
|
+
|
|
101
|
+
MIT
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Place a small post-only limit order on the current trading round."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
import time
|
|
9
|
+
|
|
10
|
+
from omnibook import Client
|
|
11
|
+
from omnibook.errors import OmnibookAPIError
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def main() -> int:
|
|
15
|
+
key = os.environ.get("OMNIBOOK_API_KEY")
|
|
16
|
+
secret = os.environ.get("OMNIBOOK_API_SECRET")
|
|
17
|
+
if not key or not secret:
|
|
18
|
+
print("Set OMNIBOOK_API_KEY and OMNIBOOK_API_SECRET", file=sys.stderr)
|
|
19
|
+
return 1
|
|
20
|
+
|
|
21
|
+
base = os.environ.get("OMNIBOOK_BASE_URL", "https://api.omnibook.xyz")
|
|
22
|
+
with Client(api_key=key, api_secret=secret, base_url=base) as client:
|
|
23
|
+
rounds = client.get_rounds(limit=5)
|
|
24
|
+
items = rounds.get("rounds") or []
|
|
25
|
+
if not items:
|
|
26
|
+
print("No rounds returned", file=sys.stderr)
|
|
27
|
+
return 1
|
|
28
|
+
market_id = int(items[0]["market_id"])
|
|
29
|
+
client_order_id = str(int(time.time() * 1000))
|
|
30
|
+
print(f"Placing post-only buy YES on market_id={market_id} id={client_order_id}")
|
|
31
|
+
try:
|
|
32
|
+
result = client.place_order(
|
|
33
|
+
client_order_id=client_order_id,
|
|
34
|
+
market_id=market_id,
|
|
35
|
+
side="buy",
|
|
36
|
+
outcome="yes",
|
|
37
|
+
type="limit",
|
|
38
|
+
tick=1,
|
|
39
|
+
qty=1,
|
|
40
|
+
tif="gtc",
|
|
41
|
+
post_only=True,
|
|
42
|
+
)
|
|
43
|
+
except OmnibookAPIError as exc:
|
|
44
|
+
print(f"Place failed: {exc}", file=sys.stderr)
|
|
45
|
+
return 1
|
|
46
|
+
print(result)
|
|
47
|
+
order_id = result.get("order_id")
|
|
48
|
+
if order_id:
|
|
49
|
+
canceled = client.cancel_order(str(order_id))
|
|
50
|
+
print("canceled:", canceled)
|
|
51
|
+
return 0
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
if __name__ == "__main__":
|
|
55
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Stream private user channel + rounds until Ctrl-C."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from omnibook import Client
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main() -> int:
|
|
13
|
+
key = os.environ.get("OMNIBOOK_API_KEY")
|
|
14
|
+
secret = os.environ.get("OMNIBOOK_API_SECRET")
|
|
15
|
+
if not key or not secret:
|
|
16
|
+
print("Set OMNIBOOK_API_KEY and OMNIBOOK_API_SECRET", file=sys.stderr)
|
|
17
|
+
return 1
|
|
18
|
+
|
|
19
|
+
base = os.environ.get("OMNIBOOK_BASE_URL", "https://api.omnibook.xyz")
|
|
20
|
+
ws_url = os.environ.get("OMNIBOOK_WS_URL", "wss://api.omnibook.xyz/v1/ws")
|
|
21
|
+
|
|
22
|
+
with Client(api_key=key, api_secret=secret, base_url=base, ws_url=ws_url) as client:
|
|
23
|
+
with client.websocket() as ws:
|
|
24
|
+
ws.subscribe(channels=["user", "rounds", "oracle"])
|
|
25
|
+
print("subscribed; waiting for messages…")
|
|
26
|
+
for msg in ws:
|
|
27
|
+
print(msg)
|
|
28
|
+
return 0
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
if __name__ == "__main__":
|
|
32
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "omnibook"
|
|
7
|
+
version = "1.1.0"
|
|
8
|
+
description = "Official Python SDK for the Omnibook prediction market exchange"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [{ name = "Raeth AI", email = "devs@omnibook.xyz" }]
|
|
13
|
+
keywords = ["omnibook", "exchange", "prediction-markets", "trading", "sdk"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.10",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Topic :: Office/Business :: Financial :: Investment",
|
|
24
|
+
"Typing :: Typed",
|
|
25
|
+
]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"httpx>=0.27",
|
|
28
|
+
"websockets>=12.0",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
dev = [
|
|
33
|
+
"pytest>=8.0",
|
|
34
|
+
"pytest-asyncio>=0.23",
|
|
35
|
+
"respx>=0.21",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://omnibook.xyz"
|
|
40
|
+
Documentation = "https://docs.omnibook.xyz/sdk/python"
|
|
41
|
+
Repository = "https://github.com/OmniBook-HQ/python-sdk-v1"
|
|
42
|
+
Issues = "https://github.com/OmniBook-HQ/python-sdk-v1/issues"
|
|
43
|
+
|
|
44
|
+
[tool.hatch.build.targets.wheel]
|
|
45
|
+
packages = ["src/omnibook"]
|
|
46
|
+
|
|
47
|
+
[tool.pytest.ini_options]
|
|
48
|
+
asyncio_mode = "auto"
|
|
49
|
+
markers = [
|
|
50
|
+
"integration: live venue tests (require OMNIBOOK_RUN_INTEGRATION=1 and API credentials)",
|
|
51
|
+
]
|
|
52
|
+
testpaths = ["tests"]
|
|
53
|
+
|
|
54
|
+
[tool.ruff]
|
|
55
|
+
line-length = 100
|
|
56
|
+
target-version = "py310"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Official Python SDK for the Omnibook prediction market exchange."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .async_client import AsyncClient
|
|
6
|
+
from .auth import DEFAULT_BASE_URL, DEFAULT_WS_URL
|
|
7
|
+
from .client import Client
|
|
8
|
+
from .errors import (
|
|
9
|
+
OmnibookAPIError,
|
|
10
|
+
OmnibookAuthError,
|
|
11
|
+
OmnibookError,
|
|
12
|
+
OmnibookRateLimitError,
|
|
13
|
+
OmnibookTimeoutError,
|
|
14
|
+
OmnibookWSError,
|
|
15
|
+
)
|
|
16
|
+
from .ws import AsyncWebSocketClient, WebSocketClient
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"AsyncClient",
|
|
20
|
+
"AsyncWebSocketClient",
|
|
21
|
+
"Client",
|
|
22
|
+
"DEFAULT_BASE_URL",
|
|
23
|
+
"DEFAULT_WS_URL",
|
|
24
|
+
"OmnibookAPIError",
|
|
25
|
+
"OmnibookAuthError",
|
|
26
|
+
"OmnibookError",
|
|
27
|
+
"OmnibookRateLimitError",
|
|
28
|
+
"OmnibookTimeoutError",
|
|
29
|
+
"OmnibookWSError",
|
|
30
|
+
"WebSocketClient",
|
|
31
|
+
"__version__",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
__version__ = "1.1.0"
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Shared REST helpers used by sync and async clients."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import Any
|
|
7
|
+
from urllib.parse import urlparse
|
|
8
|
+
|
|
9
|
+
from .auth import DEFAULT_BASE_URL, DEFAULT_WS_URL, decode_secret, signed_headers
|
|
10
|
+
from .errors import raise_for_response
|
|
11
|
+
from .models import JsonDict, compact_json_bytes, path_with_query
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BaseClient:
|
|
15
|
+
"""Credential + URL configuration shared by Client and AsyncClient."""
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
api_key: str | int,
|
|
20
|
+
api_secret: str | bytes,
|
|
21
|
+
*,
|
|
22
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
23
|
+
ws_url: str = DEFAULT_WS_URL,
|
|
24
|
+
timeout: float = 30.0,
|
|
25
|
+
) -> None:
|
|
26
|
+
self.api_key = str(api_key)
|
|
27
|
+
self._secret = decode_secret(api_secret)
|
|
28
|
+
self.base_url = base_url.rstrip("/")
|
|
29
|
+
self.ws_url = ws_url
|
|
30
|
+
self.timeout = timeout
|
|
31
|
+
parsed = urlparse(self.base_url)
|
|
32
|
+
if not parsed.scheme or not parsed.netloc:
|
|
33
|
+
raise ValueError(f"invalid base_url: {base_url!r}")
|
|
34
|
+
|
|
35
|
+
def _prepare(
|
|
36
|
+
self,
|
|
37
|
+
method: str,
|
|
38
|
+
path: str,
|
|
39
|
+
*,
|
|
40
|
+
params: dict[str, Any] | None = None,
|
|
41
|
+
json_body: JsonDict | list[Any] | None = None,
|
|
42
|
+
) -> tuple[str, bytes, dict[str, str]]:
|
|
43
|
+
target = path_with_query(path, params)
|
|
44
|
+
body = b""
|
|
45
|
+
if json_body is not None:
|
|
46
|
+
body = compact_json_bytes(json_body)
|
|
47
|
+
headers = signed_headers(self.api_key, self._secret, method, target, body)
|
|
48
|
+
return target, body, headers
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def _parse_response(status_code: int, text: str, header_map: dict[str, str]) -> Any:
|
|
52
|
+
data: Any
|
|
53
|
+
if not text:
|
|
54
|
+
data = None
|
|
55
|
+
else:
|
|
56
|
+
try:
|
|
57
|
+
data = json.loads(text)
|
|
58
|
+
except json.JSONDecodeError:
|
|
59
|
+
data = text
|
|
60
|
+
raise_for_response(status_code, data, header_map)
|
|
61
|
+
return data
|