synpath 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.
Files changed (175) hide show
  1. synpath-0.1.0/.env.example +39 -0
  2. synpath-0.1.0/.github/workflows/publish.yml +48 -0
  3. synpath-0.1.0/.github/workflows/tests.yml +21 -0
  4. synpath-0.1.0/.gitignore +12 -0
  5. synpath-0.1.0/LICENSE +21 -0
  6. synpath-0.1.0/PKG-INFO +224 -0
  7. synpath-0.1.0/README.md +174 -0
  8. synpath-0.1.0/assets/kalshi.png +0 -0
  9. synpath-0.1.0/assets/polymarket.png +0 -0
  10. synpath-0.1.0/assets/synpath-banner.png +0 -0
  11. synpath-0.1.0/clients/typescript/.gitignore +5 -0
  12. synpath-0.1.0/clients/typescript/example.ts +77 -0
  13. synpath-0.1.0/clients/typescript/package.json +20 -0
  14. synpath-0.1.0/clients/typescript/tsconfig.json +11 -0
  15. synpath-0.1.0/docs/api.md +279 -0
  16. synpath-0.1.0/docs/capabilities.md +102 -0
  17. synpath-0.1.0/docs/engine.md +354 -0
  18. synpath-0.1.0/docs/server.md +146 -0
  19. synpath-0.1.0/docs/streaming.md +232 -0
  20. synpath-0.1.0/docs/trading.md +318 -0
  21. synpath-0.1.0/examples/track_historical_book.py +222 -0
  22. synpath-0.1.0/pyproject.toml +72 -0
  23. synpath-0.1.0/synpath/__init__.py +183 -0
  24. synpath-0.1.0/synpath/__main__.py +66 -0
  25. synpath-0.1.0/synpath/base.py +723 -0
  26. synpath-0.1.0/synpath/bucket.py +154 -0
  27. synpath-0.1.0/synpath/client.py +356 -0
  28. synpath-0.1.0/synpath/engine/__init__.py +37 -0
  29. synpath-0.1.0/synpath/engine/__main__.py +354 -0
  30. synpath-0.1.0/synpath/engine/alerts.py +170 -0
  31. synpath-0.1.0/synpath/engine/engine.py +888 -0
  32. synpath-0.1.0/synpath/engine/eod.py +154 -0
  33. synpath-0.1.0/synpath/engine/events.py +140 -0
  34. synpath-0.1.0/synpath/engine/fair_values.py +117 -0
  35. synpath-0.1.0/synpath/engine/feeds.py +220 -0
  36. synpath-0.1.0/synpath/engine/journal.py +907 -0
  37. synpath-0.1.0/synpath/engine/ledger.py +353 -0
  38. synpath-0.1.0/synpath/engine/orders/__init__.py +42 -0
  39. synpath-0.1.0/synpath/engine/orders/base.py +441 -0
  40. synpath-0.1.0/synpath/engine/orders/day.py +72 -0
  41. synpath-0.1.0/synpath/engine/orders/iceberg.py +121 -0
  42. synpath-0.1.0/synpath/engine/orders/manager.py +223 -0
  43. synpath-0.1.0/synpath/engine/orders/oco.py +255 -0
  44. synpath-0.1.0/synpath/engine/orders/peg.py +168 -0
  45. synpath-0.1.0/synpath/engine/orders/routed.py +496 -0
  46. synpath-0.1.0/synpath/engine/orders/stop.py +240 -0
  47. synpath-0.1.0/synpath/engine/orders/taker.py +187 -0
  48. synpath-0.1.0/synpath/engine/orders/twap.py +190 -0
  49. synpath-0.1.0/synpath/engine/paper.py +532 -0
  50. synpath-0.1.0/synpath/engine/reconcile.py +279 -0
  51. synpath-0.1.0/synpath/engine/risk.py +403 -0
  52. synpath-0.1.0/synpath/engine/router.py +261 -0
  53. synpath-0.1.0/synpath/errors.py +98 -0
  54. synpath-0.1.0/synpath/history.py +71 -0
  55. synpath-0.1.0/synpath/hosted.py +86 -0
  56. synpath-0.1.0/synpath/hosted_auth.py +201 -0
  57. synpath-0.1.0/synpath/ids.py +61 -0
  58. synpath-0.1.0/synpath/kalshi.py +1378 -0
  59. synpath-0.1.0/synpath/matching.py +86 -0
  60. synpath-0.1.0/synpath/polymarket.py +1004 -0
  61. synpath-0.1.0/synpath/polymarket_us.py +989 -0
  62. synpath-0.1.0/synpath/remote.py +195 -0
  63. synpath-0.1.0/synpath/server/__init__.py +98 -0
  64. synpath-0.1.0/synpath/server/__main__.py +118 -0
  65. synpath-0.1.0/synpath/server/api.py +439 -0
  66. synpath-0.1.0/synpath/server/errors.py +87 -0
  67. synpath-0.1.0/synpath/server/local.py +96 -0
  68. synpath-0.1.0/synpath/server/models.py +75 -0
  69. synpath-0.1.0/synpath/server/serve.py +236 -0
  70. synpath-0.1.0/synpath/server/store.py +363 -0
  71. synpath-0.1.0/synpath/server/trading.py +764 -0
  72. synpath-0.1.0/synpath/trading/__init__.py +79 -0
  73. synpath-0.1.0/synpath/trading/__main__.py +69 -0
  74. synpath-0.1.0/synpath/trading/base.py +126 -0
  75. synpath-0.1.0/synpath/trading/credentials.py +400 -0
  76. synpath-0.1.0/synpath/trading/errors.py +94 -0
  77. synpath-0.1.0/synpath/trading/init.py +233 -0
  78. synpath-0.1.0/synpath/trading/instruments.py +162 -0
  79. synpath-0.1.0/synpath/trading/kalshi.py +957 -0
  80. synpath-0.1.0/synpath/trading/limiter.py +177 -0
  81. synpath-0.1.0/synpath/trading/money.py +172 -0
  82. synpath-0.1.0/synpath/trading/polymarket.py +1362 -0
  83. synpath-0.1.0/synpath/trading/polymarket_signing.py +478 -0
  84. synpath-0.1.0/synpath/trading/polymarket_us.py +705 -0
  85. synpath-0.1.0/synpath/trading/polymarket_us_exchange.py +825 -0
  86. synpath-0.1.0/synpath/trading/types.py +414 -0
  87. synpath-0.1.0/synpath/types.py +608 -0
  88. synpath-0.1.0/synpath/ws/__init__.py +55 -0
  89. synpath-0.1.0/synpath/ws/base.py +544 -0
  90. synpath-0.1.0/synpath/ws/grpc.py +578 -0
  91. synpath-0.1.0/synpath/ws/kalshi.py +418 -0
  92. synpath-0.1.0/synpath/ws/polymarket.py +430 -0
  93. synpath-0.1.0/synpath/ws/polymarket_us.py +299 -0
  94. synpath-0.1.0/synpath/ws/polymarket_us_exchange.py +754 -0
  95. synpath-0.1.0/tests/conftest.py +118 -0
  96. synpath-0.1.0/tests/samples/kalshi_candles.json +1 -0
  97. synpath-0.1.0/tests/samples/kalshi_events.json +1 -0
  98. synpath-0.1.0/tests/samples/kalshi_orderbook.json +1 -0
  99. synpath-0.1.0/tests/samples/kalshi_series.json +1 -0
  100. synpath-0.1.0/tests/samples/kalshi_trades.json +1 -0
  101. synpath-0.1.0/tests/samples/kalshi_trading_delete_events_orders.json +1 -0
  102. synpath-0.1.0/tests/samples/kalshi_trading_delete_events_orders_batched.json +14 -0
  103. synpath-0.1.0/tests/samples/kalshi_trading_delete_events_orders_id.json +5 -0
  104. synpath-0.1.0/tests/samples/kalshi_trading_delete_order_groups_id.json +1 -0
  105. synpath-0.1.0/tests/samples/kalshi_trading_get_account_endpoint_costs.json +90 -0
  106. synpath-0.1.0/tests/samples/kalshi_trading_get_account_limits.json +12 -0
  107. synpath-0.1.0/tests/samples/kalshi_trading_get_balance.json +24 -0
  108. synpath-0.1.0/tests/samples/kalshi_trading_get_fills.json +25 -0
  109. synpath-0.1.0/tests/samples/kalshi_trading_get_markets.json +11805 -0
  110. synpath-0.1.0/tests/samples/kalshi_trading_get_order_groups_id.json +6 -0
  111. synpath-0.1.0/tests/samples/kalshi_trading_get_orders.json +136 -0
  112. synpath-0.1.0/tests/samples/kalshi_trading_get_orders_id.json +28 -0
  113. synpath-0.1.0/tests/samples/kalshi_trading_get_orders_id_queue_position.json +3 -0
  114. synpath-0.1.0/tests/samples/kalshi_trading_get_positions.json +25 -0
  115. synpath-0.1.0/tests/samples/kalshi_trading_get_series_id.json +39 -0
  116. synpath-0.1.0/tests/samples/kalshi_trading_get_settlements.json +4 -0
  117. synpath-0.1.0/tests/samples/kalshi_trading_post_events_orders.json +7 -0
  118. synpath-0.1.0/tests/samples/kalshi_trading_post_events_orders_batched.json +18 -0
  119. synpath-0.1.0/tests/samples/kalshi_trading_post_events_orders_id_amend.json +5 -0
  120. synpath-0.1.0/tests/samples/kalshi_trading_post_events_orders_id_decrease.json +5 -0
  121. synpath-0.1.0/tests/samples/kalshi_trading_post_order_groups_create.json +5 -0
  122. synpath-0.1.0/tests/samples/kalshi_trading_put_order_groups_id_reset.json +1 -0
  123. synpath-0.1.0/tests/samples/kalshi_trading_put_order_groups_id_trigger.json +1 -0
  124. synpath-0.1.0/tests/samples/poly_book.json +1 -0
  125. synpath-0.1.0/tests/samples/poly_events.json +1 -0
  126. synpath-0.1.0/tests/samples/poly_market.json +1 -0
  127. synpath-0.1.0/tests/samples/poly_prices.json +1 -0
  128. synpath-0.1.0/tests/samples/poly_search.json +1 -0
  129. synpath-0.1.0/tests/samples/poly_trades.json +1 -0
  130. synpath-0.1.0/tests/samples/polymarket_signing_vectors.json +9430 -0
  131. synpath-0.1.0/tests/samples/polyus_bbo.json +51 -0
  132. synpath-0.1.0/tests/samples/polyus_book.json +250 -0
  133. synpath-0.1.0/tests/samples/polyus_closed_market.json +133 -0
  134. synpath-0.1.0/tests/samples/polyus_events.json +2813 -0
  135. synpath-0.1.0/tests/samples/polyus_market.json +154 -0
  136. synpath-0.1.0/tests/samples/polyus_prices.json +29 -0
  137. synpath-0.1.0/tests/samples/polyus_search.json +364 -0
  138. synpath-0.1.0/tests/samples/polyus_series.json +26 -0
  139. synpath-0.1.0/tests/samples/polyus_settlement.json +5 -0
  140. synpath-0.1.0/tests/samples/ws/kalshi_demo_transcript.jsonl +208 -0
  141. synpath-0.1.0/tests/samples/ws/polymarket_market_transcript.jsonl +133 -0
  142. synpath-0.1.0/tests/test_client.py +171 -0
  143. synpath-0.1.0/tests/test_contract.py +844 -0
  144. synpath-0.1.0/tests/test_engine.py +685 -0
  145. synpath-0.1.0/tests/test_engine_demo.py +192 -0
  146. synpath-0.1.0/tests/test_engine_orders.py +593 -0
  147. synpath-0.1.0/tests/test_engine_orders_demo.py +258 -0
  148. synpath-0.1.0/tests/test_feeds.py +249 -0
  149. synpath-0.1.0/tests/test_history_tracker.py +86 -0
  150. synpath-0.1.0/tests/test_history_types.py +60 -0
  151. synpath-0.1.0/tests/test_hosted.py +136 -0
  152. synpath-0.1.0/tests/test_hosted_auth.py +32 -0
  153. synpath-0.1.0/tests/test_init.py +104 -0
  154. synpath-0.1.0/tests/test_journal_v3.py +166 -0
  155. synpath-0.1.0/tests/test_kalshi.py +1249 -0
  156. synpath-0.1.0/tests/test_kalshi_demo.py +285 -0
  157. synpath-0.1.0/tests/test_kalshi_trading.py +561 -0
  158. synpath-0.1.0/tests/test_live.py +536 -0
  159. synpath-0.1.0/tests/test_polymarket.py +639 -0
  160. synpath-0.1.0/tests/test_polymarket_trading.py +810 -0
  161. synpath-0.1.0/tests/test_polymarket_us.py +562 -0
  162. synpath-0.1.0/tests/test_polymarket_us_trading.py +624 -0
  163. synpath-0.1.0/tests/test_remote_client.py +231 -0
  164. synpath-0.1.0/tests/test_risk_duplicates.py +42 -0
  165. synpath-0.1.0/tests/test_routed_limit.py +291 -0
  166. synpath-0.1.0/tests/test_routed_recovery.py +176 -0
  167. synpath-0.1.0/tests/test_router.py +135 -0
  168. synpath-0.1.0/tests/test_serve.py +176 -0
  169. synpath-0.1.0/tests/test_server.py +430 -0
  170. synpath-0.1.0/tests/test_server_trading.py +616 -0
  171. synpath-0.1.0/tests/test_trading_foundations.py +395 -0
  172. synpath-0.1.0/tests/test_venue_errors.py +39 -0
  173. synpath-0.1.0/tests/test_ws.py +720 -0
  174. synpath-0.1.0/tests/test_ws_grpc.py +659 -0
  175. synpath-0.1.0/tests/test_ws_live.py +112 -0
