polymarket-apis 0.2.5__py3-none-any.whl → 0.3.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.

Potentially problematic release.


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

@@ -1,2 +0,0 @@
1
- def hello() -> str:
2
- return "Hello from polymarket-apis!"
@@ -16,6 +16,7 @@ from ..types.clob_types import (
16
16
  CreateOrderOptions,
17
17
  DailyEarnedReward,
18
18
  MarketOrderArgs,
19
+ MarketRewards,
19
20
  Midpoint,
20
21
  OpenOrder,
21
22
  OrderArgs,
@@ -31,7 +32,6 @@ from ..types.clob_types import (
31
32
  Price,
32
33
  PriceHistory,
33
34
  RequestArgs,
34
- RewardsMarket,
35
35
  Spread,
36
36
  TickSize,
37
37
  TokenBidAskDict,
@@ -480,6 +480,7 @@ class PolymarketClobClient:
480
480
  self.creds,
481
481
  RequestArgs(method="POST", request_path=POST_ORDERS, body=body),
482
482
  )
483
+
483
484
  try:
484
485
  response = self.client.post(
485
486
  self._build_url("/orders"),
@@ -524,12 +525,15 @@ class PolymarketClobClient:
524
525
  return self.builder.calculate_buy_market_price(
525
526
  book.asks, amount, order_type,
526
527
  )
527
- if book.bids is None:
528
- msg = "No bid orders available"
529
- raise LiquidityError(msg)
530
- return self.builder.calculate_sell_market_price(
531
- book.bids, amount, order_type,
532
- )
528
+ if side == "SELL":
529
+ if book.bids is None:
530
+ msg = "No bid orders available"
531
+ raise LiquidityError(msg)
532
+ return self.builder.calculate_sell_market_price(
533
+ book.bids, amount, order_type,
534
+ )
535
+ msg = 'Side must be "BUY" or "SELL"'
536
+ raise ValueError(msg)
533
537
 
534
538
  def create_market_order(self, order_args: MarketOrderArgs, options: Optional[PartialCreateOrderOptions] = None):
535
539
  """Creates and signs a market order."""
@@ -633,9 +637,9 @@ class PolymarketClobClient:
633
637
  response.raise_for_status()
634
638
  return response.json()
635
639
 
636
- def get_rewards_market(self, condition_id: Keccak256) -> RewardsMarket:
640
+ def get_market_rewards(self, condition_id: Keccak256) -> MarketRewards:
637
641
  """
638
- Get the RewardsMarket for a given market (condition_id).
642
+ Get the MarketRewards for a given market (condition_id).
639
643
 
640
644
  - metadata, tokens, max_spread, min_size, rewards_config, market_competitiveness.
641
645
  """
@@ -644,7 +648,7 @@ class PolymarketClobClient:
644
648
 
645
649
  response = self.client.get(self._build_url("/rewards/markets/" + condition_id), headers=headers)
646
650
  response.raise_for_status()
647
- return next(RewardsMarket(**market) for market in response.json()["data"])
651
+ return next(MarketRewards(**market) for market in response.json()["data"])
648
652
 
649
653
  def get_trades(
650
654
  self,
@@ -653,7 +657,7 @@ class PolymarketClobClient:
653
657
  trade_id: Optional[str] = None,
654
658
  before: Optional[datetime] = None,
655
659
  after: Optional[datetime] = None,
656
- maker_address: Optional[int] = None,
660
+ proxy_address: Optional[int] = None,
657
661
  next_cursor="MA==") -> list[PolygonTrade]:
658
662
  """Fetches the trade history for a user."""
659
663
  params = {}
@@ -667,8 +671,8 @@ class PolymarketClobClient:
667
671
  params["before"] = int(before.replace(microsecond=0).timestamp())
668
672
  if after:
669
673
  params["after"] = int(after.replace(microsecond=0).timestamp())
670
- if maker_address:
671
- params["maker_address"] = maker_address
674
+ if proxy_address:
675
+ params["maker_address"] = proxy_address
672
676
 
673
677
  request_args = RequestArgs(method="GET", request_path=TRADES)
674
678
  headers = create_level_2_headers(self.signer, self.creds, request_args)
@@ -155,7 +155,7 @@ class PolymarketDataClient:
155
155
  def get_value(
156
156
  self,
157
157
  user: str,
158
- condition_id: Optional[Union[str, list[str]]] = None,
158
+ condition_ids: Optional[Union[str, list[str]]] = None,
159
159
  ) -> ValueResponse:
160
160
  """
161
161
  Get the current value of a user's position in a set of markets.
@@ -166,10 +166,10 @@ class PolymarketDataClient:
166
166
  - list[str] --> sum of the values of positions.
167
167
  """
168
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)
169
+ if isinstance(condition_ids, str):
170
+ params["market"] = condition_ids
171
+ if isinstance(condition_ids, list):
172
+ params["market"] = ",".join(condition_ids)
173
173
 
174
174
  response = self.client.get(self._build_url("/value"), params=params)
175
175
  response.raise_for_status()
@@ -30,13 +30,15 @@ class PolymarketGammaClient:
30
30
  offset: Optional[int] = None,
31
31
  order: Optional[str] = None,
32
32
  ascending: bool = True,
33
- ids: Optional[list[int]] = None,
34
- slugs: Optional[list[str]] = None,
35
33
  archived: Optional[bool] = None,
36
34
  active: Optional[bool] = None,
37
35
  closed: Optional[bool] = None,
36
+ slugs: Optional[list[str]] = None,
37
+ market_ids: Optional[list[int]] = None,
38
38
  token_ids: Optional[list[str]] = None,
39
39
  condition_ids: Optional[list[str]] = None,
40
+ tag_id: Optional[int] = None,
41
+ related_tags: Optional[bool] = False,
40
42
  liquidity_num_min: Optional[float] = None,
41
43
  liquidity_num_max: Optional[float] = None,
42
44
  volume_num_min: Optional[float] = None,
@@ -45,8 +47,6 @@ class PolymarketGammaClient:
45
47
  start_date_max: Optional[datetime] = None,
46
48
  end_date_min: Optional[datetime] = None,
47
49
  end_date_max: Optional[datetime] = None,
48
- tag_id: Optional[int] = None,
49
- related_tags: Optional[bool] = False,
50
50
  ) -> list[GammaMarket]:
