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.
- polymarket_apis/__init__.py +2 -0
- polymarket_apis/clients/__init__.py +0 -0
- polymarket_apis/clients/clob_client.py +730 -0
- polymarket_apis/clients/data_client.py +234 -0
- polymarket_apis/clients/gamma_client.py +311 -0
- polymarket_apis/clients/web3_client.py +261 -0
- polymarket_apis/clients/websockets_client.py +131 -0
- polymarket_apis/types/__init__.py +0 -0
- polymarket_apis/types/clob_types.py +494 -0
- polymarket_apis/types/common.py +49 -0
- polymarket_apis/types/data_types.py +161 -0
- polymarket_apis/types/gamma_types.py +313 -0
- polymarket_apis/types/websockets_types.py +191 -0
- polymarket_apis/utilities/__init__.py +0 -0
- polymarket_apis/utilities/config.py +36 -0
- polymarket_apis/utilities/constants.py +26 -0
- polymarket_apis/utilities/endpoints.py +37 -0
- polymarket_apis/utilities/exceptions.py +11 -0
- polymarket_apis/utilities/headers.py +54 -0
- polymarket_apis/utilities/order_builder/__init__.py +0 -0
- polymarket_apis/utilities/order_builder/builder.py +240 -0
- polymarket_apis/utilities/order_builder/helpers.py +61 -0
- polymarket_apis/utilities/signing/__init__.py +0 -0
- polymarket_apis/utilities/signing/eip712.py +28 -0
- polymarket_apis/utilities/signing/hmac.py +20 -0
- polymarket_apis/utilities/signing/model.py +8 -0
- polymarket_apis/utilities/signing/signer.py +25 -0
- polymarket_apis/utilities/web3/__init__.py +0 -0
- polymarket_apis/utilities/web3/abis/CTFExchange.json +1851 -0
- polymarket_apis/utilities/web3/abis/ConditionalTokens.json +705 -0
- polymarket_apis/utilities/web3/abis/NegRiskAdapter.json +999 -0
- polymarket_apis/utilities/web3/abis/NegRiskCtfExchange.json +1856 -0
- polymarket_apis/utilities/web3/abis/ProxyWalletFactory.json +319 -0
- polymarket_apis/utilities/web3/abis/UChildERC20Proxy.json +1438 -0
- polymarket_apis/utilities/web3/abis/__init__.py +0 -0
- polymarket_apis/utilities/web3/abis/custom_contract_errors.py +31 -0
- polymarket_apis/utilities/web3/helpers.py +8 -0
- polymarket_apis-0.2.2.dist-info/METADATA +18 -0
- polymarket_apis-0.2.2.dist-info/RECORD +40 -0
- polymarket_apis-0.2.2.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import Any, Literal, Optional, TypeVar, Union
|
|
5
|
+
|
|
6
|
+
from py_order_utils.model import SignedOrder
|
|
7
|
+
from pydantic import (
|
|
8
|
+
BaseModel,
|
|
9
|
+
ConfigDict,
|
|
10
|
+
Field,
|
|
11
|
+
RootModel,
|
|
12
|
+
ValidationError,
|
|
13
|
+
ValidationInfo,
|
|
14
|
+
ValidatorFunctionWrapHandler,
|
|
15
|
+
field_serializer,
|
|
16
|
+
field_validator,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
from ..types.common import EthAddress, Keccak256, TimeseriesPoint
|
|
20
|
+
from ..utilities.constants import ZERO_ADDRESS
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
class ApiCreds(BaseModel):
|
|
25
|
+
api_key: str = Field(alias="apiKey")
|
|
26
|
+
secret: str
|
|
27
|
+
passphrase: str
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class RequestArgs(BaseModel):
|
|
31
|
+
method: Literal["GET", "POST", "DELETE"]
|
|
32
|
+
request_path: str
|
|
33
|
+
body: Any = None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class TokenValue(BaseModel):
|
|
37
|
+
token_id: str
|
|
38
|
+
value: float
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Midpoint(TokenValue):
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Spread(TokenValue):
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class TokenValueDict(RootModel):
|
|
50
|
+
root: dict[str, float]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class BookParams(BaseModel):
|
|
54
|
+
token_id: str
|
|
55
|
+
side: Literal["BUY", "SELL"]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class Price(BookParams):
|
|
59
|
+
price: float
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class BidAsk(BaseModel):
|
|
63
|
+
BUY: Optional[float] = None # Price buyers are willing to pay
|
|
64
|
+
SELL: Optional[float] = None # Price sellers are willing to accept
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class TokenBidAsk(BidAsk):
|
|
68
|
+
token_id: str
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class TokenBidAskDict(RootModel):
|
|
72
|
+
root: dict[str, BidAsk]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class Token(BaseModel):
|
|
76
|
+
token_id: str
|
|
77
|
+
outcome: str
|
|
78
|
+
price: float
|
|
79
|
+
winner: Optional[bool] = None
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class PriceHistory(BaseModel):
|
|
83
|
+
token_id: str
|
|
84
|
+
history: list[TimeseriesPoint]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
T = TypeVar("T")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class PaginatedResponse[T](BaseModel):
|
|
91
|
+
data: list[T]
|
|
92
|
+
next_cursor: str
|
|
93
|
+
limit: int
|
|
94
|
+
count: int
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class RewardRate(BaseModel):
|
|
98
|
+
asset_address: str
|
|
99
|
+
rewards_daily_rate: float
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class RewardConfig(BaseModel):
|
|
103
|
+
asset_address: str
|
|
104
|
+
rewards_daily_rate: float = Field(alias="rate_per_day")
|
|
105
|
+
|
|
106
|
+
start_date: datetime
|
|
107
|
+
end_date: datetime
|
|
108
|
+
|
|
109
|
+
reward_id: Optional[str] = Field(None, alias="id")
|
|
110
|
+
total_rewards: float
|
|
111
|
+
total_days: Optional[int] = None
|
|
112
|
+
|
|
113
|
+
@field_validator("reward_id", mode="before")
|
|
114
|
+
def convert_id_to_str(cls, v):
|
|
115
|
+
if isinstance(v, int):
|
|
116
|
+
return str(v)
|
|
117
|
+
return v
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class Rewards(BaseModel):
|
|
121
|
+
rates: Optional[list[RewardRate]]
|
|
122
|
+
rewards_min_size: int = Field(alias="min_size")
|
|
123
|
+
rewards_max_spread: float = Field(alias="max_spread")
|
|
124
|
+
|
|
125
|
+
class EarnedReward(BaseModel):
|
|
126
|
+
asset_address: EthAddress
|
|
127
|
+
earnings: float
|
|
128
|
+
asset_rate: float
|
|
129
|
+
|
|
130
|
+
class DailyEarnedReward(BaseModel):
|
|
131
|
+
date: datetime
|
|
132
|
+
asset_address: EthAddress
|
|
133
|
+
maker_address: EthAddress
|
|
134
|
+
earnings: float
|
|
135
|
+
asset_rate: float
|
|
136
|
+
|
|
137
|
+
class PolymarketRewardItem(BaseModel):
|
|
138
|
+
market_id: str
|
|
139
|
+
condition_id: Keccak256
|
|
140
|
+
question: str
|
|
141
|
+
market_slug: str
|
|
142
|
+
market_description: str
|
|
143
|
+
event_slug: str
|
|
144
|
+
image: str
|
|
145
|
+
maker_address: EthAddress
|
|
146
|
+
tokens: list[Token]
|
|
147
|
+
rewards_config: list[RewardConfig]
|
|
148
|
+
earnings: list[EarnedReward]
|
|
149
|
+
rewards_max_spread: float
|
|
150
|
+
rewards_min_size: float
|
|
151
|
+
earning_percentage: float
|
|
152
|
+
spread: float
|
|
153
|
+
market_competitiveness: float
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class RewardsMarket(BaseModel):
|
|
157
|
+
condition_id: Keccak256
|
|
158
|
+
question: str
|
|
159
|
+
market_slug: str
|
|
160
|
+
event_slug: str
|
|
161
|
+
image: str
|
|
162
|
+
tokens: list[Token]
|
|
163
|
+
rewards_config: list[RewardConfig]
|
|
164
|
+
rewards_max_spread: float
|
|
165
|
+
rewards_min_size: int
|
|
166
|
+
market_competitiveness: float
|
|
167
|
+
|
|
168
|
+
class ClobMarket(BaseModel):
|
|
169
|
+
# Core market information
|
|
170
|
+
token_ids: list[Token] = Field(alias="tokens")
|
|
171
|
+
condition_id: Keccak256
|
|
172
|
+
question_id: Keccak256
|
|
173
|
+
question: str
|
|
174
|
+
description: str
|
|
175
|
+
market_slug: str
|
|
176
|
+
end_date_iso: Optional[datetime]
|
|
177
|
+
game_start_time: Optional[datetime] = None
|
|
178
|
+
seconds_delay: int
|
|
179
|
+
|
|
180
|
+
# Order book settings
|
|
181
|
+
enable_order_book: bool
|
|
182
|
+
accepting_orders: bool
|
|
183
|
+
accepting_order_timestamp: Optional[datetime]
|
|
184
|
+
minimum_order_size: float
|
|
185
|
+
minimum_tick_size: float
|
|
186
|
+
|
|
187
|
+
# Status flags
|
|
188
|
+
active: bool
|
|
189
|
+
closed: bool
|
|
190
|
+
archived: bool
|
|
191
|
+
|
|
192
|
+
# Negative risk settings
|
|
193
|
+
neg_risk: bool
|
|
194
|
+
neg_risk_market_id: Keccak256
|
|
195
|
+
neg_risk_request_id: Keccak256
|
|
196
|
+
|
|
197
|
+
# Fee structure
|
|
198
|
+
fpmm: str
|
|
199
|
+
maker_base_fee: float
|
|
200
|
+
taker_base_fee: float
|
|
201
|
+
|
|
202
|
+
# Features
|
|
203
|
+
notifications_enabled: bool
|
|
204
|
+
is_50_50_outcome: bool
|
|
205
|
+
|
|
206
|
+
# Visual representation
|
|
207
|
+
icon: str
|
|
208
|
+
image: str
|
|
209
|
+
|
|
210
|
+
# Rewards configuration
|
|
211
|
+
rewards: Optional[Rewards]
|
|
212
|
+
|
|
213
|
+
# Tags
|
|
214
|
+
tags: Optional[list[str]]
|
|
215
|
+
|
|
216
|
+
@field_validator("neg_risk_market_id", "neg_risk_request_id", mode="wrap")
|
|
217
|
+
@classmethod
|
|
218
|
+
def validate_neg_risk_fields(cls, value: str, handler: ValidatorFunctionWrapHandler, info: ValidationInfo) -> Optional[str]:
|
|
219
|
+
try:
|
|
220
|
+
return handler(value)
|
|
221
|
+
except ValidationError as e:
|
|
222
|
+
neg_risk = info.data.get("neg_risk", False)
|
|
223
|
+
active = info.data.get("active", False)
|
|
224
|
+
if not neg_risk and value == "":
|
|
225
|
+
return value
|
|
226
|
+
if not active:
|
|
227
|
+
return value
|
|
228
|
+
if neg_risk and value == "":
|
|
229
|
+
for _ in e.errors():
|
|
230
|
+
msg = ("Poorly setup market: negative risk is True, but either neg_risk_market_id or neg_risk_request_id is missing. "
|
|
231
|
+
f" Question: {info.data.get("question")}; Market slug: {info.data.get('market_slug')} \n")
|
|
232
|
+
logger.warning(msg)
|
|
233
|
+
@field_validator("condition_id", "question_id", mode="wrap")
|
|
234
|
+
@classmethod
|
|
235
|
+
def validate_condition_fields(cls, value: str, handler: ValidatorFunctionWrapHandler, info: ValidationInfo) -> str:
|
|
236
|
+
try:
|
|
237
|
+
return handler(value)
|
|
238
|
+
except ValueError:
|
|
239
|
+
active = info.data.get("active", False)
|
|
240
|
+
if not active:
|
|
241
|
+
return value
|
|
242
|
+
raise
|
|
243
|
+
|
|
244
|
+
class OpenOrder(BaseModel):
|
|
245
|
+
order_id: Keccak256 = Field(alias="id")
|
|
246
|
+
status: str
|
|
247
|
+
owner: str
|
|
248
|
+
maker_address: str
|
|
249
|
+
condition_id: str = Field(alias="market")
|
|
250
|
+
token_id: str = Field(alias="asset_id")
|
|
251
|
+
side: Literal["BUY", "SELL"]
|
|
252
|
+
original_size: float
|
|
253
|
+
size_matched: float
|
|
254
|
+
price: float
|
|
255
|
+
outcome: str
|
|
256
|
+
expiration: datetime
|
|
257
|
+
order_type: Literal["GTC", "FOK", "GTD"]
|
|
258
|
+
associate_trades: list[str]
|
|
259
|
+
created_at: datetime
|
|
260
|
+
|
|
261
|
+
class MakerOrder(BaseModel):
|
|
262
|
+
token_id: str = Field(alias="asset_id")
|
|
263
|
+
order_id: Keccak256
|
|
264
|
+
maker_address: EthAddress
|
|
265
|
+
owner: str
|
|
266
|
+
matched_amount: float
|
|
267
|
+
price: float
|
|
268
|
+
outcome: str
|
|
269
|
+
fee_rate_bps: float
|
|
270
|
+
|
|
271
|
+
class PolygonTrade(BaseModel):
|
|
272
|
+
trade_id: str = Field(alias="id")
|
|
273
|
+
taker_order_id: Keccak256
|
|
274
|
+
condition_id: Keccak256 = Field(alias="market")
|
|
275
|
+
id: str
|
|
276
|
+
side: Literal["BUY", "SELL"]
|
|
277
|
+
size: float
|
|
278
|
+
fee_rate_bps: float
|
|
279
|
+
price: float
|
|
280
|
+
status: str # change to literals MINED, CONFIRMED
|
|
281
|
+
match_time: datetime
|
|
282
|
+
last_update: datetime
|
|
283
|
+
outcome: str
|
|
284
|
+
bucket_index: int
|
|
285
|
+
owner: str
|
|
286
|
+
maker_address: EthAddress
|
|
287
|
+
transaction_hash: Keccak256
|
|
288
|
+
maker_orders: list[MakerOrder]
|
|
289
|
+
trader_side: Literal["TAKER", "MAKER"]
|
|
290
|
+
|
|
291
|
+
class TradeParams(BaseModel):
|
|
292
|
+
id: Optional[str] = None
|
|
293
|
+
maker_address: Optional[str] = None
|
|
294
|
+
market: Optional[str] = None
|
|
295
|
+
asset_id: Optional[str] = None
|
|
296
|
+
before: Optional[int] = None
|
|
297
|
+
after: Optional[int] = None
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
class OpenOrderParams(BaseModel):
|
|
301
|
+
order_id: Optional[str] = None
|
|
302
|
+
condition_id: Optional[str] = None
|
|
303
|
+
token_id: Optional[str] = None
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
class DropNotificationParams(BaseModel):
|
|
307
|
+
ids: Optional[list[str]]= None
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
class OrderSummary(BaseModel):
|
|
311
|
+
price: Optional[float] = None
|
|
312
|
+
size: Optional[float] = None
|
|
313
|
+
|
|
314
|
+
class PriceLevel(OrderSummary):
|
|
315
|
+
side: Literal["BUY", "SELL"]
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
class OrderBookSummary(BaseModel):
|
|
319
|
+
condition_id: Optional[Keccak256] = Field(None, alias="market")
|
|
320
|
+
token_id: Optional[str] = Field(None, alias="asset_id")
|
|
321
|
+
timestamp: Optional[datetime] = None
|
|
322
|
+
hash: Optional[str] = None
|
|
323
|
+
bids: Optional[list[OrderSummary]] = None
|
|
324
|
+
asks: Optional[list[OrderSummary]] = None
|
|
325
|
+
|
|
326
|
+
@field_serializer("bids", "asks")
|
|
327
|
+
def serialize_sizes(self, orders: list[OrderSummary]) -> list[dict]:
|
|
328
|
+
return [
|
|
329
|
+
{
|
|
330
|
+
"price": f"{order.price:.3f}".rstrip("0").rstrip("."),
|
|
331
|
+
"size": f"{order.size:.2f}".rstrip("0").rstrip("."),
|
|
332
|
+
}
|
|
333
|
+
for order in orders
|
|
334
|
+
]
|
|
335
|
+
|
|
336
|
+
@field_serializer("timestamp")
|
|
337
|
+
def serialize_timestamp(self, ts: datetime) -> str:
|
|
338
|
+
# Convert to millisecond timestamp string without decimal places
|
|
339
|
+
return str(int(ts.timestamp() * 1000))
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
class AssetType(str, Enum):
|
|
343
|
+
COLLATERAL = "COLLATERAL"
|
|
344
|
+
CONDITIONAL = "CONDITIONAL"
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
class BalanceAllowanceParams(BaseModel):
|
|
348
|
+
asset_type: Optional[AssetType] = None
|
|
349
|
+
token_id: Optional[str] = None
|
|
350
|
+
signature_type: int = -1
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
class OrderType(str, Enum):
|
|
354
|
+
GTC = "GTC" # Good Till Cancelled
|
|
355
|
+
GTD = "GTD" # Good Till Date
|
|
356
|
+
FOK = "FOK" # Fill or Kill
|
|
357
|
+
FAK = "FAK" # Fill and Kill
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
TickSize = Literal["0.1", "0.01", "0.001", "0.0001"]
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
class CreateOrderOptions(BaseModel):
|
|
364
|
+
tick_size: TickSize
|
|
365
|
+
neg_risk: bool
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
class PartialCreateOrderOptions(BaseModel):
|
|
369
|
+
tick_size: Optional[TickSize] = None
|
|
370
|
+
neg_risk: Optional[bool] = None
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
class RoundConfig(BaseModel):
|
|
374
|
+
price: int
|
|
375
|
+
size: int
|
|
376
|
+
amount: int
|
|
377
|
+
|
|
378
|
+
class OrderArgs(BaseModel):
|
|
379
|
+
token_id: str
|
|
380
|
+
"""
|
|
381
|
+
TokenID of the Conditional token asset being traded
|
|
382
|
+
"""
|
|
383
|
+
|
|
384
|
+
price: float
|
|
385
|
+
"""
|
|
386
|
+
Price used to create the order
|
|
387
|
+
"""
|
|
388
|
+
|
|
389
|
+
size: float
|
|
390
|
+
"""
|
|
391
|
+
Size in terms of the ConditionalToken
|
|
392
|
+
"""
|
|
393
|
+
|
|
394
|
+
side: str
|
|
395
|
+
"""
|
|
396
|
+
Side of the order
|
|
397
|
+
"""
|
|
398
|
+
|
|
399
|
+
fee_rate_bps: int = 0
|
|
400
|
+
"""
|
|
401
|
+
Fee rate, in basis points, charged to the order maker, charged on proceeds
|
|
402
|
+
"""
|
|
403
|
+
|
|
404
|
+
nonce: int = 0
|
|
405
|
+
"""
|
|
406
|
+
Nonce used for onchain cancellations
|
|
407
|
+
"""
|
|
408
|
+
|
|
409
|
+
expiration: int = 0
|
|
410
|
+
"""
|
|
411
|
+
Timestamp after which the order is expired.
|
|
412
|
+
"""
|
|
413
|
+
|
|
414
|
+
taker: str = ZERO_ADDRESS
|
|
415
|
+
"""
|
|
416
|
+
Address of the order taker. The zero address is used to indicate a public order.
|
|
417
|
+
"""
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
class MarketOrderArgs(BaseModel):
|
|
421
|
+
token_id: str
|
|
422
|
+
"""
|
|
423
|
+
TokenID of the Conditional token asset being traded
|
|
424
|
+
"""
|
|
425
|
+
|
|
426
|
+
amount: float
|
|
427
|
+
"""
|
|
428
|
+
BUY orders: $$$ Amount to buy
|
|
429
|
+
SELL orders: Shares to sell
|
|
430
|
+
"""
|
|
431
|
+
|
|
432
|
+
side: str
|
|
433
|
+
"""
|
|
434
|
+
Side of the order
|
|
435
|
+
"""
|
|
436
|
+
|
|
437
|
+
price: float = 0
|
|
438
|
+
"""
|
|
439
|
+
Price used to create the order
|
|
440
|
+
"""
|
|
441
|
+
|
|
442
|
+
fee_rate_bps: int = 0
|
|
443
|
+
"""
|
|
444
|
+
Fee rate, in basis points, charged to the order maker, charged on proceeds.
|
|
445
|
+
"""
|
|
446
|
+
|
|
447
|
+
nonce: int = 0
|
|
448
|
+
"""
|
|
449
|
+
Nonce used for onchain cancellations.
|
|
450
|
+
"""
|
|
451
|
+
|
|
452
|
+
taker: str = ZERO_ADDRESS
|
|
453
|
+
"""
|
|
454
|
+
Address of the order taker. The zero address is used to indicate a public order.
|
|
455
|
+
"""
|
|
456
|
+
|
|
457
|
+
order_type: OrderType = OrderType.FOK
|
|
458
|
+
|
|
459
|
+
class PostOrdersArgs(BaseModel):
|
|
460
|
+
order: SignedOrder
|
|
461
|
+
order_type: OrderType = OrderType.GTC
|
|
462
|
+
|
|
463
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
464
|
+
|
|
465
|
+
class ContractConfig(BaseModel):
|
|
466
|
+
"""Contract Configuration."""
|
|
467
|
+
|
|
468
|
+
exchange: EthAddress
|
|
469
|
+
"""
|
|
470
|
+
The exchange contract responsible for matching orders.
|
|
471
|
+
"""
|
|
472
|
+
|
|
473
|
+
collateral: EthAddress
|
|
474
|
+
"""
|
|
475
|
+
The ERC20 token used as collateral for the exchange's markets.
|
|
476
|
+
"""
|
|
477
|
+
|
|
478
|
+
conditional_tokens: EthAddress
|
|
479
|
+
"""
|
|
480
|
+
The ERC1155 conditional tokens contract.
|
|
481
|
+
"""
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
class OrderPostResponse(BaseModel):
|
|
485
|
+
error_msg: str = Field(alias="errorMsg")
|
|
486
|
+
order_id: Union[Keccak256, Literal[""]] = Field(alias="orderID")
|
|
487
|
+
taking_amount: str = Field(alias="takingAmount")
|
|
488
|
+
making_amount: str = Field(alias="makingAmount")
|
|
489
|
+
status: str = Literal["live", "matched", "delayed"]
|
|
490
|
+
success: bool
|
|
491
|
+
|
|
492
|
+
class OrderCancelResponse(BaseModel):
|
|
493
|
+
not_canceled: Optional[dict[Keccak256, str]]
|
|
494
|
+
canceled: Optional[list[Keccak256]]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
|
|
5
|
+
from pydantic import AfterValidator, BaseModel, BeforeValidator, Field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def validate_keccak256(v: str) -> str:
|
|
9
|
+
if not re.match(r"^0x[a-fA-F0-9]{64}$", v):
|
|
10
|
+
msg = "Invalid Keccak256 hash format"
|
|
11
|
+
raise ValueError(msg)
|
|
12
|
+
return v
|
|
13
|
+
|
|
14
|
+
def parse_timestamp(v: str) -> datetime:
|
|
15
|
+
# Normalize '+00' to '+0000' for timezone
|
|
16
|
+
if v.endswith("+00"):
|
|
17
|
+
v = v[:-3] + "+0000"
|
|
18
|
+
|
|
19
|
+
# Pad fractional seconds to 6 digits if present
|
|
20
|
+
if "." in v:
|
|
21
|
+
dot_pos = v.find(".")
|
|
22
|
+
tz_pos = v.find("+", dot_pos) # Find timezone start after '.'
|
|
23
|
+
if tz_pos == -1:
|
|
24
|
+
tz_pos = v.find("-", dot_pos)
|
|
25
|
+
|
|
26
|
+
if tz_pos != -1:
|
|
27
|
+
frac = v[dot_pos+1:tz_pos]
|
|
28
|
+
if len(frac) < 6:
|
|
29
|
+
frac = frac.ljust(6, "0")
|
|
30
|
+
v = f"{v[:dot_pos+1]}{frac}{v[tz_pos:]}"
|
|
31
|
+
|
|
32
|
+
# Try parsing with and without microseconds
|
|
33
|
+
for fmt in ("%Y-%m-%d %H:%M:%S.%f%z", "%Y-%m-%d %H:%M:%S%z"):
|
|
34
|
+
try:
|
|
35
|
+
return datetime.strptime(v, fmt) # noqa: DTZ007
|
|
36
|
+
except ValueError:
|
|
37
|
+
continue
|
|
38
|
+
msg = f"Time data '{v}' does not match expected formats."
|
|
39
|
+
raise ValueError(msg)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
TimestampWithTZ = Annotated[datetime, BeforeValidator(parse_timestamp)]
|
|
43
|
+
EthAddress = Annotated[str, Field(pattern=r"^0x[A-Fa-f0-9]{40}$")]
|
|
44
|
+
Keccak256 = Annotated[str, AfterValidator(validate_keccak256)]
|
|
45
|
+
EmptyString = Annotated[str, Field(pattern=r"^$", description="An empty string")]
|
|
46
|
+
|
|
47
|
+
class TimeseriesPoint(BaseModel):
|
|
48
|
+
t: datetime
|
|
49
|
+
p: float
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
from datetime import UTC, datetime
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel, Field, field_validator
|
|
5
|
+
|
|
6
|
+
from .common import EmptyString, EthAddress, Keccak256
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Position(BaseModel):
|
|
10
|
+
# User identification
|
|
11
|
+
proxy_wallet: EthAddress = Field(alias="proxyWallet")
|
|
12
|
+
|
|
13
|
+
# Asset information
|
|
14
|
+
token_id: str = Field(alias="asset")
|
|
15
|
+
condition_id: Keccak256 = Field(alias="conditionId")
|
|
16
|
+
outcome: str
|
|
17
|
+
outcome_index: int = Field(alias="outcomeIndex")
|
|
18
|
+
opposite_outcome: str = Field(alias="oppositeOutcome")
|
|
19
|
+
opposite_asset: str = Field(alias="oppositeAsset")
|
|
20
|
+
|
|
21
|
+
# Position details
|
|
22
|
+
size: float
|
|
23
|
+
avg_price: float = Field(alias="avgPrice")
|
|
24
|
+
current_price: float = Field(alias="curPrice")
|
|
25
|
+
redeemable: bool
|
|
26
|
+
|
|
27
|
+
# Financial metrics
|
|
28
|
+
initial_value: float = Field(alias="initialValue")
|
|
29
|
+
current_value: float = Field(alias="currentValue")
|
|
30
|
+
cash_pnl: float = Field(alias="cashPnl")
|
|
31
|
+
percent_pnl: float = Field(alias="percentPnl")
|
|
32
|
+
total_bought: float = Field(alias="totalBought")
|
|
33
|
+
realized_pnl: float = Field(alias="realizedPnl")
|
|
34
|
+
percent_realized_pnl: float = Field(alias="percentRealizedPnl")
|
|
35
|
+
|
|
36
|
+
# Event information
|
|
37
|
+
title: str
|
|
38
|
+
slug: str
|
|
39
|
+
icon: str
|
|
40
|
+
event_slug: str = Field(alias="eventSlug")
|
|
41
|
+
end_date: datetime = Field(alias="endDate")
|
|
42
|
+
negative_risk: bool = Field(alias="negativeRisk")
|
|
43
|
+
|
|
44
|
+
@field_validator("end_date", mode="before")
|
|
45
|
+
def handle_empty_end_date(cls, v):
|
|
46
|
+
if v == "":
|
|
47
|
+
return datetime(2099,12,31, tzinfo=UTC)
|
|
48
|
+
return v
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Trade(BaseModel):
|
|
52
|
+
# User identification
|
|
53
|
+
proxy_wallet: EthAddress = Field(alias="proxyWallet")
|
|
54
|
+
|
|
55
|
+
# Trade details
|
|
56
|
+
side: Literal["BUY", "SELL"]
|
|
57
|
+
token_id: str = Field(alias="asset")
|
|
58
|
+
condition_id: Keccak256 = Field(alias="conditionId")
|
|
59
|
+
size: float
|
|
60
|
+
price: float
|
|
61
|
+
timestamp: datetime
|
|
62
|
+
|
|
63
|
+
# Event information
|
|
64
|
+
title: str
|
|
65
|
+
slug: str
|
|
66
|
+
icon: str
|
|
67
|
+
event_slug: str = Field(alias="eventSlug")
|
|
68
|
+
outcome: str
|
|
69
|
+
outcome_index: int = Field(alias="outcomeIndex")
|
|
70
|
+
|
|
71
|
+
# User profile
|
|
72
|
+
name: str
|
|
73
|
+
pseudonym: str
|
|
74
|
+
bio: str
|
|
75
|
+
profile_image: str = Field(alias="profileImage")
|
|
76
|
+
profile_image_optimized: str = Field(alias="profileImageOptimized")
|
|
77
|
+
|
|
78
|
+
# Transaction information
|
|
79
|
+
transaction_hash: Keccak256 = Field(alias="transactionHash")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class Activity(BaseModel):
|
|
83
|
+
# User identification
|
|
84
|
+
proxy_wallet: EthAddress = Field(alias="proxyWallet")
|
|
85
|
+
|
|
86
|
+
# Activity details
|
|
87
|
+
timestamp: datetime
|
|
88
|
+
condition_id: Keccak256 | EmptyString = Field(alias="conditionId")
|
|
89
|
+
type: Literal["TRADE", "SPLIT", "MERGE", "REDEEM", "REWARD", "CONVERSION"]
|
|
90
|
+
size: float
|
|
91
|
+
usdc_size: float = Field(alias="usdcSize")
|
|
92
|
+
price: float
|
|
93
|
+
asset: str
|
|
94
|
+
side: str | None
|
|
95
|
+
outcome_index: int = Field(alias="outcomeIndex")
|
|
96
|
+
|
|
97
|
+
# Event information
|
|
98
|
+
title: str
|
|
99
|
+
slug: str
|
|
100
|
+
icon: str
|
|
101
|
+
event_slug: str = Field(alias="eventSlug")
|
|
102
|
+
outcome: str
|
|
103
|
+
|
|
104
|
+
# User profile
|
|
105
|
+
name: str
|
|
106
|
+
pseudonym: str
|
|
107
|
+
bio: str
|
|
108
|
+
profile_image: str = Field(alias="profileImage")
|
|
109
|
+
profile_image_optimized: str = Field(alias="profileImageOptimized")
|
|
110
|
+
|
|
111
|
+
# Transaction information
|
|
112
|
+
transaction_hash: Keccak256 = Field(alias="transactionHash")
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class Holder(BaseModel):
|
|
116
|
+
# User identification
|
|
117
|
+
proxy_wallet: EthAddress = Field(alias="proxyWallet")
|
|
118
|
+
|
|
119
|
+
# Holder details
|
|
120
|
+
token_id: str = Field(alias="asset")
|
|
121
|
+
amount: float
|
|
122
|
+
outcome_index: int = Field(alias="outcomeIndex")
|
|
123
|
+
|
|
124
|
+
# User profile
|
|
125
|
+
name: str
|
|
126
|
+
pseudonym: str
|
|
127
|
+
bio: str
|
|
128
|
+
profile_image: str = Field(alias="profileImage")
|
|
129
|
+
profile_image_optimized: str = Field(alias="profileImageOptimized")
|
|
130
|
+
display_username_public: bool = Field(alias="displayUsernamePublic")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class HolderResponse(BaseModel):
|
|
134
|
+
# Asset information
|
|
135
|
+
token_id: str = Field(alias="token")
|
|
136
|
+
|
|
137
|
+
# Holders list
|
|
138
|
+
holders: list[Holder]
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class ValueResponse(BaseModel):
|
|
142
|
+
# User identification
|
|
143
|
+
proxy_wallet: EthAddress = Field(alias="proxyWallet")
|
|
144
|
+
|
|
145
|
+
# Value information
|
|
146
|
+
value: float
|
|
147
|
+
|
|
148
|
+
class User(BaseModel):
|
|
149
|
+
proxy_wallet: EthAddress = Field(alias="proxyWallet")
|
|
150
|
+
name: str
|
|
151
|
+
bio: str
|
|
152
|
+
profile_image: str = Field(alias="profileImage")
|
|
153
|
+
profile_image_optimized: str = Field(alias="profileImageOptimized")
|
|
154
|
+
|
|
155
|
+
class UserMetric(User):
|
|
156
|
+
amount: float
|
|
157
|
+
pseudonym: str
|
|
158
|
+
|
|
159
|
+
class UserRank(User):
|
|
160
|
+
amount: float
|
|
161
|
+
rank: int
|