oxarchive 0.3.9__py3-none-any.whl → 0.3.11__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.
oxarchive/__init__.py CHANGED
@@ -57,7 +57,7 @@ except ImportError:
57
57
  OxArchiveWs = None # type: ignore
58
58
  WsOptions = None # type: ignore
59
59
 
60
- __version__ = "0.3.9"
60
+ __version__ = "0.3.11"
61
61
 
62
62
  __all__ = [
63
63
  # Client
@@ -6,7 +6,7 @@ from datetime import datetime
6
6
  from typing import Optional
7
7
 
8
8
  from ..http import HttpClient
9
- from ..types import FundingRate, Timestamp
9
+ from ..types import CursorResponse, FundingRate, Timestamp
10
10
 
11
11
 
12
12
  class FundingResource:
@@ -46,32 +46,44 @@ class FundingResource:
46
46
  *,
47
47
  start: Timestamp,
48
48
  end: Timestamp,
49
+ cursor: Optional[Timestamp] = None,
49
50
  limit: Optional[int] = None,
50
- offset: Optional[int] = None,
51
- ) -> list[FundingRate]:
51
+ ) -> CursorResponse[list[FundingRate]]:
52
52
  """
53
- Get funding rate history for a coin.
53
+ Get funding rate history for a coin with cursor-based pagination.
54
54
 
55
55
  Args:
56
56
  coin: The coin symbol (e.g., 'BTC', 'ETH')
57
57
  start: Start timestamp (required)
58
58
  end: End timestamp (required)
59
- limit: Maximum number of results
60
- offset: Number of results to skip
59
+ cursor: Cursor from previous response's next_cursor (timestamp)
60
+ limit: Maximum number of results (default: 100, max: 1000)
61
61
 
62
62
  Returns:
63
- List of funding rate records
63
+ CursorResponse with funding rate records and next_cursor for pagination
64
+
65
+ Example:
66
+ >>> result = client.funding.history("BTC", start=start, end=end, limit=1000)
67
+ >>> rates = result.data
68
+ >>> while result.next_cursor:
69
+ ... result = client.funding.history(
70
+ ... "BTC", start=start, end=end, cursor=result.next_cursor, limit=1000
71
+ ... )
72
+ ... rates.extend(result.data)
64
73
  """
65
74
  data = self._http.get(
66
75
  f"/v1/funding/{coin.upper()}",
67
76
  params={
68
77
  "start": self._convert_timestamp(start),
69
78
  "end": self._convert_timestamp(end),
79
+ "cursor": self._convert_timestamp(cursor),
70
80
  "limit": limit,
71
- "offset": offset,
72
81
  },
73
82
  )
74
- return [FundingRate.model_validate(item) for item in data["data"]]
83
+ return CursorResponse(
84
+ data=[FundingRate.model_validate(item) for item in data["data"]],
85
+ next_cursor=data.get("meta", {}).get("next_cursor"),
86
+ )
75
87
 
76
88
  async def ahistory(
77
89
  self,
@@ -79,20 +91,23 @@ class FundingResource:
79
91
  *,
80
92
  start: Timestamp,
81
93
  end: Timestamp,
94
+ cursor: Optional[Timestamp] = None,
82
95
  limit: Optional[int] = None,
83
- offset: Optional[int] = None,
84
- ) -> list[FundingRate]:
96
+ ) -> CursorResponse[list[FundingRate]]:
85
97
  """Async version of history(). start and end are required."""
86
98
  data = await self._http.aget(
87
99
  f"/v1/funding/{coin.upper()}",
88
100
  params={
89
101
  "start": self._convert_timestamp(start),
90
102
  "end": self._convert_timestamp(end),
103
+ "cursor": self._convert_timestamp(cursor),
91
104
  "limit": limit,
92
- "offset": offset,
93
105
  },
94
106
  )
95
- return [FundingRate.model_validate(item) for item in data["data"]]
107
+ return CursorResponse(
108
+ data=[FundingRate.model_validate(item) for item in data["data"]],
109
+ next_cursor=data.get("meta", {}).get("next_cursor"),
110
+ )
96
111
 
