polymarket-apis 0.2.2__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.

Potentially problematic release.


This version of polymarket-apis might be problematic. Click here for more details.

Files changed (40) hide show
  1. polymarket_apis/__init__.py +2 -0
  2. polymarket_apis/clients/__init__.py +0 -0
  3. polymarket_apis/clients/clob_client.py +730 -0
  4. polymarket_apis/clients/data_client.py +234 -0
  5. polymarket_apis/clients/gamma_client.py +311 -0
  6. polymarket_apis/clients/web3_client.py +261 -0
  7. polymarket_apis/clients/websockets_client.py +131 -0
  8. polymarket_apis/types/__init__.py +0 -0
  9. polymarket_apis/types/clob_types.py +494 -0
  10. polymarket_apis/types/common.py +49 -0
  11. polymarket_apis/types/data_types.py +161 -0
  12. polymarket_apis/types/gamma_types.py +313 -0
  13. polymarket_apis/types/websockets_types.py +191 -0
  14. polymarket_apis/utilities/__init__.py +0 -0
  15. polymarket_apis/utilities/config.py +36 -0
  16. polymarket_apis/utilities/constants.py +26 -0
  17. polymarket_apis/utilities/endpoints.py +37 -0
  18. polymarket_apis/utilities/exceptions.py +11 -0
  19. polymarket_apis/utilities/headers.py +54 -0
  20. polymarket_apis/utilities/order_builder/__init__.py +0 -0
  21. polymarket_apis/utilities/order_builder/builder.py +240 -0
  22. polymarket_apis/utilities/order_builder/helpers.py +61 -0
  23. polymarket_apis/utilities/signing/__init__.py +0 -0
  24. polymarket_apis/utilities/signing/eip712.py +28 -0
  25. polymarket_apis/utilities/signing/hmac.py +20 -0
  26. polymarket_apis/utilities/signing/model.py +8 -0
  27. polymarket_apis/utilities/signing/signer.py +25 -0
  28. polymarket_apis/utilities/web3/__init__.py +0 -0
  29. polymarket_apis/utilities/web3/abis/CTFExchange.json +1851 -0
  30. polymarket_apis/utilities/web3/abis/ConditionalTokens.json +705 -0
  31. polymarket_apis/utilities/web3/abis/NegRiskAdapter.json +999 -0
  32. polymarket_apis/utilities/web3/abis/NegRiskCtfExchange.json +1856 -0
  33. polymarket_apis/utilities/web3/abis/ProxyWalletFactory.json +319 -0
  34. polymarket_apis/utilities/web3/abis/UChildERC20Proxy.json +1438 -0
  35. polymarket_apis/utilities/web3/abis/__init__.py +0 -0
  36. polymarket_apis/utilities/web3/abis/custom_contract_errors.py +31 -0
  37. polymarket_apis/utilities/web3/helpers.py +8 -0
  38. polymarket_apis-0.2.2.dist-info/METADATA +18 -0
  39. polymarket_apis-0.2.2.dist-info/RECORD +40 -0
  40. polymarket_apis-0.2.2.dist-info/WHEEL +4 -0
