polymarket-apis 0.2.6__py3-none-any.whl → 0.3.1__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.

@@ -8,6 +8,7 @@ from ..types.common import EthAddress, Keccak256, TimeseriesPoint
8
8
 
9
9
  # wss://ws-subscriptions-clob.polymarket.com/ws/market types
10
10
 
11
+
11
12
  class PriceChange(BaseModel):
12
13
  best_ask: float = Field(validation_alias=AliasChoices("ba", "best_ask"))
13
14
  best_bid: float = Field(validation_alias=AliasChoices("bb", "best_bid"))
@@ -17,17 +18,22 @@ class PriceChange(BaseModel):
17
18
  token_id: str = Field(validation_alias=AliasChoices("a", "asset_id"))
18
19
  hash: str = Field(validation_alias=AliasChoices("h", "hash"))
19
20
 
21
+
20
22
  class PriceChanges(BaseModel):
21
23
  condition_id: Keccak256 = Field(validation_alias=AliasChoices("m", "market"))
22
- price_changes: list[PriceChange] = Field(validation_alias=AliasChoices("pc", "price_changes"))
24
+ price_changes: list[PriceChange] = Field(
25
+ validation_alias=AliasChoices("pc", "price_changes")
26
+ )
23
27
  timestamp: datetime = Field(validation_alias=AliasChoices("t", "timestamp"))
24
28
 
29
+
25
30
  class TickSizeChange(BaseModel):
26
31
  token_id: str = Field(alias="asset_id")
27
32
  condition_id: Keccak256 = Field(alias="market")
28
33
  old_tick_size: TickSize
29
34
  new_tick_size: TickSize
30
35
 
36
+
31
37
  class LastTradePrice(BaseModel):
32
38
  price: float
33
39
  size: float
@@ -36,42 +42,48 @@ class LastTradePrice(BaseModel):
36
42
  condition_id: Keccak256 = Field(alias="market")
37
43
  fee_rate_bps: float
38
44
 
45
+
39
46
  class OrderBookSummaryEvent(OrderBookSummary):
40
47
  event_type: Literal["book"]
41
48
 
49
+
42
50
  class PriceChangeEvent(PriceChanges):
43
51
  event_type: Literal["price_change"]
44
52
 
53
+
45
54
  class TickSizeChangeEvent(TickSizeChange):
46
55
  side: Literal["BUY", "SELL"]
47
56
  timestamp: datetime
48
57
  event_type: Literal["tick_size_change"]
49
58
 
59
+
50
60
  class LastTradePriceEvent(LastTradePrice):
51
61
  timestamp: datetime
52
62
  event_type: Literal["last_trade_price"]
53
63
 
64
+
54
65
  # wss://ws-subscriptions-clob.polymarket.com/ws/user types
55
66
 
67
+
56
68
  class OrderEvent(BaseModel):
57
69
  token_id: str = Field(alias="asset_id")
58
70
  condition_id: Keccak256 = Field(alias="market")
59
71
  order_id: Keccak256 = Field(alias="id")
60
- associated_trades: Optional[list[str]] = None # list of trade ids which
72
+ associated_trades: Optional[list[str]] = None # list of trade ids which
61
73
  maker_address: EthAddress
62
- order_owner: str = Field(alias="owner") # api key of order owner
63
- event_owner: Optional[str] = Field(None, alias="owner") # api key of event owner
74
+ order_owner: str = Field(alias="owner") # api key of order owner
75
+ event_owner: Optional[str] = Field(None, alias="owner") # api key of event owner
64
76
 
65
77
  price: float
66
78
  side: Literal["BUY", "SELL"]
67
79
  size_matched: float
68
80
  original_size: float
69
81
  outcome: str
70
- order_type: Literal["GTC", "FOK", "GTD"]
82
+ order_type: Literal["GTC", "GTD", "FOK", "FAK"]
71
83
 
72
84
  created_at: datetime
73
85
  expiration: Optional[datetime] = None
74
- timestamp: Optional[datetime] = None # time of event
86
+ timestamp: Optional[datetime] = None # time of event
75
87
 
76
88
  event_type: Optional[Literal["order"]] = None
77
89
  type: Literal["PLACEMENT", "UPDATE", "CANCELLATION"]
