razvan commited on
Commit
730d4dc
Β·
verified Β·
1 Parent(s): a7d22b3

Upload builderbrain/polymarket_client.py

Browse files
Files changed (1) hide show
  1. builderbrain/polymarket_client.py +63 -25
builderbrain/polymarket_client.py CHANGED
@@ -3,13 +3,22 @@ Polymarket API Client + Builder Code Router
3
  ===========================================
4
 
5
  Handles:
6
- - Live market data ingestion (prices, liquidity, orderbook)
7
- - Builder code registration and routing
8
- - Order construction and execution
9
- - Paper trading mode for hackathon demo
10
 
11
- Builder codes earn fees on every trade routed through them.
12
- This is the monetization layer for the intelligence agent.
 
 
 
 
 
 
 
 
 
 
13
  """
14
 
15
  import requests
@@ -54,14 +63,14 @@ class OrderIntent:
54
  outcome: str # 'YES' or 'NO'
55
  size: float # USD notional
56
  price: float # limit price 0-1
57
- builder_code: Optional[str] = None
58
  client_order_id: Optional[str] = None
59
 
60
 
61
  @dataclass
62
  class BuilderCode:
63
  """A registered builder code for fee sharing."""
64
- code: str
65
  name: str
66
  description: str
67
  fee_share_bps: int # basis points earned on routed volume
@@ -78,8 +87,13 @@ class PolymarketClient:
78
  """
79
  Polymarket API client with builder code support.
80
 
81
- Uses Gamma API for market data.
82
  Supports paper trading for hackathon demos.
 
 
 
 
 
83
  """
84
 
85
  GAMMA_API = "https://gamma-api.polymarket.com"
@@ -119,7 +133,6 @@ class PolymarketClient:
119
 
120
  markets = []
121
  for m in data.get("markets", data if isinstance(data, list) else []):
122
- # Handle different API response shapes
123
  if isinstance(m, dict):
124
  markets.append(self._parse_market(m))
125
 
@@ -130,14 +143,11 @@ class PolymarketClient:
130
 
131
  def _parse_market(self, raw: Dict) -> PolymarketMarket:
132
  """Parse raw API response into PolymarketMarket."""
133
- # Handle nested structure
134
  market_data = raw.get("market", raw)
135
 
136
- # Prices can be in different fields depending on API version
137
  yes_price = market_data.get("bestYesPrice", market_data.get("yesPrice", 0.5))
138
  no_price = market_data.get("bestNoPrice", market_data.get("noPrice", 0.5))
139
 
140
- # Normalize prices to 0-1
141
  if yes_price > 1:
142
  yes_price /= 100
143
  if no_price > 1:
@@ -160,7 +170,7 @@ class PolymarketClient:
160
  )
161
 
162
  def fetch_orderbook(self, market_id: str) -> Optional[Dict]:
163
- """Fetch L2 orderbook for a market."""
164
  url = f"{self.CLOB_API}/book/{market_id}"
165
  try:
166
  resp = self.session.get(url, timeout=10)
@@ -179,7 +189,20 @@ class PolymarketClient:
179
  description: str,
180
  fee_share_bps: int = 10,
181
  ) -> BuilderCode:
182
- """Register a builder code for fee sharing."""
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  bc = BuilderCode(
184
  code=code,
185
  name=name,
@@ -198,7 +221,7 @@ class PolymarketClient:
198
  Route an order with builder code attribution.
199
 
200
  In paper trade mode, simulates execution.
201
- In live mode, would submit to Polymarket CLOB.
202
  """
203
  # Attach builder code if provided
204
  if builder_code and builder_code in self.builder_codes:
@@ -225,7 +248,7 @@ class PolymarketClient:
225
  "size_usd": order.size,
226
  "price": order.price,
227
  "filled_at": datetime.utcnow().isoformat(),
228
- "builder_code": order.builder_code,
229
  "paper_trade": True,
230
  "fees_paid_usd": order.size * 0.002, # 20 bps
231
  }
