skewtape 0.1.3__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.
Files changed (101) hide show
  1. skewtape/__init__.py +122 -0
  2. skewtape/_generated/__init__.py +9 -0
  3. skewtape/_generated/api/__init__.py +2 -0
  4. skewtape/_generated/api/data/__init__.py +2 -0
  5. skewtape/_generated/api/data/connect_feed.py +491 -0
  6. skewtape/_generated/api/data/get_market.py +217 -0
  7. skewtape/_generated/api/data/get_matched_event.py +199 -0
  8. skewtape/_generated/api/data/get_pair.py +203 -0
  9. skewtape/_generated/api/data/get_quote_context.py +227 -0
  10. skewtape/_generated/api/data/list_bundles.py +215 -0
  11. skewtape/_generated/api/data/list_candles.py +366 -0
  12. skewtape/_generated/api/data/list_fees.py +229 -0
  13. skewtape/_generated/api/data/list_fills.py +353 -0
  14. skewtape/_generated/api/data/list_markets.py +275 -0
  15. skewtape/_generated/api/data/list_matched_events.py +287 -0
  16. skewtape/_generated/api/data/list_pairs.py +279 -0
  17. skewtape/_generated/api/data/list_resolutions.py +267 -0
  18. skewtape/_generated/api/data/list_venues.py +175 -0
  19. skewtape/_generated/api/data/post_download_url.py +276 -0
  20. skewtape/_generated/client.py +281 -0
  21. skewtape/_generated/errors.py +17 -0
  22. skewtape/_generated/models/__init__.py +124 -0
  23. skewtape/_generated/models/bundle.py +100 -0
  24. skewtape/_generated/models/bundle_file.py +78 -0
  25. skewtape/_generated/models/bundle_list.py +95 -0
  26. skewtape/_generated/models/candle.py +171 -0
  27. skewtape/_generated/models/candle_list.py +91 -0
  28. skewtape/_generated/models/candle_res.py +11 -0
  29. skewtape/_generated/models/download_url.py +74 -0
  30. skewtape/_generated/models/download_url_request.py +62 -0
  31. skewtape/_generated/models/error_body.py +202 -0
  32. skewtape/_generated/models/error_code.py +18 -0
  33. skewtape/_generated/models/error_envelope.py +68 -0
  34. skewtape/_generated/models/fee.py +196 -0
  35. skewtape/_generated/models/fee_formula.py +12 -0
  36. skewtape/_generated/models/fee_list.py +91 -0
  37. skewtape/_generated/models/fee_maker_rebate_params.py +53 -0
  38. skewtape/_generated/models/fee_rounding.py +12 -0
  39. skewtape/_generated/models/fee_taker_params.py +53 -0
  40. skewtape/_generated/models/fill.py +279 -0
  41. skewtape/_generated/models/fill_leg.py +10 -0
  42. skewtape/_generated/models/fill_list.py +91 -0
  43. skewtape/_generated/models/fill_match_model.py +10 -0
  44. skewtape/_generated/models/fill_side.py +10 -0
  45. skewtape/_generated/models/list_candles_res.py +11 -0
  46. skewtape/_generated/models/list_fills_leg.py +11 -0
  47. skewtape/_generated/models/list_matched_events_status.py +11 -0
  48. skewtape/_generated/models/list_pairs_status.py +11 -0
  49. skewtape/_generated/models/locked_bundle.py +81 -0
  50. skewtape/_generated/models/market.py +449 -0
  51. skewtape/_generated/models/market_list.py +91 -0
  52. skewtape/_generated/models/market_source_kind.py +11 -0
  53. skewtape/_generated/models/market_status.py +12 -0
  54. skewtape/_generated/models/matched_event.py +263 -0
  55. skewtape/_generated/models/matched_event_list.py +91 -0
  56. skewtape/_generated/models/matched_event_member.py +207 -0
  57. skewtape/_generated/models/matched_event_member_outcome_map.py +53 -0
  58. skewtape/_generated/models/matched_event_member_status.py +10 -0
  59. skewtape/_generated/models/matched_event_status.py +11 -0
  60. skewtape/_generated/models/outcome.py +70 -0
  61. skewtape/_generated/models/pair.py +183 -0
  62. skewtape/_generated/models/pair_list.py +91 -0
  63. skewtape/_generated/models/pair_member.py +112 -0
  64. skewtape/_generated/models/pair_member_outcome_map.py +53 -0
  65. skewtape/_generated/models/pair_status.py +11 -0
  66. skewtape/_generated/models/plan_id.py +12 -0
  67. skewtape/_generated/models/quote_context.py +124 -0
  68. skewtape/_generated/models/quote_context_member.py +253 -0
  69. skewtape/_generated/models/quote_context_member_fee_per.py +10 -0
  70. skewtape/_generated/models/quote_context_member_fee_status.py +12 -0
  71. skewtape/_generated/models/quote_context_member_outcome_map.py +54 -0
  72. skewtape/_generated/models/resolution.py +222 -0
  73. skewtape/_generated/models/resolution_event_type.py +15 -0
  74. skewtape/_generated/models/resolution_list.py +91 -0
  75. skewtape/_generated/models/resolution_source_kind.py +11 -0
  76. skewtape/_generated/models/venue.py +122 -0
  77. skewtape/_generated/models/venue_coverage.py +129 -0
  78. skewtape/_generated/models/venue_kind_rights.py +114 -0
  79. skewtape/_generated/models/venue_kind_rights_kind.py +11 -0
  80. skewtape/_generated/models/venue_kind_rights_rights.py +11 -0
  81. skewtape/_generated/models/venue_list.py +86 -0
  82. skewtape/_generated/types.py +55 -0
  83. skewtape/_keccak.py +66 -0
  84. skewtape/_msgpack.py +97 -0
  85. skewtape/backtest.py +697 -0
  86. skewtape/client.py +614 -0
  87. skewtape/download.py +159 -0
  88. skewtape/errors.py +153 -0
  89. skewtape/fees.py +248 -0
  90. skewtape/geoblock.py +80 -0
  91. skewtape/kalshi.py +574 -0
  92. skewtape/paginated.py +58 -0
  93. skewtape/quote.py +335 -0
  94. skewtape/quote_math.py +561 -0
  95. skewtape/route.py +520 -0
  96. skewtape/route_hyperliquid.py +417 -0
  97. skewtape/version.py +5 -0
  98. skewtape-0.1.3.dist-info/METADATA +189 -0
  99. skewtape-0.1.3.dist-info/RECORD +101 -0
  100. skewtape-0.1.3.dist-info/WHEEL +4 -0
  101. skewtape-0.1.3.dist-info/licenses/LICENSE +21 -0
