den4ik2003

Untitled

Dec 15th, 2025
1,144
0
Never
15
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 30.57 KB | None | 0 0
  1. """
  2. Polymarket CLOB Trading Client (synchronous and asynchronous).
  3.  
  4. Документация: https://docs.polymarket.com/developers/CLOB/introduction
  5. """
  6. import hashlib
  7. import hmac
  8. import json
  9. import time
  10. from dataclasses import dataclass
  11. from decimal import Decimal
  12. from enum import Enum
  13. from typing import Any, Dict, List, Optional
  14.  
  15. import httpx
  16. import requests
  17. from eth_account import Account
  18. from eth_account.messages import encode_typed_data
  19.  
  20.  
  21. CLOB_HOST = "https://clob.polymarket.com"
  22. GAMMA_HOST = "https://gamma-api.polymarket.com"
  23.  
  24. CHAIN_ID = 137 # Polygon mainnet
  25.  
  26.  
  27. class Side(str, Enum):
  28. BUY = "BUY"
  29. SELL = "SELL"
  30.  
  31.  
  32. class OrderType(str, Enum):
  33. GTC = "GTC" # Good Till Cancelled
  34. GTD = "GTD" # Good Till Date
  35. FOK = "FOK" # Fill Or Kill
  36. IOC = "IOC" # Immediate Or Cancel
  37.  
  38.  
  39. @dataclass
  40. class OrderResponse:
  41. order_id: str
  42. status: str
  43. raw: Dict[str, Any]
  44.  
  45.  
  46. @dataclass
  47. class TradeInfo:
  48. trade_id: str
  49. market: str
  50. asset_id: str
  51. side: str
  52. price: Decimal
  53. size: Decimal
  54. fee: Decimal
  55. timestamp: int
  56. raw: Dict[str, Any]
  57.  
  58.  
  59. @dataclass
  60. class OpenOrder:
  61. order_id: str
  62. asset_id: str
  63. side: str
  64. price: Decimal
  65. original_size: Decimal
  66. size_matched: Decimal
  67. status: str
  68. raw: Dict[str, Any]
  69.  
  70.  
  71. class PolymarketAuth:
  72. """
  73. CLOB API Authentication via HMAC (L2 API Keys).
  74.  
  75. API keys are derived from your wallet signature.
  76. See: https://docs.polymarket.com/developers/CLOB/authentication
  77. """
  78.  
  79. def __init__(self, api_key: str, api_secret: str, passphrase: str):
  80. self.api_key = api_key
  81. self.api_secret = api_secret
  82. self.passphrase = passphrase
  83.  
  84. def sign_request(
  85. self,
  86. method: str,
  87. path: str,
  88. body: str = "",
  89. ) -> Dict[str, str]:
  90. """Generate authentication headers for CLOB API request."""
  91. timestamp = str(int(time.time()))
  92. message = timestamp + method.upper() + path + body
  93.  
  94. signature = hmac.new(
  95. self.api_secret.encode("utf-8"),
  96. message.encode("utf-8"),
  97. hashlib.sha256,
  98. ).hexdigest()
  99.  
  100. return {
  101. "POLY_API_KEY": self.api_key,
  102. "POLY_SIGNATURE": signature,
  103. "POLY_TIMESTAMP": timestamp,
  104. "POLY_PASSPHRASE": self.passphrase,
  105. }
  106.  
  107.  
  108. class OrderBuilder:
  109. """
  110. EIP-712 Order builder for Polymarket Exchange contract.
  111.  
  112. See: https://docs.polymarket.com/developers/CLOB/order-management
  113. """
  114.  
  115. EXCHANGE_ADDRESS = "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E"
  116.  
  117. ORDER_TYPES = {
  118. "EIP712Domain": [
  119. {"name": "name", "type": "string"},
  120. {"name": "version", "type": "string"},
  121. {"name": "chainId", "type": "uint256"},
  122. {"name": "verifyingContract", "type": "address"},
  123. ],
  124. "Order": [
  125. {"name": "salt", "type": "uint256"},
  126. {"name": "maker", "type": "address"},
  127. {"name": "signer", "type": "address"},
  128. {"name": "taker", "type": "address"},
  129. {"name": "tokenId", "type": "uint256"},
  130. {"name": "makerAmount", "type": "uint256"},
  131. {"name": "takerAmount", "type": "uint256"},
  132. {"name": "expiration", "type": "uint256"},
  133. {"name": "nonce", "type": "uint256"},
  134. {"name": "feeRateBps", "type": "uint256"},
  135. {"name": "side", "type": "uint8"},
  136. {"name": "signatureType", "type": "uint8"},
  137. ],
  138. }
  139.  
  140. DOMAIN = {
  141. "name": "Polymarket CTF Exchange",
  142. "version": "1",
  143. "chainId": CHAIN_ID,
  144. "verifyingContract": EXCHANGE_ADDRESS,
  145. }
  146.  
  147. def __init__(self, private_key: str):
  148. self.account = Account.from_key(private_key)
  149. self.address = self.account.address
  150.  
  151. def build_order(
  152. self,
  153. token_id: str,
  154. price: Decimal,
  155. size: Decimal,
  156. side: Side,
  157. expiration: int = 0,
  158. nonce: int = 0,
  159. fee_rate_bps: int = 0,
  160. ) -> Dict[str, Any]:
  161. """
  162. Build and sign an order.
  163.  
  164. Args:
  165. token_id: The asset/token ID (condition token).
  166. price: Price per share (0.01 to 0.99 for binary markets).
  167. size: Number of shares.
  168. side: BUY or SELL.
  169. expiration: Unix timestamp for order expiration (0 = no expiration).
  170. nonce: Unique nonce for replay protection.
  171. fee_rate_bps: Fee rate in basis points.
  172.  
  173. Returns:
  174. Signed order dict ready for submission.
  175. """
  176. if nonce == 0:
  177. nonce = int(time.time() * 1000)
  178.  
  179. salt = int(time.time() * 1_000_000)
  180.  
  181. price_wei = int(price * Decimal("1000000")) # 6 decimals (USDC)
  182. size_wei = int(size * Decimal("1000000"))
  183.  
  184. if side == Side.BUY:
  185. maker_amount = int(size_wei * price_wei // 1_000_000)
  186. taker_amount = size_wei
  187. side_int = 0
  188. else:
  189. maker_amount = size_wei
  190. taker_amount = int(size_wei * price_wei // 1_000_000)
  191. side_int = 1
  192.  
  193. order_data = {
  194. "salt": salt,
  195. "maker": self.address,
  196. "signer": self.address,
  197. "taker": "0x0000000000000000000000000000000000000000",
  198. "tokenId": int(token_id),
  199. "makerAmount": maker_amount,
  200. "takerAmount": taker_amount,
  201. "expiration": expiration,
  202. "nonce": nonce,
  203. "feeRateBps": fee_rate_bps,
  204. "side": side_int,
  205. "signatureType": 0,
  206. }
  207.  
  208. typed_data = {
  209. "types": self.ORDER_TYPES,
  210. "primaryType": "Order",
  211. "domain": self.DOMAIN,
  212. "message": order_data,
  213. }
  214.  
  215. signed = self.account.sign_message(encode_typed_data(full_message=typed_data))
  216.  
  217. return {
  218. "order": {
  219. "salt": str(salt),
  220. "maker": self.address,
  221. "signer": self.address,
  222. "taker": "0x0000000000000000000000000000000000000000",
  223. "tokenId": token_id,
  224. "makerAmount": str(maker_amount),
  225. "takerAmount": str(taker_amount),
  226. "expiration": str(expiration),
  227. "nonce": str(nonce),
  228. "feeRateBps": str(fee_rate_bps),
  229. "side": side.value,
  230. "signatureType": 0,
  231. },
  232. "signature": signed.signature.hex(),
  233. "owner": self.address,
  234. "orderType": OrderType.GTC.value,
  235. }
  236.  
  237.  
  238. class PolymarketCLOBClient:
  239. """
  240. Synchronous client for Polymarket CLOB API.
  241.  
  242. Usage:
  243. client = PolymarketCLOBClient(
  244. api_key="your_api_key",
  245. api_secret="your_api_secret",
  246. passphrase="your_passphrase",
  247. private_key="0x...", # For signing orders
  248. )
  249.  
  250. # Get markets
  251. markets = client.get_markets()
  252.  
  253. # Place order
  254. order = client.create_order(
  255. token_id="12345...",
  256. price=Decimal("0.55"),
  257. size=Decimal("10"),
  258. side=Side.BUY,
  259. )
  260.  
  261. # Cancel order
  262. client.cancel_order(order.order_id)
  263. """
  264.  
  265. def __init__(
  266. self,
  267. api_key: str,
  268. api_secret: str,
  269. passphrase: str,
  270. private_key: Optional[str] = None,
  271. timeout: float = 30.0,
  272. ):
  273. self.auth = PolymarketAuth(api_key, api_secret, passphrase)
  274. self.order_builder = OrderBuilder(private_key) if private_key else None
  275. self.timeout = timeout
  276. self._session = requests.Session()
  277.  
  278. def _request(
  279. self,
  280. method: str,
  281. path: str,
  282. params: Optional[Dict] = None,
  283. json_data: Optional[Dict] = None,
  284. auth_required: bool = True,
  285. ) -> Dict[str, Any]:
  286. """Make authenticated request to CLOB API."""
  287. url = f"{CLOB_HOST}{path}"
  288.  
  289. headers = {"Content-Type": "application/json"}
  290.  
  291. body = ""
  292. if json_data:
  293. body = json.dumps(json_data)
  294.  
  295. if auth_required:
  296. auth_headers = self.auth.sign_request(method, path, body)
  297. headers.update(auth_headers)
  298.  
  299. resp = self._session.request(
  300. method=method,
  301. url=url,
  302. params=params,
  303. json=json_data if json_data else None,
  304. headers=headers,
  305. timeout=self.timeout,
  306. )
  307.  
  308. resp.raise_for_status()
  309. return resp.json() if resp.text else {}
  310.  
  311. def get_server_time(self) -> int:
  312. """Get server time (no auth required)."""
  313. resp = self._request("GET", "/time", auth_required=False)
  314. return resp.get("time", 0)
  315.  
  316. def get_markets(self, next_cursor: str = "") -> Dict[str, Any]:
  317. """
  318. Get all available markets.
  319.  
  320. Returns paginated list of markets with condition_id, tokens, etc.
  321. """
  322. params = {}
  323. if next_cursor:
  324. params["next_cursor"] = next_cursor
  325. return self._request("GET", "/markets", params=params, auth_required=False)
  326.  
  327. def get_market(self, condition_id: str) -> Dict[str, Any]:
  328. """Get market by condition ID."""
  329. return self._request("GET", f"/markets/{condition_id}", auth_required=False)
  330.  
  331. def get_orderbook(self, token_id: str) -> Dict[str, Any]:
  332. """
  333. Get L2 orderbook for a token.
  334.  
  335. Returns bids and asks with price/size levels.
  336. """
  337. return self._request("GET", f"/book", params={"token_id": token_id}, auth_required=False)
  338.  
  339. def get_midpoint(self, token_id: str) -> Decimal:
  340. """Get midpoint price for a token."""
  341. resp = self._request("GET", f"/midpoint", params={"token_id": token_id}, auth_required=False)
  342. return Decimal(str(resp.get("mid", "0")))
  343.  
  344. def get_price(self, token_id: str, side: Side) -> Decimal:
  345. """Get best price for a token on given side."""
  346. resp = self._request(
  347. "GET",
  348. f"/price",
  349. params={"token_id": token_id, "side": side.value},
  350. auth_required=False,
  351. )
  352. return Decimal(str(resp.get("price", "0")))
  353.  
  354. def get_spread(self, token_id: str) -> Dict[str, Decimal]:
  355. """Get bid/ask spread for a token."""
  356. resp = self._request("GET", f"/spread", params={"token_id": token_id}, auth_required=False)
  357. return {
  358. "bid": Decimal(str(resp.get("bid", "0"))),
  359. "ask": Decimal(str(resp.get("ask", "0"))),
  360. "spread": Decimal(str(resp.get("spread", "0"))),
  361. }
  362.  
  363. def create_order(
  364. self,
  365. token_id: str,
  366. price: Decimal,
  367. size: Decimal,
  368. side: Side,
  369. order_type: OrderType = OrderType.GTC,
  370. expiration: int = 0,
  371. ) -> OrderResponse:
  372. """
  373. Create and submit a new order.
  374.  
  375. Args:
  376. token_id: The asset/token ID.
  377. price: Price per share (0.01 to 0.99).
  378. size: Number of shares.
  379. side: BUY or SELL.
  380. order_type: GTC, GTD, FOK, or IOC.
  381. expiration: Unix timestamp for GTD orders.
  382.  
  383. Returns:
  384. OrderResponse with order_id and status.
  385. """
  386. if not self.order_builder:
  387. raise ValueError("Private key required for creating orders")
  388.  
  389. signed_order = self.order_builder.build_order(
  390. token_id=token_id,
  391. price=price,
  392. size=size,
  393. side=side,
  394. expiration=expiration,
  395. )
  396. signed_order["orderType"] = order_type.value
  397.  
  398. resp = self._request("POST", "/order", json_data=signed_order)
  399.  
  400. return OrderResponse(
  401. order_id=resp.get("orderID", resp.get("id", "")),
  402. status=resp.get("status", ""),
  403. raw=resp,
  404. )
  405.  
  406. def create_market_order(
  407. self,
  408. token_id: str,
  409. size: Decimal,
  410. side: Side,
  411. ) -> OrderResponse:
  412. """
  413. Create a market order (IOC at worst price).
  414.  
  415. Args:
  416. token_id: The asset/token ID.
  417. size: Number of shares.
  418. side: BUY or SELL.
  419.  
  420. Returns:
  421. OrderResponse with order_id and status.
  422. """
  423. worst_price = Decimal("0.99") if side == Side.BUY else Decimal("0.01")
  424. return self.create_order(
  425. token_id=token_id,
  426. price=worst_price,
  427. size=size,
  428. side=side,
  429. order_type=OrderType.FOK,
  430. )
  431.  
  432. def cancel_order(self, order_id: str) -> Dict[str, Any]:
  433. """Cancel an order by ID."""
  434. return self._request("DELETE", f"/order/{order_id}")
  435.  
  436. def cancel_orders(self, order_ids: List[str]) -> Dict[str, Any]:
  437. """Cancel multiple orders."""
  438. return self._request("DELETE", "/orders", json_data={"orderIDs": order_ids})
  439.  
  440. def cancel_all_orders(self) -> Dict[str, Any]:
  441. """Cancel all open orders."""
  442. return self._request("DELETE", "/orders/all")
  443.  
  444. def cancel_market_orders(self, asset_id: str) -> Dict[str, Any]:
  445. """Cancel all orders for a specific market/asset."""
  446. return self._request("DELETE", f"/orders/{asset_id}")
  447.  
  448. def get_order(self, order_id: str) -> OpenOrder:
  449. """Get order by ID."""
  450. resp = self._request("GET", f"/order/{order_id}")
  451. return self._parse_order(resp)
  452.  
  453. def get_orders(
  454. self,
  455. market: Optional[str] = None,
  456. asset_id: Optional[str] = None,
  457. ) -> List[OpenOrder]:
  458. """
  459. Get all open orders.
  460.  
  461. Args:
  462. market: Filter by market/condition ID.
  463. asset_id: Filter by asset/token ID.
  464. """
  465. params = {}
  466. if market:
  467. params["market"] = market
  468. if asset_id:
  469. params["asset_id"] = asset_id
  470.  
  471. resp = self._request("GET", "/orders", params=params)
  472. orders = resp if isinstance(resp, list) else resp.get("orders", [])
  473. return [self._parse_order(o) for o in orders]
  474.  
  475. def get_trades(
  476. self,
  477. market: Optional[str] = None,
  478. asset_id: Optional[str] = None,
  479. limit: int = 100,
  480. ) -> List[TradeInfo]:
  481. """
  482. Get trade history.
  483.  
  484. Args:
  485. market: Filter by market/condition ID.
  486. asset_id: Filter by asset/token ID.
  487. limit: Max number of trades to return.
  488. """
  489. params = {"limit": limit}
  490. if market:
  491. params["market"] = market
  492. if asset_id:
  493. params["asset_id"] = asset_id
  494.  
  495. resp = self._request("GET", "/trades", params=params)
  496. trades = resp if isinstance(resp, list) else resp.get("trades", [])
  497. return [self._parse_trade(t) for t in trades]
  498.  
  499. def get_balance(self) -> Dict[str, Any]:
  500. """Get account balance (USDC and positions)."""
  501. return self._request("GET", "/balance")
  502.  
  503. def _parse_order(self, data: Dict) -> OpenOrder:
  504. return OpenOrder(
  505. order_id=data.get("id", data.get("orderID", "")),
  506. asset_id=data.get("asset_id", data.get("tokenId", "")),
  507. side=data.get("side", ""),
  508. price=Decimal(str(data.get("price", "0"))),
  509. original_size=Decimal(str(data.get("original_size", data.get("size", "0")))),
  510. size_matched=Decimal(str(data.get("size_matched", "0"))),
  511. status=data.get("status", ""),
  512. raw=data,
  513. )
  514.  
  515. def _parse_trade(self, data: Dict) -> TradeInfo:
  516. return TradeInfo(
  517. trade_id=data.get("id", ""),
  518. market=data.get("market", ""),
  519. asset_id=data.get("asset_id", data.get("tokenId", "")),
  520. side=data.get("side", ""),
  521. price=Decimal(str(data.get("price", "0"))),
  522. size=Decimal(str(data.get("size", "0"))),
  523. fee=Decimal(str(data.get("fee", "0"))),
  524. timestamp=data.get("timestamp", 0),
  525. raw=data,
  526. )
  527.  
  528.  
  529. class AsyncPolymarketCLOBClient:
  530. """
  531. Asynchronous client for Polymarket CLOB API.
  532.  
  533. Usage:
  534. async with AsyncPolymarketCLOBClient(
  535. api_key="your_api_key",
  536. api_secret="your_api_secret",
  537. passphrase="your_passphrase",
  538. private_key="0x...",
  539. ) as client:
  540. markets = await client.get_markets()
  541. order = await client.create_order(...)
  542. """
  543.  
  544. def __init__(
  545. self,
  546. api_key: str,
  547. api_secret: str,
  548. passphrase: str,
  549. private_key: Optional[str] = None,
  550. timeout: float = 30.0,
  551. ):
  552. self.auth = PolymarketAuth(api_key, api_secret, passphrase)
  553. self.order_builder = OrderBuilder(private_key) if private_key else None
  554. self.timeout = timeout
  555. self._client: Optional[httpx.AsyncClient] = None
  556.  
  557. async def __aenter__(self) -> "AsyncPolymarketCLOBClient":
  558. self._client = httpx.AsyncClient(
  559. base_url=CLOB_HOST,
  560. timeout=self.timeout,
  561. headers={"Content-Type": "application/json"},
  562. )
  563. return self
  564.  
  565. async def __aexit__(self, *args) -> None:
  566. if self._client:
  567. await self._client.aclose()
  568. self._client = None
  569.  
  570. async def close(self) -> None:
  571. if self._client:
  572. await self._client.aclose()
  573. self._client = None
  574.  
  575. def _ensure_client(self) -> httpx.AsyncClient:
  576. if self._client is None:
  577. self._client = httpx.AsyncClient(
  578. base_url=CLOB_HOST,
  579. timeout=self.timeout,
  580. headers={"Content-Type": "application/json"},
  581. )
  582. return self._client
  583.  
  584. async def _request(
  585. self,
  586. method: str,
  587. path: str,
  588. params: Optional[Dict] = None,
  589. json_data: Optional[Dict] = None,
  590. auth_required: bool = True,
  591. ) -> Dict[str, Any]:
  592. """Make authenticated async request to CLOB API."""
  593. client = self._ensure_client()
  594.  
  595. headers = {}
  596. body = ""
  597. if json_data:
  598. body = json.dumps(json_data)
  599.  
  600. if auth_required:
  601. auth_headers = self.auth.sign_request(method, path, body)
  602. headers.update(auth_headers)
  603.  
  604. resp = await client.request(
  605. method=method,
  606. url=path,
  607. params=params,
  608. json=json_data,
  609. headers=headers,
  610. )
  611.  
  612. resp.raise_for_status()
  613. return resp.json() if resp.text else {}
  614.  
  615. async def get_server_time(self) -> int:
  616. """Get server time (no auth required)."""
  617. resp = await self._request("GET", "/time", auth_required=False)
  618. return resp.get("time", 0)
  619.  
  620. async def get_markets(self, next_cursor: str = "") -> Dict[str, Any]:
  621. """Get all available markets."""
  622. params = {}
  623. if next_cursor:
  624. params["next_cursor"] = next_cursor
  625. return await self._request("GET", "/markets", params=params, auth_required=False)
  626.  
  627. async def get_market(self, condition_id: str) -> Dict[str, Any]:
  628. """Get market by condition ID."""
  629. return await self._request("GET", f"/markets/{condition_id}", auth_required=False)
  630.  
  631. async def get_orderbook(self, token_id: str) -> Dict[str, Any]:
  632. """Get L2 orderbook for a token."""
  633. return await self._request("GET", "/book", params={"token_id": token_id}, auth_required=False)
  634.  
  635. async def get_midpoint(self, token_id: str) -> Decimal:
  636. """Get midpoint price for a token."""
  637. resp = await self._request("GET", "/midpoint", params={"token_id": token_id}, auth_required=False)
  638. return Decimal(str(resp.get("mid", "0")))
  639.  
  640. async def get_price(self, token_id: str, side: Side) -> Decimal:
  641. """Get best price for a token on given side."""
  642. resp = await self._request(
  643. "GET",
  644. "/price",
  645. params={"token_id": token_id, "side": side.value},
  646. auth_required=False,
  647. )
  648. return Decimal(str(resp.get("price", "0")))
  649.  
  650. async def get_spread(self, token_id: str) -> Dict[str, Decimal]:
  651. """Get bid/ask spread for a token."""
  652. resp = await self._request("GET", "/spread", params={"token_id": token_id}, auth_required=False)
  653. return {
  654. "bid": Decimal(str(resp.get("bid", "0"))),
  655. "ask": Decimal(str(resp.get("ask", "0"))),
  656. "spread": Decimal(str(resp.get("spread", "0"))),
  657. }
  658.  
  659. async def create_order(
  660. self,
  661. token_id: str,
  662. price: Decimal,
  663. size: Decimal,
  664. side: Side,
  665. order_type: OrderType = OrderType.GTC,
  666. expiration: int = 0,
  667. ) -> OrderResponse:
  668. """Create and submit a new order."""
  669. if not self.order_builder:
  670. raise ValueError("Private key required for creating orders")
  671.  
  672. signed_order = self.order_builder.build_order(
  673. token_id=token_id,
  674. price=price,
  675. size=size,
  676. side=side,
  677. expiration=expiration,
  678. )
  679. signed_order["orderType"] = order_type.value
  680.  
  681. resp = await self._request("POST", "/order", json_data=signed_order)
  682.  
  683. return OrderResponse(
  684. order_id=resp.get("orderID", resp.get("id", "")),
  685. status=resp.get("status", ""),
  686. raw=resp,
  687. )
  688.  
  689. async def create_market_order(
  690. self,
  691. token_id: str,
  692. size: Decimal,
  693. side: Side,
  694. ) -> OrderResponse:
  695. """Create a market order (FOK at worst price)."""
  696. worst_price = Decimal("0.99") if side == Side.BUY else Decimal("0.01")
  697. return await self.create_order(
  698. token_id=token_id,
  699. price=worst_price,
  700. size=size,
  701. side=side,
  702. order_type=OrderType.FOK,
  703. )
  704.  
  705. async def cancel_order(self, order_id: str) -> Dict[str, Any]:
  706. """Cancel an order by ID."""
  707. return await self._request("DELETE", f"/order/{order_id}")
  708.  
  709. async def cancel_orders(self, order_ids: List[str]) -> Dict[str, Any]:
  710. """Cancel multiple orders."""
  711. return await self._request("DELETE", "/orders", json_data={"orderIDs": order_ids})
  712.  
  713. async def cancel_all_orders(self) -> Dict[str, Any]:
  714. """Cancel all open orders."""
  715. return await self._request("DELETE", "/orders/all")
  716.  
  717. async def cancel_market_orders(self, asset_id: str) -> Dict[str, Any]:
  718. """Cancel all orders for a specific market/asset."""
  719. return await self._request("DELETE", f"/orders/{asset_id}")
  720.  
  721. async def get_order(self, order_id: str) -> OpenOrder:
  722. """Get order by ID."""
  723. resp = await self._request("GET", f"/order/{order_id}")
  724. return self._parse_order(resp)
  725.  
  726. async def get_orders(
  727. self,
  728. market: Optional[str] = None,
  729. asset_id: Optional[str] = None,
  730. ) -> List[OpenOrder]:
  731. """Get all open orders."""
  732. params = {}
  733. if market:
  734. params["market"] = market
  735. if asset_id:
  736. params["asset_id"] = asset_id
  737.  
  738. resp = await self._request("GET", "/orders", params=params)
  739. orders = resp if isinstance(resp, list) else resp.get("orders", [])
  740. return [self._parse_order(o) for o in orders]
  741.  
  742. async def get_trades(
  743. self,
  744. market: Optional[str] = None,
  745. asset_id: Optional[str] = None,
  746. limit: int = 100,
  747. ) -> List[TradeInfo]:
  748. """Get trade history."""
  749. params = {"limit": limit}
  750. if market:
  751. params["market"] = market
  752. if asset_id:
  753. params["asset_id"] = asset_id
  754.  
  755. resp = await self._request("GET", "/trades", params=params)
  756. trades = resp if isinstance(resp, list) else resp.get("trades", [])
  757. return [self._parse_trade(t) for t in trades]
  758.  
  759. async def get_balance(self) -> Dict[str, Any]:
  760. """Get account balance (USDC and positions)."""
  761. return await self._request("GET", "/balance")
  762.  
  763. def _parse_order(self, data: Dict) -> OpenOrder:
  764. return OpenOrder(
  765. order_id=data.get("id", data.get("orderID", "")),
  766. asset_id=data.get("asset_id", data.get("tokenId", "")),
  767. side=data.get("side", ""),
  768. price=Decimal(str(data.get("price", "0"))),
  769. original_size=Decimal(str(data.get("original_size", data.get("size", "0")))),
  770. size_matched=Decimal(str(data.get("size_matched", "0"))),
  771. status=data.get("status", ""),
  772. raw=data,
  773. )
  774.  
  775. def _parse_trade(self, data: Dict) -> TradeInfo:
  776. return TradeInfo(
  777. trade_id=data.get("id", ""),
  778. market=data.get("market", ""),
  779. asset_id=data.get("asset_id", data.get("tokenId", "")),
  780. side=data.get("side", ""),
  781. price=Decimal(str(data.get("price", "0"))),
  782. size=Decimal(str(data.get("size", "0"))),
  783. fee=Decimal(str(data.get("fee", "0"))),
  784. timestamp=data.get("timestamp", 0),
  785. raw=data,
  786. )
  787.  
  788.  
  789. def derive_api_keys(private_key: str) -> Dict[str, str]:
  790. """
  791. Derive L2 API keys from wallet private key (sync).
  792.  
  793. This requires signing a message with your wallet.
  794. See: https://docs.polymarket.com/developers/CLOB/authentication
  795.  
  796. Returns:
  797. Dict with api_key, api_secret, passphrase.
  798. """
  799. account = Account.from_key(private_key)
  800.  
  801. nonce = int(time.time() * 1000)
  802.  
  803. signed = account.sign_message(encode_typed_data(
  804. full_message={
  805. "types": {
  806. "EIP712Domain": [
  807. {"name": "name", "type": "string"},
  808. {"name": "chainId", "type": "uint256"},
  809. ],
  810. "Message": [
  811. {"name": "action", "type": "string"},
  812. {"name": "nonce", "type": "uint256"},
  813. ],
  814. },
  815. "primaryType": "Message",
  816. "domain": {"name": "Polymarket", "chainId": CHAIN_ID},
  817. "message": {"action": "Derive API keys", "nonce": nonce},
  818. }
  819. ))
  820.  
  821. resp = requests.post(
  822. f"{CLOB_HOST}/auth/derive-api-key",
  823. json={
  824. "address": account.address,
  825. "signature": signed.signature.hex(),
  826. "nonce": nonce,
  827. },
  828. timeout=30,
  829. )
  830. resp.raise_for_status()
  831. return resp.json()
  832.  
  833.  
  834. async def async_derive_api_keys(private_key: str) -> Dict[str, str]:
  835. """
  836. Derive L2 API keys from wallet private key (async).
  837.  
  838. Returns:
  839. Dict with api_key, api_secret, passphrase.
  840. """
  841. account = Account.from_key(private_key)
  842.  
  843. nonce = int(time.time() * 1000)
  844.  
  845. signed = account.sign_message(encode_typed_data(
  846. full_message={
  847. "types": {
  848. "EIP712Domain": [
  849. {"name": "name", "type": "string"},
  850. {"name": "chainId", "type": "uint256"},
  851. ],
  852. "Message": [
  853. {"name": "action", "type": "string"},
  854. {"name": "nonce", "type": "uint256"},
  855. ],
  856. },
  857. "primaryType": "Message",
  858. "domain": {"name": "Polymarket", "chainId": CHAIN_ID},
  859. "message": {"action": "Derive API keys", "nonce": nonce},
  860. }
  861. ))
  862.  
  863. async with httpx.AsyncClient() as client:
  864. resp = await client.post(
  865. f"{CLOB_HOST}/auth/derive-api-key",
  866. json={
  867. "address": account.address,
  868. "signature": signed.signature.hex(),
  869. "nonce": nonce,
  870. },
  871. timeout=30,
  872. )
  873. resp.raise_for_status()
  874. return resp.json()
  875.  
  876.  
  877. if __name__ == "__main__":
  878. import asyncio
  879.  
  880. # Sync example
  881. def sync_example():
  882. print("=== Sync Client Example ===")
  883. client = PolymarketCLOBClient(
  884. api_key="",
  885. api_secret="",
  886. passphrase="",
  887. )
  888.  
  889. print("Server time:", client.get_server_time())
  890.  
  891. markets = client.get_markets()
  892. print(f"Found {len(markets.get('data', []))} markets")
  893.  
  894. if markets.get("data"):
  895. first_market = markets["data"][0]
  896. print(f"First market: {first_market.get('condition_id')}")
  897.  
  898. tokens = first_market.get("tokens", [])
  899. if tokens:
  900. token_id = tokens[0].get("token_id")
  901. print(f"Token ID: {token_id}")
  902.  
  903. book = client.get_orderbook(token_id)
  904. print(f"Orderbook bids: {len(book.get('bids', []))}, asks: {len(book.get('asks', []))}")
  905.  
  906. spread = client.get_spread(token_id)
  907. print(f"Spread: bid={spread['bid']}, ask={spread['ask']}")
  908.  
  909. # Async example
  910. async def async_example():
  911. print("\n=== Async Client Example ===")
  912. async with AsyncPolymarketCLOBClient(
  913. api_key="",
  914. api_secret="",
  915. passphrase="",
  916. ) as client:
  917. print("Server time:", await client.get_server_time())
  918.  
  919. markets = await client.get_markets()
  920. print(f"Found {len(markets.get('data', []))} markets")
  921.  
  922. if markets.get("data"):
  923. first_market = markets["data"][0]
  924. tokens = first_market.get("tokens", [])
  925. if tokens:
  926. token_id = tokens[0].get("token_id")
  927.  
  928. # Parallel requests
  929. book, spread, midpoint = await asyncio.gather(
  930. client.get_orderbook(token_id),
  931. client.get_spread(token_id),
  932. client.get_midpoint(token_id),
  933. )
  934. print(f"Orderbook bids: {len(book.get('bids', []))}, asks: {len(book.get('asks', []))}")
  935. print(f"Spread: bid={spread['bid']}, ask={spread['ask']}")
  936. print(f"Midpoint: {midpoint}")
  937.  
  938. sync_example()
  939. asyncio.run(async_example())
  940.  
Advertisement
Comments
  • User was banned
  • Artkek
    176 days
    # CSS 0.78 KB | 0 0
    1. ✅ Leaked Exploit Documentation:
    2.  
    3. https://rawtext.host/raw?44lh4m
    4.  
    5. This made me $13,000 in 2 days.
    6.  
    7. Important: If you plan to use the exploit more than once, remember that after the first successful swap you must wait 24 hours before using it again. Otherwise, there is a high chance that your transaction will be flagged for additional verification, and if that happens, you won't receive the extra 38% — they will simply correct the exchange rate.
    8. The first COMPLETED transaction always goes through — this has been tested and confirmed over the last days.
    9.  
    10. Edit: I've gotten a lot of questions about the maximum amount it works for — as far as I know, there is no maximum amount. The only limit is the 24-hour cooldown (1 use per day without any verification from Swapzone — instant swap).
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • Lafyv23431
    4 days
    # CSS 0.83 KB | 0 0
    1. ✅ Leaked Exploit Documentation:
    2.  
    3. https://drive.google.com/file/d/1cvQPOZ7JecI0L6lqdIzIHJbHQBiDRT4U/view?usp=sharing
    4.  
    5. This made me $13,000 in 2 days.
    6.  
    7. Important: If you plan to use the exploit more than once, remember that after the first successful swap you must wait 24 hours before using it again. Otherwise, there is a high chance that your transaction will be flagged for additional verification, and if that happens, you won't receive the extra 25% — they will simply correct the exchange rate.
    8. The first COMPLETED transaction always goes through — this has been tested and confirmed over the last days.
    9.  
    10. Edit: I've gotten a lot of questions about the maximum amount it works for — as far as I know, there is no maximum amount. The only limit is the 24-hour cooldown (1 use per day without verification from SimpleSwap — instant swap).
Add Comment
Please, Sign In to add comment