51
51
  params = {}
52
52
  if limit:
@@ -56,8 +56,6 @@ class PolymarketGammaClient:
56
56
  if order:
57
57
  params["order"] = order
58
58
  params["ascending"] = ascending
59
- if ids:
60
- params["id"] = ids
61
59
  if slugs:
62
60
  params["slug"] = slugs
63
61
  if archived is not None:
@@ -66,6 +64,8 @@ class PolymarketGammaClient:
66
64
  params["active"] = active
67
65
  if closed is not None:
68
66
  params["closed"] = closed
67
+ if market_ids:
68
+ params["id"] = market_ids
69
69
  if token_ids:
70
70
  params["clob_token_ids"] = token_ids
71
71
  if condition_ids:
@@ -243,13 +243,16 @@ class PolymarketGammaClient:
243
243
  sort: Literal["volume", "volume_24hr", "liquidity", "start_date", "end_date", "competitive"] = "volume_24hr",
244
244
  page: int = 1,
245
245
  limit_per_type: int = 50, # max is 50
246
+ presets: Optional[Literal["EventsHybrid", "EventsTitle"] | list[Literal["EventsHybrid", "EventsTitle"]]] = None,
246
247
  ) -> EventList:
247
248
  """Search for events by query. Should emulate the website search function."""
248
- params = {"q": query,"page": page, "limit_per_type": limit_per_type, "events_status": status, "active": active, "presets": "EventsTitle"}
249
+ params = {"q": query,"page": page, "limit_per_type": limit_per_type, "events_status": status, "active": active}
249
250
  if sort:
250
251
  params["sort"] = sort
251
252
  if sort == "end_date":
252
253
  params["ascending"] = "true"
254
+ if presets:
255
+ params["presets"] = presets
253
256
  response = self.client.get(self._build_url("/public-search"), params=params)
254
257
  response.raise_for_status()
255
258
  return EventList(**response.json())
@@ -1,182 +1,43 @@
1
- from typing import Literal, Optional
1
+ from typing import Literal
2
2
 
3
3
  from gql import Client, gql
4
- from gql.transport.httpx import HTTPXAsyncTransport
5
- from graphql import GraphQLInputObjectType, GraphQLObjectType
4
+ from gql.transport.httpx import HTTPXAsyncTransport, HTTPXTransport
6
5
 
7
6
  from ..utilities.config import GRAPHQL_ENDPOINTS
8
7
 
8
+ EndpointName = Literal[
9
+ "activity_subgraph",
10
+ "fpmm_subgraph",
11
+ "open_interest_subgraph",
12
+ "orderbook_subgraph",
13
+ "pnl_subgraph",
14
+ "positions_subgraph",
15
+ "sports_oracle_subgraph",
16
+ "wallet_subgraph",
17
+ ]
9
18
 
10
- class PolymarketGraphQLClient:
11
- def __init__(self,
12
- endpoint_name: Literal[
13
- "activity_subgraph",
14
- "fpmm_subgraph",
15
- "open_interest_subgraph",
16
- "orderbook_subgraph",
17
- "pnl_subgraph",
18
- "positions_subgraph",
19
- "sports_oracle_subgraph",
20
- "wallet_subgraph",
21
- ]):
22
- if endpoint_name not in GRAPHQL_ENDPOINTS:
23
- msg = f"Invalid endpoint name: {endpoint_name}. Must be one of {list(GRAPHQL_ENDPOINTS.keys())}"
24
- raise ValueError(msg)
25
- endpoint_url = GRAPHQL_ENDPOINTS[endpoint_name]
26
- self.transport = HTTPXAsyncTransport(url=endpoint_url)
27
- self.client = Client(transport=self.transport, fetch_schema_from_transport=True)
28
- self.schema = None
29
- self.object_types = []
30
- self.query_fields = []
31
- self.subscription_fields = []
32
- self.filter_input_types = []
33
-
34
- async def _init_schema(self):
35
- async with self.client as session:
36
- self.schema = session.schema
37
-
38
- async def _get_query_fields(self):
39
- if self.query_fields:
40
- return self.query_fields
41
- if not self.schema:
42
- await self._init_schema()
43
- self.query_fields = [field for field in self.schema.type_map["Query"].fields if not field.startswith("_")]
44
-
45
- return self.query_fields
46
-
47
- async def _get_subscription_fields(self):
48
- if self.subscription_fields:
49
- return self.subscription_fields
50
- if not self.schema:
51
- await self._init_schema()
52
- if "Subscription" in self.schema.type_map:
53
- self.subscription_fields = [field for field in self.schema.type_map["Subscription"].fields if not field.startswith("_")]
54
- else:
55
- self.subscription_fields = []
56
- return self.subscription_fields
57
-
58
- async def _get_object_types(self):
59
- if self.object_types:
60
- return self.object_types
61
- if not self.schema:
62
- await self._init_schema()
63
- for object_name, object in self.schema.type_map.items():
64
- if type(object) is GraphQLObjectType and not object_name.startswith("_"):
65
- self.object_types.append(object_name)
66
- return self.object_types
67
-
68
- async def _get_filter_input_types(self):
69
- if self.filter_input_types:
70
- return self.filter_input_types
71
- if not self.schema:
72
- await self._init_schema()
73
- for object_name, object in self.schema.type_map.items():
74
- if isinstance(object, GraphQLInputObjectType) and not object_name.startswith("_"):
75
- self.filter_input_types.append(object_name)
76
- return self.filter_input_types
77
-
78
- async def list_queries(self):
79
- if self.query_fields:
80
- return self.query_fields
81
- return await self._get_query_fields()
82
19
 
