polymarket-apis 0.2.2__py3-none-any.whl → 0.2.4__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,15 +1,28 @@
1
1
  from collections.abc import Callable
2
- from typing import Any
2
+ from json import JSONDecodeError
3
+ from typing import Any, Optional
3
4
 
4
5
  from lomond import WebSocket
5
6
  from lomond.persist import persist
6
7
  from pydantic import ValidationError
7
8
 
9
+ from polymarket_apis.utilities.exceptions import AuthenticationRequiredError
10
+
8
11
  from ..types.clob_types import ApiCreds
9
12
  from ..types.websockets_types import (
13
+ ActivityOrderMatchEvent,
14
+ ActivityTradeEvent,
10
15
  CommentEvent,
11
- LiveDataOrderMatchEvent,
16
+ CryptoPriceSubscribeEvent,
17
+ CryptoPriceUpdateEvent,
18
+ LastTradePriceEvent,
19
+ LiveDataLastTradePriceEvent,
20
+ LiveDataOrderBookSummaryEvent,
21
+ LiveDataOrderEvent,
22
+ LiveDataPriceChangeEvent,
23
+ LiveDataTickSizeChangeEvent,
12
24
  LiveDataTradeEvent,
25
+ MarketStatusChangeEvent,
13
26
  OrderBookSummaryEvent,
14
27
  OrderEvent,
15
28
  PriceChangeEvent,
@@ -23,28 +36,42 @@ from ..types.websockets_types import (
23
36
 
24
37
  def _process_market_event(event):
25
38
  try:
26
- event = event.json
27
- for message in event:
28
- match message["event_type"]:
29
- case "book":
30
- print(OrderBookSummaryEvent(**message), "\n")
31
- case "price_change":
32
- print(PriceChangeEvent(**message), "\n")
33
- case "tick_size_change":
34
- print(TickSizeChangeEvent(**message), "\n")
35
- except ValidationError as e:
39
+ message = event.json
40
+ if isinstance(message, list):
41
+ for item in message:
42
+ try:
43
+ print(OrderBookSummaryEvent(**item), "\n")
44
+ except ValidationError as e:
45
+ print(item.text)
46
+ print(e.errors())
47
+ return
48
+ match message["event_type"]:
49
+ case "book":
50
+ print(OrderBookSummaryEvent(**message), "\n")
51
+ case "price_change":
52
+ print(PriceChangeEvent(**message), "\n")
53
+ case "tick_size_change":
54
+ print(TickSizeChangeEvent(**message), "\n")
55
+ case "last_trade_price":
56
+ print(LastTradePriceEvent(**message), "\n")
57
+ case _:
58
+ print(message)
59
+ except JSONDecodeError:
36
60
  print(event.text)
37
- print(e.errors(), "\n")
61
+ except ValidationError as e:
62
+ print(e.errors())
63
+ print(event.json)
38
64
 
39
65
  def _process_user_event(event):
40
66
  try:
41
- event = event.json
42
- for message in event:
43
- match message["event_type"]:
44
- case "order":
45
- print(OrderEvent(**message), "\n")
46
- case "trade":
47
- print(TradeEvent(**message), "\n")
67
+ message = event.json
68
+ match message["event_type"]:
69
+ case "order":
70
+ print(OrderEvent(**message), "\n")
71
+ case "trade":
72
+ print(TradeEvent(**message), "\n")
73
+ except JSONDecodeError:
74
+ print(event.text)
48
75
  except ValidationError as e:
49
76
  print(event.text)
50
77
  print(e.errors(), "\n")
@@ -54,9 +81,9 @@ def _process_live_data_event(event):
54
81
  message = event.json
55
82
  match message["type"]:
56
83
  case "trades":
57
- print(LiveDataTradeEvent(**message), "\n")
84
+ print(ActivityTradeEvent(**message), "\n")
58
85
  case "orders_matched":
59
- print(LiveDataOrderMatchEvent(**message), "\n")
86
+ print(ActivityOrderMatchEvent(**message), "\n")
60
87
  case "comment_created" | "comment_removed":
61
88
  print(CommentEvent(**message), "\n")
62
89
  case "reaction_created" | "reaction_removed":
@@ -65,9 +92,31 @@ def _process_live_data_event(event):
65
92
  print(RequestEvent(**message), "\n")
66
93
  case "quote_created" | "quote_edited" | "quote_canceled" | "quote_expired":
67
94
  print(QuoteEvent(**message), "\n")
68
- except ValidationError as e:
95
+ case "subscribe":
96
+ print(CryptoPriceSubscribeEvent(**message), "\n")
97
+ case "update":
98
+ print(CryptoPriceUpdateEvent(**message), "\n")
99
+ case "agg_orderbook":
100
+ print(LiveDataOrderBookSummaryEvent(**message), "\n")
101
+ case "price_change":
102
+ print(LiveDataPriceChangeEvent(**message), "\n")
103
+ case "last_trade_price":
104
+ print(LiveDataLastTradePriceEvent(**message), "\n")
105
+ case "tick_size_change":
106
+ print(LiveDataTickSizeChangeEvent(**message), "\n")
107
+ case "market_created" | "market_resolved":
108
+ print(MarketStatusChangeEvent(**message), "\n")
109
+ case "order":
110
+ print(LiveDataOrderEvent(**message), "\n")
111
+ case "trade":
112
+ print(LiveDataTradeEvent(**message), "\n")
113
+ case _:
114
+ print(message)
115
+ except JSONDecodeError:
69
116
  print(event.text)
117
+ except ValidationError as e:
70
118
  print(e.errors(), "\n")
119
+ print(event.text)
71
120
 
72
121
  class PolymarketWebsocketsClient:
73
122
  def __init__(self):
@@ -85,6 +134,7 @@ class PolymarketWebsocketsClient:
85
134
 
86
135
  """
87
136
  websocket = WebSocket(self.url_market)
137
+
88
138
  for event in persist(websocket): # persist automatically reconnects
89
139
  if event.name == "ready":
90
140
  websocket.send_json(
@@ -103,29 +153,52 @@ class PolymarketWebsocketsClient:
103
153
 
104
154
  """
105
155
  websocket = WebSocket(self.url_user)
156
+
106
157
  for event in persist(websocket):
107
158
  if event.name == "ready":
108
159
  websocket.send_json(
109
- auth = creds.model_dump(by_alias=True),
160
+ auth=creds.model_dump(by_alias=True),
110
161
  )
111
162
  elif event.name == "text":
112
163
  process_event(event)
113
164
 
114
- def live_data_socket(self, subscriptions: list[dict[str, Any]], process_event: Callable = _process_live_data_event):
165
+ def live_data_socket(self, subscriptions: list[dict[str, Any]], process_event: Callable = _process_live_data_event, creds: Optional[ApiCreds] = None):
166
+ # info on how to subscribe found at https://github.com/Polymarket/real-time-data-client?tab=readme-ov-file#subscribe
115
167
  """
116
168
  Connect to the live data websocket and subscribe to specified events.
117
169
 
118
170
  Args:
119
171
  subscriptions: List of subscription configurations
120
172
  process_event: Callback function to process received events
173
+ creds: ApiCreds for authentication if subscribing to clob_user topic
121
174
 
122
175
  """
123
176
  websocket = WebSocket(self.url_live_data)
177
+
178
+ needs_auth = any(sub.get("topic") == "clob_user" for sub in subscriptions)
179
+ if needs_auth and creds is None:
180
+ msg = "ApiCreds credentials are required for the clob_user topic subscriptions"
181
+ raise AuthenticationRequiredError(msg)
182
+
124
183
  for event in persist(websocket):
125
184
  if event.name == "ready":
126
- websocket.send_json(
127
- action="subscribe",
128
- subscriptions=subscriptions,
129
- )
185
+ if needs_auth:
186
+ subscriptions_with_creds = []
187
+ for sub in subscriptions:
188
+ if sub.get("topic") == "clob_user":
189
+ sub_copy = sub.copy()
190
+ sub_copy["clob_auth"] = creds.model_dump()
191
+ subscriptions_with_creds.append(sub_copy)
192
+ else:
193
+ subscriptions_with_creds.append(sub)
194
+ subscriptions = subscriptions_with_creds
195
+
196
+ payload = {
197
+ "action": "subscribe",
198
+ "subscriptions": subscriptions,
199
+ }
200
+
201
+ websocket.send_json(**payload)
202
+
130
203
  elif event.name == "text":
131
204
  process_event(event)
@@ -22,7 +22,7 @@ from ..utilities.constants import ZERO_ADDRESS
22
22
  logger = logging.getLogger(__name__)
23
23
 
24
24
  class ApiCreds(BaseModel):
25
- api_key: str = Field(alias="apiKey")
25
+ key: str = Field(alias="apiKey")
26
26
  secret: str
27
27
  passphrase: str
28
28
 
@@ -12,6 +12,8 @@ def validate_keccak256(v: str) -> str:
12
12
  return v
13
13
 
14
14
  def parse_timestamp(v: str) -> datetime:
15
+ if isinstance(v, datetime):
16
+ return v
15
17
  # Normalize '+00' to '+0000' for timezone
16
18
  if v.endswith("+00"):
17
19
  v = v[:-3] + "+0000"
@@ -45,5 +47,5 @@ Keccak256 = Annotated[str, AfterValidator(validate_keccak256)]
45
47
  EmptyString = Annotated[str, Field(pattern=r"^$", description="An empty string")]
46
48
 
47
49
  class TimeseriesPoint(BaseModel):
48
- t: datetime
49
- p: float
50
+ value: float
51
+ timestamp: datetime