parlayx 0.0.0__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.
parlayx/__init__.py ADDED
@@ -0,0 +1,122 @@
1
+ """Official Python client for the ParlayX public API.
2
+
3
+ from parlayx import ParlayX
4
+
5
+ with ParlayX.from_env() as client:
6
+ who = client.whoami()
7
+ balance = client.kalshi.get_balance()
8
+
9
+ `AsyncParlayX` is the same surface with `await`. Every method returns its typed payload
10
+ and raises `RequestError` on any non-2xx response.
11
+ """
12
+
13
+ from ._async_client import AsyncKalshiResource, AsyncParlayX, AsyncPolymarketResource
14
+ from ._generated import models
15
+ from ._generated.models import (
16
+ ApiErrorCode,
17
+ Competition,
18
+ Event,
19
+ EventMarket,
20
+ GetKalshiOrderFillsResponse,
21
+ GetPolymarketOrderFillsResponse,
22
+ KalshiAction,
23
+ KalshiBalance,
24
+ KalshiFillView,
25
+ KalshiListing,
26
+ KalshiOrderRequest,
27
+ KalshiOrderType,
28
+ KalshiOrderView,
29
+ KalshiPosition,
30
+ KalshiSide,
31
+ ListCompetitionsResponse,
32
+ ListEventMarketsResponse,
33
+ ListEventsResponse,
34
+ ListKalshiOrdersResponse,
35
+ ListKalshiPositionsResponse,
36
+ ListPolymarketOrdersResponse,
37
+ ListPolymarketPositionsResponse,
38
+ ListSportsResponse,
39
+ LookupMarketResponse,
40
+ Outcome,
41
+ PolymarketBalance,
42
+ PolymarketFillView,
43
+ PolymarketLimitOrder,
44
+ PolymarketListing,
45
+ PolymarketMarketOrder,
46
+ PolymarketOrderView,
47
+ PolymarketPosition,
48
+ PolymarketSide,
49
+ ProphetXListing,
50
+ ServerTimeResponse,
51
+ Sport,
52
+ SubmitKalshiOrderResponse,
53
+ SubmitPolymarketOrderResponse,
54
+ TradingOrderStatus,
55
+ TradingOrderType,
56
+ WhoamiResponse,
57
+ )
58
+ from .client import KalshiResource, ParlayX, PolymarketOrder, PolymarketResource
59
+ from .errors import RequestError
60
+ from .pagination import (
61
+ apaginate_kalshi_orders,
62
+ apaginate_polymarket_orders,
63
+ paginate_kalshi_orders,
64
+ paginate_polymarket_orders,
65
+ )
66
+
67
+ __all__ = [
68
+ "ApiErrorCode",
69
+ "AsyncKalshiResource",
70
+ "AsyncParlayX",
71
+ "AsyncPolymarketResource",
72
+ "Competition",
73
+ "Event",
74
+ "EventMarket",
75
+ "GetKalshiOrderFillsResponse",
76
+ "GetPolymarketOrderFillsResponse",
77
+ "KalshiAction",
78
+ "KalshiBalance",
79
+ "KalshiFillView",
80
+ "KalshiListing",
81
+ "KalshiOrderRequest",
82
+ "KalshiOrderType",
83
+ "KalshiOrderView",
84
+ "KalshiPosition",
85
+ "KalshiResource",
86
+ "KalshiSide",
87
+ "ListCompetitionsResponse",
88
+ "ListEventMarketsResponse",
89
+ "ListEventsResponse",
90
+ "ListKalshiOrdersResponse",
91
+ "ListKalshiPositionsResponse",
92
+ "ListPolymarketOrdersResponse",
93
+ "ListPolymarketPositionsResponse",
94
+ "ListSportsResponse",
95
+ "LookupMarketResponse",
96
+ "Outcome",
97
+ "ParlayX",
98
+ "PolymarketBalance",
99
+ "PolymarketFillView",
100
+ "PolymarketLimitOrder",
101
+ "PolymarketListing",
102
+ "PolymarketMarketOrder",
103
+ "PolymarketOrder",
104
+ "PolymarketOrderView",
105
+ "PolymarketPosition",
106
+ "PolymarketResource",
107
+ "PolymarketSide",
108
+ "ProphetXListing",
109
+ "RequestError",
110
+ "ServerTimeResponse",
111
+ "Sport",
112
+ "SubmitKalshiOrderResponse",
113
+ "SubmitPolymarketOrderResponse",
114
+ "TradingOrderStatus",
115
+ "TradingOrderType",
116
+ "WhoamiResponse",
117
+ "apaginate_kalshi_orders",
118
+ "apaginate_polymarket_orders",
119
+ "models",
120
+ "paginate_kalshi_orders",
121
+ "paginate_polymarket_orders",
122
+ ]
@@ -0,0 +1,323 @@
1
+ """Asynchronous ParlayX API client.
2
+
3
+ Mirrors `parlayx.client` method for method; the two differ by `await` and nothing else.
4
+ Both sign through the same module and share one set of models.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from types import TracebackType
10
+ from typing import Any
11
+
12
+ import httpx
13
+ from pydantic import BaseModel
14
+
15
+ from ._generated.models import (
16
+ GetKalshiOrderFillsResponse,
17
+ GetPolymarketOrderFillsResponse,
18
+ KalshiBalance,
19
+ KalshiOrderRequest,
20
+ KalshiOrderView,
21
+ ListCompetitionsResponse,
22
+ ListEventMarketsResponse,
23
+ ListEventsResponse,
24
+ ListKalshiOrdersResponse,
25
+ ListKalshiPositionsResponse,
26
+ ListPolymarketOrdersResponse,
27
+ ListPolymarketPositionsResponse,
28
+ ListSportsResponse,
29
+ LookupMarketResponse,
30
+ PolymarketBalance,
31
+ PolymarketOrderView,
32
+ ServerTimeResponse,
33
+ SubmitKalshiOrderResponse,
34
+ SubmitPolymarketOrderResponse,
35
+ WhoamiResponse,
36
+ )
37
+ from ._transport import (
38
+ DEFAULT_BASE_URL,
39
+ Credentials,
40
+ ModelT,
41
+ build_signed_request,
42
+ new_idempotency_key,
43
+ parse,
44
+ path_segment,
45
+ )
46
+ from .client import PolymarketOrder, _credentials_from_env
47
+
48
+
49
+ class AsyncPolymarketResource:
50
+ """Polymarket orders, positions and balance."""
51
+
52
+ def __init__(self, client: AsyncParlayX) -> None:
53
+ self._client = client
54
+
55
+ async def submit_order(
56
+ self, order: PolymarketOrder, *, idempotency_key: str | None = None
57
+ ) -> SubmitPolymarketOrderResponse:
58
+ """Submit an order.
59
+
60
+ An idempotency key is attached for you and defaults to a fresh value per call,
61
+ so an unkeyed retry submits a SECOND order. Pass your own to make a specific
62
+ retry safe.
63
+ """
64
+ return await self._client._request(
65
+ "POST",
66
+ "/v1/polymarket/orders",
67
+ SubmitPolymarketOrderResponse,
68
+ body=order,
69
+ idempotency_key=new_idempotency_key(idempotency_key),
70
+ )
71
+
72
+ async def list_orders(
73
+ self,
74
+ *,
75
+ token_id: str | None = None,
76
+ status: str | None = None,
77
+ page_size: int | None = None,
78
+ page_token: str | None = None,
79
+ ) -> ListPolymarketOrdersResponse:
80
+ """List orders, most recent first."""
81
+ return await self._client._request(
82
+ "GET",
83
+ "/v1/polymarket/orders",
84
+ ListPolymarketOrdersResponse,
85
+ params={
86
+ "tokenId": token_id,
87
+ "status": status,
88
+ "pageSize": page_size,
89
+ "pageToken": page_token,
90
+ },
91
+ )
92
+
93
+ async def get_order(self, order_id: str) -> PolymarketOrderView:
94
+ """Fetch a single order by id."""
95
+ return await self._client._request(
96
+ "GET", f"/v1/polymarket/orders/{path_segment(order_id)}", PolymarketOrderView
97
+ )
98
+
99
+ async def cancel_order(self, order_id: str) -> PolymarketOrderView:
100
+ """Cancel a single open order by id."""
101
+ return await self._client._request(
102
+ "DELETE", f"/v1/polymarket/orders/{path_segment(order_id)}", PolymarketOrderView
103
+ )
104
+
105
+ async def get_order_fills(self, order_id: str) -> GetPolymarketOrderFillsResponse:
106
+ """List the fills recorded against an order."""
107
+ return await self._client._request(
108
+ "GET",
109
+ f"/v1/polymarket/orders/{path_segment(order_id)}/fills",
110
+ GetPolymarketOrderFillsResponse,
111
+ )
112
+
113
+ async def list_positions(
114
+ self, *, token_id: str | None = None
115
+ ) -> ListPolymarketPositionsResponse:
116
+ """List open positions."""
117
+ return await self._client._request(
118
+ "GET",
119
+ "/v1/polymarket/positions",
120
+ ListPolymarketPositionsResponse,
121
+ params={"tokenId": token_id},
122
+ )
123
+
124
+ async def get_balance(self) -> PolymarketBalance:
125
+ """Read the venue balance."""
126
+ return await self._client._request("GET", "/v1/polymarket/balance", PolymarketBalance)
127
+
128
+
129
+ class AsyncKalshiResource:
130
+ """Kalshi orders, positions and balance."""
131
+
132
+ def __init__(self, client: AsyncParlayX) -> None:
133
+ self._client = client
134
+
135
+ async def submit_order(
136
+ self, order: KalshiOrderRequest, *, idempotency_key: str | None = None
137
+ ) -> SubmitKalshiOrderResponse:
138
+ """Submit an order.
139
+
140
+ An idempotency key is attached for you and defaults to a fresh value per call,
141
+ so an unkeyed retry submits a SECOND order. Pass your own to make a specific
142
+ retry safe.
143
+ """
144
+ return await self._client._request(
145
+ "POST",
146
+ "/v1/kalshi/orders",
147
+ SubmitKalshiOrderResponse,
148
+ body=order,
149
+ idempotency_key=new_idempotency_key(idempotency_key),
150
+ )
151
+
152
+ async def list_orders(
153
+ self,
154
+ *,
155
+ ticker: str | None = None,
156
+ status: str | None = None,
157
+ page_size: int | None = None,
158
+ page_token: str | None = None,
159
+ ) -> ListKalshiOrdersResponse:
160
+ """List orders, most recent first."""
161
+ return await self._client._request(
162
+ "GET",
163
+ "/v1/kalshi/orders",
164
+ ListKalshiOrdersResponse,
165
+ params={
166
+ "ticker": ticker,
167
+ "status": status,
168
+ "pageSize": page_size,
169
+ "pageToken": page_token,
170
+ },
171
+ )
172
+
173
+ async def get_order(self, order_id: str) -> KalshiOrderView:
174
+ """Fetch a single order by id."""
175
+ return await self._client._request(
176
+ "GET", f"/v1/kalshi/orders/{path_segment(order_id)}", KalshiOrderView
177
+ )
178
+
179
+ async def cancel_order(self, order_id: str) -> KalshiOrderView:
180
+ """Cancel a single open order by id."""
181
+ return await self._client._request(
182
+ "DELETE", f"/v1/kalshi/orders/{path_segment(order_id)}", KalshiOrderView
183
+ )
184
+
185
+ async def get_order_fills(self, order_id: str) -> GetKalshiOrderFillsResponse:
186
+ """List the fills recorded against an order."""
187
+ return await self._client._request(
188
+ "GET", f"/v1/kalshi/orders/{path_segment(order_id)}/fills", GetKalshiOrderFillsResponse
189
+ )
190
+
191
+ async def list_positions(self, *, ticker: str | None = None) -> ListKalshiPositionsResponse:
192
+ """List open positions."""
193
+ return await self._client._request(
194
+ "GET",
195
+ "/v1/kalshi/positions",
196
+ ListKalshiPositionsResponse,
197
+ params={"ticker": ticker},
198
+ )
199
+
200
+ async def get_balance(self) -> KalshiBalance:
201
+ """Read the venue balance."""
202
+ return await self._client._request("GET", "/v1/kalshi/balance", KalshiBalance)
203
+
204
+
205
+ class AsyncParlayX:
206
+ """Asynchronous ParlayX API client.
207
+
208
+ Every method returns its typed payload and raises `RequestError` on any non-2xx
209
+ response. Create one and reuse it; each instance holds a connection pool.
210
+ """
211
+
212
+ def __init__(
213
+ self,
214
+ *,
215
+ key_id: str,
216
+ private_key_hex: str,
217
+ base_url: str = DEFAULT_BASE_URL,
218
+ timeout: float = 30.0,
219
+ transport: httpx.AsyncBaseTransport | None = None,
220
+ ) -> None:
221
+ self._credentials = Credentials(key_id=key_id, private_key_hex=private_key_hex)
222
+ self._http = httpx.AsyncClient(base_url=base_url, timeout=timeout, transport=transport)
223
+ self.polymarket = AsyncPolymarketResource(self)
224
+ self.kalshi = AsyncKalshiResource(self)
225
+
226
+ @classmethod
227
+ def from_env(cls, **kwargs: Any) -> AsyncParlayX:
228
+ """Build a client from PARLAYX_KEY_ID and PARLAYX_PRIVATE_KEY_HEX."""
229
+ credentials = _credentials_from_env()
230
+ return cls(key_id=credentials.key_id, private_key_hex=credentials.private_key_hex, **kwargs)
231
+
232
+ async def _request(
233
+ self,
234
+ method: str,
235
+ path: str,
236
+ model: type[ModelT],
237
+ *,
238
+ params: dict[str, Any] | None = None,
239
+ body: BaseModel | None = None,
240
+ idempotency_key: str | None = None,
241
+ ) -> ModelT:
242
+ request = build_signed_request(
243
+ self._http,
244
+ self._credentials,
245
+ method,
246
+ path,
247
+ params=params,
248
+ body=body,
249
+ idempotency_key=idempotency_key,
250
+ )
251
+ return parse(await self._http.send(request), model)
252
+
253
+ async def whoami(self) -> WhoamiResponse:
254
+ """Identify the calling key: its actor, pod, scope and capabilities."""
255
+ return await self._request("GET", "/v1/whoami", WhoamiResponse)
256
+
257
+ async def get_time(self) -> ServerTimeResponse:
258
+ """Read the server clock.
259
+
260
+ Verified without a timestamp, so a host whose clock has drifted past the
261
+ signature window can still call it and measure the offset.
262
+ """
263
+ return await self._request("GET", "/v1/time", ServerTimeResponse)
264
+
265
+ async def list_sports(self) -> ListSportsResponse:
266
+ """List the sports carried by the discovery surface."""
267
+ return await self._request("GET", "/v1/sports", ListSportsResponse)
268
+
269
+ async def list_competitions(self, *, sport: str | None = None) -> ListCompetitionsResponse:
270
+ """List competitions, optionally filtered to one sport."""
271
+ return await self._request(
272
+ "GET", "/v1/sports/competitions", ListCompetitionsResponse, params={"sport": sport}
273
+ )
274
+
275
+ async def list_events(self, *, competition_id: str) -> ListEventsResponse:
276
+ """List the events in a competition."""
277
+ return await self._request(
278
+ "GET",
279
+ "/v1/sports/events",
280
+ ListEventsResponse,
281
+ params={"competitionId": competition_id},
282
+ )
283
+
284
+ async def list_event_markets(self, event_id: str) -> ListEventMarketsResponse:
285
+ """List the markets on an event."""
286
+ return await self._request(
287
+ "GET", f"/v1/sports/events/{path_segment(event_id)}/markets", ListEventMarketsResponse
288
+ )
289
+
290
+ async def lookup_market(
291
+ self,
292
+ *,
293
+ token_id: str | None = None,
294
+ ticker: str | None = None,
295
+ condition_id: str | None = None,
296
+ slug: str | None = None,
297
+ ) -> LookupMarketResponse:
298
+ """Look up a single market by one venue-native identifier."""
299
+ return await self._request(
300
+ "GET",
301
+ "/v1/markets",
302
+ LookupMarketResponse,
303
+ params={
304
+ "tokenId": token_id,
305
+ "ticker": ticker,
306
+ "conditionId": condition_id,
307
+ "slug": slug,
308
+ },
309
+ )
310
+
311
+ async def close(self) -> None:
312
+ await self._http.aclose()
313
+
314
+ async def __aenter__(self) -> AsyncParlayX:
315
+ return self
316
+
317
+ async def __aexit__(
318
+ self,
319
+ exc_type: type[BaseException] | None,
320
+ exc: BaseException | None,
321
+ traceback: TracebackType | None,
322
+ ) -> None:
323
+ await self.close()
File without changes