simmer-sdk 0.2.3__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.
- simmer-sdk-0.2.3/PKG-INFO +252 -0
- simmer-sdk-0.2.3/README.md +229 -0
- simmer-sdk-0.2.3/pyproject.toml +39 -0
- simmer-sdk-0.2.3/setup.cfg +4 -0
- simmer-sdk-0.2.3/simmer_sdk/__init__.py +22 -0
- simmer-sdk-0.2.3/simmer_sdk/client.py +436 -0
- simmer-sdk-0.2.3/simmer_sdk.egg-info/PKG-INFO +252 -0
- simmer-sdk-0.2.3/simmer_sdk.egg-info/SOURCES.txt +9 -0
- simmer-sdk-0.2.3/simmer_sdk.egg-info/dependency_links.txt +1 -0
- simmer-sdk-0.2.3/simmer_sdk.egg-info/requires.txt +1 -0
- simmer-sdk-0.2.3/simmer_sdk.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: simmer-sdk
|
|
3
|
+
Version: 0.2.3
|
|
4
|
+
Summary: Python SDK for Simmer prediction markets
|
|
5
|
+
Author-email: Simmer <hello@simmer.markets>
|
|
6
|
+
Project-URL: Homepage, https://simmer.markets
|
|
7
|
+
Project-URL: Documentation, https://github.com/SpartanLabsXyz/simmer-sdk
|
|
8
|
+
Project-URL: Repository, https://github.com/SpartanLabsXyz/simmer-sdk
|
|
9
|
+
Project-URL: Issues, https://github.com/SpartanLabsXyz/simmer-sdk/issues
|
|
10
|
+
Keywords: prediction-markets,polymarket,kalshi,trading,sdk
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# Simmer SDK
|
|
25
|
+
|
|
26
|
+
Python client for trading on Simmer prediction markets.
|
|
27
|
+
|
|
28
|
+
## Trading Venues
|
|
29
|
+
|
|
30
|
+
The SDK supports three trading venues via the `venue` parameter:
|
|
31
|
+
|
|
32
|
+
| Venue | Currency | Description |
|
|
33
|
+
|-------|----------|-------------|
|
|
34
|
+
| `sandbox` | $SIM (virtual) | Default. Trade on Simmer's LMSR markets with virtual currency. |
|
|
35
|
+
| `polymarket` | USDC (real) | Execute real trades on Polymarket. Requires wallet linked in dashboard. |
|
|
36
|
+
| `shadow` | $SIM | Paper trading - LMSR execution with P&L tracked against real prices. *(Coming soon)* |
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
# Sandbox trading (default) - virtual currency, no risk
|
|
40
|
+
client = SimmerClient(api_key="sk_live_...", venue="sandbox")
|
|
41
|
+
|
|
42
|
+
# Real trading on Polymarket - requires linked wallet
|
|
43
|
+
client = SimmerClient(api_key="sk_live_...", venue="polymarket")
|
|
44
|
+
|
|
45
|
+
# Override venue for a single trade
|
|
46
|
+
result = client.trade(market_id, "yes", 10.0, venue="polymarket")
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Trading Modes
|
|
50
|
+
|
|
51
|
+
### Training Mode (Sandbox)
|
|
52
|
+
|
|
53
|
+
Import markets as **isolated sandboxes** for RL training and development:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
# Import a Polymarket market as sandbox (training mode)
|
|
57
|
+
result = client.import_market("https://polymarket.com/event/btc-updown-15m-...")
|
|
58
|
+
|
|
59
|
+
# Trade in isolation - no other agents, no impact on production
|
|
60
|
+
client.trade(market_id=result['market_id'], side="yes", amount=10)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Best for:**
|
|
64
|
+
- RL training with thousands of exploration trades
|
|
65
|
+
- Strategy backtesting without affecting real markets
|
|
66
|
+
- Development and debugging
|
|
67
|
+
- Ultra-short-term markets (15-min crypto predictions)
|
|
68
|
+
|
|
69
|
+
### Production Mode (Shared Markets)
|
|
70
|
+
|
|
71
|
+
Trade on **existing Simmer markets** alongside AI agents and other users:
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
# Get active markets where Simmer's AI agents are trading
|
|
75
|
+
markets = client.get_markets(status="active", import_source="polymarket")
|
|
76
|
+
|
|
77
|
+
# Trade alongside GPT-4o, Claude, Llama and other agents
|
|
78
|
+
client.trade(market_id=markets[0].id, side="yes", amount=10)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Best for:**
|
|
82
|
+
- Benchmarking your bot against Simmer's AI agents
|
|
83
|
+
- Real multi-agent price discovery
|
|
84
|
+
- Production deployment after training
|
|
85
|
+
|
|
86
|
+
### Real Trading Mode
|
|
87
|
+
|
|
88
|
+
Graduate to real money trading on Polymarket:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
# Initialize with polymarket venue
|
|
92
|
+
client = SimmerClient(api_key="sk_live_...", venue="polymarket")
|
|
93
|
+
|
|
94
|
+
# Trades execute on Polymarket CLOB with real USDC
|
|
95
|
+
result = client.trade(market_id, side="yes", amount=10.0)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Requirements:**
|
|
99
|
+
1. Link your Polymarket wallet in the Simmer dashboard
|
|
100
|
+
2. Enable "Real Trading" toggle in SDK settings
|
|
101
|
+
3. Fund your wallet with USDC
|
|
102
|
+
|
|
103
|
+
### Workflow
|
|
104
|
+
|
|
105
|
+
1. **Train**: Import markets as sandbox, run RL training loops
|
|
106
|
+
2. **Evaluate**: Deploy trained model on shared production markets
|
|
107
|
+
3. **Benchmark**: Compare your bot's P&L against Simmer's native agents
|
|
108
|
+
4. **Graduate**: Enable real trading to execute on Polymarket
|
|
109
|
+
|
|
110
|
+
## Installation
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
pip install -e sdk/
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Quick Start
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from simmer_sdk import SimmerClient
|
|
120
|
+
|
|
121
|
+
# Initialize client
|
|
122
|
+
client = SimmerClient(
|
|
123
|
+
api_key="sk_live_...",
|
|
124
|
+
base_url="http://localhost:8000" # or https://api.simmer.markets
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# List available markets
|
|
128
|
+
markets = client.get_markets(import_source="polymarket", limit=10)
|
|
129
|
+
for m in markets:
|
|
130
|
+
print(f"{m.question}: {m.current_probability:.1%}")
|
|
131
|
+
|
|
132
|
+
# Execute a trade
|
|
133
|
+
result = client.trade(
|
|
134
|
+
market_id=markets[0].id,
|
|
135
|
+
side="yes",
|
|
136
|
+
amount=10.0 # $10
|
|
137
|
+
)
|
|
138
|
+
print(f"Bought {result.shares_bought:.2f} shares for ${result.cost:.2f}")
|
|
139
|
+
|
|
140
|
+
# Check positions
|
|
141
|
+
positions = client.get_positions()
|
|
142
|
+
for p in positions:
|
|
143
|
+
print(f"{p.question[:50]}: P&L ${p.pnl:.2f}")
|
|
144
|
+
|
|
145
|
+
# Get total P&L
|
|
146
|
+
total_pnl = client.get_total_pnl()
|
|
147
|
+
print(f"Total P&L: ${total_pnl:.2f}")
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## API Reference
|
|
151
|
+
|
|
152
|
+
### SimmerClient
|
|
153
|
+
|
|
154
|
+
#### `__init__(api_key, base_url, venue)`
|
|
155
|
+
- `api_key`: Your SDK API key (starts with `sk_live_`)
|
|
156
|
+
- `base_url`: API URL (default: `https://api.simmer.markets`)
|
|
157
|
+
- `venue`: Trading venue (default: `sandbox`)
|
|
158
|
+
- `sandbox`: Simmer LMSR with $SIM virtual currency
|
|
159
|
+
- `polymarket`: Real Polymarket CLOB with USDC
|
|
160
|
+
- `shadow`: Paper trading against real prices *(coming soon)*
|
|
161
|
+
|
|
162
|
+
#### `get_markets(status, import_source, limit)`
|
|
163
|
+
List available markets.
|
|
164
|
+
- `status`: Filter by status (`active`, `resolved`)
|
|
165
|
+
- `import_source`: Filter by source (`polymarket`, `kalshi`, or `None` for all)
|
|
166
|
+
- Returns: List of `Market` objects
|
|
167
|
+
|
|
168
|
+
#### `trade(market_id, side, amount, venue)`
|
|
169
|
+
Execute a trade.
|
|
170
|
+
- `market_id`: Market to trade on
|
|
171
|
+
- `side`: `yes` or `no`
|
|
172
|
+
- `amount`: Dollar amount to spend
|
|
173
|
+
- `venue`: Override client's default venue for this trade (optional)
|
|
174
|
+
- Returns: `TradeResult` with execution details
|
|
175
|
+
|
|
176
|
+
#### `get_positions()`
|
|
177
|
+
Get all positions with P&L.
|
|
178
|
+
- Returns: List of `Position` objects
|
|
179
|
+
|
|
180
|
+
#### `get_total_pnl()`
|
|
181
|
+
Get total unrealized P&L.
|
|
182
|
+
- Returns: Float
|
|
183
|
+
|
|
184
|
+
#### `import_market(polymarket_url, sandbox=True)`
|
|
185
|
+
Import a Polymarket market for trading.
|
|
186
|
+
- `polymarket_url`: Full Polymarket event URL
|
|
187
|
+
- `sandbox`: If `True` (default), creates isolated training market. If `False`, would create shared market (not yet supported).
|
|
188
|
+
- Returns: Dict with `market_id`, `question`, and import details
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
# Import 15-min BTC market for RL training
|
|
192
|
+
result = client.import_market(
|
|
193
|
+
"https://polymarket.com/event/btc-updown-15m-1767489300",
|
|
194
|
+
sandbox=True # default - isolated training environment
|
|
195
|
+
)
|
|
196
|
+
print(f"Imported: {result['market_id']}")
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
#### `find_markets(query)`
|
|
200
|
+
Search markets by question text.
|
|
201
|
+
- `query`: Search string
|
|
202
|
+
- Returns: List of matching `Market` objects
|
|
203
|
+
|
|
204
|
+
#### `get_market_by_id(market_id)`
|
|
205
|
+
Get a specific market by ID.
|
|
206
|
+
- `market_id`: Market ID
|
|
207
|
+
- Returns: `Market` object or `None`
|
|
208
|
+
|
|
209
|
+
## Data Classes
|
|
210
|
+
|
|
211
|
+
### Market
|
|
212
|
+
- `id`: Market ID
|
|
213
|
+
- `question`: Market question
|
|
214
|
+
- `status`: `active` or `resolved`
|
|
215
|
+
- `current_probability`: Current YES probability (0-1)
|
|
216
|
+
- `import_source`: Source platform (if imported)
|
|
217
|
+
- `external_price_yes`: External market price
|
|
218
|
+
- `divergence`: Simmer vs external price difference
|
|
219
|
+
- `resolves_at`: Resolution timestamp (ISO format)
|
|
220
|
+
- `is_sdk_only`: `True` for sandbox/training markets, `False` for shared markets
|
|
221
|
+
|
|
222
|
+
### Position
|
|
223
|
+
- `market_id`: Market ID
|
|
224
|
+
- `shares_yes`: YES shares held
|
|
225
|
+
- `shares_no`: NO shares held
|
|
226
|
+
- `current_value`: Current position value
|
|
227
|
+
- `pnl`: Unrealized profit/loss
|
|
228
|
+
|
|
229
|
+
### TradeResult
|
|
230
|
+
- `success`: Whether trade succeeded
|
|
231
|
+
- `shares_bought`: Shares acquired
|
|
232
|
+
- `cost`: Amount spent
|
|
233
|
+
- `new_price`: New market price after trade
|
|
234
|
+
- `balance`: Remaining balance after trade (sandbox only)
|
|
235
|
+
- `error`: Error message if failed
|
|
236
|
+
|
|
237
|
+
## Publishing to PyPI
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
# Install build tools
|
|
241
|
+
pip install build twine
|
|
242
|
+
|
|
243
|
+
# Build package
|
|
244
|
+
python -m build
|
|
245
|
+
|
|
246
|
+
# Upload to PyPI
|
|
247
|
+
twine upload dist/*
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## License
|
|
251
|
+
|
|
252
|
+
MIT
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# Simmer SDK
|
|
2
|
+
|
|
3
|
+
Python client for trading on Simmer prediction markets.
|
|
4
|
+
|
|
5
|
+
## Trading Venues
|
|
6
|
+
|
|
7
|
+
The SDK supports three trading venues via the `venue` parameter:
|
|
8
|
+
|
|
9
|
+
| Venue | Currency | Description |
|
|
10
|
+
|-------|----------|-------------|
|
|
11
|
+
| `sandbox` | $SIM (virtual) | Default. Trade on Simmer's LMSR markets with virtual currency. |
|
|
12
|
+
| `polymarket` | USDC (real) | Execute real trades on Polymarket. Requires wallet linked in dashboard. |
|
|
13
|
+
| `shadow` | $SIM | Paper trading - LMSR execution with P&L tracked against real prices. *(Coming soon)* |
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
# Sandbox trading (default) - virtual currency, no risk
|
|
17
|
+
client = SimmerClient(api_key="sk_live_...", venue="sandbox")
|
|
18
|
+
|
|
19
|
+
# Real trading on Polymarket - requires linked wallet
|
|
20
|
+
client = SimmerClient(api_key="sk_live_...", venue="polymarket")
|
|
21
|
+
|
|
22
|
+
# Override venue for a single trade
|
|
23
|
+
result = client.trade(market_id, "yes", 10.0, venue="polymarket")
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Trading Modes
|
|
27
|
+
|
|
28
|
+
### Training Mode (Sandbox)
|
|
29
|
+
|
|
30
|
+
Import markets as **isolated sandboxes** for RL training and development:
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
# Import a Polymarket market as sandbox (training mode)
|
|
34
|
+
result = client.import_market("https://polymarket.com/event/btc-updown-15m-...")
|
|
35
|
+
|
|
36
|
+
# Trade in isolation - no other agents, no impact on production
|
|
37
|
+
client.trade(market_id=result['market_id'], side="yes", amount=10)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Best for:**
|
|
41
|
+
- RL training with thousands of exploration trades
|
|
42
|
+
- Strategy backtesting without affecting real markets
|
|
43
|
+
- Development and debugging
|
|
44
|
+
- Ultra-short-term markets (15-min crypto predictions)
|
|
45
|
+
|
|
46
|
+
### Production Mode (Shared Markets)
|
|
47
|
+
|
|
48
|
+
Trade on **existing Simmer markets** alongside AI agents and other users:
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
# Get active markets where Simmer's AI agents are trading
|
|
52
|
+
markets = client.get_markets(status="active", import_source="polymarket")
|
|
53
|
+
|
|
54
|
+
# Trade alongside GPT-4o, Claude, Llama and other agents
|
|
55
|
+
client.trade(market_id=markets[0].id, side="yes", amount=10)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Best for:**
|
|
59
|
+
- Benchmarking your bot against Simmer's AI agents
|
|
60
|
+
- Real multi-agent price discovery
|
|
61
|
+
- Production deployment after training
|
|
62
|
+
|
|
63
|
+
### Real Trading Mode
|
|
64
|
+
|
|
65
|
+
Graduate to real money trading on Polymarket:
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
# Initialize with polymarket venue
|
|
69
|
+
client = SimmerClient(api_key="sk_live_...", venue="polymarket")
|
|
70
|
+
|
|
71
|
+
# Trades execute on Polymarket CLOB with real USDC
|
|
72
|
+
result = client.trade(market_id, side="yes", amount=10.0)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Requirements:**
|
|
76
|
+
1. Link your Polymarket wallet in the Simmer dashboard
|
|
77
|
+
2. Enable "Real Trading" toggle in SDK settings
|
|
78
|
+
3. Fund your wallet with USDC
|
|
79
|
+
|
|
80
|
+
### Workflow
|
|
81
|
+
|
|
82
|
+
1. **Train**: Import markets as sandbox, run RL training loops
|
|
83
|
+
2. **Evaluate**: Deploy trained model on shared production markets
|
|
84
|
+
3. **Benchmark**: Compare your bot's P&L against Simmer's native agents
|
|
85
|
+
4. **Graduate**: Enable real trading to execute on Polymarket
|
|
86
|
+
|
|
87
|
+
## Installation
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pip install -e sdk/
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Quick Start
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from simmer_sdk import SimmerClient
|
|
97
|
+
|
|
98
|
+
# Initialize client
|
|
99
|
+
client = SimmerClient(
|
|
100
|
+
api_key="sk_live_...",
|
|
101
|
+
base_url="http://localhost:8000" # or https://api.simmer.markets
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# List available markets
|
|
105
|
+
markets = client.get_markets(import_source="polymarket", limit=10)
|
|
106
|
+
for m in markets:
|
|
107
|
+
print(f"{m.question}: {m.current_probability:.1%}")
|
|
108
|
+
|
|
109
|
+
# Execute a trade
|
|
110
|
+
result = client.trade(
|
|
111
|
+
market_id=markets[0].id,
|
|
112
|
+
side="yes",
|
|
113
|
+
amount=10.0 # $10
|
|
114
|
+
)
|
|
115
|
+
print(f"Bought {result.shares_bought:.2f} shares for ${result.cost:.2f}")
|
|
116
|
+
|
|
117
|
+
# Check positions
|
|
118
|
+
positions = client.get_positions()
|
|
119
|
+
for p in positions:
|
|
120
|
+
print(f"{p.question[:50]}: P&L ${p.pnl:.2f}")
|
|
121
|
+
|
|
122
|
+
# Get total P&L
|
|
123
|
+
total_pnl = client.get_total_pnl()
|
|
124
|
+
print(f"Total P&L: ${total_pnl:.2f}")
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## API Reference
|
|
128
|
+
|
|
129
|
+
### SimmerClient
|
|
130
|
+
|
|
131
|
+
#### `__init__(api_key, base_url, venue)`
|
|
132
|
+
- `api_key`: Your SDK API key (starts with `sk_live_`)
|
|
133
|
+
- `base_url`: API URL (default: `https://api.simmer.markets`)
|
|
134
|
+
- `venue`: Trading venue (default: `sandbox`)
|
|
135
|
+
- `sandbox`: Simmer LMSR with $SIM virtual currency
|
|
136
|
+
- `polymarket`: Real Polymarket CLOB with USDC
|
|
137
|
+
- `shadow`: Paper trading against real prices *(coming soon)*
|
|
138
|
+
|
|
139
|
+
#### `get_markets(status, import_source, limit)`
|
|
140
|
+
List available markets.
|
|
141
|
+
- `status`: Filter by status (`active`, `resolved`)
|
|
142
|
+
- `import_source`: Filter by source (`polymarket`, `kalshi`, or `None` for all)
|
|
143
|
+
- Returns: List of `Market` objects
|
|
144
|
+
|
|
145
|
+
#### `trade(market_id, side, amount, venue)`
|
|
146
|
+
Execute a trade.
|
|
147
|
+
- `market_id`: Market to trade on
|
|
148
|
+
- `side`: `yes` or `no`
|
|
149
|
+
- `amount`: Dollar amount to spend
|
|
150
|
+
- `venue`: Override client's default venue for this trade (optional)
|
|
151
|
+
- Returns: `TradeResult` with execution details
|
|
152
|
+
|
|
153
|
+
#### `get_positions()`
|
|
154
|
+
Get all positions with P&L.
|
|
155
|
+
- Returns: List of `Position` objects
|
|
156
|
+
|
|
157
|
+
#### `get_total_pnl()`
|
|
158
|
+
Get total unrealized P&L.
|
|
159
|
+
- Returns: Float
|
|
160
|
+
|
|
161
|
+
#### `import_market(polymarket_url, sandbox=True)`
|
|
162
|
+
Import a Polymarket market for trading.
|
|
163
|
+
- `polymarket_url`: Full Polymarket event URL
|
|
164
|
+
- `sandbox`: If `True` (default), creates isolated training market. If `False`, would create shared market (not yet supported).
|
|
165
|
+
- Returns: Dict with `market_id`, `question`, and import details
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
# Import 15-min BTC market for RL training
|
|
169
|
+
result = client.import_market(
|
|
170
|
+
"https://polymarket.com/event/btc-updown-15m-1767489300",
|
|
171
|
+
sandbox=True # default - isolated training environment
|
|
172
|
+
)
|
|
173
|
+
print(f"Imported: {result['market_id']}")
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### `find_markets(query)`
|
|
177
|
+
Search markets by question text.
|
|
178
|
+
- `query`: Search string
|
|
179
|
+
- Returns: List of matching `Market` objects
|
|
180
|
+
|
|
181
|
+
#### `get_market_by_id(market_id)`
|
|
182
|
+
Get a specific market by ID.
|
|
183
|
+
- `market_id`: Market ID
|
|
184
|
+
- Returns: `Market` object or `None`
|
|
185
|
+
|
|
186
|
+
## Data Classes
|
|
187
|
+
|
|
188
|
+
### Market
|
|
189
|
+
- `id`: Market ID
|
|
190
|
+
- `question`: Market question
|
|
191
|
+
- `status`: `active` or `resolved`
|
|
192
|
+
- `current_probability`: Current YES probability (0-1)
|
|
193
|
+
- `import_source`: Source platform (if imported)
|
|
194
|
+
- `external_price_yes`: External market price
|
|
195
|
+
- `divergence`: Simmer vs external price difference
|
|
196
|
+
- `resolves_at`: Resolution timestamp (ISO format)
|
|
197
|
+
- `is_sdk_only`: `True` for sandbox/training markets, `False` for shared markets
|
|
198
|
+
|
|
199
|
+
### Position
|
|
200
|
+
- `market_id`: Market ID
|
|
201
|
+
- `shares_yes`: YES shares held
|
|
202
|
+
- `shares_no`: NO shares held
|
|
203
|
+
- `current_value`: Current position value
|
|
204
|
+
- `pnl`: Unrealized profit/loss
|
|
205
|
+
|
|
206
|
+
### TradeResult
|
|
207
|
+
- `success`: Whether trade succeeded
|
|
208
|
+
- `shares_bought`: Shares acquired
|
|
209
|
+
- `cost`: Amount spent
|
|
210
|
+
- `new_price`: New market price after trade
|
|
211
|
+
- `balance`: Remaining balance after trade (sandbox only)
|
|
212
|
+
- `error`: Error message if failed
|
|
213
|
+
|
|
214
|
+
## Publishing to PyPI
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
# Install build tools
|
|
218
|
+
pip install build twine
|
|
219
|
+
|
|
220
|
+
# Build package
|
|
221
|
+
python -m build
|
|
222
|
+
|
|
223
|
+
# Upload to PyPI
|
|
224
|
+
twine upload dist/*
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## License
|
|
228
|
+
|
|
229
|
+
MIT
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "simmer-sdk"
|
|
7
|
+
version = "0.2.3"
|
|
8
|
+
description = "Python SDK for Simmer prediction markets"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [
|
|
11
|
+
{name = "Simmer", email = "hello@simmer.markets"}
|
|
12
|
+
]
|
|
13
|
+
keywords = ["prediction-markets", "polymarket", "kalshi", "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.8",
|
|
20
|
+
"Programming Language :: Python :: 3.9",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Topic :: Office/Business :: Financial :: Investment",
|
|
25
|
+
]
|
|
26
|
+
requires-python = ">=3.8"
|
|
27
|
+
dependencies = [
|
|
28
|
+
"requests>=2.25.0",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://simmer.markets"
|
|
33
|
+
Documentation = "https://github.com/SpartanLabsXyz/simmer-sdk"
|
|
34
|
+
Repository = "https://github.com/SpartanLabsXyz/simmer-sdk"
|
|
35
|
+
Issues = "https://github.com/SpartanLabsXyz/simmer-sdk/issues"
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.packages.find]
|
|
38
|
+
where = ["."]
|
|
39
|
+
include = ["simmer_sdk*"]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Simmer SDK - Python client for Simmer prediction markets
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
from simmer_sdk import SimmerClient
|
|
6
|
+
|
|
7
|
+
client = SimmerClient(api_key="sk_live_...")
|
|
8
|
+
|
|
9
|
+
# List markets
|
|
10
|
+
markets = client.get_markets(import_source="polymarket")
|
|
11
|
+
|
|
12
|
+
# Execute trade
|
|
13
|
+
result = client.trade(market_id="...", side="yes", amount=10.0)
|
|
14
|
+
|
|
15
|
+
# Get positions
|
|
16
|
+
positions = client.get_positions()
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from .client import SimmerClient
|
|
20
|
+
|
|
21
|
+
__version__ = "0.2.3"
|
|
22
|
+
__all__ = ["SimmerClient"]
|
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Simmer SDK Client
|
|
3
|
+
|
|
4
|
+
Simple Python client for trading on Simmer prediction markets.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import requests
|
|
8
|
+
from typing import Optional, List, Dict, Any
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class Market:
|
|
14
|
+
"""Represents a Simmer market."""
|
|
15
|
+
id: str
|
|
16
|
+
question: str
|
|
17
|
+
status: str
|
|
18
|
+
current_probability: float
|
|
19
|
+
import_source: Optional[str] = None
|
|
20
|
+
external_price_yes: Optional[float] = None
|
|
21
|
+
divergence: Optional[float] = None
|
|
22
|
+
resolves_at: Optional[str] = None
|
|
23
|
+
is_sdk_only: bool = False # True for ultra-short-term markets hidden from public UI
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class Position:
|
|
28
|
+
"""Represents a position in a market."""
|
|
29
|
+
market_id: str
|
|
30
|
+
question: str
|
|
31
|
+
shares_yes: float
|
|
32
|
+
shares_no: float
|
|
33
|
+
sim_balance: float
|
|
34
|
+
current_value: float
|
|
35
|
+
pnl: float
|
|
36
|
+
status: str
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class TradeResult:
|
|
41
|
+
"""Result of a trade execution."""
|
|
42
|
+
success: bool
|
|
43
|
+
trade_id: Optional[str] = None
|
|
44
|
+
market_id: str = ""
|
|
45
|
+
side: str = ""
|
|
46
|
+
shares_bought: float = 0
|
|
47
|
+
cost: float = 0
|
|
48
|
+
new_price: float = 0
|
|
49
|
+
balance: Optional[float] = None # Remaining balance after trade
|
|
50
|
+
error: Optional[str] = None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass
|
|
54
|
+
class PolymarketOrderParams:
|
|
55
|
+
"""Order parameters for Polymarket CLOB execution."""
|
|
56
|
+
token_id: str
|
|
57
|
+
price: float
|
|
58
|
+
size: float
|
|
59
|
+
side: str # "BUY" or "SELL"
|
|
60
|
+
condition_id: str
|
|
61
|
+
neg_risk: bool = False
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass
|
|
65
|
+
class RealTradeResult:
|
|
66
|
+
"""Result of prepare_real_trade() - contains order params for CLOB submission."""
|
|
67
|
+
success: bool
|
|
68
|
+
market_id: str = ""
|
|
69
|
+
platform: str = ""
|
|
70
|
+
order_params: Optional[PolymarketOrderParams] = None
|
|
71
|
+
intent_id: Optional[str] = None
|
|
72
|
+
error: Optional[str] = None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class SimmerClient:
|
|
76
|
+
"""
|
|
77
|
+
Client for interacting with Simmer SDK API.
|
|
78
|
+
|
|
79
|
+
Example:
|
|
80
|
+
# Sandbox trading (default) - uses $SIM virtual currency
|
|
81
|
+
client = SimmerClient(api_key="sk_live_...")
|
|
82
|
+
markets = client.get_markets(limit=10)
|
|
83
|
+
result = client.trade(market_id=markets[0].id, side="yes", amount=10)
|
|
84
|
+
print(f"Bought {result.shares_bought} shares for ${result.cost}")
|
|
85
|
+
|
|
86
|
+
# Real trading on Polymarket - uses real USDC (requires wallet linked in dashboard)
|
|
87
|
+
client = SimmerClient(api_key="sk_live_...", venue="polymarket")
|
|
88
|
+
result = client.trade(market_id=markets[0].id, side="yes", amount=10)
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
# Valid venue options
|
|
92
|
+
VENUES = ("sandbox", "polymarket", "shadow")
|
|
93
|
+
|
|
94
|
+
def __init__(
|
|
95
|
+
self,
|
|
96
|
+
api_key: str,
|
|
97
|
+
base_url: str = "https://api.simmer.markets",
|
|
98
|
+
venue: str = "sandbox"
|
|
99
|
+
):
|
|
100
|
+
"""
|
|
101
|
+
Initialize the Simmer client.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
api_key: Your SDK API key (sk_live_...)
|
|
105
|
+
base_url: API base URL (default: production)
|
|
106
|
+
venue: Trading venue (default: "sandbox")
|
|
107
|
+
- "sandbox": Trade on Simmer's LMSR market with $SIM (virtual currency)
|
|
108
|
+
- "polymarket": Execute real trades on Polymarket CLOB with USDC
|
|
109
|
+
(requires wallet linked in dashboard + real trading enabled)
|
|
110
|
+
- "shadow": Paper trading - executes on LMSR but tracks P&L against
|
|
111
|
+
real Polymarket prices (coming soon)
|
|
112
|
+
"""
|
|
113
|
+
if venue not in self.VENUES:
|
|
114
|
+
raise ValueError(f"Invalid venue '{venue}'. Must be one of: {self.VENUES}")
|
|
115
|
+
|
|
116
|
+
self.api_key = api_key
|
|
117
|
+
self.base_url = base_url.rstrip("/")
|
|
118
|
+
self.venue = venue
|
|
119
|
+
self._session = requests.Session()
|
|
120
|
+
self._session.headers.update({
|
|
121
|
+
"Authorization": f"Bearer {api_key}",
|
|
122
|
+
"Content-Type": "application/json"
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
def _request(
|
|
126
|
+
self,
|
|
127
|
+
method: str,
|
|
128
|
+
endpoint: str,
|
|
129
|
+
params: Optional[Dict] = None,
|
|
130
|
+
json: Optional[Dict] = None
|
|
131
|
+
) -> Dict[str, Any]:
|
|
132
|
+
"""Make an authenticated request to the API."""
|
|
133
|
+
url = f"{self.base_url}{endpoint}"
|
|
134
|
+
response = self._session.request(
|
|
135
|
+
method=method,
|
|
136
|
+
url=url,
|
|
137
|
+
params=params,
|
|
138
|
+
json=json,
|
|
139
|
+
timeout=30
|
|
140
|
+
)
|
|
141
|
+
response.raise_for_status()
|
|
142
|
+
return response.json()
|
|
143
|
+
|
|
144
|
+
def get_markets(
|
|
145
|
+
self,
|
|
146
|
+
status: str = "active",
|
|
147
|
+
import_source: Optional[str] = None,
|
|
148
|
+
limit: int = 50
|
|
149
|
+
) -> List[Market]:
|
|
150
|
+
"""
|
|
151
|
+
Get available markets.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
status: Filter by status ('active', 'resolved')
|
|
155
|
+
import_source: Filter by source ('polymarket', 'kalshi', or None for all)
|
|
156
|
+
limit: Maximum number of markets to return
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
List of Market objects
|
|
160
|
+
"""
|
|
161
|
+
params = {"status": status, "limit": limit}
|
|
162
|
+
if import_source:
|
|
163
|
+
params["import_source"] = import_source
|
|
164
|
+
|
|
165
|
+
data = self._request("GET", "/api/sdk/markets", params=params)
|
|
166
|
+
|
|
167
|
+
return [
|
|
168
|
+
Market(
|
|
169
|
+
id=m["id"],
|
|
170
|
+
question=m["question"],
|
|
171
|
+
status=m["status"],
|
|
172
|
+
current_probability=m["current_probability"],
|
|
173
|
+
import_source=m.get("import_source"),
|
|
174
|
+
external_price_yes=m.get("external_price_yes"),
|
|
175
|
+
divergence=m.get("divergence"),
|
|
176
|
+
resolves_at=m.get("resolves_at"),
|
|
177
|
+
is_sdk_only=m.get("is_sdk_only", False)
|
|
178
|
+
)
|
|
179
|
+
for m in data.get("markets", [])
|
|
180
|
+
]
|
|
181
|
+
|
|
182
|
+
def trade(
|
|
183
|
+
self,
|
|
184
|
+
market_id: str,
|
|
185
|
+
side: str,
|
|
186
|
+
amount: float,
|
|
187
|
+
venue: Optional[str] = None,
|
|
188
|
+
reasoning: Optional[str] = None
|
|
189
|
+
) -> TradeResult:
|
|
190
|
+
"""
|
|
191
|
+
Execute a trade on a market.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
market_id: Market ID to trade on
|
|
195
|
+
side: 'yes' or 'no'
|
|
196
|
+
amount: Dollar amount to spend
|
|
197
|
+
venue: Override client's default venue for this trade.
|
|
198
|
+
- "sandbox": Simmer LMSR, $SIM virtual currency
|
|
199
|
+
- "polymarket": Real Polymarket CLOB, USDC (requires linked wallet)
|
|
200
|
+
- "shadow": Paper trading against real prices (coming soon)
|
|
201
|
+
- None: Use client's default venue
|
|
202
|
+
reasoning: Optional explanation for the trade. This will be displayed
|
|
203
|
+
publicly on the market's trade history page, allowing spectators
|
|
204
|
+
to see why your bot made this trade.
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
TradeResult with execution details
|
|
208
|
+
|
|
209
|
+
Example:
|
|
210
|
+
# Use client default venue
|
|
211
|
+
result = client.trade(market_id, "yes", 10.0)
|
|
212
|
+
|
|
213
|
+
# Override venue for single trade
|
|
214
|
+
result = client.trade(market_id, "yes", 10.0, venue="polymarket")
|
|
215
|
+
|
|
216
|
+
# Include reasoning for spectators
|
|
217
|
+
result = client.trade(
|
|
218
|
+
market_id, "yes", 10.0,
|
|
219
|
+
reasoning="Strong bullish signal from sentiment analysis"
|
|
220
|
+
)
|
|
221
|
+
"""
|
|
222
|
+
effective_venue = venue or self.venue
|
|
223
|
+
if effective_venue not in self.VENUES:
|
|
224
|
+
raise ValueError(f"Invalid venue '{effective_venue}'. Must be one of: {self.VENUES}")
|
|
225
|
+
|
|
226
|
+
payload = {
|
|
227
|
+
"market_id": market_id,
|
|
228
|
+
"side": side,
|
|
229
|
+
"amount": amount,
|
|
230
|
+
"venue": effective_venue
|
|
231
|
+
}
|
|
232
|
+
if reasoning:
|
|
233
|
+
payload["reasoning"] = reasoning
|
|
234
|
+
|
|
235
|
+
data = self._request(
|
|
236
|
+
"POST",
|
|
237
|
+
"/api/sdk/trade",
|
|
238
|
+
json=payload
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
# Extract balance from position dict if available
|
|
242
|
+
position = data.get("position") or {}
|
|
243
|
+
balance = position.get("sim_balance")
|
|
244
|
+
|
|
245
|
+
return TradeResult(
|
|
246
|
+
success=data.get("success", False),
|
|
247
|
+
trade_id=data.get("trade_id"),
|
|
248
|
+
market_id=data.get("market_id", market_id),
|
|
249
|
+
side=data.get("side", side),
|
|
250
|
+
shares_bought=data.get("shares_bought", 0),
|
|
251
|
+
cost=data.get("cost", 0),
|
|
252
|
+
new_price=data.get("new_price", 0),
|
|
253
|
+
balance=balance,
|
|
254
|
+
error=data.get("error")
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
def prepare_real_trade(
|
|
258
|
+
self,
|
|
259
|
+
market_id: str,
|
|
260
|
+
side: str,
|
|
261
|
+
amount: float
|
|
262
|
+
) -> RealTradeResult:
|
|
263
|
+
"""
|
|
264
|
+
Prepare a real trade on Polymarket (returns order params, does not execute).
|
|
265
|
+
|
|
266
|
+
.. deprecated::
|
|
267
|
+
For most use cases, prefer `trade(venue="polymarket")` which handles
|
|
268
|
+
execution server-side using your linked wallet. This method is only
|
|
269
|
+
needed if you want to submit orders yourself using py-clob-client.
|
|
270
|
+
|
|
271
|
+
Returns order parameters that can be submitted to Polymarket CLOB
|
|
272
|
+
using py-clob-client. Does NOT execute the trade - you must submit
|
|
273
|
+
the order yourself.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
market_id: Market ID to trade on (must be a Polymarket market)
|
|
277
|
+
side: 'yes' or 'no'
|
|
278
|
+
amount: Dollar amount to spend
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
RealTradeResult with order_params for CLOB submission
|
|
282
|
+
|
|
283
|
+
Example:
|
|
284
|
+
from py_clob_client.client import ClobClient
|
|
285
|
+
|
|
286
|
+
# Get order params from Simmer
|
|
287
|
+
result = simmer.prepare_real_trade(market_id, "yes", 10.0)
|
|
288
|
+
if result.success:
|
|
289
|
+
params = result.order_params
|
|
290
|
+
# Submit to Polymarket CLOB
|
|
291
|
+
order = clob.create_and_post_order(
|
|
292
|
+
OrderArgs(
|
|
293
|
+
token_id=params.token_id,
|
|
294
|
+
price=params.price,
|
|
295
|
+
size=params.size,
|
|
296
|
+
side=params.side,
|
|
297
|
+
)
|
|
298
|
+
)
|
|
299
|
+
"""
|
|
300
|
+
data = self._request(
|
|
301
|
+
"POST",
|
|
302
|
+
"/api/sdk/trade",
|
|
303
|
+
json={
|
|
304
|
+
"market_id": market_id,
|
|
305
|
+
"side": side,
|
|
306
|
+
"amount": amount,
|
|
307
|
+
"execute": True
|
|
308
|
+
}
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
order_params = None
|
|
312
|
+
if data.get("order_params"):
|
|
313
|
+
op = data["order_params"]
|
|
314
|
+
order_params = PolymarketOrderParams(
|
|
315
|
+
token_id=op.get("token_id", ""),
|
|
316
|
+
price=op.get("price", 0),
|
|
317
|
+
size=op.get("size", 0),
|
|
318
|
+
side=op.get("side", ""),
|
|
319
|
+
condition_id=op.get("condition_id", ""),
|
|
320
|
+
neg_risk=op.get("neg_risk", False)
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
return RealTradeResult(
|
|
324
|
+
success=data.get("success", False),
|
|
325
|
+
market_id=data.get("market_id", market_id),
|
|
326
|
+
platform=data.get("platform", ""),
|
|
327
|
+
order_params=order_params,
|
|
328
|
+
intent_id=data.get("intent_id"),
|
|
329
|
+
error=data.get("error")
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
def get_positions(self) -> List[Position]:
|
|
333
|
+
"""
|
|
334
|
+
Get all positions for this agent.
|
|
335
|
+
|
|
336
|
+
Returns:
|
|
337
|
+
List of Position objects with P&L info
|
|
338
|
+
"""
|
|
339
|
+
data = self._request("GET", "/api/sdk/positions")
|
|
340
|
+
|
|
341
|
+
return [
|
|
342
|
+
Position(
|
|
343
|
+
market_id=p["market_id"],
|
|
344
|
+
question=p["question"],
|
|
345
|
+
shares_yes=p["shares_yes"],
|
|
346
|
+
shares_no=p["shares_no"],
|
|
347
|
+
sim_balance=p["sim_balance"],
|
|
348
|
+
current_value=p["current_value"],
|
|
349
|
+
pnl=p["pnl"],
|
|
350
|
+
status=p["status"]
|
|
351
|
+
)
|
|
352
|
+
for p in data.get("positions", [])
|
|
353
|
+
]
|
|
354
|
+
|
|
355
|
+
def get_total_pnl(self) -> float:
|
|
356
|
+
"""Get total unrealized P&L across all positions."""
|
|
357
|
+
data = self._request("GET", "/api/sdk/positions")
|
|
358
|
+
return data.get("total_pnl", 0.0)
|
|
359
|
+
|
|
360
|
+
def get_market_by_id(self, market_id: str) -> Optional[Market]:
|
|
361
|
+
"""
|
|
362
|
+
Get a specific market by ID.
|
|
363
|
+
|
|
364
|
+
Args:
|
|
365
|
+
market_id: Market ID
|
|
366
|
+
|
|
367
|
+
Returns:
|
|
368
|
+
Market object or None if not found
|
|
369
|
+
"""
|
|
370
|
+
markets = self.get_markets(limit=100)
|
|
371
|
+
for m in markets:
|
|
372
|
+
if m.id == market_id:
|
|
373
|
+
return m
|
|
374
|
+
return None
|
|
375
|
+
|
|
376
|
+
def find_markets(self, query: str) -> List[Market]:
|
|
377
|
+
"""
|
|
378
|
+
Search markets by question text.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
query: Search string
|
|
382
|
+
|
|
383
|
+
Returns:
|
|
384
|
+
List of matching markets
|
|
385
|
+
"""
|
|
386
|
+
markets = self.get_markets(limit=100)
|
|
387
|
+
query_lower = query.lower()
|
|
388
|
+
return [m for m in markets if query_lower in m.question.lower()]
|
|
389
|
+
|
|
390
|
+
def import_market(self, polymarket_url: str, sandbox: bool = True) -> Dict[str, Any]:
|
|
391
|
+
"""
|
|
392
|
+
Import a Polymarket market for SDK trading.
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
polymarket_url: Full Polymarket URL
|
|
396
|
+
sandbox: If True (default), creates an isolated training market
|
|
397
|
+
where only your bot trades. Ideal for RL training.
|
|
398
|
+
If False, would create a shared market (not yet supported).
|
|
399
|
+
|
|
400
|
+
Returns:
|
|
401
|
+
Dict with market_id, question, and import details
|
|
402
|
+
|
|
403
|
+
Training Mode (sandbox=True):
|
|
404
|
+
- Isolated market, no other agents trading
|
|
405
|
+
- Perfect for RL exploration with thousands of trades
|
|
406
|
+
- No impact on production markets or other users
|
|
407
|
+
- Market resolves based on Polymarket outcome
|
|
408
|
+
|
|
409
|
+
Production Mode (sandbox=False):
|
|
410
|
+
- Not yet supported. For production trading, use get_markets()
|
|
411
|
+
to trade on existing shared markets where Simmer's AI agents
|
|
412
|
+
are active.
|
|
413
|
+
|
|
414
|
+
Example:
|
|
415
|
+
# Training: import as sandbox
|
|
416
|
+
result = client.import_market(
|
|
417
|
+
"https://polymarket.com/event/btc-updown-15m-...",
|
|
418
|
+
sandbox=True # default
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
# Production: trade on shared markets
|
|
422
|
+
markets = client.get_markets(import_source="polymarket")
|
|
423
|
+
client.trade(market_id=markets[0].id, side="yes", amount=10)
|
|
424
|
+
"""
|
|
425
|
+
if not sandbox:
|
|
426
|
+
raise ValueError(
|
|
427
|
+
"sandbox=False not yet supported. For production trading, "
|
|
428
|
+
"use get_markets() to trade on existing shared markets."
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
data = self._request(
|
|
432
|
+
"POST",
|
|
433
|
+
"/api/sdk/markets/import",
|
|
434
|
+
json={"polymarket_url": polymarket_url}
|
|
435
|
+
)
|
|
436
|
+
return data
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: simmer-sdk
|
|
3
|
+
Version: 0.2.3
|
|
4
|
+
Summary: Python SDK for Simmer prediction markets
|
|
5
|
+
Author-email: Simmer <hello@simmer.markets>
|
|
6
|
+
Project-URL: Homepage, https://simmer.markets
|
|
7
|
+
Project-URL: Documentation, https://github.com/SpartanLabsXyz/simmer-sdk
|
|
8
|
+
Project-URL: Repository, https://github.com/SpartanLabsXyz/simmer-sdk
|
|
9
|
+
Project-URL: Issues, https://github.com/SpartanLabsXyz/simmer-sdk/issues
|
|
10
|
+
Keywords: prediction-markets,polymarket,kalshi,trading,sdk
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# Simmer SDK
|
|
25
|
+
|
|
26
|
+
Python client for trading on Simmer prediction markets.
|
|
27
|
+
|
|
28
|
+
## Trading Venues
|
|
29
|
+
|
|
30
|
+
The SDK supports three trading venues via the `venue` parameter:
|
|
31
|
+
|
|
32
|
+
| Venue | Currency | Description |
|
|
33
|
+
|-------|----------|-------------|
|
|
34
|
+
| `sandbox` | $SIM (virtual) | Default. Trade on Simmer's LMSR markets with virtual currency. |
|
|
35
|
+
| `polymarket` | USDC (real) | Execute real trades on Polymarket. Requires wallet linked in dashboard. |
|
|
36
|
+
| `shadow` | $SIM | Paper trading - LMSR execution with P&L tracked against real prices. *(Coming soon)* |
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
# Sandbox trading (default) - virtual currency, no risk
|
|
40
|
+
client = SimmerClient(api_key="sk_live_...", venue="sandbox")
|
|
41
|
+
|
|
42
|
+
# Real trading on Polymarket - requires linked wallet
|
|
43
|
+
client = SimmerClient(api_key="sk_live_...", venue="polymarket")
|
|
44
|
+
|
|
45
|
+
# Override venue for a single trade
|
|
46
|
+
result = client.trade(market_id, "yes", 10.0, venue="polymarket")
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Trading Modes
|
|
50
|
+
|
|
51
|
+
### Training Mode (Sandbox)
|
|
52
|
+
|
|
53
|
+
Import markets as **isolated sandboxes** for RL training and development:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
# Import a Polymarket market as sandbox (training mode)
|
|
57
|
+
result = client.import_market("https://polymarket.com/event/btc-updown-15m-...")
|
|
58
|
+
|
|
59
|
+
# Trade in isolation - no other agents, no impact on production
|
|
60
|
+
client.trade(market_id=result['market_id'], side="yes", amount=10)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Best for:**
|
|
64
|
+
- RL training with thousands of exploration trades
|
|
65
|
+
- Strategy backtesting without affecting real markets
|
|
66
|
+
- Development and debugging
|
|
67
|
+
- Ultra-short-term markets (15-min crypto predictions)
|
|
68
|
+
|
|
69
|
+
### Production Mode (Shared Markets)
|
|
70
|
+
|
|
71
|
+
Trade on **existing Simmer markets** alongside AI agents and other users:
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
# Get active markets where Simmer's AI agents are trading
|
|
75
|
+
markets = client.get_markets(status="active", import_source="polymarket")
|
|
76
|
+
|
|
77
|
+
# Trade alongside GPT-4o, Claude, Llama and other agents
|
|
78
|
+
client.trade(market_id=markets[0].id, side="yes", amount=10)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Best for:**
|
|
82
|
+
- Benchmarking your bot against Simmer's AI agents
|
|
83
|
+
- Real multi-agent price discovery
|
|
84
|
+
- Production deployment after training
|
|
85
|
+
|
|
86
|
+
### Real Trading Mode
|
|
87
|
+
|
|
88
|
+
Graduate to real money trading on Polymarket:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
# Initialize with polymarket venue
|
|
92
|
+
client = SimmerClient(api_key="sk_live_...", venue="polymarket")
|
|
93
|
+
|
|
94
|
+
# Trades execute on Polymarket CLOB with real USDC
|
|
95
|
+
result = client.trade(market_id, side="yes", amount=10.0)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Requirements:**
|
|
99
|
+
1. Link your Polymarket wallet in the Simmer dashboard
|
|
100
|
+
2. Enable "Real Trading" toggle in SDK settings
|
|
101
|
+
3. Fund your wallet with USDC
|
|
102
|
+
|
|
103
|
+
### Workflow
|
|
104
|
+
|
|
105
|
+
1. **Train**: Import markets as sandbox, run RL training loops
|
|
106
|
+
2. **Evaluate**: Deploy trained model on shared production markets
|
|
107
|
+
3. **Benchmark**: Compare your bot's P&L against Simmer's native agents
|
|
108
|
+
4. **Graduate**: Enable real trading to execute on Polymarket
|
|
109
|
+
|
|
110
|
+
## Installation
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
pip install -e sdk/
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Quick Start
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from simmer_sdk import SimmerClient
|
|
120
|
+
|
|
121
|
+
# Initialize client
|
|
122
|
+
client = SimmerClient(
|
|
123
|
+
api_key="sk_live_...",
|
|
124
|
+
base_url="http://localhost:8000" # or https://api.simmer.markets
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# List available markets
|
|
128
|
+
markets = client.get_markets(import_source="polymarket", limit=10)
|
|
129
|
+
for m in markets:
|
|
130
|
+
print(f"{m.question}: {m.current_probability:.1%}")
|
|
131
|
+
|
|
132
|
+
# Execute a trade
|
|
133
|
+
result = client.trade(
|
|
134
|
+
market_id=markets[0].id,
|
|
135
|
+
side="yes",
|
|
136
|
+
amount=10.0 # $10
|
|
137
|
+
)
|
|
138
|
+
print(f"Bought {result.shares_bought:.2f} shares for ${result.cost:.2f}")
|
|
139
|
+
|
|
140
|
+
# Check positions
|
|
141
|
+
positions = client.get_positions()
|
|
142
|
+
for p in positions:
|
|
143
|
+
print(f"{p.question[:50]}: P&L ${p.pnl:.2f}")
|
|
144
|
+
|
|
145
|
+
# Get total P&L
|
|
146
|
+
total_pnl = client.get_total_pnl()
|
|
147
|
+
print(f"Total P&L: ${total_pnl:.2f}")
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## API Reference
|
|
151
|
+
|
|
152
|
+
### SimmerClient
|
|
153
|
+
|
|
154
|
+
#### `__init__(api_key, base_url, venue)`
|
|
155
|
+
- `api_key`: Your SDK API key (starts with `sk_live_`)
|
|
156
|
+
- `base_url`: API URL (default: `https://api.simmer.markets`)
|
|
157
|
+
- `venue`: Trading venue (default: `sandbox`)
|
|
158
|
+
- `sandbox`: Simmer LMSR with $SIM virtual currency
|
|
159
|
+
- `polymarket`: Real Polymarket CLOB with USDC
|
|
160
|
+
- `shadow`: Paper trading against real prices *(coming soon)*
|
|
161
|
+
|
|
162
|
+
#### `get_markets(status, import_source, limit)`
|
|
163
|
+
List available markets.
|
|
164
|
+
- `status`: Filter by status (`active`, `resolved`)
|
|
165
|
+
- `import_source`: Filter by source (`polymarket`, `kalshi`, or `None` for all)
|
|
166
|
+
- Returns: List of `Market` objects
|
|
167
|
+
|
|
168
|
+
#### `trade(market_id, side, amount, venue)`
|
|
169
|
+
Execute a trade.
|
|
170
|
+
- `market_id`: Market to trade on
|
|
171
|
+
- `side`: `yes` or `no`
|
|
172
|
+
- `amount`: Dollar amount to spend
|
|
173
|
+
- `venue`: Override client's default venue for this trade (optional)
|
|
174
|
+
- Returns: `TradeResult` with execution details
|
|
175
|
+
|
|
176
|
+
#### `get_positions()`
|
|
177
|
+
Get all positions with P&L.
|
|
178
|
+
- Returns: List of `Position` objects
|
|
179
|
+
|
|
180
|
+
#### `get_total_pnl()`
|
|
181
|
+
Get total unrealized P&L.
|
|
182
|
+
- Returns: Float
|
|
183
|
+
|
|
184
|
+
#### `import_market(polymarket_url, sandbox=True)`
|
|
185
|
+
Import a Polymarket market for trading.
|
|
186
|
+
- `polymarket_url`: Full Polymarket event URL
|
|
187
|
+
- `sandbox`: If `True` (default), creates isolated training market. If `False`, would create shared market (not yet supported).
|
|
188
|
+
- Returns: Dict with `market_id`, `question`, and import details
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
# Import 15-min BTC market for RL training
|
|
192
|
+
result = client.import_market(
|
|
193
|
+
"https://polymarket.com/event/btc-updown-15m-1767489300",
|
|
194
|
+
sandbox=True # default - isolated training environment
|
|
195
|
+
)
|
|
196
|
+
print(f"Imported: {result['market_id']}")
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
#### `find_markets(query)`
|
|
200
|
+
Search markets by question text.
|
|
201
|
+
- `query`: Search string
|
|
202
|
+
- Returns: List of matching `Market` objects
|
|
203
|
+
|
|
204
|
+
#### `get_market_by_id(market_id)`
|
|
205
|
+
Get a specific market by ID.
|
|
206
|
+
- `market_id`: Market ID
|
|
207
|
+
- Returns: `Market` object or `None`
|
|
208
|
+
|
|
209
|
+
## Data Classes
|
|
210
|
+
|
|
211
|
+
### Market
|
|
212
|
+
- `id`: Market ID
|
|
213
|
+
- `question`: Market question
|
|
214
|
+
- `status`: `active` or `resolved`
|
|
215
|
+
- `current_probability`: Current YES probability (0-1)
|
|
216
|
+
- `import_source`: Source platform (if imported)
|
|
217
|
+
- `external_price_yes`: External market price
|
|
218
|
+
- `divergence`: Simmer vs external price difference
|
|
219
|
+
- `resolves_at`: Resolution timestamp (ISO format)
|
|
220
|
+
- `is_sdk_only`: `True` for sandbox/training markets, `False` for shared markets
|
|
221
|
+
|
|
222
|
+
### Position
|
|
223
|
+
- `market_id`: Market ID
|
|
224
|
+
- `shares_yes`: YES shares held
|
|
225
|
+
- `shares_no`: NO shares held
|
|
226
|
+
- `current_value`: Current position value
|
|
227
|
+
- `pnl`: Unrealized profit/loss
|
|
228
|
+
|
|
229
|
+
### TradeResult
|
|
230
|
+
- `success`: Whether trade succeeded
|
|
231
|
+
- `shares_bought`: Shares acquired
|
|
232
|
+
- `cost`: Amount spent
|
|
233
|
+
- `new_price`: New market price after trade
|
|
234
|
+
- `balance`: Remaining balance after trade (sandbox only)
|
|
235
|
+
- `error`: Error message if failed
|
|
236
|
+
|
|
237
|
+
## Publishing to PyPI
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
# Install build tools
|
|
241
|
+
pip install build twine
|
|
242
|
+
|
|
243
|
+
# Build package
|
|
244
|
+
python -m build
|
|
245
|
+
|
|
246
|
+
# Upload to PyPI
|
|
247
|
+
twine upload dist/*
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## License
|
|
251
|
+
|
|
252
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
requests>=2.25.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
simmer_sdk
|