83
- async def list_subscriptions(self):
84
- if self.subscription_fields:
85
- return self.subscription_fields
86
- return await self._get_subscription_fields()
87
-
88
- async def list_object_types(self):
89
- if self.object_types:
90
- return self.object_types
91
- return await self._get_object_types()
92
-
93
- async def list_filter_input_types(self):
94
- if self.filter_input_types:
95
- return self.filter_input_types
96
- return await self._get_filter_input_types()
97
-
98
- async def get_fields(self, object_name: str):
99
- if self.schema is None:
100
- await self._init_schema()
101
- if object_name not in self.schema.type_map:
102
- msg = "Invalid object name"
103
- raise ValueError(msg)
104
- return list(self.schema.type_map[object_name].fields.keys())
105
-
106
- async def query(
107
- self,
108
- endpoint: str,
109
- fields: list[str],
110
- filters: Optional[dict] = None,
111
- relationships: Optional[dict] = None,
112
- ):
113
- if not self.schema:
114
- await self._init_schema()
115
- if not self.query_fields:
116
- await self._get_query_fields()
117
- if not self.object_types:
118
- await self._get_object_types()
119
-
120
- if endpoint not in self.query_fields:
121
- msg = f"Invalid endpoint: {endpoint}"
122
- raise ValueError(msg)
123
-
124
- endpoint_field = self.schema.type_map["Query"].fields[endpoint]
125
- required_args = [
126
- arg_name for arg_name, arg in endpoint_field.args.items()
127
- if arg.type.to_kwargs().get("required", False)
128
- ]
129
- missing_args = [arg for arg in required_args if not (filters and arg in filters)]
130
- if missing_args:
131
- msg = f"Missing required argument(s) for '{endpoint}': {', '.join(missing_args)}"
132
- raise ValueError(msg)
20
+ class PolymarketGraphQLClient:
21
+ """Synchronous GraphQL client for Polymarket subgraphs."""
133
22
 
134
- def build_selection(fields, relationships):
135
- selections = []
136
- for field in fields:
137
- if relationships and field in relationships:
138
- subfields = relationships[field]
139
- selections.append(f"{field} {{ {' '.join(subfields)} }}")
140
- else:
141
- selections.append(field)
142
- return " ".join(selections)
23
+ def __init__(self, endpoint_name: EndpointName) -> None:
24
+ endpoint_url = GRAPHQL_ENDPOINTS[endpoint_name]
25
+ self.transport = HTTPXTransport(url=endpoint_url)
26
+ self.client = Client(transport=self.transport, fetch_schema_from_transport=False)
143
27
 
144
- def build_args(filters):
145
- """Build GraphQL arguments, handling both simple and complex where clauses."""
146
- if not filters:
147
- return ""
28
+ def query(self, query_string: str) -> dict:
29
+ with self.client as session:
30
+ return session.execute(gql(query_string))
148
31
 
149
- arg_strs = []
150
- for k, v in filters.items():
151
- if k == "where" and isinstance(v, dict):
152
- # Handle complex where clause
153
- where_conditions = []
154
- for where_key, where_value in v.items():
155
- if isinstance(where_value, str):
156
- where_conditions.append(f"{where_key}: {where_value}")
157
- else:
158
- where_conditions.append(f"{where_key}: {where_value}")
159
- where_str = "{" + ", ".join(where_conditions) + "}"
160
- arg_strs.append(f"{k}: {where_str}")
161
- # Handle simple key-value filters
162
- elif isinstance(v, str):
163
- arg_strs.append(f'{k}: "{v}"')
164
- else:
165
- arg_strs.append(f"{k}: {v}")
166
32
 
167
- return "(" + ", ".join(arg_strs) + ")"
33
+ class AsyncPolymarketGraphQLClient:
34
+ """Asynchronous GraphQL client for Polymarket subgraphs."""
168
35
 
169
- selection_set = build_selection(fields, relationships)
170
- args = build_args(filters)
36
+ def __init__(self, endpoint_name: EndpointName) -> None:
37
+ endpoint_url = GRAPHQL_ENDPOINTS[endpoint_name]
38
+ self.transport = HTTPXAsyncTransport(url=endpoint_url)
39
+ self.client = Client(transport=self.transport, fetch_schema_from_transport=False)
171
40
 
172
- query_str = f"""
173
- query {{
174
- {endpoint}{args} {{
175
- {selection_set}
176
- }}
177
- }}
178
- """
179
- print(query_str)
41
+ async def query(self, query_string: str) -> dict:
180
42
  async with self.client as session:
181
- result = await session.execute(gql(query_str))
182
- return result
43
+ return await session.execute(gql(query_string))
@@ -195,6 +195,7 @@ class PolymarketWeb3Client:
195
195
  self.w3.eth.wait_for_transaction_receipt(tx_hash)
196
196
 
197
197
  print("Done!")
198
+
198
199
  def redeem_position(self, condition_id: Keccak256, amounts: list[float], neg_risk: bool = True):