97
112
  def current(self, coin: str) -> FundingRate:
98
113
  """
@@ -6,7 +6,7 @@ from datetime import datetime
6
6
  from typing import Optional
7
7
 
8
8
  from ..http import HttpClient
9
- from ..types import OpenInterest, Timestamp
9
+ from ..types import CursorResponse, OpenInterest, Timestamp
10
10
 
11
11
 
12
12
  class OpenInterestResource:
@@ -46,32 +46,44 @@ class OpenInterestResource:
46
46
  *,
47
47
  start: Timestamp,
48
48
  end: Timestamp,
49
+ cursor: Optional[Timestamp] = None,
49
50
  limit: Optional[int] = None,
50
- offset: Optional[int] = None,
51
- ) -> list[OpenInterest]:
51
+ ) -> CursorResponse[list[OpenInterest]]:
52
52
  """
53
- Get open interest history for a coin.
53
+ Get open interest history for a coin with cursor-based pagination.
54
54
 
55
55
  Args:
56
56
  coin: The coin symbol (e.g., 'BTC', 'ETH')
57
57
  start: Start timestamp (required)
58
58
  end: End timestamp (required)
59
- limit: Maximum number of results
60
- offset: Number of results to skip
59
+ cursor: Cursor from previous response's next_cursor (timestamp)
60
+ limit: Maximum number of results (default: 100, max: 1000)
61
61
 
62
62
  Returns:
63
- List of open interest records
63
+ CursorResponse with open interest records and next_cursor for pagination
64
+
65
+ Example:
66
+ >>> result = client.open_interest.history("BTC", start=start, end=end, limit=1000)
67
+ >>> records = result.data
68
+ >>> while result.next_cursor:
69
+ ... result = client.open_interest.history(
70
+ ... "BTC", start=start, end=end, cursor=result.next_cursor, limit=1000
71
+ ... )
72
+ ... records.extend(result.data)
64
73
  """
65
74
  data = self._http.get(
66
75
  f"/v1/openinterest/{coin.upper()}",
67
76
  params={
68
77
  "start": self._convert_timestamp(start),
69
78
  "end": self._convert_timestamp(end),
79
+ "cursor": self._convert_timestamp(cursor),
70
80
  "limit": limit,
71
- "offset": offset,
72
81
  },
73
82
  )
74
- return [OpenInterest.model_validate(item) for item in data["data"]]
83
+ return CursorResponse(
84
+ data=[OpenInterest.model_validate(item) for item in data["data"]],
85
+ next_cursor=data.get("meta", {}).get("next_cursor"),
86
+ )
75
87
 
76
88
  async def ahistory(
77
89
  self,
@@ -79,20 +91,23 @@ class OpenInterestResource:
79
91
  *,
80
92
  start: Timestamp,
81
93
  end: Timestamp,
94
+ cursor: Optional[Timestamp] = None,
82
95
  limit: Optional[int] = None,
83
- offset: Optional[int] = None,
84
- ) -> list[OpenInterest]:
96
+ ) -> CursorResponse[list[OpenInterest]]:
85
97
  """Async version of history(). start and end are required."""
86
98
  data = await self._http.aget(
87
99
  f"/v1/openinterest/{coin.upper()}",
88
100
  params={
89
101
  "start": self._convert_timestamp(start),
90
102
  "end": self._convert_timestamp(end),
103
+ "cursor": self._convert_timestamp(cursor),
91
104
  "limit": limit,
92
- "offset": offset,
93
105
  },
94
106
  )
95
- return [OpenInterest.model_validate(item) for item in data["data"]]
107
+ return CursorResponse(
108
+ data=[OpenInterest.model_validate(item) for item in data["data"]],
109
+ next_cursor=data.get("meta", {}).get("next_cursor"),
110
+ )
96
111
 
97
112
  def current(self, coin: str) -> OpenInterest:
98
113
  """
@@ -6,7 +6,7 @@ from datetime import datetime
6
6
  from typing import Optional, Union
7
7
 
8
8
  from ..http import HttpClient
9
- from ..types import OrderBook, Timestamp
9
+ from ..types import CursorResponse, OrderBook, Timestamp
10
10
 