skewtape/__init__.py ADDED
@@ -0,0 +1,122 @@
1
+ """Skewtape: prediction-market data across venues in one schema.
2
+
3
+ import skewtape
4
+
5
+ sk = skewtape.Skewtape() # the key from SKEWTAPE_API_KEY, or api_key=...
6
+ for event in sk.events(min_confidence=0.9, resolution_equal=True):
7
+ print(event.event_id, event.question_canonical)
8
+
9
+ sk.kalshi() is the optional live Kalshi client under the user's own Kalshi
10
+ membership (skewtape.kalshi), apart from the API: Skewtape's Kalshi data comes
11
+ from the calls above.
12
+
13
+ The hand-written facade lives in this package; the client the OpenAPI
14
+ document generates lives in skewtape._generated and is never edited.
15
+ """
16
+
17
+ from . import geoblock
18
+ from .backtest import BacktestError, BacktestResult, BacktestWarning, PositionResult, Totals, Trade, TradeResult, run_backtest
19
+ from .client import DEFAULT_BASE_URL, Client, Page, Paginated, Skewtape
20
+ from .download import ChecksumError, download_file, verify_checksums
21
+ from .fees import FeeError, FeeRow, maker_fee_raw, select_fee_row, taker_fee_raw
22
+ from .errors import (
23
+ AuthenticationError,
24
+ BlockedError,
25
+ ConfigurationError,
26
+ ForbiddenPlanError,
27
+ InvalidRequestError,
28
+ NotFoundError,
29
+ QuotaExceededError,
30
+ RateLimitError,
31
+ ServerError,
32
+ SkewtapeError,
33
+ )
34
+ from .kalshi import (
35
+ DEFAULT_KALSHI_BASE_URL,
36
+ Kalshi,
37
+ KalshiCandlestick,
38
+ KalshiError,
39
+ KalshiLevel,
40
+ KalshiMarket,
41
+ KalshiOHLC,
42
+ KalshiOrderbook,
43
+ KalshiTrade,
44
+ kalshi_tickers,
45
+ series_ticker_of,
46
+ )
47
+ from .quote import BookRequest, BookUnavailable, Quote, quote_from_context
48
+ from .quote_math import (
49
+ InvalidQuoteInputError,
50
+ QuoteError,
51
+ QuoteResult,
52
+ RuleMismatchError,
53
+ VenueUnreachableError,
54
+ compute_quote,
55
+ )
56
+ from .route import DFlowOrderRequest, PolymarketOrder, RouteError, route
57
+ from .route_hyperliquid import HyperliquidApproveBuilderFee, HyperliquidOrder, hyperliquid_approve_builder_fee
58
+ from .version import __version__
59
+
60
+ __all__ = [
61
+ "DEFAULT_BASE_URL",
62
+ "DEFAULT_KALSHI_BASE_URL",
63
+ "AuthenticationError",
64
+ "BacktestError",
65
+ "BacktestResult",
66
+ "BacktestWarning",
67
+ "BlockedError",
68
+ "BookRequest",
69
+ "BookUnavailable",
70
+ "ChecksumError",
71
+ "Client",
72
+ "ConfigurationError",
73
+ "DFlowOrderRequest",
74
+ "FeeError",
75
+ "FeeRow",
76
+ "ForbiddenPlanError",
77
+ "HyperliquidApproveBuilderFee",
78
+ "HyperliquidOrder",
79
+ "InvalidQuoteInputError",
80
+ "InvalidRequestError",
81
+ "Kalshi",
82
+ "KalshiCandlestick",
83
+ "KalshiError",
84
+ "KalshiLevel",
85
+ "KalshiMarket",
86
+ "KalshiOHLC",
87
+ "KalshiOrderbook",
88
+ "KalshiTrade",
89
+ "NotFoundError",
90
+ "Page",
91
+ "Paginated",
92
+ "PolymarketOrder",
93
+ "PositionResult",
94
+ "QuotaExceededError",
95
+ "Quote",
96
+ "QuoteError",
97
+ "QuoteResult",
98
+ "RateLimitError",
99
+ "RouteError",
100
+ "RuleMismatchError",
101
+ "ServerError",
102
+ "Skewtape",
103
+ "SkewtapeError",
104
+ "Totals",
105
+ "Trade",
106
+ "TradeResult",
107
+ "VenueUnreachableError",
108
+ "__version__",
109
+ "compute_quote",
110
+ "download_file",
111
+ "geoblock",
112
+ "hyperliquid_approve_builder_fee",
113
+ "kalshi_tickers",
114
+ "maker_fee_raw",
115
+ "quote_from_context",
116
+ "route",
117
+ "run_backtest",
118
+ "select_fee_row",
119
+ "series_ticker_of",
120
+ "taker_fee_raw",
121
+ "verify_checksums",
122
+ ]
@@ -0,0 +1,9 @@
1
+ # Generated by openapi-python-client 0.29.1 from openapi/skewtape.public.v1.yaml. Do not edit: scripts/sdk-generate.sh rewrites this file.
2
+ """A client library for accessing Skewtape API"""
3
+
4
+ from .client import AuthenticatedClient, Client
5
+
6
+ __all__ = (
7
+ "AuthenticatedClient",
8
+ "Client",
9
+ )
@@ -0,0 +1,2 @@
1
+ # Generated by openapi-python-client 0.29.1 from openapi/skewtape.public.v1.yaml. Do not edit: scripts/sdk-generate.sh rewrites this file.
2
+ """Contains methods for accessing the API"""
@@ -0,0 +1,2 @@
1
+ # Generated by openapi-python-client 0.29.1 from openapi/skewtape.public.v1.yaml. Do not edit: scripts/sdk-generate.sh rewrites this file.
2
+ """Contains endpoint functions for accessing the API"""
@@ -0,0 +1,491 @@
1
+ # Generated by openapi-python-client 0.29.1 from openapi/skewtape.public.v1.yaml. Do not edit: scripts/sdk-generate.sh rewrites this file.
2
+ from http import HTTPStatus
3
+ from typing import Any, cast
4
+
5
+ import httpx
6
+
7
+ from ... import errors
8
+ from ...client import AuthenticatedClient, Client
9
+ from ...models.error_envelope import ErrorEnvelope
10
+ from ...types import UNSET, Response, Unset
11
+
12
+
13
+ def _get_kwargs(
14
+ *,
15
+ x_api_key: str | Unset = UNSET,
16
+ ) -> dict[str, Any]:
17
+ headers: dict[str, Any] = {}
18
+ if not isinstance(x_api_key, Unset):
19
+ headers["X-API-Key"] = x_api_key
20
+
21
+ _kwargs: dict[str, Any] = {
22
+ "method": "get",
23
+ "url": "/v1/feed",
24
+ }
25
+
26
+ _kwargs["headers"] = headers
27
+ return _kwargs
28
+
29
+
30
+ def _parse_response(
31
+ *, client: AuthenticatedClient | Client, response: httpx.Response
32
+ ) -> Any | ErrorEnvelope | None:
33
+ if response.status_code == 101:
34
+ response_101 = cast(Any, None)
35
+ return response_101
36
+
37
+ if response.status_code == 401:
38
+ response_401 = ErrorEnvelope.from_dict(response.json())
39
+
40
+ return response_401
41
+
42
+ if response.status_code == 503:
43
+ response_503 = ErrorEnvelope.from_dict(response.json())
44
+
45
+ return response_503
46
+
47
+ if client.raise_on_unexpected_status:
48
+ raise errors.UnexpectedStatus(response.status_code, response.content)
49
+ else:
50
+ return None
51
+
52
+
53
+ def _build_response(
54
+ *, client: AuthenticatedClient | Client, response: httpx.Response
55
+ ) -> Response[Any | ErrorEnvelope]:
56
+ return Response(
57
+ status_code=HTTPStatus(response.status_code),
58
+ content=response.content,
59
+ headers=response.headers,
60
+ parsed=_parse_response(client=client, response=response),
61
+ )
62
+
63
+
64
+ def sync_detailed(
65
+ *,
66
+ client: AuthenticatedClient | Client,
67
+ x_api_key: str | Unset = UNSET,
68
+ ) -> Response[Any | ErrorEnvelope]:
69
+ """Live feed (WebSocket)
70
+
71
+ The live feed, over a WebSocket. Builder and Pro plans: Builder holds
72
+ up to 3 open connections and Pro up to 10, counted across all of an
73
+ account's keys. The live feed guide, https://docs.skewtape.com/feed,
74
+ has the whole protocol with examples; this is its summary.
75
+
76
+ Authentication: the X-API-Key header on the upgrade request, or the
77
+ first message {"op":"auth","key":"st_live_..."} within 10 seconds.
78
+
79
+ Topics: markets, resolutions, trades and matched_events on Builder
80
+ and Pro; event_spread on Pro only. A subscribe that names a topic
81
+ outside the plan is refused with error topic_not_in_plan and an
82
+ upgrade_url, and nothing of that subscribe applies: the previous
83
+ subscription stays, and on a first subscribe there is none. Send it
84
+ again without the topic. A topic the feed does not have is refused
85
+ with error invalid_subscription. A plan that loses a topic during a
86
+ connection is told the same way and keeps its other topics.
87
+
88
+ From the client:
89
+ {"op":"subscribe","topics":["markets","trades","event_spread"],
90
+ "venues":["kalshi"],"markets":[{"venue":"polymarket","market_id":"0x..."}],
91
+ "events":["st_evt_00412"],"resume_from":123} replaces the
92
+ subscription. topics is required; venues, markets and events are
93
+ optional filters, ORed. events, matched event ids, narrows
94
+ event_spread and matched_events, whose events belong to a matched
95
+ event rather than a venue, so the venue and market filters do not
96
+ narrow them; it needs one of those two topics, and leaves the other
97
+ topics to the venue and market filters, whole when there are none.
98
+ A market filter cuts a trades_batch down to its markets' fills.
99
+ {"op":"ping"} is answered {"op":"pong"} at once, even during a
100
+ replay.
101
+
102
+ matched_events carries the changes to what /v1/matched_events
103
+ returns, within five minutes: an event published (kind
104
+ matched_event_published), changed (matched_event_changed: any field
105
+ but updated_at, reviewer_notes included, so a Builder key can get a
106
+ change it cannot see) or retired (matched_event_retired: no longer
107
+ returned, whatever the reason; its payload is the event_id and the
108
+ status alone). A published or changed event's payload is the event as
109
+ /v1/matched_events returns it. The topic has no snapshot: read
110
+ /v1/matched_events once for the current set, then follow the changes.
111
+
112
+ From the server, each message with an op: hello (connection id, plan,
113
+ current seq, replay window and reconnect schedule), subscribed (what
114
+ was accepted, replay_to and masked_venues), snapshot (the latest
115
+ spread of each matched event the subscription names, sent right
116
+ after subscribed; it has no seq), event (seq, topic, kind, venue,
117
+ market_id, at, payload), resync (resume_from is older than the replay
118
+ window: read the REST API for what you missed), reconnect (at 55
119
+ minutes, with resume_from; the connection closes at 59) and error
120
+ (code, message, and upgrade_url on a plan refusal).
121
+
122
+ Trades: one event per fill, up to 400 a second per venue, the rest of
123
+ that second in one trades_batch. A fill can arrive twice: dedupe by
124
+ (venue, tx_hash, log_index). A trade_withdrawn names a streamed trade
125
+ (limitless, myriad, predictfun) whose block a chain reorganisation
126
+ replaced without it: drop the trade with that identity. If its transaction is mined again, it
127
+ arrives as a new trade under its new identity. event_spread: a matched event's
128
+ fee-adjusted spread, at most one event per matched event every five
129
+ seconds.
130
+
131
+ Resume: keep the last seq you saw and send it as resume_from. The feed
132
+ replays the stored events after it that match the subscription, then
133
+ goes on live with no gap and no duplicate. A gap notice (topic feed,
134
+ kind gap) means a venue stream reconnected: read the REST API for that
135
+ window.
136
+
137
+ Data policy: a subscription naming a venue or a market of a venue the
138
+ policy blocks for live data is refused (error venue_blocked). A masked
139
+ venue's events arrive with the withheld fields null and blocked true.
140
+ A matched event the feed serves arrives whole: a member of a blocked venue keeps its
141
+ ids, its side labels are null, its outcome_map is empty and blocked
142
+ is true.
143
+
144
+ Close codes: 4000 reconnect (connect again with resume_from), 4001
145
+ unauthorized (also when the key is revoked: keys are checked again
146
+ every few minutes), 4003 plan (no feed, or a plan change left no
147
+ topic of the subscription), 4008 connection limit, 4009 slow consumer (resume from the
148
+ seq in the close reason), 4029 too many failed key lookups, 4400 bad
149
+ message, 1001 server restarting, 1011 server error (connect again
150
+ with resume_from).
151
+
152
+ Args:
153
+ x_api_key (str | Unset):
154
+
155
+ Raises:
156
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
157
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
158
+
159
+ Returns:
160
+ Response[Any | ErrorEnvelope]
161
+ """
162
+
163
+ kwargs = _get_kwargs(
164
+ x_api_key=x_api_key,
165
+ )
166
+
167
+ response = client.get_httpx_client().request(
168
+ **kwargs,
169
+ )
170
+
171
+ return _build_response(client=client, response=response)
172
+
173
+
174
+ def sync(
175
+ *,
176
+ client: AuthenticatedClient | Client,
177
+ x_api_key: str | Unset = UNSET,
178
+ ) -> Any | ErrorEnvelope | None:
179
+ """Live feed (WebSocket)
180
+
181
+ The live feed, over a WebSocket. Builder and Pro plans: Builder holds
182
+ up to 3 open connections and Pro up to 10, counted across all of an
183
+ account's keys. The live feed guide, https://docs.skewtape.com/feed,
184
+ has the whole protocol with examples; this is its summary.
185
+
186
+ Authentication: the X-API-Key header on the upgrade request, or the
187
+ first message {"op":"auth","key":"st_live_..."} within 10 seconds.
188
+
189
+ Topics: markets, resolutions, trades and matched_events on Builder
190
+ and Pro; event_spread on Pro only. A subscribe that names a topic
191
+ outside the plan is refused with error topic_not_in_plan and an
192
+ upgrade_url, and nothing of that subscribe applies: the previous
193
+ subscription stays, and on a first subscribe there is none. Send it
194
+ again without the topic. A topic the feed does not have is refused
195
+ with error invalid_subscription. A plan that loses a topic during a
196
+ connection is told the same way and keeps its other topics.
197
+
198
+ From the client:
199
+ {"op":"subscribe","topics":["markets","trades","event_spread"],
200
+ "venues":["kalshi"],"markets":[{"venue":"polymarket","market_id":"0x..."}],
201
+ "events":["st_evt_00412"],"resume_from":123} replaces the
202
+ subscription. topics is required; venues, markets and events are
203
+ optional filters, ORed. events, matched event ids, narrows
204
+ event_spread and matched_events, whose events belong to a matched
205
+ event rather than a venue, so the venue and market filters do not
206
+ narrow them; it needs one of those two topics, and leaves the other
207
+ topics to the venue and market filters, whole when there are none.
208
+ A market filter cuts a trades_batch down to its markets' fills.
209
+ {"op":"ping"} is answered {"op":"pong"} at once, even during a
210
+ replay.
211
+
212
+ matched_events carries the changes to what /v1/matched_events
213
+ returns, within five minutes: an event published (kind
214
+ matched_event_published), changed (matched_event_changed: any field
215
+ but updated_at, reviewer_notes included, so a Builder key can get a
216
+ change it cannot see) or retired (matched_event_retired: no longer
217
+ returned, whatever the reason; its payload is the event_id and the
218
+ status alone). A published or changed event's payload is the event as
219
+ /v1/matched_events returns it. The topic has no snapshot: read
220
+ /v1/matched_events once for the current set, then follow the changes.
221
+
222
+ From the server, each message with an op: hello (connection id, plan,
223
+ current seq, replay window and reconnect schedule), subscribed (what
224
+ was accepted, replay_to and masked_venues), snapshot (the latest
225
+ spread of each matched event the subscription names, sent right
226
+ after subscribed; it has no seq), event (seq, topic, kind, venue,
227
+ market_id, at, payload), resync (resume_from is older than the replay
228
+ window: read the REST API for what you missed), reconnect (at 55
229
+ minutes, with resume_from; the connection closes at 59) and error
230
+ (code, message, and upgrade_url on a plan refusal).
231
+
232
+ Trades: one event per fill, up to 400 a second per venue, the rest of
233
+ that second in one trades_batch. A fill can arrive twice: dedupe by
234
+ (venue, tx_hash, log_index). A trade_withdrawn names a streamed trade
235
+ (limitless, myriad, predictfun) whose block a chain reorganisation
236
+ replaced without it: drop the trade with that identity. If its transaction is mined again, it
237
+ arrives as a new trade under its new identity. event_spread: a matched event's
238
+ fee-adjusted spread, at most one event per matched event every five
239
+ seconds.
240
+
241
+ Resume: keep the last seq you saw and send it as resume_from. The feed
242
+ replays the stored events after it that match the subscription, then
243
+ goes on live with no gap and no duplicate. A gap notice (topic feed,
244
+ kind gap) means a venue stream reconnected: read the REST API for that
245
+ window.
246
+
247
+ Data policy: a subscription naming a venue or a market of a venue the
248
+ policy blocks for live data is refused (error venue_blocked). A masked
249
+ venue's events arrive with the withheld fields null and blocked true.
250
+ A matched event the feed serves arrives whole: a member of a blocked venue keeps its
251
+ ids, its side labels are null, its outcome_map is empty and blocked
252
+ is true.
253
+
254
+ Close codes: 4000 reconnect (connect again with resume_from), 4001
255
+ unauthorized (also when the key is revoked: keys are checked again
256
+ every few minutes), 4003 plan (no feed, or a plan change left no
257
+ topic of the subscription), 4008 connection limit, 4009 slow consumer (resume from the
258
+ seq in the close reason), 4029 too many failed key lookups, 4400 bad
259
+ message, 1001 server restarting, 1011 server error (connect again
260
+ with resume_from).
261
+
262
+ Args:
263
+ x_api_key (str | Unset):
264
+
265
+ Raises:
266
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
267
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
268
+
269
+ Returns:
270
+ Any | ErrorEnvelope
271
+ """
272
+
273
+ return sync_detailed(
274
+ client=client,
275
+ x_api_key=x_api_key,
276
+ ).parsed
277
+
278
+
279
+ async def asyncio_detailed(
280
+ *,
281
+ client: AuthenticatedClient | Client,
282
+ x_api_key: str | Unset = UNSET,
283
+ ) -> Response[Any | ErrorEnvelope]:
284
+ """Live feed (WebSocket)
285
+
286
+ The live feed, over a WebSocket. Builder and Pro plans: Builder holds
287
+ up to 3 open connections and Pro up to 10, counted across all of an
288
+ account's keys. The live feed guide, https://docs.skewtape.com/feed,
289
+ has the whole protocol with examples; this is its summary.
290
+
291
+ Authentication: the X-API-Key header on the upgrade request, or the
292
+ first message {"op":"auth","key":"st_live_..."} within 10 seconds.
293
+
294
+ Topics: markets, resolutions, trades and matched_events on Builder
295
+ and Pro; event_spread on Pro only. A subscribe that names a topic
296
+ outside the plan is refused with error topic_not_in_plan and an
297
+ upgrade_url, and nothing of that subscribe applies: the previous
298
+ subscription stays, and on a first subscribe there is none. Send it
299
+ again without the topic. A topic the feed does not have is refused
300
+ with error invalid_subscription. A plan that loses a topic during a
301
+ connection is told the same way and keeps its other topics.
302
+
303
+ From the client:
304
+ {"op":"subscribe","topics":["markets","trades","event_spread"],
305
+ "venues":["kalshi"],"markets":[{"venue":"polymarket","market_id":"0x..."}],
306
+ "events":["st_evt_00412"],"resume_from":123} replaces the
307
+ subscription. topics is required; venues, markets and events are
308
+ optional filters, ORed. events, matched event ids, narrows
309
+ event_spread and matched_events, whose events belong to a matched
310
+ event rather than a venue, so the venue and market filters do not
311
+ narrow them; it needs one of those two topics, and leaves the other
312
+ topics to the venue and market filters, whole when there are none.
313
+ A market filter cuts a trades_batch down to its markets' fills.
314
+ {"op":"ping"} is answered {"op":"pong"} at once, even during a
315
+ replay.
316
+
317
+ matched_events carries the changes to what /v1/matched_events
318
+ returns, within five minutes: an event published (kind
319
+ matched_event_published), changed (matched_event_changed: any field
320
+ but updated_at, reviewer_notes included, so a Builder key can get a
321
+ change it cannot see) or retired (matched_event_retired: no longer
322
+ returned, whatever the reason; its payload is the event_id and the
323
+ status alone). A published or changed event's payload is the event as
324
+ /v1/matched_events returns it. The topic has no snapshot: read
325
+ /v1/matched_events once for the current set, then follow the changes.
326
+
327
+ From the server, each message with an op: hello (connection id, plan,
328
+ current seq, replay window and reconnect schedule), subscribed (what
329
+ was accepted, replay_to and masked_venues), snapshot (the latest
330
+ spread of each matched event the subscription names, sent right
331
+ after subscribed; it has no seq), event (seq, topic, kind, venue,
332
+ market_id, at, payload), resync (resume_from is older than the replay
333
+ window: read the REST API for what you missed), reconnect (at 55
334
+ minutes, with resume_from; the connection closes at 59) and error
335
+ (code, message, and upgrade_url on a plan refusal).
336
+
337
+ Trades: one event per fill, up to 400 a second per venue, the rest of
338
+ that second in one trades_batch. A fill can arrive twice: dedupe by
339
+ (venue, tx_hash, log_index). A trade_withdrawn names a streamed trade
340
+ (limitless, myriad, predictfun) whose block a chain reorganisation
341
+ replaced without it: drop the trade with that identity. If its transaction is mined again, it
342
+ arrives as a new trade under its new identity. event_spread: a matched event's
343
+ fee-adjusted spread, at most one event per matched event every five
344
+ seconds.
345
+
346
+ Resume: keep the last seq you saw and send it as resume_from. The feed
347
+ replays the stored events after it that match the subscription, then
348
+ goes on live with no gap and no duplicate. A gap notice (topic feed,
349
+ kind gap) means a venue stream reconnected: read the REST API for that
350
+ window.
351
+
352
+ Data policy: a subscription naming a venue or a market of a venue the
353
+ policy blocks for live data is refused (error venue_blocked). A masked
354
+ venue's events arrive with the withheld fields null and blocked true.
355
+ A matched event the feed serves arrives whole: a member of a blocked venue keeps its
356
+ ids, its side labels are null, its outcome_map is empty and blocked
357
+ is true.
358
+
359
+ Close codes: 4000 reconnect (connect again with resume_from), 4001
360
+ unauthorized (also when the key is revoked: keys are checked again
361
+ every few minutes), 4003 plan (no feed, or a plan change left no
362
+ topic of the subscription), 4008 connection limit, 4009 slow consumer (resume from the
363
+ seq in the close reason), 4029 too many failed key lookups, 4400 bad
364
+ message, 1001 server restarting, 1011 server error (connect again
365
+ with resume_from).
366
+
367
+ Args:
368
+ x_api_key (str | Unset):
369
+
370
+ Raises:
371
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
372
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
373
+
374
+ Returns:
375
+ Response[Any | ErrorEnvelope]
376
+ """
377
+
378
+ kwargs = _get_kwargs(
379
+ x_api_key=x_api_key,
380
+ )
381
+
382
+ response = await client.get_async_httpx_client().request(**kwargs)
383
+
384
+ return _build_response(client=client, response=response)
385
+
386
+
387
+ async def asyncio(
388
+ *,
389
+ client: AuthenticatedClient | Client,
390
+ x_api_key: str | Unset = UNSET,
391
+ ) -> Any | ErrorEnvelope | None:
392
+ """Live feed (WebSocket)
393
+
394
+ The live feed, over a WebSocket. Builder and Pro plans: Builder holds
395
+ up to 3 open connections and Pro up to 10, counted across all of an
396
+ account's keys. The live feed guide, https://docs.skewtape.com/feed,
397
+ has the whole protocol with examples; this is its summary.
398
+
399
+ Authentication: the X-API-Key header on the upgrade request, or the
400
+ first message {"op":"auth","key":"st_live_..."} within 10 seconds.
401
+
402
+ Topics: markets, resolutions, trades and matched_events on Builder
403
+ and Pro; event_spread on Pro only. A subscribe that names a topic
404
+ outside the plan is refused with error topic_not_in_plan and an
405
+ upgrade_url, and nothing of that subscribe applies: the previous
406
+ subscription stays, and on a first subscribe there is none. Send it
407
+ again without the topic. A topic the feed does not have is refused
408
+ with error invalid_subscription. A plan that loses a topic during a
409
+ connection is told the same way and keeps its other topics.
410
+
411
+ From the client:
412
+ {"op":"subscribe","topics":["markets","trades","event_spread"],
413
+ "venues":["kalshi"],"markets":[{"venue":"polymarket","market_id":"0x..."}],
414
+ "events":["st_evt_00412"],"resume_from":123} replaces the
415
+ subscription. topics is required; venues, markets and events are
416
+ optional filters, ORed. events, matched event ids, narrows
417
+ event_spread and matched_events, whose events belong to a matched
418
+ event rather than a venue, so the venue and market filters do not
419
+ narrow them; it needs one of those two topics, and leaves the other
420
+ topics to the venue and market filters, whole when there are none.
421
+ A market filter cuts a trades_batch down to its markets' fills.
422
+ {"op":"ping"} is answered {"op":"pong"} at once, even during a
423
+ replay.
424
+
425
+ matched_events carries the changes to what /v1/matched_events
426
+ returns, within five minutes: an event published (kind
427
+ matched_event_published), changed (matched_event_changed: any field
428
+ but updated_at, reviewer_notes included, so a Builder key can get a
429
+ change it cannot see) or retired (matched_event_retired: no longer
430
+ returned, whatever the reason; its payload is the event_id and the
431
+ status alone). A published or changed event's payload is the event as
432
+ /v1/matched_events returns it. The topic has no snapshot: read
433
+ /v1/matched_events once for the current set, then follow the changes.
434
+
435
+ From the server, each message with an op: hello (connection id, plan,
436
+ current seq, replay window and reconnect schedule), subscribed (what
437
+ was accepted, replay_to and masked_venues), snapshot (the latest
438
+ spread of each matched event the subscription names, sent right
439
+ after subscribed; it has no seq), event (seq, topic, kind, venue,
440
+ market_id, at, payload), resync (resume_from is older than the replay
441
+ window: read the REST API for what you missed), reconnect (at 55
442
+ minutes, with resume_from; the connection closes at 59) and error
443
+ (code, message, and upgrade_url on a plan refusal).
444
+
445
+ Trades: one event per fill, up to 400 a second per venue, the rest of
446
+ that second in one trades_batch. A fill can arrive twice: dedupe by
447
+ (venue, tx_hash, log_index). A trade_withdrawn names a streamed trade
448
+ (limitless, myriad, predictfun) whose block a chain reorganisation
449
+ replaced without it: drop the trade with that identity. If its transaction is mined again, it
450
+ arrives as a new trade under its new identity. event_spread: a matched event's
451
+ fee-adjusted spread, at most one event per matched event every five
452
+ seconds.
453
+
454
+ Resume: keep the last seq you saw and send it as resume_from. The feed
455
+ replays the stored events after it that match the subscription, then
456
+ goes on live with no gap and no duplicate. A gap notice (topic feed,
457
+ kind gap) means a venue stream reconnected: read the REST API for that
458
+ window.
459
+
460
+ Data policy: a subscription naming a venue or a market of a venue the
461
+ policy blocks for live data is refused (error venue_blocked). A masked
462
+ venue's events arrive with the withheld fields null and blocked true.
463
+ A matched event the feed serves arrives whole: a member of a blocked venue keeps its
464
+ ids, its side labels are null, its outcome_map is empty and blocked
465
+ is true.
466
+
467
+ Close codes: 4000 reconnect (connect again with resume_from), 4001
468
+ unauthorized (also when the key is revoked: keys are checked again
469
+ every few minutes), 4003 plan (no feed, or a plan change left no
470
+ topic of the subscription), 4008 connection limit, 4009 slow consumer (resume from the
471
+ seq in the close reason), 4029 too many failed key lookups, 4400 bad
472
+ message, 1001 server restarting, 1011 server error (connect again
473
+ with resume_from).
474
+
475
+ Args:
476
+ x_api_key (str | Unset):
477
+
478
+ Raises:
479
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
480
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
481
+
482
+ Returns:
483
+ Any | ErrorEnvelope
484
+ """
485
+
486
+ return (
487
+ await asyncio_detailed(
488
+ client=client,
489
+ x_api_key=x_api_key,
490
+ )
491
+ ).parsed