199
200
  """
200
201
  Redeem a position into usdc.
@@ -6,8 +6,6 @@ from lomond import WebSocket
6
6
  from lomond.persist import persist
7
7
  from pydantic import ValidationError
8
8
 
9
- from polymarket_apis.utilities.exceptions import AuthenticationRequiredError
10
-
11
9
  from ..types.clob_types import ApiCreds
12
10
  from ..types.websockets_types import (
13
11
  ActivityOrderMatchEvent,
@@ -32,6 +30,7 @@ from ..types.websockets_types import (
32
30
  TickSizeChangeEvent,
33
31
  TradeEvent,
34
32
  )
33
+ from ..utilities.exceptions import AuthenticationRequiredError
35
34
 
36
35
 
37
36
  def _process_market_event(event):
@@ -153,7 +153,7 @@ class PolymarketRewardItem(BaseModel):
153
153
  market_competitiveness: float
154
154
 
155
155
 
156
- class RewardsMarket(BaseModel):
156
+ class MarketRewards(BaseModel):
157
157
  condition_id: Keccak256
158
158
  question: str
159
159
  market_slug: str
@@ -254,7 +254,7 @@ class OpenOrder(BaseModel):
254
254
  price: float
255
255
  outcome: str
256
256
  expiration: datetime
257
- order_type: Literal["GTC", "FOK", "GTD"]
257
+ order_type: Literal["GTC", "GTD"]
258
258
  associate_trades: list[str]
259
259
  created_at: datetime
260
260
 
@@ -12,11 +12,11 @@ class Position(BaseModel):
12
12
 
13
13
  # Asset information
14
14
  token_id: str = Field(alias="asset")
15
+ complementary_token_id: str = Field(alias="oppositeAsset")
15
16
  condition_id: Keccak256 = Field(alias="conditionId")
16
17
  outcome: str
18
+ complementary_outcome: str = Field(alias="oppositeOutcome")
17
19
  outcome_index: int = Field(alias="outcomeIndex")
18
- opposite_outcome: str = Field(alias="oppositeOutcome")
19
- opposite_asset: str = Field(alias="oppositeAsset")
20
20
 
21
21
  # Position details
22
22
  size: float
@@ -332,7 +332,7 @@ class Pagination(BaseModel):
332
332
  total_results: int = Field(alias="totalResults")
333
333
 
334
334
  class EventList(BaseModel):
335
- events: list[QueryEvent]
335
+ events: Optional[list[QueryEvent]] = None
336
336
  pagination: Pagination
337
337
 
338
338
  class QueryMarket(BaseModel):
@@ -67,7 +67,7 @@ class OrderEvent(BaseModel):
67
67
  size_matched: float
68
68
  original_size: float
69
69
  outcome: str
70
- order_type: Literal["GTC", "FOK", "GTD"]
70
+ order_type: Literal["GTC", "GTD", "FOK", "FAK"]
71
71
 
72
72
  created_at: datetime
73
73
  expiration: Optional[datetime] = None
@@ -284,16 +284,16 @@ class MarketStatusChangeEvent(BaseModel):
284
284
  topic: Literal["clob_market"]
285
285
 
286
286
  class LiveDataOrderEvent(BaseModel):
287
- connection_id: str
288
287
  payload: OrderEvent
289
288
  timestamp: datetime
289
+ connection_id: str
290
290
  type: Literal["order"]
291
291
  topic: Literal["clob_user"]
292
292
 
293
293
  class LiveDataTradeEvent(BaseModel):
294
- connection_id: str
295
294
  payload: TradeEvent
296
295
  timestamp: datetime
296
+ connection_id: str
297
297
  type: Literal["trade"]
298
298
  topic: Literal["clob_user"]
299
299
 
@@ -220,7 +220,7 @@ class OrderBuilder:
220
220
  def calculate_sell_market_price(
221
221
  self,
222
222
  bids: list[OrderSummary], # expected to be sorted from worst to best price (low to high)
223
- amount_to_match: float, # in usdc
223
+ amount_to_match: float, # in shares
224
224
  order_type: OrderType,
225
225
  ) -> float:
226
226
  if not bids:
@@ -231,8 +231,10 @@ class OrderBuilder:
231
231
  for p in reversed(bids):
232
232
  sum += float(p.size)
233
233
  if sum >= amount_to_match:
234
+ print(f"market order price {p.price}")
234
235
  return float(p.price)
235
236
 
237
+
236
238
  if order_type == OrderType.FOK:
237
239
  msg = "no match"
238
240
  raise ValueError(msg)
@@ -1,32 +1,34 @@
1
1
  import hashlib
2
- from decimal import Decimal
3
- from math import ceil, floor
2
+ from decimal import ROUND_CEILING, ROUND_FLOOR, ROUND_HALF_UP, Decimal
4
3
 
5
4
  from ...types.clob_types import OrderBookSummary, TickSize
6
5
 
7
6
 
8
7
  def round_down(x: float, sig_digits: int) -> float:
9
- return floor(x * (10**sig_digits)) / (10**sig_digits)
8
+ exp = Decimal(1).scaleb(-sig_digits)
9
+ return float(Decimal(str(x)).quantize(exp=exp, rounding=ROUND_FLOOR))
10
10
 
11
11
 
12
12
  def round_normal(x: float, sig_digits: int) -> float:
13
- return round(x * (10**sig_digits)) / (10**sig_digits)
13
+ exp = Decimal(1).scaleb(-sig_digits)
14
+ return float(Decimal(str(x)).quantize(exp=exp, rounding=ROUND_HALF_UP))
14
15
 
15
16
 
16
17
  def round_up(x: float, sig_digits: int) -> float:
17
- return ceil(x * (10**sig_digits)) / (10**sig_digits)
18
+ exp = Decimal(1).scaleb(-sig_digits)
19
+ return float(Decimal(str(x)).quantize(exp=exp, rounding=ROUND_CEILING))
18
20
 
19
21
 
20
22
  def to_token_decimals(x: float) -> int:
21
- f = (10**6) * x
22
- if decimal_places(f) > 0:
23
- f = round_normal(f, 0)
24
- return int(f)
23
+ exp = Decimal(1)
24
+ return int(
25
+ Decimal(str(x)) * Decimal(10**6).quantize(exp=exp, rounding=ROUND_HALF_UP),
26
+ )
25
27
 
26
28
 
27
29
  def decimal_places(x: float) -> int:
28
30
  """
29
- Returns the number of decimal places in a float.
31
+ Returns the number of decimal places in a numeric value.
30
32
 
31
33
  Assumes x is always a finite, non-special value (not NaN or Infinity).