11
11
 
12
12
  class OrderBookResource:
@@ -94,35 +94,47 @@ class OrderBookResource:
94
94
  *,
95
95
  start: Timestamp,
96
96
  end: Timestamp,
97
+ cursor: Optional[Timestamp] = None,
97
98
  limit: Optional[int] = None,
98
- offset: Optional[int] = None,
99
99
  depth: Optional[int] = None,
100
- ) -> list[OrderBook]:
100
+ ) -> CursorResponse[list[OrderBook]]:
101
101
  """
102
- Get historical order book snapshots.
102
+ Get historical order book snapshots with cursor-based pagination.
103
103
 
104
104
  Args:
105
105
  coin: The coin symbol (e.g., 'BTC', 'ETH')
106
106
  start: Start timestamp (required)
107
107
  end: End timestamp (required)
108
- limit: Maximum number of results
109
- offset: Number of results to skip
108
+ cursor: Cursor from previous response's next_cursor (timestamp)
109
+ limit: Maximum number of results (default: 100, max: 1000)
110
110
  depth: Number of price levels per side
111
111
 
112
112
  Returns:
113
- List of order book snapshots
113
+ CursorResponse with order book snapshots and next_cursor for pagination
114
+
115
+ Example:
116
+ >>> result = client.orderbook.history("BTC", start=start, end=end, limit=1000)
117
+ >>> snapshots = result.data
118
+ >>> while result.next_cursor:
119
+ ... result = client.orderbook.history(
120
+ ... "BTC", start=start, end=end, cursor=result.next_cursor, limit=1000
121
+ ... )
122
+ ... snapshots.extend(result.data)
114
123
  """
115
124
  data = self._http.get(
116
125
  f"/v1/orderbook/{coin.upper()}/history",
117
126
  params={
118
127
  "start": self._convert_timestamp(start),
119
128
  "end": self._convert_timestamp(end),
129
+ "cursor": self._convert_timestamp(cursor),
120
130
  "limit": limit,
121
- "offset": offset,
122
131
  "depth": depth,
123
132
  },
124
133
  )
125
- return [OrderBook.model_validate(item) for item in data["data"]]
134
+ return CursorResponse(
135
+ data=[OrderBook.model_validate(item) for item in data["data"]],
136
+ next_cursor=data.get("meta", {}).get("next_cursor"),
137
+ )
126
138
 
127
139
  async def ahistory(
128
140
  self,
@@ -130,19 +142,22 @@ class OrderBookResource:
130
142
  *,
131
143
  start: Timestamp,
132
144
  end: Timestamp,
145
+ cursor: Optional[Timestamp] = None,
133
146
  limit: Optional[int] = None,
134
- offset: Optional[int] = None,
135
147
  depth: Optional[int] = None,
136
- ) -> list[OrderBook]:
148
+ ) -> CursorResponse[list[OrderBook]]:
137
149
  """Async version of history(). start and end are required."""
138
150
  data = await self._http.aget(
139
151
  f"/v1/orderbook/{coin.upper()}/history",
140
152
  params={
141
153
  "start": self._convert_timestamp(start),
142
154
  "end": self._convert_timestamp(end),
155
+ "cursor": self._convert_timestamp(cursor),
143
156
  "limit": limit,
144
- "offset": offset,
145
157
  "depth": depth,
146
158
  },
147
159
  )
148
- return [OrderBook.model_validate(item) for item in data["data"]]
160
+ return CursorResponse(
161
+ data=[OrderBook.model_validate(item) for item in data["data"]],
162
+ next_cursor=data.get("meta", {}).get("next_cursor"),
163
+ )
@@ -2,20 +2,11 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import warnings
6
- from dataclasses import dataclass
7
5
  from datetime import datetime
8
6
  from typing import Literal, Optional
9
7
 
10
8
  from ..http import HttpClient
11
- from ..types import Trade, Timestamp
12
-
13
-
14
- @dataclass
15
- class CursorResponse:
16
- """Response with cursor for pagination."""
17
- data: list[Trade]
18
- next_cursor: Optional[str] = None
9
+ from ..types import CursorResponse, Trade, Timestamp
19
10
 