@@ -0,0 +1,39 @@
1
+ # Copy to .env (git-ignored) and fill in what you have. Nothing here reaches
2
+ # the repository; `synpath doctor` reports what loads.
3
+
4
+ # Kalshi: API key id and the RSA private key file (PEM, the BEGIN...END block only).
5
+ KALSHI_KEY_ID=
6
+ KALSHI_PRIVATE_KEY_PATH=/Users/you/.kalshi/kalshi.pem
7
+ # prod (the default) trades real money on Kalshi; demo uses Kalshi's practice exchange with a demo key.
8
+ KALSHI_ENV=prod
9
+
10
+ # Polymarket (global CLOB V2). The signer's private key, hex, and the wallet the
11
+ # orders spend from: signature type 3 with the Deposit Wallet address as funder
12
+ # for any account created since May 2026 (polymarket.com -> profile menu);
13
+ # 1 or 2 for legacy proxy / Safe wallets; 0 only for an allowlisted EOA.
14
+ POLYMARKET_PRIVATE_KEY=
15
+ POLYMARKET_SIGNATURE_TYPE=3
16
+ POLYMARKET_FUNDER=
17
+ # Optional: existing CLOB API credentials (derived from the key when absent),
18
+ # a builder code, a Relayer API key for gasless approvals / split / merge /
19
+ # redeem, and a Polygon RPC endpoint for an EOA's own transactions.
20
+ POLYMARKET_API_KEY=
21
+ POLYMARKET_API_SECRET=
22
+ POLYMARKET_API_PASSPHRASE=
23
+ POLYMARKET_BUILDER_CODE=
24
+ POLYMARKET_RELAYER_API_KEY=
25
+ POLYMARKET_RELAYER_API_KEY_ADDRESS=
26
+ POLYMARKET_RPC_URL=
27
+
28
+ # Polymarket US, retail API (api.polymarket.us): key id and base64 secret from
29
+ # polymarket.us/developer after identity verification in the app.
30
+ POLYMARKET_US_KEY_ID=
31
+ POLYMARKET_US_SECRET_KEY=
32
+
33
+ # Polymarket US, exchange API (polymarketexchange.com): an onboarded firm's
34
+ # client id, RSA private key file, participant id and trading account.
35
+ POLYMARKET_US_CLIENT_ID=
36
+ POLYMARKET_US_PRIVATE_KEY_PATH=
37
+ POLYMARKET_US_PARTICIPANT_ID=
38
+ POLYMARKET_US_ACCOUNT=
39
+ POLYMARKET_US_ENV=preprod
@@ -0,0 +1,48 @@
1
+ name: publish
2
+
3
+ # Publishing a GitHub release uploads that version to PyPI through Trusted
4
+ # Publishing: PyPI trusts this workflow in this repository, so no API token is
5
+ # stored anywhere. The release tag must match the version in pyproject.toml.
6
+ on:
7
+ release:
8
+ types: [published]
9
+
10
+ jobs:
11
+ build:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: actions/setup-python@v5
16
+ with:
17
+ python-version: "3.12"
18
+ - name: Tag matches the package version
19
+ run: |
20
+ version=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
21
+ test "${GITHUB_REF_NAME#v}" = "$version" || { echo "tag $GITHUB_REF_NAME does not match version $version"; exit 1; }
22
+ - run: pip install build twine
23
+ - run: python -m build
24
+ - run: twine check dist/*
25
+ - name: Install the wheel and run it, outside the checkout
26
+ run: |
27
+ python -m venv /tmp/check
28
+ /tmp/check/bin/pip install dist/*.whl
29
+ cd /tmp && /tmp/check/bin/python -c "import synpath; print(synpath.__file__)" && /tmp/check/bin/synpath --help > /dev/null
30
+ - uses: actions/upload-artifact@v4
31
+ with:
32
+ name: dist
33
+ path: dist/
34
+
35
+ publish:
36
+ needs: build
37
+ runs-on: ubuntu-latest
38
+ environment:
39
+ name: pypi
40
+ url: https://pypi.org/project/synpath/
41
+ permissions:
42
+ id-token: write
43
+ steps:
44
+ - uses: actions/download-artifact@v4
45
+ with:
46
+ name: dist
47
+ path: dist/
48
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,21 @@
1
+ name: tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ python-version: ["3.10", "3.12", "3.13"]
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: ${{ matrix.python-version }}
20
+ - run: pip install -e ".[dev]"
21
+ - run: pytest -q
@@ -0,0 +1,12 @@
1
+ .venv/
2
+ __pycache__/
3
+ .pytest_cache/
4
+ *.egg-info/
5
+ dist/
6
+
7
+ # secrets: venue credentials live in the environment or a local .env, never in git
8
+ .env
9
+ .env.*
10
+ !.env.example
11
+ *.pem
12
+ *.key
synpath-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Synpath
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.
synpath-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,224 @@
1
+ Metadata-Version: 2.5
2
+ Name: synpath
3
+ Version: 0.1.0
4
+ Summary: One API for prediction markets. Kalshi and Polymarket, normalized, with every price labelled by source and time.
5
+ Project-URL: Homepage, https://synpath.dev
6
+ Project-URL: Documentation, https://synpath.dev/docs
7
+ Project-URL: Repository, https://github.com/Synpath-ai/synpath
8
+ Project-URL: Issues, https://github.com/Synpath-ai/synpath/issues
9
+ Project-URL: Discord, https://discord.gg/QZzsHTpGA3
10
+ Author: Synpath
11
+ License: MIT
12
+ License-File: LICENSE
13
+ Keywords: kalshi,market-data,polymarket,prediction-markets,trading,unified-api
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Intended Audience :: Financial and Insurance Industry
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Topic :: Office/Business :: Financial
19
+ Requires-Python: >=3.10
20
+ Requires-Dist: aiosqlite>=0.20
21
+ Requires-Dist: cryptography>=42
22
+ Requires-Dist: eth-account>=0.13
23
+ Requires-Dist: fastapi>=0.110
24
+ Requires-Dist: httpx>=0.27
25
+ Requires-Dist: pydantic>=2.6
26
+ Requires-Dist: pyjwt[crypto]>=2.8
27
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
28
+ Requires-Dist: uvicorn[standard]>=0.27
29
+ Requires-Dist: websockets>=13
30
+ Provides-Extra: dev
31
+ Requires-Dist: anyio>=4; extra == 'dev'
32
+ Requires-Dist: cryptography>=42; extra == 'dev'
33
+ Requires-Dist: grpcio-tools>=1.60; extra == 'dev'
34
+ Requires-Dist: grpcio>=1.60; extra == 'dev'
35
+ Requires-Dist: protobuf>=5.26; extra == 'dev'
36
+ Requires-Dist: pyjwt[crypto]>=2.8; extra == 'dev'
37
+ Requires-Dist: pytest>=8; extra == 'dev'
38
+ Requires-Dist: respx>=0.21; extra == 'dev'
39
+ Provides-Extra: engine
40
+ Provides-Extra: grpc
41
+ Requires-Dist: cryptography>=42; extra == 'grpc'
42
+ Requires-Dist: grpcio-tools>=1.60; extra == 'grpc'
43
+ Requires-Dist: grpcio>=1.60; extra == 'grpc'
44
+ Requires-Dist: protobuf>=5.26; extra == 'grpc'
45
+ Requires-Dist: pyjwt[crypto]>=2.8; extra == 'grpc'
46
+ Provides-Extra: server
47
+ Provides-Extra: trading
48
+ Provides-Extra: ws
49
+ Description-Content-Type: text/markdown
50
+
51
+ <div align="center">
52
+
53
+ <img src="https://raw.githubusercontent.com/Synpath-ai/synpath/main/assets/synpath-banner.png" alt="Synpath: one API for every prediction market" width="100%">
54
+
55
+ # Synpath – One API for prediction markets
56
+
57
+ **Build on prediction markets without building against each one.**<br/>
58
+ One open-source API across Kalshi, Polymarket and Polymarket US: market data, order entry, live streams, and an execution engine that remembers what it sent. In-process from Python, over REST and WebSocket from anywhere.
59
+
60
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: MIT"></a>
61
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.10%2B-blue.svg" alt="Python 3.10+"></a>
62
+ <a href="https://discord.gg/QZzsHTpGA3"><img src="https://img.shields.io/badge/Discord-join-5865F2?logo=discord&logoColor=white" alt="Discord"></a>
63
+
64
+ </div>
65
+
66
+ **🆕 New: Every Kalshi order book, every tick.**<br/>
67
+ Full-depth books and trades for every past Kalshi trade. Query any moment in under a minute. **[Get the data →](https://synpath.dev/tick-data)**
68
+
69
+ ---
70
+
71
+ ## Supported Exchanges
72
+
73
+ | | Exchange | id | Market data | Order entry | Streams | Order books |
74
+ |:-:|---|---|:-:|:-:|:-:|---|
75
+ | <img src="assets/kalshi.png" width="20" height="20" alt="Kalshi"> | [Kalshi](https://kalshi.com) | `kalshi` | ✓ | ✓ | WebSocket | one book per market, both sides read it |
76
+ | <img src="assets/polymarket.png" width="20" height="20" alt="Polymarket"> | [Polymarket](https://polymarket.com) | `polymarket` | ✓ | ✓ | WebSocket | one book per outcome token |
77
+ | <img src="assets/polymarket.png" width="20" height="20" alt="Polymarket US"> | [Polymarket US](https://polymarket.us) | `polymarket_us` | ✓ | ✓ retail and exchange APIs | WebSocket and gRPC | one book per market, both sides read it |
78
+
79
+ ## Why Synpath
80
+
81
+ - **Liquidity is fragmented. Your time shouldn't be.** The same market trades on Kalshi, Polymarket and Polymarket US, each with its own API, units and quirks. Synpath gives you one interface for all of them, and smart order routing buys from whichever book is cheapest. Spend your time on alpha, not plumbing.
82
+
83
+ - **Traders deserve advanced order types.** Stops, trailing stops, icebergs, OCO, brackets, TWAP and pegs, on every venue, even where the exchange has none. Orders are journaled before they're sent, so a crash never places one twice.
84
+
85
+ - **A home after Dome and pmxt.** Dome's API shut down in April 2026, and pmxt hasn't shipped since July 2026. Synpath is MIT-licensed, actively maintained, and follows ccxt conventions.
86
+
87
+ ## Installation
88
+
89
+ ```bash
90
+ pip install synpath # market data, order entry, streams, the engine and the server
91
+ pip install "synpath[grpc]" # + Polymarket US exchange gRPC streams
92
+ ```
93
+
94
+ Python 3.10 or newer.
95
+
96
+ **Hosted API.** Run `synpath login`, then `synpath keys create`. It serves tick-level Kalshi order book and trade history, and cross-venue market matching. Trading stays on your machine: Synpath never holds your keys or funds.
97
+
98
+ ## Quick Start
99
+
100
+ **Markets and quotes**
101
+
102
+ ```python
103
+ import synpath
104
+
105
+ kalshi = synpath.Kalshi()
106
+ market = kalshi.fetch_markets(limit=1)[0]
107
+
108
+ print(market.title) # Will Elon Musk visit Mars before Aug 1, 2099?
109
+ print(market.yes.quote.bid, market.yes.quote.ask) # 0.1 0.12
110
+ ```
111
+
112
+ **Order books and search**
113
+
114
+ ```python
115
+ book = kalshi.fetch_order_book(market.id, depth=5)
116
+ book.best_bid, book.best_ask # best first on both sides
117
+ kalshi.fetch_order_book(market.id, side="no") # what NO costs
118
+
119
+ markets = kalshi.fetch_markets(query="trump", limit=10)
120
+ fee = kalshi.fetch_fee_schedule(markets[0].id)
121
+ fee.estimate(price=0.50, contracts=100) # 1.75
122
+ ```
123
+
124
+ **Same code, every venue**
125
+
126
+ ```python
127
+ for venue_id in synpath.exchanges: # ['kalshi', 'polymarket', 'polymarket_us']
128
+ with synpath.exchange(venue_id) as venue:
129
+ page = venue.fetch_markets(limit=5)
130
+
131
+ client = synpath.Client() # or one client, routed by the id
132
+ client.fetch_market("polymarket:2252244") # every id starts with its venue
133
+ ```
134
+
135
+ **Order entry**
136
+
137
+ ```python
138
+ from decimal import Decimal
139
+ from synpath import KalshiTrading, OrderRequest, Side, load_credentials, require
140
+
141
+ async with KalshiTrading(require("kalshi", load_credentials())) as kalshi:
142
+ order = await kalshi.create_order(OrderRequest(
143
+ market_id=market.id, side=Side.BUY, # buy takes YES, sell takes NO
144
+ amount=Decimal("10"), price=Decimal("0.42"), # always the YES price
145
+ ))
146
+ ```
147
+
148
+ **Live streams**
149
+
150
+ ```python
151
+ from synpath import PolymarketMarketStream, BookEvent
152
+
153
+ async with PolymarketMarketStream() as stream:
154
+ await stream.watch_order_book(["polymarket:2252244"]) # both sides of the market
155
+ async for event in stream:
156
+ if isinstance(event, BookEvent):
157
+ print(event.market_id, event.side, event.best_bid, event.best_ask)
158
+ ```
159
+
160
+ **The engine, and the orders the venues do not hold**
161
+
162
+ ```python
163
+ from synpath.engine import Engine, EngineConfig, RiskConfig
164
+ from synpath import OrderType
165
+
166
+ async with Engine({"kalshi": kalshi}, EngineConfig(journal_path="trading.db"),
167
+ risk=RiskConfig(max_order_contracts=Decimal("100"))) as engine:
168
+ await engine.submit(OrderRequest(
169
+ market_id=market.id, side=Side.SELL, amount=Decimal("20"),
170
+ type=OrderType.TRAILING_STOP, stop_price=Decimal("0.40"),
171
+ params={"trail": "0.03"}, book="alpha",
172
+ ))
173
+ ```
174
+
175
+ **REST server**
176
+
177
+ ```bash
178
+ python -m synpath.server # http://127.0.0.1:8000/docs
179
+ curl "localhost:8000/venues/kalshi/markets?limit=1"
180
+ ```
181
+
182
+ Trading has its own app, with per-account keys and an append-only audit log:
183
+
184
+ ```bash
185
+ python -m synpath.server bootstrap --control control.db # the first key
186
+ python -m synpath.server schema --trading --out openapi.json
187
+ npx openapi-typescript openapi.json --default-non-nullable false -o src/synpath.d.ts
188
+ ```
189
+
190
+ ## Documentation
191
+
192
+ - [CLI](https://synpath.dev/docs/quickstart): hosted data API sign-in and keys versus local venue trading server setup
193
+ - [Unified API](https://synpath.dev/docs/sdks/python): methods, data structures, identifiers, paging, status, errors
194
+ - [Capabilities](https://synpath.dev/docs/concepts/exchanges): what each venue can and cannot answer
195
+ - [HTTP server](https://synpath.dev/docs/api-reference): routes, envelopes, typed client generation, trading routes with per-account keys, mounting behind your own auth
196
+ - [Trading](https://synpath.dev/docs/concepts/orders): credentials, `Decimal` money, order entry on Kalshi, Polymarket and Polymarket US
197
+ - [Streaming](https://synpath.dev/docs/ws-api-reference): WebSocket and gRPC books, trades, orders and fills as self-healing event streams
198
+ - [Engine](docs/engine.md): a journal written before anything is sent, pre-trade risk, a ledger, reconciliation, a kill switch, the order types the venues do not hold, buckets that route one order across venues, paper trading
199
+ - [TypeScript client](https://synpath.dev/docs/sdks/typescript): generate a typed client from the OpenAPI document, place through cancel without Python
200
+
201
+ ## Development
202
+
203
+ ```bash
204
+ git clone https://github.com/Synpath-ai/synpath
205
+ cd synpath
206
+ pip install -e ".[dev]"
207
+
208
+ pytest # offline, against recorded venue payloads
209
+ pytest -m live # market data against the real venues
210
+ pytest -m demo # order entry on a venue's demo environment
211
+ ```
212
+
213
+ The `demo` tests place and cancel real orders on a demo exchange with the
214
+ credentials in your environment, and leave the account flat. `python -m
215
+ synpath.trading doctor` says which venues are configured without printing a
216
+ secret.
217
+
218
+ ## Prior Art
219
+
220
+ [ccxt](https://github.com/ccxt/ccxt) set the conventions this library follows. [pmxt](https://github.com/pmxt-dev/pmxt) got to prediction markets first.
221
+
222
+ ## License
223
+
224
+ MIT
@@ -0,0 +1,174 @@
1
+ <div align="center">
2
+
3
+ <img src="https://raw.githubusercontent.com/Synpath-ai/synpath/main/assets/synpath-banner.png" alt="Synpath: one API for every prediction market" width="100%">
4
+
5
+ # Synpath – One API for prediction markets
6
+
7
+ **Build on prediction markets without building against each one.**<br/>
8
+ One open-source API across Kalshi, Polymarket and Polymarket US: market data, order entry, live streams, and an execution engine that remembers what it sent. In-process from Python, over REST and WebSocket from anywhere.
9
+
10
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: MIT"></a>
11
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.10%2B-blue.svg" alt="Python 3.10+"></a>
12
+ <a href="https://discord.gg/QZzsHTpGA3"><img src="https://img.shields.io/badge/Discord-join-5865F2?logo=discord&logoColor=white" alt="Discord"></a>
13
+
14
+ </div>
15
+
16
+ **🆕 New: Every Kalshi order book, every tick.**<br/>
17
+ Full-depth books and trades for every past Kalshi trade. Query any moment in under a minute. **[Get the data →](https://synpath.dev/tick-data)**
18
+
19
+ ---
20
+
21
+ ## Supported Exchanges
22
+
23
+ | | Exchange | id | Market data | Order entry | Streams | Order books |
24
+ |:-:|---|---|:-:|:-:|:-:|---|
25
+ | <img src="assets/kalshi.png" width="20" height="20" alt="Kalshi"> | [Kalshi](https://kalshi.com) | `kalshi` | ✓ | ✓ | WebSocket | one book per market, both sides read it |
26
+ | <img src="assets/polymarket.png" width="20" height="20" alt="Polymarket"> | [Polymarket](https://polymarket.com) | `polymarket` | ✓ | ✓ | WebSocket | one book per outcome token |
27
+ | <img src="assets/polymarket.png" width="20" height="20" alt="Polymarket US"> | [Polymarket US](https://polymarket.us) | `polymarket_us` | ✓ | ✓ retail and exchange APIs | WebSocket and gRPC | one book per market, both sides read it |
28
+
29
+ ## Why Synpath
30
+
31
+ - **Liquidity is fragmented. Your time shouldn't be.** The same market trades on Kalshi, Polymarket and Polymarket US, each with its own API, units and quirks. Synpath gives you one interface for all of them, and smart order routing buys from whichever book is cheapest. Spend your time on alpha, not plumbing.
32
+
33
+ - **Traders deserve advanced order types.** Stops, trailing stops, icebergs, OCO, brackets, TWAP and pegs, on every venue, even where the exchange has none. Orders are journaled before they're sent, so a crash never places one twice.
34
+
35
+ - **A home after Dome and pmxt.** Dome's API shut down in April 2026, and pmxt hasn't shipped since July 2026. Synpath is MIT-licensed, actively maintained, and follows ccxt conventions.
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ pip install synpath # market data, order entry, streams, the engine and the server
41
+ pip install "synpath[grpc]" # + Polymarket US exchange gRPC streams
42
+ ```
43
+
44
+ Python 3.10 or newer.
45
+
46
+ **Hosted API.** Run `synpath login`, then `synpath keys create`. It serves tick-level Kalshi order book and trade history, and cross-venue market matching. Trading stays on your machine: Synpath never holds your keys or funds.
47
+
48
+ ## Quick Start
49
+
50
+ **Markets and quotes**
51
+
52
+ ```python
53
+ import synpath
54
+
55
+ kalshi = synpath.Kalshi()
56
+ market = kalshi.fetch_markets(limit=1)[0]
57
+
58
+ print(market.title) # Will Elon Musk visit Mars before Aug 1, 2099?
59
+ print(market.yes.quote.bid, market.yes.quote.ask) # 0.1 0.12
60
+ ```
61
+
62
+ **Order books and search**
63
+
64
+ ```python
65
+ book = kalshi.fetch_order_book(market.id, depth=5)
66
+ book.best_bid, book.best_ask # best first on both sides
67
+ kalshi.fetch_order_book(market.id, side="no") # what NO costs
68
+
69
+ markets = kalshi.fetch_markets(query="trump", limit=10)
70
+ fee = kalshi.fetch_fee_schedule(markets[0].id)
71
+ fee.estimate(price=0.50, contracts=100) # 1.75
72
+ ```
73
+
74
+ **Same code, every venue**
75
+
76
+ ```python
77
+ for venue_id in synpath.exchanges: # ['kalshi', 'polymarket', 'polymarket_us']
78
+ with synpath.exchange(venue_id) as venue:
79
+ page = venue.fetch_markets(limit=5)
80
+
81
+ client = synpath.Client() # or one client, routed by the id
82
+ client.fetch_market("polymarket:2252244") # every id starts with its venue
83
+ ```
84
+
85
+ **Order entry**
86
+
87
+ ```python
88
+ from decimal import Decimal
89
+ from synpath import KalshiTrading, OrderRequest, Side, load_credentials, require
90
+
91
+ async with KalshiTrading(require("kalshi", load_credentials())) as kalshi:
92
+ order = await kalshi.create_order(OrderRequest(
93
+ market_id=market.id, side=Side.BUY, # buy takes YES, sell takes NO
94
+ amount=Decimal("10"), price=Decimal("0.42"), # always the YES price
95
+ ))
96
+ ```
97
+
98
+ **Live streams**
99
+
100
+ ```python
101
+ from synpath import PolymarketMarketStream, BookEvent
102
+
103
+ async with PolymarketMarketStream() as stream:
104
+ await stream.watch_order_book(["polymarket:2252244"]) # both sides of the market
105
+ async for event in stream:
106
+ if isinstance(event, BookEvent):
107
+ print(event.market_id, event.side, event.best_bid, event.best_ask)
108
+ ```
109
+
110
+ **The engine, and the orders the venues do not hold**
111
+
112
+ ```python
113
+ from synpath.engine import Engine, EngineConfig, RiskConfig
114
+ from synpath import OrderType
115
+
116
+ async with Engine({"kalshi": kalshi}, EngineConfig(journal_path="trading.db"),
117
+ risk=RiskConfig(max_order_contracts=Decimal("100"))) as engine:
118
+ await engine.submit(OrderRequest(
119
+ market_id=market.id, side=Side.SELL, amount=Decimal("20"),
120
+ type=OrderType.TRAILING_STOP, stop_price=Decimal("0.40"),
121
+ params={"trail": "0.03"}, book="alpha",
122
+ ))
123
+ ```
124
+
125
+ **REST server**
126
+
127
+ ```bash
128
+ python -m synpath.server # http://127.0.0.1:8000/docs
129
+ curl "localhost:8000/venues/kalshi/markets?limit=1"
130
+ ```
131
+
132
+ Trading has its own app, with per-account keys and an append-only audit log:
133
+
134
+ ```bash
135
+ python -m synpath.server bootstrap --control control.db # the first key
136
+ python -m synpath.server schema --trading --out openapi.json
137
+ npx openapi-typescript openapi.json --default-non-nullable false -o src/synpath.d.ts
138
+ ```
139
+
140
+ ## Documentation
141
+
142
+ - [CLI](https://synpath.dev/docs/quickstart): hosted data API sign-in and keys versus local venue trading server setup
143
+ - [Unified API](https://synpath.dev/docs/sdks/python): methods, data structures, identifiers, paging, status, errors
144
+ - [Capabilities](https://synpath.dev/docs/concepts/exchanges): what each venue can and cannot answer
145
+ - [HTTP server](https://synpath.dev/docs/api-reference): routes, envelopes, typed client generation, trading routes with per-account keys, mounting behind your own auth
146
+ - [Trading](https://synpath.dev/docs/concepts/orders): credentials, `Decimal` money, order entry on Kalshi, Polymarket and Polymarket US
147
+ - [Streaming](https://synpath.dev/docs/ws-api-reference): WebSocket and gRPC books, trades, orders and fills as self-healing event streams
148
+ - [Engine](docs/engine.md): a journal written before anything is sent, pre-trade risk, a ledger, reconciliation, a kill switch, the order types the venues do not hold, buckets that route one order across venues, paper trading
149
+ - [TypeScript client](https://synpath.dev/docs/sdks/typescript): generate a typed client from the OpenAPI document, place through cancel without Python
150
+
151
+ ## Development
152
+
153
+ ```bash
154
+ git clone https://github.com/Synpath-ai/synpath
155
+ cd synpath
156
+ pip install -e ".[dev]"
157
+
158
+ pytest # offline, against recorded venue payloads
159
+ pytest -m live # market data against the real venues
160
+ pytest -m demo # order entry on a venue's demo environment
161
+ ```
162
+
163
+ The `demo` tests place and cancel real orders on a demo exchange with the
164
+ credentials in your environment, and leave the account flat. `python -m
165
+ synpath.trading doctor` says which venues are configured without printing a
166
+ secret.
167
+
168
+ ## Prior Art
169
+
170
+ [ccxt](https://github.com/ccxt/ccxt) set the conventions this library follows. [pmxt](https://github.com/pmxt-dev/pmxt) got to prediction markets first.
171
+
172
+ ## License
173
+
174
+ MIT
Binary file
Binary file
Binary file
@@ -0,0 +1,5 @@
1
+ node_modules/
2
+ dist/
3
+ src/
4
+ openapi.json
5
+ package-lock.json
@@ -0,0 +1,77 @@
1
+ /**
2
+ * The step's exit criterion: a TypeScript user places, watches and cancels an
3
+ * order without Python. Types come from the generated `synpath.d.ts`, so a
4
+ * wrong field name is a compile error rather than a runtime surprise.
5
+ */
6
+ import { readFileSync } from "node:fs";
7
+ import { homedir } from "node:os";
8
+ import { join } from "node:path";
9
+ import WebSocket from "ws";
10
+ import type { components } from "./src/synpath.js"; // generated: npm run types; see synpath.dev/docs/sdks/typescript
11
+
12
+ type Order = components["schemas"]["Order"];
13
+ type OrderRequest = components["schemas"]["OrderRequest"];
14
+ type PageResponseOrder = components["schemas"]["PageResponse_Order_"];
15
+
16
+ // Your own `synpath serve`: market data at /, trading at /trading.
17
+ const server = process.env.SYNPATH_SERVER ?? "http://127.0.0.1:8000";
18
+ const base = server.replace(/\/$/, "") + "/trading";
19
+
20
+ // The server's access token: given, or, for a server on this machine, read from where
21
+ // `synpath serve` left it (~/.synpath/servers.json, readable by you only).
22
+ function localKey(url: string): string | undefined {
23
+ const { hostname, port, protocol } = new URL(url);
24
+ if (!["127.0.0.1", "localhost", "::1"].includes(hostname)) return undefined;
25
+ const name = `127.0.0.1:${port || (protocol === "https:" ? 443 : 80)}`;
26
+ try {
27
+ const home = process.env.SYNPATH_HOME ?? join(homedir(), ".synpath");
28
+ return JSON.parse(readFileSync(join(home, "servers.json"), "utf8"))[name]?.access_token ?? undefined;
29
+ } catch {
30
+ return undefined;
31
+ }
32
+ }
33
+ const key = process.env.SYNPATH_ACCESS_TOKEN ?? localKey(server);
34
+ if (!key) throw new Error(`no access token for ${server}: set SYNPATH_ACCESS_TOKEN, or start synpath serve on this machine`);
35
+ const headers = { authorization: `Bearer ${key}`, "content-type": "application/json" };
36
+
37
+ async function call<T>(path: string, init: RequestInit = {}): Promise<T> {
38
+ const response = await fetch(base + path, { ...init, headers: { ...headers, ...(init.headers ?? {}) } });
39
+ if (!response.ok) {
40
+ const { error } = await response.json(); // { code, message, details } on every route
41
+ throw new Error(`${init.method ?? "GET"} ${path} → ${response.status} ${error.code}: ${error.message}`);
42
+ }
43
+ return (await response.json()) as T;
44
+ }
45
+
46
+ const seen: string[] = [];
47
+ const socket = new WebSocket(`${base.replace(/^http/, "ws")}/ws/events?key=${key}&since=0`);
48
+ await new Promise<void>((resolve) => socket.on("open", () => resolve()));
49
+ socket.on("message", (raw) => seen.push(JSON.parse(raw.toString()).kind));
50
+
51
+ const request: OrderRequest = {
52
+ market_id: "kalshi:KX-A",
53
+ side: "buy",
54
+ amount: "5",
55
+ type: "limit",
56
+ price: "0.35",
57
+ book: "typescript",
58
+ account: { venue: "kalshi", name: "desk-a", subaccount: null },
59
+ };
60
+
61
+ const placed = await call<Order>("/orders", { method: "POST", body: JSON.stringify(request) });
62
+ console.log(`placed ${placed.id} ${placed.side} ${placed.amount} at ${placed.price} (${placed.status})`);
63
+
64
+ const open = await call<PageResponseOrder>("/orders");
65
+ console.log(`open ${open.count} order(s): ${open.data.map((o) => o.id).join(", ")}`);
66
+
67
+ const canceled = await call<Order>(`/orders/${placed.id}`, { method: "DELETE" });
68
+ console.log(`cancel ${canceled.id} → ${canceled.status}`);
69
+
70
+ await new Promise((resolve) => setTimeout(resolve, 600));
71
+ socket.close();
72
+ const interesting = seen.filter((k) => k.startsWith("order.") || k.startsWith("intent."));
73
+ console.log(`watched ${seen.length} events, including ${[...new Set(interesting)].join(", ")}`);
74
+
75
+ const after = await call<PageResponseOrder>("/orders");
76
+ if (after.count !== 0) throw new Error(`expected no open orders, got ${after.count}`);
77
+ console.log("done the account is flat, no Python was involved");
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "synpath-typescript-example",
3
+ "private": true,
4
+ "type": "module",
5
+ "description": "Place, watch and cancel an order on a self-hosted Synpath server from TypeScript.",
6
+ "scripts": {
7
+ "types": "openapi-typescript openapi.json --default-non-nullable false -o src/synpath.d.ts",
8
+ "build": "tsc",
9
+ "start": "node dist/example.js"
10
+ },
11
+ "dependencies": {
12
+ "ws": "^8.18.0"
13
+ },
14
+ "devDependencies": {
15
+ "@types/node": "^22.0.0",
16
+ "@types/ws": "^8.5.12",
17
+ "openapi-typescript": "^7.4.0",
18
+ "typescript": "^5.6.0"
19
+ }
20
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "outDir": "dist",
7
+ "strict": true,
8
+ "skipLibCheck": true
9
+ },
10
+ "include": ["example.ts", "src/**/*.d.ts"]
11
+ }