@@ -84,35 +96,42 @@ class OrderEvent(BaseModel):
84
96
  return None
85
97
  return v
86
98
 
99
+
87
100
  class TradeEvent(BaseModel):
88
101
  token_id: str = Field(alias="asset_id")
89
102
  condition_id: Keccak256 = Field(alias="market")
90
103
  taker_order_id: Keccak256
91
104
  maker_orders: list[MakerOrder]
92
105
  trade_id: str = Field(alias="id")
93
- trade_owner: Optional[str] = Field(None, alias="owner") # api key of trade owner
94
- event_owner: str = Field(alias="owner") # api key of event owner
106
+ trade_owner: Optional[str] = Field(None, alias="owner") # api key of trade owner
107
+ event_owner: str = Field(alias="owner") # api key of event owner
95
108
 
96
109
  price: float
97
110
  size: float
98
111
  side: Literal["BUY", "SELL"]
99
112
  outcome: str
100
113
 
101
- last_update: datetime # time of last update to trade
102
- matchtime: Optional[datetime] = None # time trade was matched
103
- timestamp: Optional[datetime] = None # time of event
114
+ last_update: datetime # time of last update to trade
115
+ matchtime: Optional[datetime] = None # time trade was matched
116
+ timestamp: Optional[datetime] = None # time of event
104
117
 
105
118
  event_type: Optional[Literal["trade"]] = None
106
119
  type: Optional[Literal["TRADE"]] = None
107
120
 
108
121
  status: Literal["MATCHED", "MINED", "CONFIRMED", "RETRYING", "FAILED"]
109
122
 
123
+
110
124
  # wss://ws-live-data.polymarket.com types
111
125
 
126
+
112
127
  # Payload models
113
128
  class ActivityTrade(BaseModel):
114
- token_id: str = Field(alias="asset") # ERC1155 token ID of conditional token being traded
115
- condition_id: str = Field(alias="conditionId") # Id of market which is also the CTF condition ID
129
+ token_id: str = Field(
130
+ alias="asset"
131
+ ) # ERC1155 token ID of conditional token being traded
132
+ condition_id: str = Field(
133
+ alias="conditionId"
134
+ ) # Id of market which is also the CTF condition ID
116
135
  event_slug: str = Field(alias="eventSlug") # Slug of the event
117
136
  outcome: str # Human readable outcome of the market
118
137
  outcome_index: int = Field(alias="outcomeIndex") # Index of the outcome
@@ -131,16 +150,26 @@ class ActivityTrade(BaseModel):
131
150
  profile_image: str = Field(alias="profileImage") # URL to the user profile image
132
151
  profile_image_optimized: Optional[str] = Field(None, alias="profileImageOptimized")
133
152
 
153
+
134
154
  class Comment(BaseModel):
135
155
  id: str # Unique identifier of comment
136
156
  body: str # Content of the comment
137
- parent_entity_type: str = Field(alias="parentEntityType") # Type of the parent entity (Event or Series)
157
+ parent_entity_type: str = Field(
158
+ alias="parentEntityType"
159
+ ) # Type of the parent entity (Event or Series)
138
160
  parent_entity_id: int = Field(alias="parentEntityID") # ID of the parent entity
139
- parent_comment_id: Optional[str] = Field(None, alias="parentCommentID") # ID of the parent comment
161
+ parent_comment_id: Optional[str] = Field(
162
+ None, alias="parentCommentID"
163
+ ) # ID of the parent comment
140
164
  user_address: str = Field(alias="userAddress") # Address of the user
141
- reply_address: Optional[str] = Field(None, alias="replyAddress") # Address of the reply user
165
+ reply_address: Optional[str] = Field(
166
+ None, alias="replyAddress"
167
+ ) # Address of the reply user
142
168
  created_at: datetime = Field(alias="createdAt") # Creation timestamp
143
- updated_at: Optional[datetime] = Field(None, alias="updatedAt") # Last update timestamp
169
+ updated_at: Optional[datetime] = Field(
170
+ None, alias="updatedAt"
171
+ ) # Last update timestamp
172
+
144
173
 
145
174
  class Reaction(BaseModel):
146
175
  id: str # Unique identifier of reaction
@@ -150,47 +179,81 @@ class Reaction(BaseModel):
150
179
  user_address: str = Field(alias="userAddress") # Address of the user