@@ -244,13 +267,29 @@ class PolymarketClient:
244
  return execution
245
 
246
  def _live_execute(self, order: OrderIntent) -> Dict:
247
- """Placeholder for live execution via CLOB API."""
248
- # TODO: Implement Polymarket CLOB signing + submission
249
- # Requires API key + wallet integration
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  return {
251
  "status": "PENDING",
252
  "order": asdict(order),
253
- "note": "Live execution requires CLOB API key + wallet",
254
  }
255
 
256
  # ────────────────────────────── Portfolio ──────────────────────────────
@@ -285,6 +324,8 @@ class BuilderCodeRouter:
285
  - Market category alignment
286
  - Fee share optimization
287
  - Volume tracking for leaderboard
 
 
288
  """
289
 
290
  def __init__(self, client: PolymarketClient):
@@ -305,8 +346,6 @@ class BuilderCodeRouter:
305
  return self.category_map.get(market.category)
306
 
307
  elif strategy == "max_fee_share":
308
- # Route to builder code with highest fee share
309
- # (incentivizes registering competitive codes)
310
  best = None
311
  best_bps = 0
312
  for code, bc in self.client.builder_codes.items():
@@ -316,7 +355,6 @@ class BuilderCodeRouter:
316
  return best
317
 
318
  elif strategy == "volume_leader":
319
- # Route to builder with most volume (social proof)
320
  best = None
321
  best_vol = 0
322
  for code, bc in self.client.builder_codes.items():
@@ -344,7 +382,7 @@ class BuilderCodeRouter:
344
  outcome=outcome,
345
  size=size_usd,
346
  price=price,
347
- builder_code=builder_code,
348
  )
349
 
350
  return self.client.route_order(order, builder_code)
 
3
  ===========================================
4
 
5
  Handles:
6
+ - Live market data ingestion via Polymarket Gamma API
7
+ - Builder code registration and order attribution
8
+ - Order construction and execution (paper or live)
 
9
 
10
+ Builder Code Details (from Polymarket docs):
11
+ - Register at: https://polymarket.com/settings?tab=builder
12
+ - Code format: bytes32 (e.g. 0x0000...0001)
13
+ - Attached per order as an order FIELD (not header/query param):
14
+ { "builderCode": "0x...", "price": 0.55, "size": 100, ... }
15
+ - Serialized on-chain as the `builder` field
16
+ - No extra auth required beyond normal CLOB credentials
17
+ - Track volume at: https://builders.polymarket.com
18
+
19
+ API:
20
+ - Gamma API: https://gamma-api.polymarket.com
21
+ - CLOB API: https://clob.polymarket.com
22
  """
23
 
24
  import requests
 
63
  outcome: str # 'YES' or 'NO'
64
  size: float # USD notional
65
  price: float # limit price 0-1
66
+ builder_code: Optional[str] = None # bytes32 hex string, e.g. "0x0000...0001"
67
  client_order_id: Optional[str] = None
68
 
69
 
70
  @dataclass
71
  class BuilderCode:
72
  """A registered builder code for fee sharing."""
73
+ code: str # bytes32 hex string
74
  name: str
75
  description: str
76
  fee_share_bps: int # basis points earned on routed volume
 
87
  """
88
  Polymarket API client with builder code support.
89
 
90
+ Uses Gamma API for market data, CLOB API for orderbook.
91
  Supports paper trading for hackathon demos.
92
+
93
+ Builder Code Registration:
94
+ 1. Go to https://polymarket.com/settings?tab=builder
95
+ 2. Copy your bytes32 builder code
96
+ 3. Pass it to BuilderCodeRouter or OrderIntent.builder_code
97
  """
98
 
99
  GAMMA_API = "https://gamma-api.polymarket.com"
 
133
 
134
  markets = []
135
  for m in data.get("markets", data if isinstance(data, list) else []):
 
136
  if isinstance(m, dict):
137
  markets.append(self._parse_market(m))
138
 
 
143
 
144
  def _parse_market(self, raw: Dict) -> PolymarketMarket:
145
  """Parse raw API response into PolymarketMarket."""
 
146
  market_data = raw.get("market", raw)
147
 
 
148
  yes_price = market_data.get("bestYesPrice", market_data.get("yesPrice", 0.5))
149
  no_price = market_data.get("bestNoPrice", market_data.get("noPrice", 0.5))
150
 
 
151
  if yes_price > 1:
152
  yes_price /= 100
153
  if no_price > 1:
 
170
  )
171
 
172
  def fetch_orderbook(self, market_id: str) -> Optional[Dict]:
173
+ """Fetch L2 orderbook for a market from CLOB API."""
174
  url = f"{self.CLOB_API}/book/{market_id}"
175
  try:
176
  resp = self.session.get(url, timeout=10)
 
189
  description: str,
190
  fee_share_bps: int = 10,
191
  ) -> BuilderCode:
192
+ """
193
+ Register a builder code for fee sharing.
194
+
195
+ Note: The actual registration happens on Polymarket at:
196
+ https://polymarket.com/settings?tab=builder
197
+
198
+ This method just registers it locally for tracking.
199
+
200
+ Args:
201
+ code: bytes32 hex string, e.g. "0x00000000000000000000000000000001"
202
+ name: Human-readable builder name
203
+ description: What this builder does
204
+ fee_share_bps: Basis points earned on routed volume
205
+ """
206
  bc = BuilderCode(
207
  code=code,
208
  name=name,
 
221
  Route an order with builder code attribution.
222
 
223
  In paper trade mode, simulates execution.
224
+ In live mode, would submit to Polymarket CLOB with builderCode field.
225
  """
226
  # Attach builder code if provided
227
  if builder_code and builder_code in self.builder_codes:
 
248
  "size_usd": order.size,
249
  "price": order.price,
250
  "filled_at": datetime.utcnow().isoformat(),
251
+ "builder_code": order.builder_code, # bytes32 hex
252
  "paper_trade": True,
253
  "fees_paid_usd": order.size * 0.002, # 20 bps
254
  }
 
267
  return execution
268
 
269
  def _live_execute(self, order: OrderIntent) -> Dict:
270
+ """
271
+ Placeholder for live execution via Polymarket CLOB.
272
+
273
+ In production, submit to CLOB with builderCode as order field:
274
+
275
+ Example (TypeScript pattern from Polymarket docs):
276
+ const response = await client.createAndPostOrder(
277
+ {
278
+ tokenID: "0x...",
279
+ price: 0.55,
280
+ size: 100,
281
+ side: Side.BUY,
282
+ builderCode: "0x0000...0001", // bytes32 hex
283
+ },
284
+ { tickSize: "0.01", negRisk: false },
285
+ );
286
+
287
+ The builderCode is serialized into the on-chain order as the `builder` field.
288
+ """
289
  return {
290
  "status": "PENDING",
291
  "order": asdict(order),
292
+ "note": "Live execution requires CLOB API key + wallet + builder code registration at polymarket.com/settings?tab=builder",
293
  }
294
 
295
  # ────────────────────────────── Portfolio ──────────────────────────────
 
324
  - Market category alignment
325
  - Fee share optimization
326
  - Volume tracking for leaderboard
327
+
328
+ Builder Leaderboard: https://builders.polymarket.com
329
  """
330
 
331
  def __init__(self, client: PolymarketClient):
 
346
  return self.category_map.get(market.category)
347
 
348
  elif strategy == "max_fee_share":
 
 
349
  best = None
350
  best_bps = 0
351
  for code, bc in self.client.builder_codes.items():
 
355
  return best
356
 
357
  elif strategy == "volume_leader":
 
358
  best = None
359
  best_vol = 0
360
  for code, bc in self.client.builder_codes.items():
 
382
  outcome=outcome,
383
  size=size_usd,
384
  price=price,
385
+ builder_code=builder_code, # bytes32 hex string
386
  )
387
 
388
  return self.client.route_order(order, builder_code)