@@ -0,0 +1,234 @@
1
+ from datetime import datetime
2
+ from typing import Literal, Optional, Union
3
+ from urllib.parse import urljoin
4
+
5
+ import httpx
6
+
7
+ from ..types.common import EthAddress, TimeseriesPoint
8
+ from ..types.data_types import (
9
+ Activity,
10
+ HolderResponse,
11
+ Position,
12
+ Trade,
13
+ UserMetric,
14
+ UserRank,
15
+ ValueResponse,
16
+ )
17
+
18
+
19
+ class PolymarketDataClient:
20
+ def __init__(self, base_url: str = "https://data-api.polymarket.com"):
21
+ self.base_url = base_url
22
+ self.client = httpx.Client(http2=True, timeout=30.0)
23
+
24
+ def _build_url(self, endpoint: str) -> str:
25
+ return urljoin(self.base_url, endpoint)
26
+
27
+ def get_positions(
28
+ self,
29
+ user: str,
30
+ condition_id: Optional[Union[str, list[str]]] = None,
31
+ size_threshold: float = 1.0,
32
+ redeemable: Optional[bool] = None,
33
+ mergeable: Optional[bool] = None,
34
+ title: Optional[str] = None,
35
+ limit: int = 100,
36
+ offset: int = 0,
37
+ sort_by: Literal[
38
+ "TOKENS",
39
+ "CURRENT",
40
+ "INITIAL",
41
+ "CASHPNL",
42
+ "PERCENTPNL",
43
+ "TITLE",
44
+ "RESOLVING",
45
+ "PRICE",
46
+ ] = "TOKENS",
47
+ sort_direction: Literal["ASC", "DESC"] = "DESC",
48
+ ) -> list[Position]:
49
+ params = {
50
+ "user": user,
51
+ "sizeThreshold": size_threshold,
52
+ "limit": min(limit, 500),
53
+ "offset": offset,
54
+ }
55
+ if isinstance(condition_id, str):
56
+ params["market"] = condition_id
57
+ if isinstance(condition_id, list):
58
+ params["market"] = ",".join(condition_id)
59
+ if redeemable is not None:
60
+ params["redeemable"] = redeemable
61
+ if mergeable is not None:
62
+ params["mergeable"] = mergeable
63
+ if title:
64
+ params["title"] = title
65
+ if sort_by:
66
+ params["sortBy"] = sort_by
67
+ if sort_direction:
68
+ params["sortDirection"] = sort_direction
69
+
70
+ response = self.client.get(self._build_url("/positions"), params=params)
71
+ response.raise_for_status()
72
+ return [Position(**pos) for pos in response.json()]
73
+
74
+ def get_trades(
75
+ self,
76
+ limit: int = 100,
77
+ offset: int = 0,
78
+ taker_only: bool = True,
79
+ filter_type: Optional[Literal["CASH", "TOKENS"]] = None,
80
+ filter_amount: Optional[float] = None,
81
+ condition_id: Optional[str] = None,
82
+ user: Optional[str] = None,
83
+ side: Optional[Literal["BUY", "SELL"]] = None,
84
+ ) -> list[Trade]:
85
+ params = {
86
+ "limit": min(limit, 500),
87
+ "offset": offset,
88
+ "takerOnly": taker_only,
89
+ }
90
+ if filter_type:
91
+ params["filterType"] = filter_type
92
+ if filter_amount:
93
+ params["filterAmount"] = filter_amount
94
+ if condition_id:
95
+ params["market"] = condition_id
96
+ if user:
97
+ params["user"] = user
98
+ if side:
99
+ params["side"] = side
100
+
101
+ response = self.client.get(self._build_url("/trades"), params=params)
102
+ response.raise_for_status()
103
+ return [Trade(**trade) for trade in response.json()]
104
+
105
+ def get_activity(
106
+ self,
107
+ user: str,
108
+ limit: int = 100,
109
+ offset: int = 0,
110
+ condition_id: Optional[Union[str, list[str]]] = None,
111
+ type: Optional[Union[Literal["TRADE", "SPLIT", "MERGE", "REDEEM", "REWARD", "CONVERSION"], list[Literal["TRADE", "SPLIT", "MERGE", "REDEEM", "REWARD", "CONVERSION"]]]] = None,
112
+ start: Optional[datetime] = None,
113
+ end: Optional[datetime] = None,
114
+ side: Optional[Literal["BUY", "SELL"]] = None,
115
+ sort_by: Literal["TIMESTAMP", "TOKENS", "CASH"] = "TIMESTAMP",
116
+ sort_direction: Literal["ASC", "DESC"] = "DESC",
117
+ ) -> list[Activity]:
118
+ params = {"user": user, "limit": min(limit, 500), "offset": offset}
119
+ if isinstance(condition_id, str):
120
+ params["market"] = condition_id
121
+ if isinstance(condition_id, list):
122
+ params["market"] = ",".join(condition_id)
123
+ if isinstance(type, str):
124
+ params["type"] = type
125
+ if isinstance(type, list):
126
+ params["type"] = ",".join(type)
127
+ if start:
128
+ params["start"] = int(start.timestamp())
129
+ if end:
130
+ params["end"] = int(end.timestamp())
131
+ if side:
132
+ params["side"] = side
133
+ if sort_by:
134
+ params["sortBy"] = sort_by
135
+ if sort_direction:
136
+ params["sortDirection"] = sort_direction
137
+
138
+ response = self.client.get(self._build_url("/activity"), params=params)
139
+ response.raise_for_status()
140
+ return [Activity(**activity) for activity in response.json()]
141
+
142
+ def get_holders(
143
+ self,
144
+ condition_id: str,
145
+ limit: int = 20,
146
+ ) -> list[HolderResponse]:
147
+ """Takes in a condition_id and returns a list of at most 20 top holders for each corresponding token_id."""
148
+ params = {"market": condition_id, "limit": limit}
149
+ response = self.client.get(self._build_url("/holders"), params=params)
150
+ response.raise_for_status()
151
+ return [
152
+ HolderResponse(**holder_data) for holder_data in response.json()
153
+ ]
154
+
155
+ def get_value(
156
+ self,
157
+ user: str,
158
+ condition_id: Optional[Union[str, list[str]]] = None,
159
+ ) -> ValueResponse:
160
+ """
161
+ Get the current value of a user's position in a set of markets.
162
+
163
+ Takes in condition_id as:
164
+ - None --> total value of positions
165
+ - str --> value of position
166
+ - list[str] --> sum of the values of positions.
167
+ """
168
+ params = {"user": user}
169
+ if isinstance(condition_id, str):
170
+ params["market"] = condition_id
171
+ if isinstance(condition_id, list):
172
+ params["market"] = ",".join(condition_id)
173
+
174
+ response = self.client.get(self._build_url("/value"), params=params)
175
+ response.raise_for_status()
176
+ return ValueResponse(**response.json()[0])
177
+
178
+ # website endpoints
179
+
180
+ def get_pnl(
181
+ self,
182
+ user: EthAddress,
183
+ period: Literal["all", "1m", "1w", "1d"] = "all",
184
+ frequency: Literal["1h", "3h", "12h", "1d"] = "1h",
185
+ ) -> list[TimeseriesPoint]:
186
+ """Get a user's PnL timeseries in the last day, week, month or all with a given frequency."""
187
+ params = {
188
+ "user_address": user,
189
+ "interval": period,
190
+ "fidelity": frequency,
191
+ }
192
+
193
+ response = self.client.get("https://user-pnl-api.polymarket.com/user-pnl", params=params)
194
+ response.raise_for_status()
195
+ return [TimeseriesPoint(**point) for point in response.json()]
196
+
197
+ def get_user_metric(self, user: EthAddress, metric: Literal["profit", "volume"] = "profit", window: Literal["1d", "7d", "30d", "all"] = "all"):
198
+ """Get a user's overall profit or volume in the last day, week, month or all."""
199
+ params = {
200
+ "address": user,
201
+ "window": window,
202
+ "limit": 1,
203
+ }
204
+ response = self.client.get("https://lb-api.polymarket.com/" + metric, params=params)
205
+ response.raise_for_status()
206
+ return UserMetric(**response.json()[0])
207
+
208
+ def get_leaderboard_user_rank(self, user: EthAddress, metric: Literal["profit", "volume"] = "profit", window: Literal["1d", "7d", "30d", "all"] = "all"):
209
+ """Get a user's rank on the leaderboard by profit or volume."""
210
+ params = {
211
+ "address": user,
212
+ "window": window,
213
+ "rankType": "pnl" if metric == "profit" else "vol",
214
+ }
215
+ response = self.client.get("https://lb-api.polymarket.com/rank", params=params)
216
+ response.raise_for_status()
217
+ return UserRank(**response.json()[0])
218
+
219
+ def get_leaderboard_top_users(self, metric: Literal["profit", "volume"] = "profit", window: Literal["1d", "7d", "30d", "all"] = "all", limit: int = 100):
220
+ """Get the leaderboard of the top at most 100 users by profit or volume."""
221
+ params = {
222
+ "window": window,
223
+ "limit": limit,
224
+ }
225
+ response = self.client.get("https://lb-api.polymarket.com/" + metric, params=params)
226
+ response.raise_for_status()
227
+ return [UserMetric(**user) for user in response.json()]
228
+
229
+
230
+ def __enter__(self):
231
+ return self
232
+
233
+ def __exit__(self, exc_type, exc_val, exc_tb):
234
+ self.client.close()
@@ -0,0 +1,311 @@
1
+ import json
2
+ from datetime import datetime
3
+ from typing import Optional, Union
4
+ from urllib.parse import urljoin
5
+
6
+ import httpx
7
+
8
+ from ..types.gamma_types import Event, GammaMarket, QueryEvent
9
+
10
+
11
+ class PolymarketGammaClient:
12
+ def __init__(self, base_url: str = "https://gamma-api.polymarket.com"):
13
+ self.base_url = base_url
14
+ self.client = httpx.Client(http2=True, timeout=30.0)
15
+
16
+ def _build_url(self, endpoint: str) -> str:
17
+ return urljoin(self.base_url, endpoint)
18
+
19
+ def get_markets(
20
+ self,
21
+ limit: Optional[int] = None,
22
+ offset: Optional[int] = None,
23
+ order: Optional[str] = None,
24
+ ascending: bool = True,
25
+ ids: Optional[list[int]] = None,
26
+ slugs: Optional[list[str]] = None,
27
+ archived: Optional[bool] = None,
28
+ active: Optional[bool] = None,
29
+ closed: Optional[bool] = None,
30
+ token_ids: Optional[list[str]] = None,
31
+ condition_ids: Optional[list[str]] = None,
32
+ liquidity_num_min: Optional[float] = None,
33
+ liquidity_num_max: Optional[float] = None,
34
+ volume_num_min: Optional[float] = None,
35
+ volume_num_max: Optional[float] = None,
36
+ start_date_min: Optional[datetime] = None,
37
+ start_date_max: Optional[datetime] = None,
38
+ end_date_min: Optional[datetime] = None,
39
+ end_date_max: Optional[datetime] = None,
40
+ tag_id: Optional[int] = None,
41
+ related_tags: Optional[bool] = False,
42
+ ) -> list[GammaMarket]:
43
+ params = {}
44
+ if limit:
45
+ params["limit"] = limit
46
+ if offset:
47
+ params["offset"] = offset
48
+ if order:
49
+ params["order"] = order
50
+ params["ascending"] = ascending
51
+ if ids:
52
+ params["id"] = ids
53
+ if slugs:
54
+ params["slug"] = slugs
55
+ if archived is not None:
56
+ params["archived"] = archived
57
+ if active is not None:
58
+ params["active"] = active
59
+ if closed is not None:
60
+ params["closed"] = closed
61
+ if token_ids:
62
+ params["clob_token_ids"] = token_ids
63
+ if condition_ids:
64
+ params["condition_ids"] = condition_ids
65
+ if liquidity_num_min:
66
+ params["liquidity_num_min"] = liquidity_num_min
67
+ if liquidity_num_max:
68
+ params["liquidity_num_max"] = liquidity_num_max
69
+ if volume_num_min:
70
+ params["volume_num_min"] = volume_num_min
71
+ if volume_num_max:
72
+ params["volume_num_max"] = volume_num_max
73
+ if start_date_min:
74
+ params["start_date_min"] = start_date_min.isoformat()
75
+ if start_date_max:
76
+ params["start_date_max"] = start_date_max.isoformat()
77
+ if end_date_min:
78
+ params["end_date_min"] = end_date_min.isoformat()
79
+ if end_date_max:
80
+ params["end_date_max"] = end_date_max.isoformat()
81
+ if tag_id:
82
+ params["tag_id"] = tag_id
83
+ if related_tags:
84
+ params["related_tags"] = related_tags
85
+
86
+ response = self.client.get(self._build_url("/markets"), params=params)
87
+ response.raise_for_status()
88
+ return [GammaMarket(**market) for market in response.json()]
89
+
90
+ def get_market(self, market_id: str) -> GammaMarket:
91
+ """Get a GammaMarket by market_id."""
92
+ response = self.client.get(self._build_url(f"/markets/{market_id}"))
93
+ response.raise_for_status()
94
+ return GammaMarket(**response.json())
95
+
96
+ def get_events(
97
+ self,
98
+ limit: Optional[int] = None,
99
+ offset: Optional[int] = None,
100
+ order: Optional[str] = None,
101
+ ascending: bool = True,
102
+ event_ids: Optional[Union[str, list[str]]] = None,
103
+ slugs: Optional[list[str]] = None,
104
+ archived: Optional[bool] = None,
105
+ active: Optional[bool] = None,
106
+ closed: Optional[bool] = None,
107
+ liquidity_min: Optional[float] = None,
108
+ liquidity_max: Optional[float] = None,
109
+ volume_min: Optional[float] = None,
110
+ volume_max: Optional[float] = None,
111
+ start_date_min: Optional[datetime] = None,
112
+ start_date_max: Optional[datetime] = None,
113
+ end_date_min: Optional[datetime] = None,
114
+ end_date_max: Optional[datetime] = None,
115
+ tag: Optional[str] = None,
116
+ tag_id: Optional[int] = None,
117
+ tag_slug: Optional[str] = None,
118
+ related_tags: bool = False,
119
+ ) -> list[Event]:
120
+ params = {}
121
+ if limit:
122
+ params["limit"] = limit
123
+ if offset:
124
+ params["offset"] = offset
125
+ if order:
126
+ params["order"] = order
127
+ params["ascending"] = ascending
128
+ if event_ids:
129
+ params["id"] = event_ids
130
+ if slugs:
131
+ params["slug"] = slugs
132
+ if archived is not None:
133
+ params["archived"] = archived
134
+ if active is not None:
135
+ params["active"] = active
136
+ if closed is not None:
137
+ params["closed"] = closed
138
+ if liquidity_min:
139
+ params["liquidity_min"] = liquidity_min
140
+ if liquidity_max:
141
+ params["liquidity_max"] = liquidity_max
142
+ if volume_min:
143
+ params["volume_min"] = volume_min
144
+ if volume_max:
145
+ params["volume_max"] = volume_max
146
+ if start_date_min:
147
+ params["start_date_min"] = start_date_min.isoformat()
148
+ if start_date_max:
149
+ params["start_date_max"] = start_date_max.isoformat()
150
+ if end_date_min:
151
+ params["end_date_min"] = end_date_min.isoformat()
152
+ if end_date_max:
153
+ params["end_date_max"] = end_date_max.isoformat()
154
+ if tag:
155
+ params["tag"] = tag
156
+ elif tag_id:
157
+ params["tag_id"] = tag_id
158
+ if related_tags:
159
+ params["related_tags"] = related_tags
160
+ elif tag_slug:
161
+ params["tag_slug"] = tag_slug
162
+
163
+ response = self.client.get(self._build_url("/events"), params=params)
164
+ response.raise_for_status()
165
+ return [Event(**event) for event in response.json()]
166
+
167
+ def get_event(self, event_id: int) -> Event:
168
+ response = self.client.get(self._build_url(f"/events/{event_id}"))
169
+ response.raise_for_status()
170
+ return Event(**response.json())
171
+
172
+ def get_all_events(
173
+ self,
174
+ order: Optional[str] = None,
175
+ ascending: bool = True,
176
+ event_ids: Optional[Union[str, list[str]]] = None,
177
+ slugs: Optional[list[str]] = None,
178
+ archived: Optional[bool] = None,
179
+ active: Optional[bool] = None,
180
+ closed: Optional[bool] = None,
181
+ liquidity_min: Optional[float] = None,
182
+ liquidity_max: Optional[float] = None,
183
+ volume_min: Optional[float] = None,
184
+ volume_max: Optional[float] = None,
185
+ start_date_min: Optional[datetime] = None,
186
+ start_date_max: Optional[datetime] = None,
187
+ end_date_min: Optional[datetime] = None,
188
+ end_date_max: Optional[datetime] = None,
189
+ tag: Optional[str] = None,
190
+ tag_id: Optional[int] = None,
191
+ tag_slug: Optional[str] = None,
192
+ related_tags: bool = False,
193
+ ) -> list[Event]:
194
+ offset = 0
195
+ events = []
196
+
197
+ while True:
198
+ part = self.get_events(
199
+ limit=500,
200
+ offset=offset,
201
+ order=order,
202
+ ascending=ascending,
203
+ event_ids=event_ids,
204
+ slugs=slugs,
205
+ archived=archived,
206
+ active=active,
207
+ closed=closed,
208
+ liquidity_min=liquidity_min,
209
+ liquidity_max=liquidity_max,
210
+ volume_min=volume_min,
211
+ volume_max=volume_max,
212
+ start_date_min=start_date_min,
213
+ start_date_max=start_date_max,
214
+ end_date_min=end_date_min,
215
+ end_date_max=end_date_max,
216
+ tag=tag,
217
+ tag_id=tag_id,
218
+ tag_slug=tag_slug,
219
+ related_tags=related_tags,
220
+ )
221
+ events.extend(part)
222
+
223
+ if len(part) < 500:
224
+ break
225
+
226
+ offset += 500
227
+
228
+ return events
229
+
230
+ def search_events(self, query: str, active: bool = True) -> list[QueryEvent]:
231
+ """Search for events by query."""
232
+ params = {"q": query, "events_status": "active" if active else "resolved"}
233
+ response = self.client.get("https://polymarket.com/api/events/global", params=params)
234
+ response.raise_for_status()
235
+ return [QueryEvent(**event) for event in response.json()["events"]]
236
+
237
+ def grok_market_summary(self, condition_id: str):
238
+ market = self.get_markets(condition_ids=[condition_id])[0]
239
+
240
+ params = {
241
+ "marketName": market.group_item_title,
242
+ "eventTitle": market.question,
243
+ "odds": market.outcome_prices[0],
244
+ "marketDescription": market.description,
245
+ "isNegRisk": market.neg_risk,
246
+ }
247
+
248
+ with self.client.stream(method="GET", url="https://polymarket.com/api/grok/market-explanation", params=params) as stream:
249
+ messages = []
250
+ citations = []
251
+ for line in stream.iter_lines():
252
+ if line:
253
+ line_str = line
254
+ if line_str.startswith("data: "):
255
+ json_part = line_str[len("data: "):]
256
+ try:
257
+ data = json.loads(json_part)
258
+ # Extract content if present
259
+ content = data.get("choices", [{}])[0].get("delta", {}).get("content", "")
260
+ if content:
261
+ messages.append(content)
262
+ print(content, end="") # Stream content
263
+ # Extract citations if present
264
+ if "citations" in data:
265
+ citations.extend(data["citations"])
266
+ except json.JSONDecodeError:
267
+ pass
268
+
269
+ # After streaming, print citations if any
270
+ if citations:
271
+ print("\n\nCitations:")
272
+ for cite in citations:
273
+ print(f"- {cite}")
274
+
275
+ def grok_event_summary(self, event_slug: str):
276
+ params = {
277
+ "prompt": event_slug,
278
+ }
279
+
280
+ with self.client.stream(method="GET", url="https://polymarket.com/api/grok/event-summary", params=params) as stream:
281
+ messages = []
282
+ citations = []
283
+ for line in stream.iter_lines():
284
+ if line:
285
+ line_str = line
286
+ if line_str.startswith("data: "):
287
+ json_part = line_str[len("data: "):]
288
+ try:
289
+ data = json.loads(json_part)
290
+ # Extract content if present
291
+ content = data.get("choices", [{}])[0].get("delta", {}).get("content", "")
292
+ if content:
293
+ messages.append(content)
294
+ print(content, end="") # Stream content
295
+ # Extract citations if present
296
+ if "citations" in data:
297
+ citations.extend(data["citations"])
298
+ except json.JSONDecodeError:
299
+ pass
300
+
301
+ # After streaming, print citations if any
302
+ if citations:
303
+ print("\n\nCitations:")
304
+ for cite in citations:
305
+ print(f"- {cite}")
306
+
307
+ def __enter__(self):
308
+ return self
309
+
310
+ def __exit__(self, exc_type, exc_val, exc_tb):
311
+ self.client.close()