20
11
 
21
12
  class TradesResource:
@@ -64,7 +55,7 @@ class TradesResource:
64
55
  cursor: Optional[Timestamp] = None,
65
56
  limit: Optional[int] = None,
66
57
  side: Optional[Literal["buy", "sell"]] = None,
67
- ) -> CursorResponse:
58
+ ) -> CursorResponse[list[Trade]]:
68
59
  """
69
60
  Get trade history for a coin using cursor-based pagination.
70
61
 
@@ -118,7 +109,7 @@ class TradesResource:
118
109
  cursor: Optional[Timestamp] = None,
119
110
  limit: Optional[int] = None,
120
111
  side: Optional[Literal["buy", "sell"]] = None,
121
- ) -> CursorResponse:
112
+ ) -> CursorResponse[list[Trade]]:
122
113
  """
123
114
  Async version of list().
124
115
 
@@ -139,83 +130,6 @@ class TradesResource:
139
130
  next_cursor=data.get("meta", {}).get("next_cursor"),
140
131
  )
141
132
 
142
- def list_with_offset(
143
- self,
144
- coin: str,
145
- *,
146
- start: Timestamp,
147
- end: Timestamp,
148
- limit: Optional[int] = None,
149
- offset: Optional[int] = None,
150
- side: Optional[Literal["buy", "sell"]] = None,
151
- ) -> list[Trade]:
152
- """
153
- Get trade history using offset-based pagination.
154
-
155
- .. deprecated::
156
- Use list() with cursor-based pagination instead for better performance.
157
-
158
- Args:
159
- coin: The coin symbol (e.g., 'BTC', 'ETH')
160
- start: Start timestamp (required)
161
- end: End timestamp (required)
162
- limit: Maximum number of results
163
- offset: Number of results to skip
164
- side: Filter by trade side
165
-
166
- Returns:
167
- List of trades
168
- """
169
- warnings.warn(
170
- "list_with_offset() is deprecated. Use list() with cursor-based pagination instead.",
171
- DeprecationWarning,
172
- stacklevel=2,
173
- )
174
- data = self._http.get(
175
- f"/v1/trades/{coin.upper()}",
176
- params={
177
- "start": self._convert_timestamp(start),
178
- "end": self._convert_timestamp(end),
179
- "limit": limit,
180
- "offset": offset,
181
- "side": side,
182
- },
183
- )
184
- return [Trade.model_validate(item) for item in data["data"]]
185
-
186
- async def alist_with_offset(
187
- self,
188
- coin: str,
189
- *,
190
- start: Timestamp,
191
- end: Timestamp,
192
- limit: Optional[int] = None,
193
- offset: Optional[int] = None,
194
- side: Optional[Literal["buy", "sell"]] = None,
195
- ) -> list[Trade]:
196
- """
197
- Async version of list_with_offset().
198
-
199
- .. deprecated::
200
- Use alist() with cursor-based pagination instead.
201
- """
202
- warnings.warn(
203
- "alist_with_offset() is deprecated. Use alist() with cursor-based pagination instead.",
204
- DeprecationWarning,
205
- stacklevel=2,
206
- )
207
- data = await self._http.aget(
208
- f"/v1/trades/{coin.upper()}",
209
- params={
210
- "start": self._convert_timestamp(start),
211
- "end": self._convert_timestamp(end),
212
- "limit": limit,
213
- "offset": offset,
214
- "side": side,
215
- },
216
- )
217
- return [Trade.model_validate(item) for item in data["data"]]
218
-
219
133
  def recent(self, coin: str, limit: Optional[int] = None) -> list[Trade]:
