polymarket-apis 0.3.0__py3-none-any.whl → 0.3.9__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.
- polymarket_apis/__init__.py +42 -0
- polymarket_apis/clients/__init__.py +23 -0
- polymarket_apis/clients/clob_client.py +224 -117
- polymarket_apis/clients/data_client.py +220 -67
- polymarket_apis/clients/gamma_client.py +589 -101
- polymarket_apis/clients/graphql_client.py +28 -11
- polymarket_apis/clients/web3_client.py +538 -131
- polymarket_apis/clients/websockets_client.py +24 -7
- polymarket_apis/types/__init__.py +167 -0
- polymarket_apis/types/clob_types.py +35 -14
- polymarket_apis/types/common.py +105 -35
- polymarket_apis/types/data_types.py +48 -3
- polymarket_apis/types/gamma_types.py +529 -257
- polymarket_apis/types/web3_types.py +45 -0
- polymarket_apis/types/websockets_types.py +92 -41
- polymarket_apis/utilities/config.py +1 -0
- polymarket_apis/utilities/constants.py +5 -4
- polymarket_apis/utilities/exceptions.py +9 -0
- polymarket_apis/utilities/order_builder/builder.py +38 -22
- polymarket_apis/utilities/order_builder/helpers.py +0 -1
- polymarket_apis/utilities/signing/hmac.py +5 -1
- polymarket_apis/utilities/signing/signer.py +2 -2
- polymarket_apis/utilities/web3/abis/Safe.json +1138 -0
- polymarket_apis/utilities/web3/abis/SafeProxyFactory.json +224 -0
- polymarket_apis/utilities/web3/abis/custom_contract_errors.py +1 -1
- polymarket_apis/utilities/web3/helpers.py +235 -0
- {polymarket_apis-0.3.0.dist-info → polymarket_apis-0.3.9.dist-info}/METADATA +48 -8
- polymarket_apis-0.3.9.dist-info/RECORD +44 -0
- polymarket_apis/utilities/schemas/activity-subgraph.graphql +0 -86
- polymarket_apis/utilities/schemas/open-interest.graphql +0 -30
- polymarket_apis-0.3.0.dist-info/RECORD +0 -43
- {polymarket_apis-0.3.0.dist-info → polymarket_apis-0.3.9.dist-info}/WHEEL +0 -0
|
@@ -4,10 +4,14 @@ from urllib.parse import urljoin
|
|
|
4
4
|
|
|
5
5
|
import httpx
|
|
6
6
|
|
|
7
|
+
from ..clients.graphql_client import PolymarketGraphQLClient
|
|
7
8
|
from ..types.common import EthAddress, TimeseriesPoint
|
|
8
9
|
from ..types.data_types import (
|
|
9
10
|
Activity,
|
|
11
|
+
EventLiveVolume,
|
|
12
|
+
GQLPosition,
|
|
10
13
|
HolderResponse,
|
|
14
|
+
MarketValue,
|
|
11
15
|
Position,
|
|
12
16
|
Trade,
|
|
13
17
|
UserMetric,
|
|
@@ -20,33 +24,76 @@ class PolymarketDataClient:
|
|
|
20
24
|
def __init__(self, base_url: str = "https://data-api.polymarket.com"):
|
|
21
25
|
self.base_url = base_url
|
|
22
26
|
self.client = httpx.Client(http2=True, timeout=30.0)
|
|
27
|
+
self.gql_positions_client = PolymarketGraphQLClient(
|
|
28
|
+
endpoint_name="positions_subgraph"
|
|
29
|
+
)
|
|
23
30
|
|
|
24
31
|
def _build_url(self, endpoint: str) -> str:
|
|
25
32
|
return urljoin(self.base_url, endpoint)
|
|
26
33
|
|
|
34
|
+
def get_ok(self) -> str:
|
|
35
|
+
response = self.client.get(self.base_url)
|
|
36
|
+
response.raise_for_status()
|
|
37
|
+
return response.json()["data"]
|
|
38
|
+
|
|
39
|
+
def get_all_positions(
|
|
40
|
+
self,
|
|
41
|
+
user: EthAddress,
|
|
42
|
+
size_threshold: float = 0.0,
|
|
43
|
+
):
|
|
44
|
+
# data-api /positions endpoint does not support fetching all positions without filters
|
|
45
|
+
# a workaround is to use the GraphQL positions subgraph directly
|
|
46
|
+
query = f"""query {{
|
|
47
|
+
userBalances(where: {{
|
|
48
|
+
user: "{user.lower()}",
|
|
49
|
+
balance_gt: "{int(size_threshold * 10**6)}"
|
|
50
|
+
}}) {{
|
|
51
|
+
user
|
|
52
|
+
asset {{
|
|
53
|
+
id
|
|
54
|
+
condition {{
|
|
55
|
+
id
|
|
56
|
+
}}
|
|
57
|
+
complement
|
|
58
|
+
outcomeIndex
|
|
59
|
+
}}
|
|
60
|
+
balance
|
|
61
|
+
}}
|
|
62
|
+
}}
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
response = self.gql_positions_client.query(query)
|
|
66
|
+
return [GQLPosition(**pos) for pos in response["userBalances"]]
|
|
67
|
+
|
|
27
68
|
def get_positions(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
69
|
+
self,
|
|
70
|
+
user: EthAddress,
|
|
71
|
+
condition_id: Optional[
|
|
72
|
+
Union[str, list[str]]
|
|
73
|
+
] = None, # mutually exclusive with event_id
|
|
74
|
+
event_id: Optional[
|
|
75
|
+
Union[int, list[int]]
|
|
76
|
+
] = None, # mutually exclusive with condition_id
|
|
77
|
+
size_threshold: float = 1.0,
|
|
78
|
+
redeemable: bool = False,
|
|
79
|
+
mergeable: bool = False,
|
|
80
|
+
title: Optional[str] = None,
|
|
81
|
+
limit: int = 100,
|
|
82
|
+
offset: int = 0,
|
|
83
|
+
sort_by: Literal[
|
|
84
|
+
"TOKENS",
|
|
85
|
+
"CURRENT",
|
|
86
|
+
"INITIAL",
|
|
87
|
+
"CASHPNL",
|
|
88
|
+
"PERCENTPNL",
|
|
89
|
+
"TITLE",
|
|
90
|
+
"RESOLVING",
|
|
91
|
+
"PRICE",
|
|
92
|
+
"AVGPRICE",
|
|
93
|
+
] = "TOKENS",
|
|
94
|
+
sort_direction: Literal["ASC", "DESC"] = "DESC",
|
|
48
95
|
) -> list[Position]:
|
|
49
|
-
params = {
|
|
96
|
+
params: dict[str, str | list[str] | int | float] = {
|
|
50
97
|
"user": user,
|
|
51
98
|
"sizeThreshold": size_threshold,
|
|
52
99
|
"limit": min(limit, 500),
|
|
@@ -56,6 +103,10 @@ class PolymarketDataClient:
|
|
|
56
103
|
params["market"] = condition_id
|
|
57
104
|
if isinstance(condition_id, list):
|
|
58
105
|
params["market"] = ",".join(condition_id)
|
|
106
|
+
if isinstance(event_id, str):
|
|
107
|
+
params["eventId"] = event_id
|
|
108
|
+
if isinstance(event_id, list):
|
|
109
|
+
params["eventId"] = [str(i) for i in event_id]
|
|
59
110
|
if redeemable is not None:
|
|
60
111
|
params["redeemable"] = redeemable
|
|
61
112
|
if mergeable is not None:
|
|
@@ -72,17 +123,20 @@ class PolymarketDataClient:
|
|
|
72
123
|
return [Position(**pos) for pos in response.json()]
|
|
73
124
|
|
|
74
125
|
def get_trades(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
126
|
+
self,
|
|
127
|
+
limit: int = 100,
|
|
128
|
+
offset: int = 0,
|
|
129
|
+
taker_only: bool = True,
|
|
130
|
+
filter_type: Optional[Literal["CASH", "TOKENS"]] = None,
|
|
131
|
+
filter_amount: Optional[
|
|
132
|
+
float
|
|
133
|
+
] = None, # must be provided together with filter_type
|
|
134
|
+
condition_id: Optional[str | list[str]] = None,
|
|
135
|
+
event_id: Optional[int | list[int]] = None,
|
|
136
|
+
user: Optional[str] = None,
|
|
137
|
+
side: Optional[Literal["BUY", "SELL"]] = None,
|
|
84
138
|
) -> list[Trade]:
|
|
85
|
-
params = {
|
|
139
|
+
params: dict[str, int | bool | float | str | list[str]] = {
|
|
86
140
|
"limit": min(limit, 500),
|
|
87
141
|
"offset": offset,
|
|
88
142
|
"takerOnly": taker_only,
|
|
@@ -91,8 +145,14 @@ class PolymarketDataClient:
|
|
|
91
145
|
params["filterType"] = filter_type
|
|
92
146
|
if filter_amount:
|
|
93
147
|
params["filterAmount"] = filter_amount
|
|
94
|
-
if condition_id:
|
|
148
|
+
if isinstance(condition_id, str):
|
|
95
149
|
params["market"] = condition_id
|
|
150
|
+
if isinstance(condition_id, list):
|
|
151
|
+
params["market"] = ",".join(condition_id)
|
|
152
|
+
if isinstance(event_id, str):
|
|
153
|
+
params["eventId"] = event_id
|
|
154
|
+
if isinstance(event_id, list):
|
|
155
|
+
params["eventId"] = [str(i) for i in event_id]
|
|
96
156
|
if user:
|
|
97
157
|
params["user"] = user
|
|
98
158
|
if side:
|
|
@@ -103,23 +163,39 @@ class PolymarketDataClient:
|
|
|
103
163
|
return [Trade(**trade) for trade in response.json()]
|
|
104
164
|
|
|
105
165
|
def get_activity(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
166
|
+
self,
|
|
167
|
+
user: EthAddress,
|
|
168
|
+
limit: int = 100,
|
|
169
|
+
offset: int = 0,
|
|
170
|
+
condition_id: Optional[Union[str, list[str]]] = None,
|
|
171
|
+
event_id: Optional[Union[int, list[int]]] = None,
|
|
172
|
+
type: Optional[
|
|
173
|
+
Union[
|
|
174
|
+
Literal["TRADE", "SPLIT", "MERGE", "REDEEM", "REWARD", "CONVERSION"],
|
|
175
|
+
list[
|
|
176
|
+
Literal["TRADE", "SPLIT", "MERGE", "REDEEM", "REWARD", "CONVERSION"]
|
|
177
|
+
],
|
|
178
|
+
]
|
|
179
|
+
] = None,
|
|
180
|
+
start: Optional[datetime] = None,
|
|
181
|
+
end: Optional[datetime] = None,
|
|
182
|
+
side: Optional[Literal["BUY", "SELL"]] = None,
|
|
183
|
+
sort_by: Literal["TIMESTAMP", "TOKENS", "CASH"] = "TIMESTAMP",
|
|
184
|
+
sort_direction: Literal["ASC", "DESC"] = "DESC",
|
|
117
185
|
) -> list[Activity]:
|
|
118
|
-
params
|
|
186
|
+
params: dict[str, str | list[str] | int] = {
|
|
187
|
+
"user": user,
|
|
188
|
+
"limit": min(limit, 500),
|
|
189
|
+
"offset": offset,
|
|
190
|
+
}
|
|
119
191
|
if isinstance(condition_id, str):
|
|
120
192
|
params["market"] = condition_id
|
|
121
193
|
if isinstance(condition_id, list):
|
|
122
194
|
params["market"] = ",".join(condition_id)
|
|
195
|
+
if isinstance(event_id, str):
|
|
196
|
+
params["eventId"] = event_id
|
|
197
|
+
if isinstance(event_id, list):
|
|
198
|
+
params["eventId"] = [str(i) for i in event_id]
|
|
123
199
|
if isinstance(type, str):
|
|
124
200
|
params["type"] = type
|
|
125
201
|
if isinstance(type, list):
|
|
@@ -140,22 +216,25 @@ class PolymarketDataClient:
|
|
|
140
216
|
return [Activity(**activity) for activity in response.json()]
|
|
141
217
|
|
|
142
218
|
def get_holders(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
219
|
+
self,
|
|
220
|
+
condition_id: str,
|
|
221
|
+
limit: int = 500,
|
|
222
|
+
min_balance: int = 1,
|
|
146
223
|
) -> list[HolderResponse]:
|
|
147
|
-
"""Takes in a condition_id and returns
|
|
148
|
-
params
|
|
224
|
+
"""Takes in a condition_id and returns top holders for each corresponding token_id."""
|
|
225
|
+
params: dict[str, int | str] = {
|
|
226
|
+
"market": condition_id,
|
|
227
|
+
"limit": limit,
|
|
228
|
+
"min_balance": min_balance,
|
|
229
|
+
}
|
|
149
230
|
response = self.client.get(self._build_url("/holders"), params=params)
|
|
150
231
|
response.raise_for_status()
|
|
151
|
-
return [
|
|
152
|
-
HolderResponse(**holder_data) for holder_data in response.json()
|
|
153
|
-
]
|
|
232
|
+
return [HolderResponse(**holder_data) for holder_data in response.json()]
|
|
154
233
|
|
|
155
234
|
def get_value(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
235
|
+
self,
|
|
236
|
+
user: EthAddress,
|
|
237
|
+
condition_ids: Optional[Union[str, list[str]]] = None,
|
|
159
238
|
) -> ValueResponse:
|
|
160
239
|
"""
|
|
161
240
|
Get the current value of a user's position in a set of markets.
|
|
@@ -175,13 +254,67 @@ class PolymarketDataClient:
|
|
|
175
254
|
response.raise_for_status()
|
|
176
255
|
return ValueResponse(**response.json()[0])
|
|
177
256
|
|
|
257
|
+
def get_closed_positions(
|
|
258
|
+
self,
|
|
259
|
+
user: EthAddress,
|
|
260
|
+
condition_ids: Optional[Union[str, list[str]]] = None,
|
|
261
|
+
) -> list[Position]:
|
|
262
|
+
"""Get all closed positions."""
|
|
263
|
+
params = {"user": user}
|
|
264
|
+
if isinstance(condition_ids, str):
|
|
265
|
+
params["market"] = condition_ids
|
|
266
|
+
if isinstance(condition_ids, list):
|
|
267
|
+
params["market"] = ",".join(condition_ids)
|
|
268
|
+
|
|
269
|
+
response = self.client.get(self._build_url("/closed-positions"), params=params)
|
|
270
|
+
response.raise_for_status()
|
|
271
|
+
return [Position(**pos) for pos in response.json()]
|
|
272
|
+
|
|
273
|
+
def get_total_markets_traded(
|
|
274
|
+
self,
|
|
275
|
+
user: EthAddress,
|
|
276
|
+
) -> int:
|
|
277
|
+
"""Get the total number of markets a user has traded in."""
|
|
278
|
+
params = {"user": user}
|
|
279
|
+
|
|
280
|
+
response = self.client.get(self._build_url("/traded"), params=params)
|
|
281
|
+
response.raise_for_status()
|
|
282
|
+
return response.json()["traded"]
|
|
283
|
+
|
|
284
|
+
def get_open_interest(
|
|
285
|
+
self,
|
|
286
|
+
condition_ids: Optional[Union[str, list[str]]] = None,
|
|
287
|
+
) -> list[MarketValue]:
|
|
288
|
+
"""Get open interest."""
|
|
289
|
+
params = {}
|
|
290
|
+
|
|
291
|
+
if isinstance(condition_ids, str):
|
|
292
|
+
params["market"] = condition_ids
|
|
293
|
+
if isinstance(condition_ids, list):
|
|
294
|
+
params["market"] = ",".join(condition_ids)
|
|
295
|
+
|
|
296
|
+
response = self.client.get(self._build_url("/oi"), params=params)
|
|
297
|
+
response.raise_for_status()
|
|
298
|
+
return [MarketValue(**oi) for oi in response.json()]
|
|
299
|
+
|
|
300
|
+
def get_live_volume(
|
|
301
|
+
self,
|
|
302
|
+
event_id: int,
|
|
303
|
+
) -> EventLiveVolume:
|
|
304
|
+
"""Get live volume for a given event."""
|
|
305
|
+
params = {"id": str(event_id)}
|
|
306
|
+
|
|
307
|
+
response = self.client.get(self._build_url("/live-volume"), params=params)
|
|
308
|
+
response.raise_for_status()
|
|
309
|
+
return EventLiveVolume(**response.json()[0])
|
|
310
|
+
|
|
178
311
|
# website endpoints
|
|
179
312
|
|
|
180
313
|
def get_pnl(
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
314
|
+
self,
|
|
315
|
+
user: EthAddress,
|
|
316
|
+
period: Literal["all", "1m", "1w", "1d"] = "all",
|
|
317
|
+
frequency: Literal["1h", "3h", "12h", "1d"] = "1h",
|
|
185
318
|
) -> list[TimeseriesPoint]:
|
|
186
319
|
"""Get a user's PnL timeseries in the last day, week, month or all with a given frequency."""
|
|
187
320
|
params = {
|
|
@@ -190,22 +323,36 @@ class PolymarketDataClient:
|
|
|
190
323
|
"fidelity": frequency,
|
|
191
324
|
}
|
|
192
325
|
|
|
193
|
-
response = self.client.get(
|
|
326
|
+
response = self.client.get(
|
|
327
|
+
"https://user-pnl-api.polymarket.com/user-pnl", params=params
|
|
328
|
+
)
|
|
194
329
|
response.raise_for_status()
|
|
195
330
|
return [TimeseriesPoint(**point) for point in response.json()]
|
|
196
331
|
|
|
197
|
-
def get_user_metric(
|
|
332
|
+
def get_user_metric(
|
|
333
|
+
self,
|
|
334
|
+
user: EthAddress,
|
|
335
|
+
metric: Literal["profit", "volume"] = "profit",
|
|
336
|
+
window: Literal["1d", "7d", "30d", "all"] = "all",
|
|
337
|
+
):
|
|
198
338
|
"""Get a user's overall profit or volume in the last day, week, month or all."""
|
|
199
|
-
params = {
|
|
339
|
+
params: dict[str, int | str] = {
|
|
200
340
|
"address": user,
|
|
201
341
|
"window": window,
|
|
202
342
|
"limit": 1,
|
|
203
343
|
}
|
|
204
|
-
response = self.client.get(
|
|
344
|
+
response = self.client.get(
|
|
345
|
+
"https://lb-api.polymarket.com/" + metric, params=params
|
|
346
|
+
)
|
|
205
347
|
response.raise_for_status()
|
|
206
348
|
return UserMetric(**response.json()[0])
|
|
207
349
|
|
|
208
|
-
def get_leaderboard_user_rank(
|
|
350
|
+
def get_leaderboard_user_rank(
|
|
351
|
+
self,
|
|
352
|
+
user: EthAddress,
|
|
353
|
+
metric: Literal["profit", "volume"] = "profit",
|
|
354
|
+
window: Literal["1d", "7d", "30d", "all"] = "all",
|
|
355
|
+
):
|
|
209
356
|
"""Get a user's rank on the leaderboard by profit or volume."""
|
|
210
357
|
params = {
|
|
211
358
|
"address": user,
|
|
@@ -216,17 +363,23 @@ class PolymarketDataClient:
|
|
|
216
363
|
response.raise_for_status()
|
|
217
364
|
return UserRank(**response.json()[0])
|
|
218
365
|
|
|
219
|
-
def get_leaderboard_top_users(
|
|
366
|
+
def get_leaderboard_top_users(
|
|
367
|
+
self,
|
|
368
|
+
metric: Literal["profit", "volume"] = "profit",
|
|
369
|
+
window: Literal["1d", "7d", "30d", "all"] = "all",
|
|
370
|
+
limit: int = 100,
|
|
371
|
+
):
|
|
220
372
|
"""Get the leaderboard of the top at most 100 users by profit or volume."""
|
|
221
373
|
params = {
|
|
222
374
|
"window": window,
|
|
223
375
|
"limit": limit,
|
|
224
376
|
}
|
|
225
|
-
response = self.client.get(
|
|
377
|
+
response = self.client.get(
|
|
378
|
+
"https://lb-api.polymarket.com/" + metric, params=params
|
|
379
|
+
)
|
|
226
380
|
response.raise_for_status()
|
|
227
381
|
return [UserMetric(**user) for user in response.json()]
|
|
228
382
|
|
|
229
|
-
|
|
230
383
|
def __enter__(self):
|
|
231
384
|
return self
|
|
232
385
|
|