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.
@@ -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
@@ -0,0 +1,16 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .eggs/
5
+ dist/
6
+ build/
7
+ .pytest_cache/
8
+ .mypy_cache/
9
+ .ruff_cache/
10
+ .venv/
11
+ venv/
12
+ .env
13
+ *.egg
14
+ .coverage
15
+ htmlcov/
16
+ .DS_Store
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.
@@ -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
@@ -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