hermes-pmxt 0.3.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,95 @@
1
+ name: Build and Publish
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+ workflow_dispatch:
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ test:
15
+ name: Test Python ${{ matrix.python-version }}
16
+ runs-on: ubuntu-latest
17
+ strategy:
18
+ fail-fast: false
19
+ matrix:
20
+ python-version: ["3.10", "3.11", "3.12"]
21
+
22
+ steps:
23
+ - name: Checkout
24
+ uses: actions/checkout@v4
25
+
26
+ - name: Set up Python ${{ matrix.python-version }}
27
+ uses: actions/setup-python@v5
28
+ with:
29
+ python-version: ${{ matrix.python-version }}
30
+ cache: pip
31
+
32
+ - name: Install package with dev dependencies
33
+ run: |
34
+ python -m pip install --upgrade pip
35
+ python -m pip install -e ".[dev]"
36
+
37
+ - name: Lint
38
+ run: python -m ruff check hermes_pmxt/ tests/
39
+
40
+ - name: Run unit tests
41
+ run: python -m pytest -q -m unit --tb=short
42
+
43
+ - name: Verify import
44
+ run: python -c "import hermes_pmxt; print('OK v' + hermes_pmxt.__version__)"
45
+
46
+ build:
47
+ name: Build package
48
+ runs-on: ubuntu-latest
49
+ needs: test
50
+
51
+ steps:
52
+ - name: Checkout
53
+ uses: actions/checkout@v4
54
+
55
+ - name: Set up Python
56
+ uses: actions/setup-python@v5
57
+ with:
58
+ python-version: "3.12"
59
+ cache: pip
60
+
61
+ - name: Install build tools
62
+ run: |
63
+ python -m pip install --upgrade pip
64
+ python -m pip install build twine
65
+
66
+ - name: Build distributions
67
+ run: python -m build
68
+
69
+ - name: Check distributions
70
+ run: python -m twine check dist/*
71
+
72
+ - name: Upload distributions
73
+ uses: actions/upload-artifact@v4
74
+ with:
75
+ name: python-package-distributions
76
+ path: dist/
77
+
78
+ publish:
79
+ name: Publish to PyPI
80
+ runs-on: ubuntu-latest
81
+ needs: build
82
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
83
+ permissions:
84
+ id-token: write
85
+ contents: read
86
+
87
+ steps:
88
+ - name: Download distributions
89
+ uses: actions/download-artifact@v4
90
+ with:
91
+ name: python-package-distributions
92
+ path: dist/
93
+
94
+ - name: Publish distributions to PyPI
95
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,8 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ *.pyo
5
+ .pytest_cache/
6
+ dist/
7
+ *.egg-info/
8
+ .env
@@ -0,0 +1,111 @@
1
+ # Learnings -- Building hermes-pmxt v0.3.0
2
+
3
+ Things discovered during upgrade that differ from docs/research.
4
+
5
+ ## pmxt SDK Realities (v2.50.x)
6
+
7
+ ### Version metadata is inconsistent across sources
8
+ - PyPI: `pmxt 2.50.16`
9
+ - Raw Python pyproject.toml in monorepo: `2.18.0`
10
+ - monorepo package.json: `pmxtjs ^2.17.1`
11
+ - Generated pmxt-mcp tools.ts: `2.50.16` (2026-06-18)
12
+ - **Lesson**: Rely on runtime capability detection, not version strings.
13
+
14
+ ### Dual API hosts
15
+ - `api.pmxt.dev` - reads, Router, MCP, venue passthrough
16
+ - `trade.pmxt.dev` - hosted writes + hosted account state
17
+ - Both authenticate with the same `pmxt_api_key`.
18
+
19
+ ### Python SDK has hosted mode built-in
20
+ - `Exchange.__init__()` accepts `pmxt_api_key`, `wallet_address`, `base_url`
21
+ - Auto-resolves base URL: `PMXT_BASE_URL` → `pmxt_api_key` presence → localhost
22
+ - `build_order` + `submit_order` exist natively in Python SDK >= 2.50
23
+ - `call_api(operation_id, params)` exposes raw OpenAPI endpoints
24
+
25
+ ### Router is NOT a separate Python class
26
+ - Router appears as `exchange="router"` target
27
+ - Router methods: `compareMarketPrices`, `fetchMarketMatches`, `fetchArbitrage`, etc.
28
+ - Available via `pmxt_call("methodName", "router", params={...})`
29
+
30
+ ### server.status() returns a dict, not an object
31
+ - Keys: `running`, `pid`, `port`, `version`, `uptime_seconds`, `lock_file`
32
+ - Must use `.get()` not attribute access.
33
+
34
+ ### fetch_market() (singular) doesn't work by ID
35
+ - `exchange.fetch_market(market_id="701486")` throws `PmxtError: Unknown error`
36
+ - **Workaround**: Use `fetch_markets(query=keyword, limit=N)`.
37
+
38
+ ### outcome_id is Very Long
39
+ - Polymarket outcome_ids are 70+ character token IDs
40
+ - Use labels for display, pass IDs as-is for API calls
41
+
42
+ ## pmxt-mcp Design Patterns Worth Adopting
43
+
44
+ ### Auto-generated tool surface
45
+ - PMXT-MCP generates `src/generated/tools.ts` from OpenAPI + method-verbs.json
46
+ - Auto-runs on every PMXT release via GitHub Actions `sync-mcp.yml`
47
+ - hermes-pmxt should adopt: `scripts/sync_pmxt_registry.py`
48
+
49
+ ### Flat agent-friendly schemas
50
+ - Complex params flattened to top-level MCP tool inputs
51
+ - `ArgSpec` metadata for runtime positional reconstruction
52
+ - `flatten: true` flags merged params for cleaner agent UX
53
+
54
+ ### Safety annotations built into tools
55
+ - `readOnlyHint: true` - safe for repeated calls
56
+ - `destructiveHint: true` - requires confirmation
57
+ - `idempotentHint: true` - safe to retry
58
+ - hermes-pmxt mirrors this in registry.py
59
+
60
+ ### Three config modes: hosted / local / custom
61
+ - `PMXT_API_URL` overrides everything
62
+ - `PMXT_API_KEY` → hosted `api.pmxt.dev`
63
+ - Neither → local `http://localhost:3847`
64
+ - hermes-pmxt mirrors this in config.py
65
+
66
+ ### Compact result shaping
67
+ - `verbose=false` (default): compact agent-friendly output
68
+ - `verbose=true`: raw uncompacted
69
+ - Strips market status when active, truncates descriptions
70
+ - hermes-pmxt mirrors this in shaper.py
71
+
72
+ ### Instructions favor events first
73
+ - pmxt-mcp tells agents: "users say 'market', they mean 'event'"
74
+ - Discovery: fetchEvents → drill to markets → outcomes
75
+
76
+ ## Price Scale
77
+ All prices confirmed as 0.0-1.0 (probabilities). Kalshi internally uses 0-100
78
+ but pmxt normalizes to 0-1 in the Python SDK.
79
+
80
+ ## Trade Timestamps
81
+ All timestamps are Unix milliseconds. Divide by 1000 for Python datetime.
82
+
83
+ ## Kalshi Behavior
84
+ - Returns markets with `before`/`not before` label style
85
+ - Read-only without API keys (local sidecar mode)
86
+ - Search is slower than Polymarket
87
+
88
+ ## hermes-pmxt Architecture Decisions (v0.3.0)
89
+
90
+ ### Lazy import over eager import
91
+ - `exchanges.py` uses `_get_pmxt()` lazy getter
92
+ - Package imports cleanly without pmxt installed
93
+ - Only raises ImportError when pmxt functionality is used
94
+
95
+ ### Generated registry over manual wrappers
96
+ - `registry.py` has ~33 tool definitions with safety annotations
97
+ - `pmxt_call()` dispatches to SDK methods with guard rails
98
+ - Handwritten wrappers for common flows only
99
+
100
+ ### confirmed=True gate for destructive ops
101
+ - `createOrder`, `submitOrder`, `cancelOrder` require `confirmed=True`
102
+ - `_require_confirmed()` returns human-readable error when not confirmed
103
+
104
+ ### Runtime status as first troubleshooting step
105
+ - `pmxt_runtime_status()` shows mode, URL, version, sidecar health
106
+ - Works without pmxt installed
107
+
108
+ ### Exchange list with capability detection
109
+ - 17 known exchanges in registry
110
+ - `pmxt_list_exchanges()` reports which are available in installed build
111
+ - Aliases for common naming variants
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 HERMES-PMXT
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,269 @@
1
+ Metadata-Version: 2.4
2
+ Name: hermes-pmxt
3
+ Version: 0.3.0
4
+ Summary: Prediction market integration for Hermes Agent -- search, compare, and trade across prediction market exchanges via pmxt
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.10
8
+ Requires-Dist: pmxt>=2.50.0
9
+ Provides-Extra: dev
10
+ Requires-Dist: pytest-mock>=3.0; extra == 'dev'
11
+ Requires-Dist: pytest>=8.0; extra == 'dev'
12
+ Requires-Dist: ruff>=0.4; extra == 'dev'
13
+ Description-Content-Type: text/markdown
14
+
15
+ # hermes-pmxt
16
+
17
+ Prediction market integration for [Hermes Agent](https://github.com/NousResearch/hermes-agent).
18
+ Search markets, compare prices, detect arbitrage, and trade across prediction market
19
+ exchanges via [pmxt](https://github.com/pmxt-dev/pmxt) (>= 2.50.0).
20
+
21
+ ## What This Is
22
+
23
+ A Hermes skill + Python toolset that gives any Hermes agent real-time access to prediction
24
+ markets. Instead of hallucinating probabilities, the agent checks actual market prices.
25
+
26
+ ```
27
+ User: "Will Trump win 2028?"
28
+ Agent: *calls pmxt_search + pmxt_quote*
29
+ Agent: "The market implies a 1.9% chance (No: 98.1%). Polymarket is pricing this very low."
30
+ ```
31
+
32
+ ## Installation
33
+
34
+ Install directly with `pip`, matching the one-command style used by Hermes plugins such
35
+ as Mnemosyne. You do not need to clone this repository unless you are developing it.
36
+
37
+ ```bash
38
+ pip install "git+https://github.com/0xharryriddle/hermes-pmxt.git"
39
+ ```
40
+
41
+ For local development from a checkout:
42
+
43
+ ```bash
44
+ python3 -m venv .venv
45
+ source .venv/bin/activate
46
+ pip install -e ".[dev]"
47
+ ```
48
+
49
+ ### Hermes Skill Setup
50
+
51
+ After installing the package, copy or symlink `skill/SKILL.md` into your Hermes skills
52
+ directory so agents know when and how to use the tools. Example:
53
+
54
+ ```bash
55
+ mkdir -p ~/.hermes/skills/pmxt
56
+ cp skill/SKILL.md ~/.hermes/skills/pmxt/SKILL.md
57
+ ```
58
+
59
+ If your Hermes install supports GitHub-backed skill/plugin installation, point it at
60
+ `https://github.com/0xharryriddle/hermes-pmxt` and enable the `pmxt` skill.
61
+
62
+ ## Modes
63
+
64
+ hermes-pmxt supports three runtime modes:
65
+
66
+ | Mode | Config | Behavior |
67
+ |------|--------|----------|
68
+ | **Hosted** | Set `PMXT_API_KEY` | Talks to `https://api.pmxt.dev`. Handles exchange connections, caching, and rate limits automatically. Recommended for most users. |
69
+ | **Custom** | Set `PMXT_API_URL` or `PMXT_BASE_URL` | Points to any PMXT-compatible server. |
70
+ | **Local Sidecar** | No API key/URL set | Assumes PMXT core is running at `http://localhost:3847`. For self-hosting / development. |
71
+
72
+ Check your current mode:
73
+ ```python
74
+ from hermes_pmxt import pmxt_runtime_status
75
+ print(pmxt_runtime_status())
76
+ ```
77
+
78
+ ## Quick Start
79
+
80
+ ```python
81
+ from hermes_pmxt import pmxt_search, pmxt_quote, pmxt_runtime_status
82
+
83
+ # Check status
84
+ print(pmxt_runtime_status())
85
+
86
+ # Search
87
+ result = pmxt_search("bitcoin", exchange="polymarket", limit=5)
88
+ for m in result["data"]:
89
+ prices = m.get("outcomes", [])
90
+ if prices:
91
+ print(f"{m['title'][:60]}: YES={prices[0]['price']*100:.1f}%")
92
+
93
+ # Quote
94
+ quote = pmxt_quote("bitcoin reach", exchange="polymarket")
95
+ print(f"YES: {quote['data']['yes_pct']} NO: {quote['data']['no_pct']}")
96
+ ```
97
+
98
+ ## Data Model
99
+
100
+ ```
101
+ Event (broad topic)
102
+ └── Market (tradeable question)
103
+ ├── Outcome "Yes"
104
+ └── Outcome "No"
105
+ ```
106
+
107
+ When users ask about a topic, start with events (`pmxt_events`), then drill down to
108
+ markets and outcomes.
109
+
110
+ ## Tools
111
+
112
+ ### Discovery & Research
113
+
114
+ | Function | Auth | Description |
115
+ |----------|------|-------------|
116
+ | `pmxt_search(query, exchange?, limit?, sort?, search_in?, slug?)` | No* | Search markets by keyword |
117
+ | `pmxt_events(query, exchange?, limit?, sort?, search_in?, slug?)` | No* | Search event groups |
118
+ | `pmxt_quote(identifier, exchange)` | No* | Get YES/NO probabilities |
119
+ | `pmxt_order_book(outcome_id, exchange, limit?)` | No* | Order book depth |
120
+ | `pmxt_ohlcv(outcome_id, exchange, resolution?, limit?)` | No* | Price candles |
121
+ | `pmxt_trades(outcome_id, exchange, limit?)` | No* | Recent trades |
122
+ | `pmxt_execution_price(outcome_id, exchange, side, amount)` | No* | Slippage estimate |
123
+
124
+ ### Cross-Venue & Arbitrage
125
+
126
+ | Function | Auth | Description |
127
+ |----------|------|-------------|
128
+ | `pmxt_compare_market(query, exchanges?, limit?)` | No* | Compare prices across exchanges |
129
+ | `pmxt_arbitrage_scan(query, exchanges?, threshold?)` | No* | Detect arbitrage opportunities |
130
+ | `pmxt_call("compareMarketPrices", "router", ...)` | No* | Native router comparison |
131
+ | `pmxt_call("fetchArbitrage", "router", ...)` | No* | Native arbitrage search |
132
+ | `pmxt_call("fetchHedges", "router", ...)` | No* | Hedging opportunities |
133
+
134
+ ### Portfolio & Account
135
+
136
+ | Function | Auth | Description |
137
+ |----------|------|-------------|
138
+ | `pmxt_balance(exchange)` | Yes | Account balance |
139
+ | `pmxt_positions(exchange)` | Yes | Open positions |
140
+ | `pmxt_portfolio(exchanges?)` | Yes | Cross-exchange portfolio |
141
+
142
+ ### Trading (All Destructive -- Require Explicit Confirmation)
143
+
144
+ | Function | Auth | Description |
145
+ |----------|------|-------------|
146
+ | `pmxt_build_order(...)` | Yes | Build/sign order without submitting (SAFE) |
147
+ | `pmxt_submit_order(built, exchange, confirmed=True)` | Yes | Submit a pre-built order |
148
+ | `pmxt_cancel_order(order_id, exchange, confirmed=True)` | Yes | Cancel an open order |
149
+ | `pmxt_order(...)` | Yes | Legacy one-step order (prefer build+submit) |
150
+
151
+ ### Generic API Call
152
+
153
+ | Function | Auth | Description |
154
+ |----------|------|-------------|
155
+ | `pmxt_call(method, exchange, ...)` | Varies | Generic PMXT API call with safety checks |
156
+
157
+ ### Server & Diagnostics
158
+
159
+ | Function | Auth | Description |
160
+ |----------|------|-------------|
161
+ | `pmxt_runtime_status()` | No | Full runtime status |
162
+ | `pmxt_list_exchanges()` | No | Known/available exchanges |
163
+ | `pmxt_server_status()` | No | Sidecar diagnostics |
164
+ | `pmxt_server_start()` | No | Start sidecar |
165
+ | `pmxt_server_stop()` | No | Stop sidecar |
166
+
167
+ \* Read-only tools work without credentials in local sidecar mode. Hosted mode requires `PMXT_API_KEY` for all operations.
168
+
169
+ ## Trading Safety
170
+
171
+ **Destructive operations (create, submit, cancel orders) require explicit user confirmation.**
172
+
173
+ ```python
174
+ # SAFE: Build order for preview (does NOT place any order)
175
+ built = pmxt_build_order(
176
+ market_id="market-uuid",
177
+ outcome="yes",
178
+ side="buy",
179
+ order_type="limit",
180
+ amount=10,
181
+ price=0.55,
182
+ exchange="polymarket",
183
+ )
184
+
185
+ # DESTRUCTIVE: Submit requires confirmed=True
186
+ result = pmxt_submit_order(built, "polymarket", confirmed=True)
187
+
188
+ # Without confirmed=True:
189
+ result = pmxt_submit_order(built, "polymarket")
190
+ # => {"success": False, "error": "Operation 'submit_order' is destructive..."}
191
+ ```
192
+
193
+ ## Supported Exchanges
194
+
195
+ hermes-pmxt knows about 17 venues including:
196
+
197
+ - `polymarket` / `polymarket_us`
198
+ - `kalshi` / `kalshi-demo`
199
+ - `limitless`
200
+ - `probable` / `baozi` / `myriad` / `opinion`
201
+ - `metaculus` / `smarkets`
202
+ - `gemini-titan` / `hyperliquid` / `suibets` / `rain`
203
+ - `mock` / `router`
204
+
205
+ Actual availability depends on the installed `pmxt` build. Run `pmxt_list_exchanges()` to check.
206
+
207
+ ## Environment Variables
208
+
209
+ ```bash
210
+ # Hosted mode (recommended)
211
+ export PMXT_API_KEY="pmxt_live_..."
212
+ export PMXT_WALLET_ADDRESS="0x..."
213
+ export PMXT_PRIVATE_KEY="0x..."
214
+
215
+ # Custom server
216
+ export PMXT_API_URL="https://your-server.com"
217
+ # or
218
+ export PMXT_BASE_URL="https://your-server.com"
219
+
220
+ # Venue-specific (self-hosted mode)
221
+ export POLYMARKET_PRIVATE_KEY="0x..."
222
+ export POLYMARKET_PROXY_ADDRESS="0x..." # Optional
223
+ export KALSHI_API_KEY="..."
224
+ export KALSHI_PRIVATE_KEY="..."
225
+ export LIMITLESS_API_KEY="..."
226
+ export LIMITLESS_PRIVATE_KEY="..."
227
+ export POLYMARKET_US_API_KEY="..."
228
+ export POLYMARKET_US_PRIVATE_KEY="..."
229
+ ```
230
+
231
+ ## Project Structure
232
+
233
+ ```
234
+ hermes-pmxt/
235
+ ├── hermes_pmxt/
236
+ │ ├── __init__.py # Public API exports
237
+ │ ├── config.py # Runtime config and mode detection
238
+ │ ├── exchanges.py # Exchange initialization + normalization
239
+ │ ├── registry.py # Tool registry with safety annotations
240
+ │ ├── shaper.py # Result shaping for LLM context
241
+ │ └── tools.py # Core tool functions
242
+ ├── skill/
243
+ │ └── SKILL.md # Hermes agent skill instructions
244
+ ├── examples/
245
+ │ └── demo.py # Interactive demo
246
+ ├── tests/
247
+ │ ├── conftest.py # Test path setup
248
+ │ ├── test_exchanges.py # Exchange wiring unit tests
249
+ │ └── test_tools.py # Unit + integration tests
250
+ ├── pyproject.toml
251
+ └── README.md
252
+ ```
253
+
254
+ ## Testing
255
+
256
+ ```bash
257
+ # Unit tests (no pmxt required)
258
+ python3 -m pytest -q -m unit
259
+
260
+ # All non-destructive tests
261
+ python3 -m pytest -q -m "not trading"
262
+
263
+ # Integration tests (need pmxt + sidecar/API)
264
+ python3 -m pytest -q -m integration
265
+ ```
266
+
267
+ ## License
268
+
269
+ MIT