151
180
  created_at: datetime = Field(alias="createdAt") # Creation timestamp
152
181
 
182
+
153
183
  class Request(BaseModel):
154
184
  request_id: str = Field(alias="requestId") # Unique identifier for the request
155
185
  proxy_address: str = Field(alias="proxyAddress") # Proxy address
156
186
  user_address: str = Field(alias="userAddress") # User address
157
- condition_id: Keccak256 = Field(alias="market") # Id of market which is also the CTF condition ID
158
- token_id: str = Field(alias="token") # ERC1155 token ID of conditional token being traded
159
- complement_token_id: str = Field(alias="complement") # Complement ERC1155 token ID of conditional token being traded
160
- state: Literal["STATE_REQUEST_EXPIRED", "STATE_USER_CANCELED", "STATE_REQUEST_CANCELED", "STATE_MAKER_CANCELED", "STATE_ACCEPTING_QUOTES", "STATE_REQUEST_QUOTED", "STATE_QUOTE_IMPROVED"] # Current state of the request
187
+ condition_id: Keccak256 = Field(
188
+ alias="market"
189
+ ) # Id of market which is also the CTF condition ID
190
+ token_id: str = Field(
191
+ alias="token"
192
+ ) # ERC1155 token ID of conditional token being traded
193
+ complement_token_id: str = Field(
194
+ alias="complement"
195
+ ) # Complement ERC1155 token ID of conditional token being traded
196
+ state: Literal[
197
+ "STATE_REQUEST_EXPIRED",
198
+ "STATE_USER_CANCELED",
199
+ "STATE_REQUEST_CANCELED",
200
+ "STATE_MAKER_CANCELED",
201
+ "STATE_ACCEPTING_QUOTES",
202
+ "STATE_REQUEST_QUOTED",
203
+ "STATE_QUOTE_IMPROVED",
204
+ ] # Current state of the request
161
205
  side: Literal["BUY", "SELL"] # Indicates buy or sell side
162
206
  price: float # Price from in/out sizes
163
207
  size_in: float = Field(alias="sizeIn") # Input size of the request
164
208
  size_out: float = Field(alias="sizeOut") # Output size of the request
165
209
  expiry: Optional[datetime] = None
166
210
 
211
+
167
212
  class Quote(BaseModel):
168
213
  quote_id: str = Field(alias="quoteId") # Unique identifier for the quote
169
214
  request_id: str = Field(alias="requestId") # Associated request identifier
170
215
  proxy_address: str = Field(alias="proxyAddress") # Proxy address
171
216
  user_address: str = Field(alias="userAddress") # User address
172
- condition_id: Keccak256 = Field(alias="condition") # Id of market which is also the CTF condition ID
173
- token_id: str = Field(alias="token") # ERC1155 token ID of conditional token being traded
174
- complement_token_id: str = Field(alias="complement") # Complement ERC1155 token ID of conditional token being traded
175
- state: Literal["STATE_REQUEST_EXPIRED", "STATE_USER_CANCELED", "STATE_REQUEST_CANCELED", "STATE_MAKER_CANCELED", "STATE_ACCEPTING_QUOTES", "STATE_REQUEST_QUOTED", "STATE_QUOTE_IMPROVED"] # Current state of the quote
217
+ condition_id: Keccak256 = Field(
218
+ alias="condition"
219
+ ) # Id of market which is also the CTF condition ID
220
+ token_id: str = Field(
221
+ alias="token"
222
+ ) # ERC1155 token ID of conditional token being traded
223
+ complement_token_id: str = Field(
224
+ alias="complement"
225
+ ) # Complement ERC1155 token ID of conditional token being traded
226
+ state: Literal[
227
+ "STATE_REQUEST_EXPIRED",
228
+ "STATE_USER_CANCELED",
229
+ "STATE_REQUEST_CANCELED",
230
+ "STATE_MAKER_CANCELED",
231
+ "STATE_ACCEPTING_QUOTES",
232
+ "STATE_REQUEST_QUOTED",
233
+ "STATE_QUOTE_IMPROVED",
234
+ ] # Current state of the quote
176
235
  side: Literal["BUY", "SELL"] # Indicates buy or sell side
177
236
  size_in: float = Field(alias="sizeIn") # Input size of the quote
