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.
- skewtape/__init__.py +122 -0
- skewtape/_generated/__init__.py +9 -0
- skewtape/_generated/api/__init__.py +2 -0
- skewtape/_generated/api/data/__init__.py +2 -0
- skewtape/_generated/api/data/connect_feed.py +491 -0
- skewtape/_generated/api/data/get_market.py +217 -0
- skewtape/_generated/api/data/get_matched_event.py +199 -0
- skewtape/_generated/api/data/get_pair.py +203 -0
- skewtape/_generated/api/data/get_quote_context.py +227 -0
- skewtape/_generated/api/data/list_bundles.py +215 -0
- skewtape/_generated/api/data/list_candles.py +366 -0
- skewtape/_generated/api/data/list_fees.py +229 -0
- skewtape/_generated/api/data/list_fills.py +353 -0
- skewtape/_generated/api/data/list_markets.py +275 -0
- skewtape/_generated/api/data/list_matched_events.py +287 -0
- skewtape/_generated/api/data/list_pairs.py +279 -0
- skewtape/_generated/api/data/list_resolutions.py +267 -0
- skewtape/_generated/api/data/list_venues.py +175 -0
- skewtape/_generated/api/data/post_download_url.py +276 -0
- skewtape/_generated/client.py +281 -0
- skewtape/_generated/errors.py +17 -0
- skewtape/_generated/models/__init__.py +124 -0
- skewtape/_generated/models/bundle.py +100 -0
- skewtape/_generated/models/bundle_file.py +78 -0
- skewtape/_generated/models/bundle_list.py +95 -0
- skewtape/_generated/models/candle.py +171 -0
- skewtape/_generated/models/candle_list.py +91 -0
- skewtape/_generated/models/candle_res.py +11 -0
- skewtape/_generated/models/download_url.py +74 -0
- skewtape/_generated/models/download_url_request.py +62 -0
- skewtape/_generated/models/error_body.py +202 -0
- skewtape/_generated/models/error_code.py +18 -0
- skewtape/_generated/models/error_envelope.py +68 -0
- skewtape/_generated/models/fee.py +196 -0
- skewtape/_generated/models/fee_formula.py +12 -0
- skewtape/_generated/models/fee_list.py +91 -0
- skewtape/_generated/models/fee_maker_rebate_params.py +53 -0
- skewtape/_generated/models/fee_rounding.py +12 -0
- skewtape/_generated/models/fee_taker_params.py +53 -0
- skewtape/_generated/models/fill.py +279 -0
- skewtape/_generated/models/fill_leg.py +10 -0
- skewtape/_generated/models/fill_list.py +91 -0
- skewtape/_generated/models/fill_match_model.py +10 -0
- skewtape/_generated/models/fill_side.py +10 -0
- skewtape/_generated/models/list_candles_res.py +11 -0
- skewtape/_generated/models/list_fills_leg.py +11 -0
- skewtape/_generated/models/list_matched_events_status.py +11 -0
- skewtape/_generated/models/list_pairs_status.py +11 -0
- skewtape/_generated/models/locked_bundle.py +81 -0
- skewtape/_generated/models/market.py +449 -0
- skewtape/_generated/models/market_list.py +91 -0
- skewtape/_generated/models/market_source_kind.py +11 -0
- skewtape/_generated/models/market_status.py +12 -0
- skewtape/_generated/models/matched_event.py +263 -0
- skewtape/_generated/models/matched_event_list.py +91 -0
- skewtape/_generated/models/matched_event_member.py +207 -0
- skewtape/_generated/models/matched_event_member_outcome_map.py +53 -0
- skewtape/_generated/models/matched_event_member_status.py +10 -0
- skewtape/_generated/models/matched_event_status.py +11 -0
- skewtape/_generated/models/outcome.py +70 -0
- skewtape/_generated/models/pair.py +183 -0
- skewtape/_generated/models/pair_list.py +91 -0
- skewtape/_generated/models/pair_member.py +112 -0
- skewtape/_generated/models/pair_member_outcome_map.py +53 -0
- skewtape/_generated/models/pair_status.py +11 -0
- skewtape/_generated/models/plan_id.py +12 -0
- skewtape/_generated/models/quote_context.py +124 -0
- skewtape/_generated/models/quote_context_member.py +253 -0
- skewtape/_generated/models/quote_context_member_fee_per.py +10 -0
- skewtape/_generated/models/quote_context_member_fee_status.py +12 -0
- skewtape/_generated/models/quote_context_member_outcome_map.py +54 -0
- skewtape/_generated/models/resolution.py +222 -0
- skewtape/_generated/models/resolution_event_type.py +15 -0
- skewtape/_generated/models/resolution_list.py +91 -0
- skewtape/_generated/models/resolution_source_kind.py +11 -0
- skewtape/_generated/models/venue.py +122 -0
- skewtape/_generated/models/venue_coverage.py +129 -0
- skewtape/_generated/models/venue_kind_rights.py +114 -0
- skewtape/_generated/models/venue_kind_rights_kind.py +11 -0
- skewtape/_generated/models/venue_kind_rights_rights.py +11 -0
- skewtape/_generated/models/venue_list.py +86 -0
- skewtape/_generated/types.py +55 -0
- skewtape/_keccak.py +66 -0
- skewtape/_msgpack.py +97 -0
- skewtape/backtest.py +697 -0
- skewtape/client.py +614 -0
- skewtape/download.py +159 -0
- skewtape/errors.py +153 -0
- skewtape/fees.py +248 -0
- skewtape/geoblock.py +80 -0
- skewtape/kalshi.py +574 -0
- skewtape/paginated.py +58 -0
- skewtape/quote.py +335 -0
- skewtape/quote_math.py +561 -0
- skewtape/route.py +520 -0
- skewtape/route_hyperliquid.py +417 -0
- skewtape/version.py +5 -0
- skewtape-0.1.3.dist-info/METADATA +189 -0
- skewtape-0.1.3.dist-info/RECORD +101 -0
- skewtape-0.1.3.dist-info/WHEEL +4 -0
- 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,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
|