220
134
  """
221
135
  Get most recent trades for a coin.
@@ -240,60 +154,3 @@ class TradesResource:
240
154
  params={"limit": limit},
241
155
  )
242
156
  return [Trade.model_validate(item) for item in data["data"]]
243
-
244
- def list_cursor(
245
- self,
246
- coin: str,
247
- *,
248
- start: Timestamp,
249
- end: Timestamp,
250
- cursor: Optional[Timestamp] = None,
251
- limit: Optional[int] = None,
252
- side: Optional[Literal["buy", "sell"]] = None,
253
- ) -> CursorResponse:
254
- """
255
- Get trade history using cursor-based pagination.
256
-
257
- .. deprecated::
258
- Use list() instead - it now uses cursor-based pagination by default.
259
-
260
- Args:
261
- coin: The coin symbol (e.g., 'BTC', 'ETH')
262
- start: Start timestamp (required)
263
- end: End timestamp (required)
264
- cursor: Cursor from previous response's next_cursor (timestamp)
265
- limit: Maximum number of results
266
- side: Filter by trade side
267
-
268
- Returns:
269
- CursorResponse with trades and next_cursor for pagination
270
- """
271
- warnings.warn(
272
- "list_cursor() is deprecated. Use list() instead - it now uses cursor-based pagination by default.",
273
- DeprecationWarning,
274
- stacklevel=2,
275
- )
276
- return self.list(coin, start=start, end=end, cursor=cursor, limit=limit, side=side)
277
-
278
- async def alist_cursor(
279
- self,
280
- coin: str,
281
- *,
282
- start: Timestamp,
283
- end: Timestamp,
284
- cursor: Optional[Timestamp] = None,
285
- limit: Optional[int] = None,
286
- side: Optional[Literal["buy", "sell"]] = None,
287
- ) -> CursorResponse:
288
- """
289
- Async version of list_cursor().
290
-
291
- .. deprecated::
292
- Use alist() instead - it now uses cursor-based pagination by default.
293
- """
294
- warnings.warn(
295
- "alist_cursor() is deprecated. Use alist() instead - it now uses cursor-based pagination by default.",
296
- DeprecationWarning,
297
- stacklevel=2,
298
- )
299
- return await self.alist(coin, start=start, end=end, cursor=cursor, limit=limit, side=side)
oxarchive/types.py CHANGED
@@ -411,6 +411,21 @@ class OxArchiveError(Exception):
411
411
  return f"[{self.code}] {self.message}"
412
412
 
413
413
 
414
+ # =============================================================================
415
+ # Pagination Types
416
+ # =============================================================================
417
+
418
+
419
+ class CursorResponse(BaseModel, Generic[T]):
420
+ """Response with cursor for pagination."""
421
+
422
+ data: T
423
+ """The paginated data."""
424
+
425
+ next_cursor: Optional[str] = None
426
+ """Cursor for the next page (use as cursor parameter)."""
427
+
428
+
414
429
  # Type alias for timestamp parameters
415
430
  Timestamp = Union[int, str, datetime]
416
431
  """Timestamp can be Unix ms (int), ISO string, or datetime object."""
oxarchive/websocket.py CHANGED
@@ -32,6 +32,7 @@ from typing import Any, Callable, Optional, Set, Union
32
32
 
33
33
  try:
34
34
  from websockets.asyncio.client import connect as ws_connect, ClientConnection
35
+ from websockets.exceptions import ConnectionClosed
35
36
  from websockets.protocol import State as WsState
36
37
  except ImportError:
37
38
  raise ImportError(
@@ -650,7 +651,7 @@ class OxArchiveWs:
650
651
  try:
651
652
  message = await self._ws.recv()
652
653
  self._handle_message(message)
653
- except websockets.ConnectionClosed as e:
654
+ except ConnectionClosed as e:
654
655
  logger.info(f"Connection closed: {e.code} {e.reason}")
655
656
  if self._on_close:
656
657
  self._on_close(e.code, e.reason)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oxarchive
3
- Version: 0.3.9
3
+ Version: 0.3.11
4
4
  Summary: Official Python SDK for 0xarchive - Hyperliquid Historical Data API
5
5
  Project-URL: Homepage, https://0xarchive.io
6
6
  Project-URL: Documentation, https://0xarchive.io/docs/sdks
@@ -0,0 +1,14 @@
1
+ oxarchive/__init__.py,sha256=Gjh2Ygt74OHNVSp-NLYumXBM8WGR62QJf95oBDBlFJ4,2182
2
+ oxarchive/client.py,sha256=3P0fvOcyM5BWppkVV4054NduDHKvRg-cWeluoGymmRk,3163
3
+ oxarchive/http.py,sha256=SY_o9Ag8ADo1HI3i3uAKW1xwkYjPE75gRAjnMsddAGs,4211
4
+ oxarchive/types.py,sha256=RUnni6RMGHxK3-tTajcDVtT3Rk_sJqlOJLiG5SMc3Do,11278
5
+ oxarchive/websocket.py,sha256=MyffJxaabDBonECcJg9vF4hAQ_4thjP4DaS7MaR4uno,28381
6
+ oxarchive/resources/__init__.py,sha256=WQ4GYQ8p3L0D2Isk4IV4h1DRpvyZlt6tOF1t_CJr6ls,385
7
+ oxarchive/resources/funding.py,sha256=g-c_zeCxFoPKEYymeDVswsIvfZIAKz4wd_2pTYpe8zQ,4368
8
+ oxarchive/resources/instruments.py,sha256=flD1sH6x3P3CTqV1ZwkfwbranVacmhsHn5Dhr7lGQhM,1606
9
+ oxarchive/resources/openinterest.py,sha256=Il9OzvnYM0-UCuspPUGqXy6dHMfxBvbLojPDKfS4kzU,4436
10
+ oxarchive/resources/orderbook.py,sha256=2aq9v2gK5ZJ2C_i6vY2plvtMdI0n50Gx8GsJLUIAEJA,5437
11
+ oxarchive/resources/trades.py,sha256=6Gm0ryUk1XBg2Tv7g69D5QQFuN6ZKLCAD1-TJ_J0jjU,5386
12
+ oxarchive-0.3.11.dist-info/METADATA,sha256=N_uQ-prJ3oKnXcpyjhumUvlmOAAIQP0VI5da4n4jMOA,11071
13
+ oxarchive-0.3.11.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
14
+ oxarchive-0.3.11.dist-info/RECORD,,
@@ -1,14 +0,0 @@
1
- oxarchive/__init__.py,sha256=c1JP0iZrqbnOrcTSfbMHDoojzHoDmDobFv7Mvv7qk5A,2181
2
- oxarchive/client.py,sha256=3P0fvOcyM5BWppkVV4054NduDHKvRg-cWeluoGymmRk,3163
3
- oxarchive/http.py,sha256=SY_o9Ag8ADo1HI3i3uAKW1xwkYjPE75gRAjnMsddAGs,4211
4
- oxarchive/types.py,sha256=lM3WrATnHcxGs0fLq54Lka-FTvXqiwWe2He2Qn9SrSA,10844
5
- oxarchive/websocket.py,sha256=0w7OebFUo_4c3AnjG-9vfX9IBHKyTbvgAWUXhxFO53I,28336
6
- oxarchive/resources/__init__.py,sha256=WQ4GYQ8p3L0D2Isk4IV4h1DRpvyZlt6tOF1t_CJr6ls,385
7
- oxarchive/resources/funding.py,sha256=TXkZxodVQTVcVbzNG6SpMQAzf8AkLm2NYZJxnP4MNXw,3500
8
- oxarchive/resources/instruments.py,sha256=flD1sH6x3P3CTqV1ZwkfwbranVacmhsHn5Dhr7lGQhM,1606
9
- oxarchive/resources/openinterest.py,sha256=h13yLA72LpfryUf8IqF6W7uE4ObYY2Qbc-auv4LtPqc,3552
10
- oxarchive/resources/orderbook.py,sha256=o_DTdpzKrZvHL9YXm8cGGUugPM8uUa6r9O_72r1ByV0,4557
11
- oxarchive/resources/trades.py,sha256=XCi2rXA2hxaTt0KNlWw8f7W0hzAvNWyT7DaivMz_rHw,10012
12
- oxarchive-0.3.9.dist-info/METADATA,sha256=O7olK5nYQ8HeHaxQwgvRUtayOudvTcgAYnhv1eTv-ro,11070
13
- oxarchive-0.3.9.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
14
- oxarchive-0.3.9.dist-info/RECORD,,