178
237
  size_out: float = Field(alias="sizeOut") # Output size of the quote
179
238
  expiry: Optional[datetime] = None
180
239
 
240
+
181
241
  class CryptoPriceSubscribe(BaseModel):
182
242
  data: list[TimeseriesPoint]
183
243
  symbol: str
184
244
 
245
+
185
246
  class CryptoPriceUpdate(TimeseriesPoint):
186
247
  symbol: str
187
248
  full_accuracy_value: str
188
249
 
250
+
189
251
  class AggOrderBookSummary(OrderBookSummary):
190
252
  min_order_size: float
191
253
  tick_size: TickSize
192
254
  neg_risk: bool
193
255
 
256
+
194
257
  class LiveDataClobMarket(BaseModel):
195
258
  token_ids: list[str] = Field(alias="asset_ids")
196
259
  condition_id: Keccak256 = Field(alias="market")
@@ -198,6 +261,7 @@ class LiveDataClobMarket(BaseModel):
198
261
  tick_size: TickSize
199
262
  neg_risk: bool
200
263
 
264
+
201
265
  # Event models
202
266
  class ActivityTradeEvent(BaseModel):
203
267
  payload: ActivityTrade
@@ -205,36 +269,44 @@ class ActivityTradeEvent(BaseModel):
205
269
  type: Literal["trades"]
206
270
  topic: Literal["activity"]
207
271
 
272
+
208
273
  class ActivityOrderMatchEvent(BaseModel):
209
274
  payload: ActivityTrade
210
275
  timestamp: datetime
211
276
  type: Literal["orders_matched"]
212
277
  topic: Literal["activity"]
213
278
 
279
+
214
280
  class CommentEvent(BaseModel):
215
281
  payload: Comment
216
282
  timestamp: datetime
217
283
  type: Literal["comment_created", "comment_removed"]
218
284
  topic: Literal["comments"]
219
285
 
286
+
220
287
  class ReactionEvent(BaseModel):
221
288
  payload: Reaction
222
289
  timestamp: datetime
223
290
  type: Literal["reaction_created", "reaction_removed"]
224
291
  topic: Literal["comments"]
225
292
 
293
+
226
294
  class RequestEvent(BaseModel):
227
295
  payload: Request
228
296
  timestamp: datetime
229
- type: Literal["request_created", "request_edited", "request_canceled", "request_expired"]
297
+ type: Literal[
298
+ "request_created", "request_edited", "request_canceled", "request_expired"
299
+ ]
230
300
  topic: Literal["rfq"]
231
301
 
302
+
232
303
  class QuoteEvent(BaseModel):
233
304
  payload: Quote
234
305
  timestamp: datetime
235
306
  type: Literal["quote_created", "quote_edited", "quote_canceled", "quote_expired"]
236
307
  topic: Literal["rfq"]
237
308
 
309
+
238
310
  class CryptoPriceUpdateEvent(BaseModel):
239
311
  payload: CryptoPriceUpdate
240
312
  timestamp: datetime
@@ -242,12 +314,14 @@ class CryptoPriceUpdateEvent(BaseModel):
242
314
  type: Literal["update"]
243
315
  topic: Literal["crypto_prices", "crypto_prices_chainlink"]
244
316
 
317
+
245
318
  class CryptoPriceSubscribeEvent(BaseModel):
246
319
  payload: CryptoPriceSubscribe
247
320
  timestamp: datetime
248
321
  type: Literal["subscribe"]
249
322
  topic: Literal["crypto_prices", "crypto_prices_chainlink"]
250
323
 
324
+
251
325
  class LiveDataOrderBookSummaryEvent(BaseModel):
252
326
  payload: list[AggOrderBookSummary] | AggOrderBookSummary
253
327
  timestamp: datetime
@@ -255,6 +329,7 @@ class LiveDataOrderBookSummaryEvent(BaseModel):
255
329
  type: Literal["agg_orderbook"]
256
330
  topic: Literal["clob_market"]
257
331
 
332
+
258
333
  class LiveDataPriceChangeEvent(BaseModel):
259
334
  payload: PriceChanges
260
335
  timestamp: datetime
@@ -262,6 +337,7 @@ class LiveDataPriceChangeEvent(BaseModel):
262
337
  type: Literal["price_change"]
263
338
  topic: Literal["clob_market"]
