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