32
34
  """
@@ -34,7 +36,7 @@ def decimal_places(x: float) -> int:
34
36
  if not isinstance(exponent, int):
35
37
  msg = "Input must be a finite float."
36
38
  raise TypeError(msg)
37
- return abs(exponent)
39
+ return max(0, -exponent)
38
40
 
39
41
 
40
42
 
@@ -0,0 +1,86 @@
1
+ type Split @entity {
2
+ "Transaction Hash"
3
+ id: ID!
4
+ "Timestamp at which split occurred"
5
+ timestamp: BigInt!
6
+ "Address which is performing this split"
7
+ stakeholder: String!
8
+ "Condition on which split is occuring"
9
+ condition: String!
10
+ "The amount of collateral/outcome tokens being split"
11
+ amount: BigInt!
12
+ }
13
+
14
+ type Merge @entity {
15
+ "Transaction Hash"
16
+ id: ID!
17
+ "Timestamp at which merge occurred"
18
+ timestamp: BigInt!
19
+ "Address which is performing this merge"
20
+ stakeholder: String!
21
+ "Token which is collateralising positions being merged"
22
+ condition: String!
23
+ "The amount of collateral/outcome tokens being merged"
24
+ amount: BigInt!
25
+ }
26
+
27
+ type Redemption @entity {
28
+ "Transaction Hash"
29
+ id: ID!
30
+ "Timestamp at which redemption occurred"
31
+ timestamp: BigInt!
32
+ "Address which is redeeming these outcomes"
33
+ redeemer: String!
34
+ "Condition on which redemption is occuring"
35
+ condition: String!
36
+ "Outcomes which are being redeemed"
37
+ indexSets: [BigInt!]!
38
+ "The amount of collateral being claimed"
39
+ payout: BigInt!
40
+ }
41
+
42
+ type NegRiskConversion @entity {
43
+ "Transaction Hash"
44
+ id: ID!
45
+ "Timestamp at which conversion occurred"
46
+ timestamp: BigInt!
47
+ "Address which is performing this conversion"
48
+ stakeholder: String!
49
+ "Neg Risk Market Id assigned to the event"
50
+ negRiskMarketId: String!
51
+ "The amount of each token being converted"
52
+ amount: BigInt!
53
+ "The index set of the outcome tokens being converted"
54
+ indexSet: BigInt!
55
+ "The number of questions at the time of conversion"
56
+ questionCount: Int!
57
+ }
58
+
59
+ # Neg Risk Events/Markets
60
+ type NegRiskEvent @entity {
61
+ "negRiskMarketId"
62
+ id: ID!
63
+ "Question Count"
64
+ questionCount: Int!
65
+ }
66
+
67
+ # Fixed Product Market Makers
68
+ type FixedProductMarketMaker @entity {
69
+ "Market maker address"
70
+ id: ID!
71
+ }
72
+
73
+ # Metadata for the market, keyed by positionId
74
+ type Position @entity {
75
+ "ERC1155 TokenID of the CTF Asset"
76
+ id: ID!
77
+ "Condition that the token is linked to"
78
+ condition: String!
79
+ "Outcome Index"
80
+ outcomeIndex: BigInt!
81
+ }
82
+
83
+ type Condition @entity {
84
+ "Condition ID"
85
+ id: ID!
86
+ }
@@ -0,0 +1,30 @@
1
+ type Condition @entity {
2
+ "conditionId"
3
+ id: ID!
4
+ }
5
+
6
+ # Neg Risk Events/Markets
7
+ type NegRiskEvent @entity {
8
+ "negRiskMarketId"
9
+ id: ID!
10
+ "Fee Bps"
11
+ feeBps: BigInt!
12
+ "Question Count"
13
+ questionCount: Int!
14
+ }
15
+
16
+ # Market Open Interest
17
+ type MarketOpenInterest @entity {
18
+ "Condition ID"
19
+ id: ID!
20
+ "Open interest for the market"
21
+ amount: BigInt!
22
+ }
23
+
24
+ # Global Open Interest
25
+ type GlobalOpenInterest @entity {
26
+ "Empty string"
27
+ id: ID!
28
+ "Global Open Interest"
29
+ amount: BigInt!
30
+ }
@@ -0,0 +1,161 @@
1
+ Metadata-Version: 2.4
2
+ Name: polymarket-apis
3
+ Version: 0.3.0
4
+ Summary: Unified Polymarket APIs - clob, gamma, data, web3, websockets
5
+ Author-email: Razvan Gheorghe <razvan@gheorghe.me>
6
+ Requires-Python: >=3.12
7
+ Requires-Dist: gql[httpx]>=4.0.0
8
+ Requires-Dist: httpx[http2]>=0.25.1
9
+ Requires-Dist: lomond>=0.3.3
10
+ Requires-Dist: poly-eip712-structs>=0.0.1
11
+ Requires-Dist: py-order-utils>=0.3.2
12
+ Requires-Dist: pydantic>=2.10.5
13
+ Requires-Dist: web3>=7.0
14
+ Requires-Dist: wsaccel>=0.6.7
15
+ Description-Content-Type: text/markdown
16
+
17
+ # polymarket-apis [![PyPI version](https://img.shields.io/pypi/v/polymarket-apis.svg)](https://pypi.org/project/polymarket-apis/)
18
+
19
+ Polymarket CLOB, Gamma, Data, Web3, Websockets and GraphQL clients.
20
+
21
+ ## Polymarket Mental Models
22
+
23
+ ### Events, Markets and Outcomes
24
+
25
+ The Polymarket ecosystem is organized hierarchically:
26
+
27
+ ```mermaid
28
+ flowchart LR
29
+ A([Event]) --- B([Market A])
30
+ A --- C([Market B])
31
+ A ~~~ Dot1@{ shape: sm-circ}
32
+ A ~~~ Dot2@{ shape: sm-circ}
33
+ A ~~~ Dot3@{ shape: sm-circ}
34
+ A -.- D([Market n])
35
+ B --- E([Outcome 1])
36
+ B --- F([Outcome 2])
37
+ C --- G([Outcome 1])
38
+ C --- H([Outcome 2])
39
+ ```
40
+
41
+ - **Event** — represents a proposition or question such as “How many Fed rate cuts in 2025?”.
42
+ - Identified by a human-readable **`slug`** (e.g. `how-many-fed-rate-cuts-in-2025`) and an **event `id`** (e.g. `16085`).
43
+
44
+ - **Market** — represents a specific option for the related event (e.g. 1 rate cut in 2025). An Event has 1 or more corresponding Markets. (e.g. 9 options in this case - {0, 1, 2, ..., 7, 8 or more} rate cuts in 2025)
45
+ - Identified by a **`condition id`** (e.g. `0x8e9b6942b4dac3117dadfacac2edb390b6d62d59c14152774bb5fcd983fc134e` for 1 rate cut in 2025), a human-readable **`slug`** (e.g. `'will-1-fed-rate-cut-happen-in-2025'`) and a **market `id`** (e.g. `516724`).
46
+
47
+ - **Outcome** — represents a binary option related to a market. (most commonly `Yes`/`No`, but can be e.g. `Paris Saint-Germain`/`Inter Milan` in the case of a match where draws are not possible)
48
+ - Identified by a **`token id`** (e.g. `15353185604353847122370324954202969073036867278400776447048296624042585335546` for the `Yes` outcome in the 1 rate cut in 2025 market)
49
+
50
+ ### Tokens
51
+ - **Tokens** are the blockchain implementation of **Outcomes** - tradable digital assets on the Polygon blockchain that users buy, hold and sell on Polygon.
52
+ - This helps ensure the logic of binary outcome prediction markets through smart contracts (e.g. collateralization, token prices going to $1.00 or $0.00 after resolution, splits/merges).
53
+
54
+ ### Splits and Merges
55
+ - Holding 1 `Yes` share + 1 `No` share in a **Market** (e.g. `'will-1-fed-rate-cut-happen-in-2025'`) covers the entire universe of possibilities and guarantees a $1.00 payout regardless of outcome. This mathematical relationship enables Polymarket's core mechanisms: splitting (1 USDC → 1 `Yes` + 1 `No`) and merging (1 `Yes` + 1 `No` → 1 USDC) at any point before resolution.
56
+ - Splits are the only way tokens are created. Either a user splits USDC into equal shares of the complementary tokens or Polymarket automatically splits USDC when it matches an `Yes` buy order at e.g. 30¢ with a `No` buy order at 70¢.
57
+
58
+ ### Unified Order Book
59
+ - Polymarket uses traditional exchange mechanics - a Central Limit Order Book (CLOB), where users place buy and sell orders that get matched by price and time priority.
60
+ - However, because the `Yes` and `No` outcomes form a complete probability universe, certain orders become mathematically equivalent - allowing the matching engine to find trades as exemplified above.
61
+ - This unified structure means every **BUY** order for `Outcome 1` at price **X** is simultaneously visible as a **SELL** order for `Outcome 2` at price **(100¢ - X)**, creating deeper liquidity and tighter spreads than separate order books would allow.
62
+
63
+ ### Negative Risk and Conversions
64
+ - If the **Markets** in and **Event** collectively cover a complete universe of possibilities (e.g. {0, 1, 2, ..., 7, 8 or more} rate cuts in 2025) and only one winner is possible, two collections of positions (made up of tokens and USDC) become mathematically equivalent and the **Event** is said to support negative risk.
65
+ - e.g. Hold 1 `No` token in the 0 rate cuts in 2025. This is equivalent to holding 1 `Yes` token in each of the other **Markets** {1, 2, ..., 7, 8 or more}.
66
+ - An interesting consequence is that holding `No` tokens in more than one **Market** is equivalent to `Yes` tokens ***and*** some USDC.
67
+ - e.g. Hold 1 `No` token on each of {0, 1, 2, ..., 7, 8 or more} rate cuts in 2025. Because only one winner is possible, this guarantees that 8 out of the 9 **Markets** resolve to `No`. This is equivalent to a position of 8 USDC.
68
+ - e.g. Hold 1 `No` token on each of {0, 1} rate cuts in 2025. This is equivalent to 1 `Yes` token in {2, ..., 7, 8 or more} and 1 USDC.
69
+ - Polymarket allows for the one way (for capital efficiency) conversion from `No` tokens to a collection of `Yes` tokens and USDC before resolution through a smart contract.
70
+
71
+ ## Clients overview
72
+ - ### PolymarketClobClient - Order book related operations
73
+ - #### Order book
74
+ - get one or more order books, best price, spread, midpoint, last trade price by `token_id`(s)
75
+ - #### Orders
76
+ - create and post limit or market orders
77
+ - cancel one or more orders by `order_id`(s)
78
+ - get active orders
79
+ - #### Trades
80
+ - get trade history for a user with filtering by `condition_id`, `token_id`, `trade_id`, time window
81
+ - #### Rewards
82
+ - check if one or more orders are scoring for liquidity rewards by `order_id`(s)
83
+ - get daily earned rewards
84
+ - check if a **Market** offers rewards by `condition_id` - **get_market_rewards()**
85
+ - get all active markets that offer rewards sorted by different metrics and ordered, filtered by a query, show your favourites from the web app - **get_reward_markets()** (*naming would do with some work*)
86
+ - #### Miscellaneous
87
+ - get price history
88
+ - get **ClobMarket** by `condition_id`
89
+ - get all **ClobMarkets**
90
+
91
+ ### PolymarketGammaClient - Market/Event related operations
92
+ - #### Market
93
+ - get **GammaMarket** by `market_id`
94
+ - get **GammaMarkets** with pagination (offset and limit), filter by `slug`s, `market_id`s, `token_id`s, `condition_id`s, `tag_id` or filtered by active, closed, archived, liquidity window, volume window, start date window, end date window and ordered
95
+ - #### Event
96
+ - get **Event** by `event_id`
97
+ - get **Events** with pagination, filter by `slug`s, `event_id`s, `tag_id` or filtered by active, closed, archived, liquidity window, volume window, start date window, end date window and ordered
98
+ - get all **Events** given some filtration
99
+ - search **Events**, filter by text query, active/resolved, sort by volume/volume_24hr/liquidity/start_date/end_date/competitive (tries to mimic the web app search bar)
100
+ - #### Miscellaneous
101
+ - grok event summary by **Event** `slug`
102
+ - grok election market explanation by candidate name and election title
103
+
104
+ ### PolymarketDataClient - Portfolio related operations
105
+ - #### Positions
106
+ - get positions with pagination (offset and limit) by user address, filter by `condition_id`, position size, redeemability, mergeability, title
107
+ - #### Trades
108
+ - get trades with pagination, filter by `condition id`, user address, side, taker only or not, cash amount/token amount
109
+ - #### Activity
110
+ - get activity with pagination by user address, filter by type (trade, split, merge, redeem, reward, conversion), `condition_id`, time window, side, sort by timestamp/tokens/cash
111
+ - #### Holders
112
+ - get top holders by `condition_id`
113
+ - #### Value
114
+ - get positions value by user address and condition_ids
115
+ - `condition_ids` is ***None*** → total value of positions
116
+ - `condition_ids` is ***str*** → value of positions on a market
117
+ - `condition_ids` is ***list[str]*** → sum of values of positions on multiple markets
118
+ - #### Miscellaneous
119
+ - get pnl timeseries by user address for a period (1d, 1w, 1m, all) with frequency (1h, 3h, 12h, 1d)
120
+ - get overall pnl/volume by user address for a recent window (1d, 7d, 30d, all)
121
+ - get user rank on the profit/volume leaderboards by user address for a recent window (1d, 7d, 30d, all)
122
+ - get top users on the profit/volume leaderboards (at most 100) for a recent window (1d, 7d, 30d, all)
123
+
124
+ ### PolymarketWeb3Client - Blockchain related operations
125
+ - #### Balance
126
+ - get usdc balance by user address
127
+ - get token balance by `token_id` and user address
128
+ - #### Token/USDC conversions
129
+ - split USDC into complementary tokens - needs `condition_id`, amount, neg_risk bool
130
+ - merge complementary tokens into USDC - needs `condition_id`, amount, neg_risk bool
131
+ - redeem token into USDC - needs `condition_id`, amounts array [`Yes` shares, `No` shares], neg_risk bool
132
+ - convert 1 or more `No` tokens in a *negative risk* **Event** into a collection of USDC and `Yes` tokens on the other **Markets** in the **Event**
133
+
134
+ ### PolymarketWebsocketsClient - Real time data subscriptions
135
+ - subscribe to **market socket** with `token_ids` list, receive different event types:
136
+ - order book summary
137
+ - price change
138
+ - tick size change
139
+ - last trade price
140
+ - subscribe to **user socket** with **ApiCreds**, receive different event types:
141
+ - order (status - live, canceled, matched)
142
+ - trade (status - matched, mined, confirmed, retrying, failed)
143
+ - subscribe to **live data socket** with any combination described [here](https://github.com/Polymarket/real-time-data-client?tab=readme-ov-file#subscribe) - ***newest endpoint*** - includes the other sockets, receive:
144
+ - all of the above event types
145
+ - market (created, resolved)
146
+ - comment/reaction (created, removed)
147
+ - trades/orders_matched (all, not just yours) - filter by **Event** `slug` or **Market** `slug`
148
+ - crypto price
149
+ - request/quote (created, edited, canceled, expired) - rqf not public yet
150
+
151
+ ### PolymarketGraphQLClient/AsyncPolymarketGraphQLClient - Goldsky hosted Subgraphs queries
152
+ - instantiate with an endpoint name from:
153
+ - activity_subgraph
154
+ - fpmm_subgraph
155
+ - open_interest_subgraph
156
+ - orderbook_subgraph
157
+ - pnl_subgraph
158
+ - positions_subgraph
159
+ - sports_oracle_subgraph
160
+ - wallet_subgraph
161
+ - ****query()**** takes in a GraphQL query string and returns the raw json
@@ -1,17 +1,17 @@
1
- polymarket_apis/__init__.py,sha256=8a6dT19YqnOcLEmprl8vavi97JpaRrcDWBTllGsrmbE,61
1
+ polymarket_apis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  polymarket_apis/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- polymarket_apis/clients/clob_client.py,sha256=pE-q-4hAUyKIUHN5tbBxX4o4uzMGiMcTNQ2dIJLQkDY,31025
4
- polymarket_apis/clients/data_client.py,sha256=sAJqesjKT8cRNr-j4OsXj-xYHEOzt0WWCvXcvejX7uI,8641
5
- polymarket_apis/clients/gamma_client.py,sha256=yGi7l0vceBgrwMlqLFZYyQ_Lj13hNaDwaBtLSDoTAnM,12190
6
- polymarket_apis/clients/graphql_client.py,sha256=Jgrz107EVpHRSAXvY45hRUKuPndl9-41_EaqIYbb5mo,6907
7
- polymarket_apis/clients/web3_client.py,sha256=0SX25LMP59FWzfsoshxt-_XNPcPXnmvwmCaHu3K4K2E,11222
8
- polymarket_apis/clients/websockets_client.py,sha256=wueJ54ZgJgeiJmKi5EEIB8_YwkdD3y2Dr4t9WjFMXuU,7581
3
+ polymarket_apis/clients/clob_client.py,sha256=HO9ACvdODfj6n_W7lkgpCAtm7Y-vEhGK-8bmEtnh8sI,31152
4
+ polymarket_apis/clients/data_client.py,sha256=zkfYoejsrAjwbhBfpMVLiM3YYoXqgvLhnHZ5fc7QVZI,8646
5
+ polymarket_apis/clients/gamma_client.py,sha256=-GhPBoRnnMl1Ex6oGI6ToKfGXGXVRjVZ-G1vO4eJjVI,12370
6
+ polymarket_apis/clients/graphql_client.py,sha256=kN3hk0kKmZhwnN5YfC-8XbebNyoho7dRdZC9p6aC8_A,1511
7
+ polymarket_apis/clients/web3_client.py,sha256=XQApKIAkS0-TAWrApI1DFDvW0udbpkWzXiyGXo-GNec,11223
8
+ polymarket_apis/clients/websockets_client.py,sha256=gJiJ087PenrpNG4Y2ffu0S1he82TQOJhD9JbLyMbc_U,7566
9
9
  polymarket_apis/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- polymarket_apis/types/clob_types.py,sha256=O7_6x5gtLjjkcVhcUMv7aIyFhdk2LRfRObPibRwdC44,11699
10
+ polymarket_apis/types/clob_types.py,sha256=LboySm6z_Pb-sf30BHATfi1-CC0LFT_CHv-gzJdkxk0,11692
11
11
  polymarket_apis/types/common.py,sha256=eTY2kjOArzrZRVSmEJvxEyxGJeMx045R073EMZMzCKM,1629
12
- polymarket_apis/types/data_types.py,sha256=K-bE5g1owFozvznmQZ0MicxGccqVSuEibuVzXFAoa4E,4408
13
- polymarket_apis/types/gamma_types.py,sha256=CfjuQbkNaM-PcZoezSZvMk4ru7a4AzXh64Clbf13MHc,12518
14
- polymarket_apis/types/websockets_types.py,sha256=96xQl6xQ4ReL-b4a-UYNittPvXJBXuzX1RBxlJHJg-s,11993
12
+ polymarket_apis/types/data_types.py,sha256=5vLo4CnAXApHhbPrtj_xnYQPeRnTYhjuY8EGJU7KeUU,4421
13
+ polymarket_apis/types/gamma_types.py,sha256=xSafNRgkYXqIq3Vd5J3k018ue79rEiulczwUHuP8Uxo,12535
14
+ polymarket_apis/types/websockets_types.py,sha256=4a__lrxnFWkWnOa8i-6wsV5Q0AaLq3srPNDhnKyfGAo,12000
15
15
  polymarket_apis/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  polymarket_apis/utilities/config.py,sha256=IfIBJa6rSi-e-9_HjeI8j4cD3MXtzRjoLzgcgn5tHbc,2448
17
17
  polymarket_apis/utilities/constants.py,sha256=OdxaAsyYzAK1RXty5VtvidMgan8YW-JfHS3Rdxp6n_U,580
@@ -19,8 +19,10 @@ polymarket_apis/utilities/endpoints.py,sha256=bxZyrJBPbVauWc-eR0RMh6KDqU-SmO_3Lf
19
19
  polymarket_apis/utilities/exceptions.py,sha256=58vuiLuZxX6d05qa29jgEOC4ZYBv368JaO8DAFMD0Dc,363
20
20
  polymarket_apis/utilities/headers.py,sha256=Cc5WEnIBLYAgfwvmCXRBwA2zUYME8fDy4PbwlwlB6Oo,1510
21
21
  polymarket_apis/utilities/order_builder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- polymarket_apis/utilities/order_builder/builder.py,sha256=-eQ092J6Wkproy2SY6USFGWAniS5ZMBg85Ij_cnqqPs,8332
23
- polymarket_apis/utilities/order_builder/helpers.py,sha256=QN39noGcWrGAy89dJc8DvEGJwiX0fMVc62R-pkclAu4,1714
22
+ polymarket_apis/utilities/order_builder/builder.py,sha256=GMKkc96ZpLBBBQDRqIRKs8Kjl75zUbdRpS8IAFV_7Fg,8390
23
+ polymarket_apis/utilities/order_builder/helpers.py,sha256=eMcx17W41rKQ7hDP4Ch8QRtS3WQ5zNMyKtgmOe8-3fk,1944
24
+ polymarket_apis/utilities/schemas/activity-subgraph.graphql,sha256=B6FnnOUvix2qE4S4puPgzt-fSz_SlnJaEKrKE0kGCuc,2033
25
+ polymarket_apis/utilities/schemas/open-interest.graphql,sha256=42sNE49KfEJMoxDapKvo38eOYiaiOWZyINTKevWhpUY,478
24
26
  polymarket_apis/utilities/signing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
27
  polymarket_apis/utilities/signing/eip712.py,sha256=fGLurznnfY5M0VCP1Txyq_FYQRfggyHoTwz-PGIK2Eg,895
26
28
  polymarket_apis/utilities/signing/hmac.py,sha256=vAYb98tMHWqNqAJNFXUnAddS38dA_aOy3Wp3DcP_7PM,702
@@ -36,6 +38,6 @@ polymarket_apis/utilities/web3/abis/ProxyWalletFactory.json,sha256=5KjBHUWdkc_kd
36
38
  polymarket_apis/utilities/web3/abis/UChildERC20Proxy.json,sha256=ZyQC38U0uxInlmnW2VXDVD3TJfTIRmSNMkTxQsaG7oA,27396
37
39
  polymarket_apis/utilities/web3/abis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
40
  polymarket_apis/utilities/web3/abis/custom_contract_errors.py,sha256=ZCeJPK5tobPAR9vaNxw_pQZwKyZc_R0GdggfWaeXvOs,1176
39
- polymarket_apis-0.2.5.dist-info/METADATA,sha256=B3ij72Emt46fd9e8pfCxn7rtxG2lulL5T9Xz33Et8Uo,558
40
- polymarket_apis-0.2.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
41
- polymarket_apis-0.2.5.dist-info/RECORD,,
41
+ polymarket_apis-0.3.0.dist-info/METADATA,sha256=dYODMIGPXBh9NVImiRM-P7EXzL26gE6GZYRq6dzxFaU,10454
42
+ polymarket_apis-0.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
43
+ polymarket_apis-0.3.0.dist-info/RECORD,,
@@ -1,18 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: polymarket-apis
3
- Version: 0.2.5
4
- Summary: Unified Polymarket APIs - clob, gamma, data, web3, websockets
5
- Author-email: Razvan Gheorghe <razvan@gheorghe.me>
6
- Requires-Python: >=3.12
7
- Requires-Dist: httpx[http2]>=0.25.1
8
- Requires-Dist: lomond>=0.3.3
9
- Requires-Dist: poly-eip712-structs>=0.0.1
10
- Requires-Dist: py-order-utils>=0.3.2
11
- Requires-Dist: pydantic>=2.10.5
12
- Requires-Dist: web3>=7.0
13
- Requires-Dist: wsaccel>=0.6.7
14
- Description-Content-Type: text/markdown
15
-
16
- # polymarket-apis
17
-
18
- Polymarket CLOB, Gamma, Data and Web3 and Websockets clients.