264
339
 
340
+
265
341
  class LiveDataLastTradePriceEvent(BaseModel):
266
342
  payload: LastTradePrice
267
343
  timestamp: datetime
@@ -269,6 +345,7 @@ class LiveDataLastTradePriceEvent(BaseModel):
269
345
  type: Literal["last_trade_price"]
270
346
  topic: Literal["clob_market"]
271
347
 
348
+
272
349
  class LiveDataTickSizeChangeEvent(BaseModel):
273
350
  payload: TickSizeChange
274
351
  timestamp: datetime
@@ -276,6 +353,7 @@ class LiveDataTickSizeChangeEvent(BaseModel):
276
353
  type: Literal["tick_size_change"]
277
354
  topic: Literal["clob_market"]
278
355
 
356
+
279
357
  class MarketStatusChangeEvent(BaseModel):
280
358
  payload: LiveDataClobMarket
281
359
  timestamp: datetime
@@ -283,23 +361,24 @@ class MarketStatusChangeEvent(BaseModel):
283
361
  type: Literal["market_created", "market_resolved"]
284
362
  topic: Literal["clob_market"]
285
363
 
364
+
286
365
  class LiveDataOrderEvent(BaseModel):
287
- connection_id: str
288
366
  payload: OrderEvent
289
367
  timestamp: datetime
368
+ connection_id: str
290
369
  type: Literal["order"]
291
370
  topic: Literal["clob_user"]
292
371
 
372
+
293
373
  class LiveDataTradeEvent(BaseModel):
294
- connection_id: str
295
374
  payload: TradeEvent
296
375
  timestamp: datetime
376
+ connection_id: str
297
377
  type: Literal["trade"]
298
378
  topic: Literal["clob_user"]
299
379
 
380
+
300
381
  class ErrorEvent(BaseModel):
301
382
  message: str
302
383
  connection_id: str = Field(alias="connectionId")
303
384
  request_id: str = Field(alias="requestId")
304
-
305
-
@@ -26,6 +26,7 @@ NEG_RISK_CONFIG = {
26
26
  ),
27
27
  }
28
28
 
29
+
29
30
  def get_contract_config(chain_id: int, neg_risk: bool = False) -> ContractConfig:
30
31
  """Get the contract configuration for the chain."""
31
32
  config = NEG_RISK_CONFIG.get(chain_id) if neg_risk else CONFIG.get(chain_id)
@@ -1,17 +1,22 @@
1
1
  class InvalidPriceError(Exception):
2
2
  pass
3
3
 
4
+
4
5
  class InvalidTickSizeError(Exception):
5
6
  pass
6
7
 
8
+
7
9
  class InvalidFeeRateError(Exception):
8
10
  pass
9
11
 
12
+
10
13
  class LiquidityError(Exception):
11
14
  pass
12
15
 
16
+
13
17
  class MissingOrderbookError(Exception):
14
18
  pass
15
19
 
20
+
16
21
  class AuthenticationRequiredError(ValueError):
17
22
  """Raised when authentication credentials are required but not provided."""
@@ -54,7 +54,11 @@ class OrderBuilder:
54
54
  self.funder = funder if funder is not None else self.signer.address()
55
55
 
56
56
  def get_order_amounts(
57
- self, side: str, size: float, price: float, round_config: RoundConfig,
57
+ self,
58
+ side: str,
59
+ size: float,
60
+ price: float,
61
+ round_config: RoundConfig,
58
62
  ):
59
63
  raw_price = round_normal(price, round_config.price)
60
64
 
@@ -88,7 +92,11 @@ class OrderBuilder:
88
92
  raise ValueError(msg)
89
93
 
90
94
  def get_market_order_amounts(
91
- self, side: str, amount: float, price: float, round_config: RoundConfig,
95
+ self,
96
+ side: str,
97
+ amount: float,
98
+ price: float,
99
+ round_config: RoundConfig,
92
100
  ):
93
101
  raw_price = round_normal(price, round_config.price)
94
102
 
@@ -122,7 +130,9 @@ class OrderBuilder:
122
130
  raise ValueError(msg)
123
131
 
124
132
  def create_order(
125
- self, order_args: OrderArgs, options: CreateOrderOptions,
133
+ self,
134
+ order_args: OrderArgs,
135
+ options: CreateOrderOptions,
126
136
  ) -> SignedOrder:
127
137
  """Creates and signs an order."""
128
138
  side, maker_amount, taker_amount = self.get_order_amounts(
@@ -147,7 +157,8 @@ class OrderBuilder:
147
157
  )
148
158
 
149
159
  contract_config = get_contract_config(
150
- self.signer.get_chain_id(), options.neg_risk,
160
+ self.signer.get_chain_id(),
161
+ options.neg_risk,
151
162
  )
152
163
 
153
164
  order_builder = UtilsOrderBuilder(
@@ -159,7 +170,9 @@ class OrderBuilder:
159
170
  return order_builder.build_signed_order(data)
160
171
 
161
172
  def create_market_order(
162
- self, order_args: MarketOrderArgs, options: CreateOrderOptions,
173
+ self,
174
+ order_args: MarketOrderArgs,
175
+ options: CreateOrderOptions,
163
176
  ) -> SignedOrder:
164
177
  """Creates and signs a market order."""
165
178
  side, maker_amount, taker_amount = self.get_market_order_amounts(
@@ -184,7 +197,8 @@ class OrderBuilder:
184
197
  )
185
198
 
186
199
  contract_config = get_contract_config(
187
- self.signer.get_chain_id(), options.neg_risk,
200
+ self.signer.get_chain_id(),
201
+ options.neg_risk,
188
202
  )
189
203
 
190
204
  order_builder = UtilsOrderBuilder(
@@ -196,10 +210,12 @@ class OrderBuilder:
196
210
  return order_builder.build_signed_order(data)
197
211
 
198
212
  def calculate_buy_market_price(
199
- self,
200
- asks: list[OrderSummary], # expected to be sorted from worst to best price (high to low)
201
- amount_to_match: float, # in usdc
202
- order_type: OrderType,
213
+ self,
214
+ asks: list[
215
+ OrderSummary
216
+ ], # expected to be sorted from worst to best price (high to low)
217
+ amount_to_match: float, # in usdc
218
+ order_type: OrderType,
203
219
  ) -> float:
204
220
  if not asks:
205
221
  msg = "No ask orders available"
@@ -218,10 +234,12 @@ class OrderBuilder:
218
234
  return float(asks[0].price)
219
235
 
220
236
  def calculate_sell_market_price(
221
- self,
222
- bids: list[OrderSummary], # expected to be sorted from worst to best price (low to high)
223
- amount_to_match: float, # in usdc
224
- order_type: OrderType,
237
+ self,
238
+ bids: list[
239
+ OrderSummary
240
+ ], # expected to be sorted from worst to best price (low to high)
241
+ amount_to_match: float, # in shares
242
+ order_type: OrderType,
225
243
  ) -> float:
226
244
  if not bids:
227
245
  msg = "No bid orders available"
@@ -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,8 +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)
38
-
39
+ return max(0, -exponent)
39
40
 
40
41
 
41
42
  def generate_orderbook_summary_hash(orderbook: OrderBookSummary) -> str:
@@ -4,7 +4,11 @@ import hmac
4
4
 
5
5
 
6
6
  def build_hmac_signature(
7
- secret: str, timestamp: str, method: str, request_path: str, body=None,
7
+ secret: str,
8
+ timestamp: str,
9
+ method: str,
10
+ request_path: str,
11
+ body=None,
8
12
  ):
9
13
  """Creates an HMAC signature by signing a payload with the secret."""
10
14
  base64_secret = base64.urlsafe_b64decode(secret)
@@ -28,4 +28,4 @@ CUSTOM_ERROR_DICT = {
28
28
  "0xcd7769ff": "NotApprovedForAll()",
29
29
  "0x80fee105": "OnlyOracle()",
30
30
  "0x155a4b5c": "UnexpectedCollateralToken()",
31
- }
31
+ }
@@ -2,6 +2,7 @@ def get_market_index(question_id: str) -> int:
2
2
  """Extract the market index from a question ID (last 2 hex characters)."""
3
3
  return int(question_id[-2:], 16)
4
4
 
5
+
5
6
  def get_index_set(question_ids: list[str]) -> int:
6
7
  """Calculate bitwise index set from question IDs."""
7
8
  indices = [get_market_index(question_id) for question_id in question_ids]