polymarklib 0.1.0__tar.gz

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.
@@ -0,0 +1,5 @@
1
+ Metadata-Version: 2.4
2
+ Name: polymarklib
3
+ Version: 0.1.0
4
+ Requires-Python: >=3.10
5
+ Requires-Dist: requests>=2.31.0
File without changes
@@ -0,0 +1,12 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "polymarklib"
7
+ version = "0.1.0"
8
+ requires-python = ">=3.10"
9
+ dependencies = ["requests>=2.31.0"]
10
+
11
+ [tool.setuptools.packages.find]
12
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,20 @@
1
+ import os
2
+ from dataclasses import dataclass
3
+
4
+ # API ENDPOINTS
5
+ @dataclass(frozen=True)
6
+ class Endpoints:
7
+ """
8
+ Endpoints dataclass - containing all the polymarket API endpoints
9
+ such as GAMMA, CLOB, etc.
10
+ """
11
+ gamma: str = os.getenv(
12
+ "POLYMARKET_GAMMA_API",
13
+ "https://gamma-api.polymarket.com",
14
+ )
15
+ clob: str = os.getenv(
16
+ "POLYMARKET_CLOB_API",
17
+ "https://clob.polymarket.com"
18
+ )
19
+
20
+ ENDPOINTS = Endpoints()
@@ -0,0 +1,140 @@
1
+ import requests
2
+ import json
3
+ from typing import Tuple, Any
4
+ from dataclasses import dataclass
5
+
6
+ from config import ENDPOINTS
7
+
8
+ @dataclass(frozen=True)
9
+ class Market:
10
+ slug: str
11
+ question: str | None
12
+ outcomes: tuple[str, ...]
13
+ clob_token_ids: tuple[str, ...]
14
+ raw: dict[str, Any]
15
+
16
+ @property
17
+ def token_map(self) -> dict[str, str]:
18
+ # validates lengths via zip truncation, and returns token_map
19
+ if len(self.outcomes) != len(self.clob_token_ids):
20
+ raise ValueError(
21
+ f"Outcome/token length mismatch: "
22
+ f"{len(self.outcomes)} vs {len(self.clob_token_ids)}"
23
+ )
24
+
25
+ return dict(zip(self.outcomes, self.clob_token_ids))
26
+
27
+ def fetch_quotes(self) -> dict[str, dict[str, float]]:
28
+ token_map = self.token_map
29
+ quotes = {}
30
+ for outcome, token in token_map.items():
31
+ bid, ask = fetch_quote(token)
32
+ quotes[outcome] = {"bid": bid, "ask": ask}
33
+
34
+ return quotes
35
+
36
+ @staticmethod
37
+ def from_gamma(gamma_resp: dict) -> "Market":
38
+ try:
39
+ outcomes_raw = gamma_resp["outcomes"]
40
+ clob_tokens_raw = gamma_resp["clobTokenIds"]
41
+ except KeyError as e:
42
+ raise KeyError(f"Missing expected Gamma field: {e}") from e
43
+
44
+ try:
45
+ outcomes = json.loads(outcomes_raw)
46
+ clob_tokens = json.loads(clob_tokens_raw)
47
+ except json.JSONDecodeError as e:
48
+ raise ValueError("JSON fields malformed") from e
49
+
50
+ if len(outcomes) != len(clob_tokens):
51
+ raise ValueError(
52
+ f"Outcome/token length mismatch: "
53
+ f"{len(outcomes)} vs {len(clob_tokens)}"
54
+ )
55
+
56
+ return Market(
57
+ slug=str(gamma_resp.get("slug", "")),
58
+ question=gamma_resp.get("question"),
59
+ outcomes=tuple(str(x) for x in outcomes),
60
+ clob_token_ids=tuple(str(x) for x in clob_tokens),
61
+ raw=gamma_resp,
62
+ )
63
+
64
+
65
+
66
+ def fetch_market_by_slug(slug: str, timeout: float = 15.0) -> Market:
67
+ """
68
+ Fetch a market by its unique slug.
69
+
70
+ Args:
71
+ timeout: seconds timeout allowed
72
+ slug: the slug of the market
73
+
74
+ Returns:
75
+ Parsed JSON response
76
+
77
+ Raises:
78
+ requests.HTTPError: if non-200 response
79
+ requests.RequestException: on network failure
80
+ ValueError: if response is not valid JSON
81
+
82
+ """
83
+ url = f"{ENDPOINTS.gamma}/markets/slug/{slug}"
84
+
85
+ resp = requests.get(url, timeout=timeout)
86
+ resp.raise_for_status()
87
+
88
+ data = resp.json()
89
+
90
+ return Market.from_gamma(data)
91
+
92
+
93
+
94
+ def fetch_quote(token_id: str, timeout: float = 15.0) -> Tuple[float, float]:
95
+ """
96
+ Fetches a price quote for a specific token ID
97
+
98
+ Arguments
99
+ token_id: the CLOB token representing the market position
100
+ timeout: timeout for the http request
101
+
102
+ Returns
103
+ (bid, ask) for that token
104
+
105
+ Raises
106
+ requests.HTTPError: if non-200 response
107
+ requests.RequestException: on network failure
108
+ ValueError: if response is not valid JSON
109
+ KeyError: if price key not in JSON response
110
+ """
111
+ base = f"{ENDPOINTS.clob}/price"
112
+
113
+ bid_resp = requests.get(base, params={"token_id": token_id, "side": "sell"}, timeout=timeout)
114
+ ask_resp = requests.get(base, params={"token_id": token_id, "side": "buy"}, timeout=timeout)
115
+
116
+ bid_resp.raise_for_status()
117
+ ask_resp.raise_for_status()
118
+
119
+ try:
120
+ bid_json = bid_resp.json()
121
+ ask_json = ask_resp.json()
122
+ except json.JSONDecodeError as e:
123
+ raise ValueError(f"Response was not valid JSON") from e
124
+
125
+ try:
126
+ bid_raw = bid_json["price"]
127
+ ask_raw = ask_json["price"]
128
+ except KeyError as e:
129
+ raise KeyError(f"Missing price field: {e}") from e
130
+
131
+ if bid_raw is None or ask_raw is None:
132
+ raise ValueError(f"Price was null (bid={bid_raw}, ask={ask_raw})")
133
+
134
+ try:
135
+ bid = float(bid_raw)
136
+ ask = float(ask_raw)
137
+ except (TypeError, ValueError) as e:
138
+ raise ValueError(f"Price was not numeric (bid={bid_raw}, ask={ask_raw})") from e
139
+
140
+ return bid, ask
@@ -0,0 +1,63 @@
1
+ import os
2
+
3
+ from py_clob_client import ClobClient
4
+ from py_clob_client.clob_types import *
5
+ from py_clob_client.exceptions import PolyApiException
6
+ from py_clob_client.order_builder.constants import BUY, SELL
7
+
8
+ from config import ENDPOINTS
9
+
10
+ class Spender:
11
+ def __init__(self, *, wallet_address: str, signature_type: int, private_key: str | None = None, allow_live: bool = False):
12
+ if not allow_live:
13
+ raise RuntimeError(
14
+ "Live trading disabled. Pass allow_live=True to enable"
15
+ )
16
+
17
+ if private_key is None:
18
+ private_key = os.getenv("POLYMARKET_PRIVATE_KEY")
19
+
20
+ if not private_key:
21
+ raise ValueError("No private key provided")
22
+
23
+ self.auth_client = ClobClient(
24
+ ENDPOINTS.clob,
25
+ key=private_key,
26
+ chain_id=137,
27
+ signature_type=signature_type,
28
+ funder=wallet_address
29
+ )
30
+
31
+ creds = self.auth_client.derive_api_key()
32
+ self.auth_client.set_api_creds(creds)
33
+
34
+ def get_balance(self):
35
+ balance = self.auth_client.get_balance_allowance(BalanceAllowanceParams(asset_type=AssetType.COLLATERAL))
36
+ usdc_balance = int(balance['balance']) / 1e6
37
+ return usdc_balance
38
+
39
+
40
+ def place_order(self, * token_id: str, amount: float, order_type: OrderType, side: str) -> dict[str, Any]:
41
+ """
42
+ Places a real polymarket bet
43
+ :param order_type: e.g FOK
44
+ :param token_id: the order ID
45
+ :param amount: the amount in USDC
46
+ :return:
47
+ """
48
+ if side not in (BUY, SELL):
49
+ raise ValueError("Side must be BUY or SELL")
50
+
51
+ market_order = MarketOrderArgs(
52
+ token_id=token_id,
53
+ amount=amount,
54
+ side=side,
55
+ order_type=order_type
56
+ )
57
+ signed = self.auth_client.create_market_order(market_order)
58
+
59
+ try:
60
+ resp = self.auth_client.post_order(signed, order_type)
61
+ return {"ok": True, "resp": resp}
62
+ except PolyApiException as e:
63
+ return {"ok": False, "reason": "", "error": str(e)}
@@ -0,0 +1,57 @@
1
+ import requests
2
+ from dataclasses import dataclass
3
+ from typing import Any
4
+
5
+ @dataclass(frozen=True)
6
+ class Action:
7
+ user_id: str
8
+ event_type: str
9
+ usdc_size: float
10
+ price: float
11
+ side: str
12
+ asset: str
13
+ title: str
14
+ slug: str
15
+ timestamp: int
16
+
17
+ class UsersClient:
18
+ def __init__(self, session=None):
19
+ self.session = session
20
+
21
+ def fetch_activity(self, user_id: str, limit: int = 100, timeout: int = 15) -> tuple[Action, ...]:
22
+ """
23
+ """
24
+ s = self.session or requests
25
+
26
+ url = "https://data-api.polymarket.com/activity"
27
+ params = {
28
+ "user": user_id,
29
+ "limit": limit,
30
+ "sortBy": "TIMESTAMP",
31
+ "sortDirection": "DESC"
32
+ }
33
+ resp = s.get(url, params=params, timeout=timeout)
34
+ resp.raise_for_status()
35
+
36
+ try:
37
+ data: list[dict] = resp.json()
38
+ except ValueError as e:
39
+ raise ValueError("Response was not valid JSON") from e
40
+
41
+ activity: list[Action] = []
42
+ for entry in data:
43
+ action: Action = Action(
44
+ user_id=user_id,
45
+ event_type=entry["type"],
46
+ usdc_size=float(entry["size"]),
47
+ price=float(entry["price"]),
48
+ side=entry["side"],
49
+ asset=entry["asset"],
50
+ title=entry["title"],
51
+ slug=entry["slug"],
52
+ timestamp=int(entry["timestamp"])
53
+ )
54
+ activity.append(action)
55
+
56
+ return tuple(activity)
57
+
@@ -0,0 +1,5 @@
1
+ Metadata-Version: 2.4
2
+ Name: polymarklib
3
+ Version: 0.1.0
4
+ Requires-Python: >=3.10
5
+ Requires-Dist: requests>=2.31.0
@@ -0,0 +1,12 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/polymarklib/__init__.py
4
+ src/polymarklib/config.py
5
+ src/polymarklib/markets.py
6
+ src/polymarklib/order.py
7
+ src/polymarklib/users.py
8
+ src/polymarklib.egg-info/PKG-INFO
9
+ src/polymarklib.egg-info/SOURCES.txt
10
+ src/polymarklib.egg-info/dependency_links.txt
11
+ src/polymarklib.egg-info/requires.txt
12
+ src/polymarklib.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ requests>=2.31.0
@@ -0,0 +1 @@
1
+ polymarklib