oxarchive 0.1.1__tar.gz → 0.3.1__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oxarchive
3
- Version: 0.1.1
3
+ Version: 0.3.1
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
@@ -140,20 +140,6 @@ trades = client.trades.list(
140
140
  )
141
141
  ```
142
142
 
143
- ### Candles (OHLCV)
144
-
145
- ```python
146
- # Get hourly candles
147
- candles = client.candles.list(
148
- "BTC",
149
- interval="1h",
150
- start="2024-01-01",
151
- end="2024-01-02"
152
- )
153
-
154
- # Available intervals: '1m', '5m', '15m', '1h', '4h', '1d'
155
- ```
156
-
157
143
  ### Instruments
158
144
 
159
145
  ```python
@@ -272,8 +258,8 @@ async def main():
272
258
  print(f"{ts}: {data['mid_price']}")
273
259
  )
274
260
 
275
- ws.on_replay_start(lambda ch, coin, total, speed:
276
- print(f"Starting replay of {total} records at {speed}x")
261
+ ws.on_replay_start(lambda ch, coin, start, end, speed:
262
+ print(f"Starting replay from {start} to {end} at {speed}x")
277
263
  )
278
264
 
279
265
  ws.on_replay_complete(lambda ch, coin, sent:
@@ -316,8 +302,8 @@ async def main():
316
302
  all_data.extend([r.data for r in records])
317
303
  )
318
304
 
319
- ws.on_stream_progress(lambda sent, total, pct:
320
- print(f"Progress: {pct:.1f}%")
305
+ ws.on_stream_progress(lambda snapshots_sent:
306
+ print(f"Sent: {snapshots_sent} snapshots")
321
307
  )
322
308
 
323
309
  ws.on_stream_complete(lambda ch, coin, sent:
@@ -104,20 +104,6 @@ trades = client.trades.list(
104
104
  )
105
105
  ```
106
106
 
107
- ### Candles (OHLCV)
108
-
109
- ```python
110
- # Get hourly candles
111
- candles = client.candles.list(
112
- "BTC",
113
- interval="1h",
114
- start="2024-01-01",
115
- end="2024-01-02"
116
- )
117
-
118
- # Available intervals: '1m', '5m', '15m', '1h', '4h', '1d'
119
- ```
120
-
121
107
  ### Instruments
122
108
 
123
109
  ```python
@@ -236,8 +222,8 @@ async def main():
236
222
  print(f"{ts}: {data['mid_price']}")
237
223
  )
238
224
 
239
- ws.on_replay_start(lambda ch, coin, total, speed:
240
- print(f"Starting replay of {total} records at {speed}x")
225
+ ws.on_replay_start(lambda ch, coin, start, end, speed:
226
+ print(f"Starting replay from {start} to {end} at {speed}x")
241
227
  )
242
228
 
243
229
  ws.on_replay_complete(lambda ch, coin, sent:
@@ -280,8 +266,8 @@ async def main():
280
266
  all_data.extend([r.data for r in records])
281
267
  )
282
268
 
283
- ws.on_stream_progress(lambda sent, total, pct:
284
- print(f"Progress: {pct:.1f}%")
269
+ ws.on_stream_progress(lambda snapshots_sent:
270
+ print(f"Sent: {snapshots_sent} snapshots")
285
271
  )
286
272
 
287
273
  ws.on_stream_complete(lambda ch, coin, sent:
@@ -20,8 +20,6 @@ from .client import Client
20
20
  from .types import (
21
21
  OrderBook,
22
22
  Trade,
23
- Candle,
24
- CandleInterval,
25
23
  Instrument,
26
24
  FundingRate,
27
25
  OpenInterest,
@@ -70,8 +68,6 @@ __all__ = [
70
68
  # Types
71
69
  "OrderBook",
72
70
  "Trade",
73
- "Candle",
74
- "CandleInterval",
75
71
  "Instrument",
76
72
  "FundingRate",
77
73
  "OpenInterest",
@@ -8,7 +8,6 @@ from .http import HttpClient
8
8
  from .resources import (
9
9
  OrderBookResource,
10
10
  TradesResource,
11
- CandlesResource,
12
11
  InstrumentsResource,
13
12
  FundingResource,
14
13
  OpenInterestResource,
@@ -81,9 +80,6 @@ class Client:
81
80
  self.trades = TradesResource(self._http)
82
81
  """Trade/fill history"""
83
82
 
84
- self.candles = CandlesResource(self._http)
85
- """OHLCV candles"""
86
-
87
83
  self.instruments = InstrumentsResource(self._http)
88
84
  """Trading instruments metadata"""
89
85
 
@@ -2,7 +2,6 @@
2
2
 
3
3
  from .orderbook import OrderBookResource
4
4
  from .trades import TradesResource
5
- from .candles import CandlesResource
6
5
  from .instruments import InstrumentsResource
7
6
  from .funding import FundingResource
8
7
  from .openinterest import OpenInterestResource
@@ -10,7 +9,6 @@ from .openinterest import OpenInterestResource
10
9
  __all__ = [
11
10
  "OrderBookResource",
12
11
  "TradesResource",
13
- "CandlesResource",
14
12
  "InstrumentsResource",
15
13
  "FundingResource",
16
14
  "OpenInterestResource",
@@ -44,8 +44,8 @@ class FundingResource:
44
44
  self,
45
45
  coin: str,
46
46
  *,
47
- start: Optional[Timestamp] = None,
48
- end: Optional[Timestamp] = None,
47
+ start: Timestamp,
48
+ end: Timestamp,
49
49
  limit: Optional[int] = None,
50
50
  offset: Optional[int] = None,
51
51
  ) -> list[FundingRate]:
@@ -54,8 +54,8 @@ class FundingResource:
54
54
 
55
55
  Args:
56
56
  coin: The coin symbol (e.g., 'BTC', 'ETH')
57
- start: Start timestamp
58
- end: End timestamp
57
+ start: Start timestamp (required)
58
+ end: End timestamp (required)
59
59
  limit: Maximum number of results
60
60
  offset: Number of results to skip
61
61
 
@@ -77,12 +77,12 @@ class FundingResource:
77
77
  self,
78
78
  coin: str,
79
79
  *,
80
- start: Optional[Timestamp] = None,
81
- end: Optional[Timestamp] = None,
80
+ start: Timestamp,
81
+ end: Timestamp,
82
82
  limit: Optional[int] = None,
83
83
  offset: Optional[int] = None,
84
84
  ) -> list[FundingRate]:
85
- """Async version of history()."""
85
+ """Async version of history(). start and end are required."""
86
86
  data = await self._http.aget(
87
87
  f"/v1/funding/{coin.upper()}",
88
88
  params={
@@ -44,8 +44,8 @@ class OpenInterestResource:
44
44
  self,
45
45
  coin: str,
46
46
  *,
47
- start: Optional[Timestamp] = None,
48
- end: Optional[Timestamp] = None,
47
+ start: Timestamp,
48
+ end: Timestamp,
49
49
  limit: Optional[int] = None,
50
50
  offset: Optional[int] = None,
51
51
  ) -> list[OpenInterest]:
@@ -54,8 +54,8 @@ class OpenInterestResource:
54
54
 
55
55
  Args:
56
56
  coin: The coin symbol (e.g., 'BTC', 'ETH')
57
- start: Start timestamp
58
- end: End timestamp
57
+ start: Start timestamp (required)
58
+ end: End timestamp (required)
59
59
  limit: Maximum number of results
60
60
  offset: Number of results to skip
61
61
 
@@ -77,12 +77,12 @@ class OpenInterestResource:
77
77
  self,
78
78
  coin: str,
79
79
  *,
80
- start: Optional[Timestamp] = None,
81
- end: Optional[Timestamp] = None,
80
+ start: Timestamp,
81
+ end: Timestamp,
82
82
  limit: Optional[int] = None,
83
83
  offset: Optional[int] = None,
84
84
  ) -> list[OpenInterest]:
85
- """Async version of history()."""
85
+ """Async version of history(). start and end are required."""
86
86
  data = await self._http.aget(
87
87
  f"/v1/openinterest/{coin.upper()}",
88
88
  params={
@@ -92,8 +92,8 @@ class OrderBookResource:
92
92
  self,
93
93
  coin: str,
94
94
  *,
95
- start: Optional[Timestamp] = None,
96
- end: Optional[Timestamp] = None,
95
+ start: Timestamp,
96
+ end: Timestamp,
97
97
  limit: Optional[int] = None,
98
98
  offset: Optional[int] = None,
99
99
  depth: Optional[int] = None,
@@ -103,8 +103,8 @@ class OrderBookResource:
103
103
 
104
104
  Args:
105
105
  coin: The coin symbol (e.g., 'BTC', 'ETH')
106
- start: Start timestamp
107
- end: End timestamp
106
+ start: Start timestamp (required)
107
+ end: End timestamp (required)
108
108
  limit: Maximum number of results
109
109
  offset: Number of results to skip
110
110
  depth: Number of price levels per side
@@ -128,13 +128,13 @@ class OrderBookResource:
128
128
  self,
129
129
  coin: str,
130
130
  *,
131
- start: Optional[Timestamp] = None,
132
- end: Optional[Timestamp] = None,
131
+ start: Timestamp,
132
+ end: Timestamp,
133
133
  limit: Optional[int] = None,
134
134
  offset: Optional[int] = None,
135
135
  depth: Optional[int] = None,
136
136
  ) -> list[OrderBook]:
137
- """Async version of history()."""
137
+ """Async version of history(). start and end are required."""
138
138
  data = await self._http.aget(
139
139
  f"/v1/orderbook/{coin.upper()}/history",
140
140
  params={
@@ -0,0 +1,299 @@
1
+ """Trades API resource."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import warnings
6
+ from dataclasses import dataclass
7
+ from datetime import datetime
8
+ from typing import Literal, Optional
9
+
10
+ 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
19
+
20
+
21
+ class TradesResource:
22
+ """
23
+ Trades API resource.
24
+
25
+ Example:
26
+ >>> # Get recent trades
27
+ >>> trades = client.trades.recent("BTC")
28
+ >>>
29
+ >>> # Get trade history with cursor-based pagination (recommended)
30
+ >>> result = client.trades.list("BTC", start="2024-01-01", end="2024-01-02")
31
+ >>> trades = result.data
32
+ >>>
33
+ >>> # Get all pages
34
+ >>> while result.next_cursor:
35
+ ... result = client.trades.list("BTC", start="2024-01-01", end="2024-01-02", cursor=result.next_cursor)
36
+ ... trades.extend(result.data)
37
+ """
38
+
39
+ def __init__(self, http: HttpClient):
40
+ self._http = http
41
+
42
+ def _convert_timestamp(self, ts: Optional[Timestamp]) -> Optional[int]:
43
+ """Convert timestamp to Unix milliseconds."""
44
+ if ts is None:
45
+ return None
46
+ if isinstance(ts, int):
47
+ return ts
48
+ if isinstance(ts, datetime):
49
+ return int(ts.timestamp() * 1000)
50
+ if isinstance(ts, str):
51
+ try:
52
+ dt = datetime.fromisoformat(ts.replace("Z", "+00:00"))
53
+ return int(dt.timestamp() * 1000)
54
+ except ValueError:
55
+ return int(ts)
56
+ return None
57
+
58
+ def list(
59
+ self,
60
+ coin: str,
61
+ *,
62
+ start: Timestamp,
63
+ end: Timestamp,
64
+ cursor: Optional[Timestamp] = None,
65
+ limit: Optional[int] = None,
66
+ side: Optional[Literal["buy", "sell"]] = None,
67
+ ) -> CursorResponse:
68
+ """
69
+ Get trade history for a coin using cursor-based pagination.
70
+
71
+ Uses cursor-based pagination by default, which is more efficient for large datasets.
72
+ Use the next_cursor from the response as the cursor parameter to get the next page.
73
+
74
+ Args:
75
+ coin: The coin symbol (e.g., 'BTC', 'ETH')
76
+ start: Start timestamp (required)
77
+ end: End timestamp (required)
78
+ cursor: Cursor from previous response's next_cursor (timestamp)
79
+ limit: Maximum number of results (default: 100, max: 1000)
80
+ side: Filter by trade side
81
+
82
+ Returns:
83
+ CursorResponse with trades and next_cursor for pagination
84
+
85
+ Example:
86
+ >>> # First page
87
+ >>> result = client.trades.list("BTC", start=start, end=end, limit=1000)
88
+ >>> trades = result.data
89
+ >>>
90
+ >>> # Subsequent pages
91
+ >>> while result.next_cursor:
92
+ ... result = client.trades.list(
93
+ ... "BTC", start=start, end=end, cursor=result.next_cursor, limit=1000
94
+ ... )
95
+ ... trades.extend(result.data)
96
+ """
97
+ data = self._http.get(
98
+ f"/v1/trades/{coin.upper()}",
99
+ params={
100
+ "start": self._convert_timestamp(start),
101
+ "end": self._convert_timestamp(end),
102
+ "cursor": self._convert_timestamp(cursor),
103
+ "limit": limit,
104
+ "side": side,
105
+ },
106
+ )
107
+ return CursorResponse(
108
+ data=[Trade.model_validate(item) for item in data["data"]],
109
+ next_cursor=data.get("meta", {}).get("next_cursor"),
110
+ )
111
+
112
+ async def alist(
113
+ self,
114
+ coin: str,
115
+ *,
116
+ start: Timestamp,
117
+ end: Timestamp,
118
+ cursor: Optional[Timestamp] = None,
119
+ limit: Optional[int] = None,
120
+ side: Optional[Literal["buy", "sell"]] = None,
121
+ ) -> CursorResponse:
122
+ """
123
+ Async version of list().
124
+
125
+ Uses cursor-based pagination by default.
126
+ """
127
+ data = await self._http.aget(
128
+ f"/v1/trades/{coin.upper()}",
129
+ params={
130
+ "start": self._convert_timestamp(start),
131
+ "end": self._convert_timestamp(end),
132
+ "cursor": self._convert_timestamp(cursor),
133
+ "limit": limit,
134
+ "side": side,
135
+ },
136
+ )
137
+ return CursorResponse(
138
+ data=[Trade.model_validate(item) for item in data["data"]],
139
+ next_cursor=data.get("meta", {}).get("next_cursor"),
140
+ )
141
+
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
+ def recent(self, coin: str, limit: Optional[int] = None) -> list[Trade]:
220
+ """
221
+ Get most recent trades for a coin.
222
+
223
+ Args:
224
+ coin: The coin symbol (e.g., 'BTC', 'ETH')
225
+ limit: Number of trades to return (default: 100)
226
+
227
+ Returns:
228
+ List of recent trades
229
+ """
230
+ data = self._http.get(
231
+ f"/v1/trades/{coin.upper()}/recent",
232
+ params={"limit": limit},
233
+ )
234
+ return [Trade.model_validate(item) for item in data["data"]]
235
+
236
+ async def arecent(self, coin: str, limit: Optional[int] = None) -> list[Trade]:
237
+ """Async version of recent()."""
238
+ data = await self._http.aget(
239
+ f"/v1/trades/{coin.upper()}/recent",
240
+ params={"limit": limit},
241
+ )
242
+ 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)