oxarchive 0.5.1__py3-none-any.whl → 0.5.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.
- oxarchive/__init__.py +1 -1
- oxarchive/exchanges.py +4 -0
- oxarchive/resources/__init__.py +2 -0
- oxarchive/resources/liquidations.py +198 -0
- oxarchive/resources/trades.py +9 -5
- oxarchive/types.py +47 -2
- {oxarchive-0.5.1.dist-info → oxarchive-0.5.3.dist-info}/METADATA +7 -9
- {oxarchive-0.5.1.dist-info → oxarchive-0.5.3.dist-info}/RECORD +9 -8
- {oxarchive-0.5.1.dist-info → oxarchive-0.5.3.dist-info}/WHEEL +0 -0
oxarchive/__init__.py
CHANGED
oxarchive/exchanges.py
CHANGED
|
@@ -11,6 +11,7 @@ from .resources import (
|
|
|
11
11
|
FundingResource,
|
|
12
12
|
OpenInterestResource,
|
|
13
13
|
CandlesResource,
|
|
14
|
+
LiquidationsResource,
|
|
14
15
|
)
|
|
15
16
|
|
|
16
17
|
|
|
@@ -48,6 +49,9 @@ class HyperliquidClient:
|
|
|
48
49
|
self.candles = CandlesResource(http, base_path)
|
|
49
50
|
"""OHLCV candle data"""
|
|
50
51
|
|
|
52
|
+
self.liquidations = LiquidationsResource(http, base_path)
|
|
53
|
+
"""Liquidation events (May 2025+)"""
|
|
54
|
+
|
|
51
55
|
|
|
52
56
|
class LighterClient:
|
|
53
57
|
"""
|
oxarchive/resources/__init__.py
CHANGED
|
@@ -6,6 +6,7 @@ from .instruments import InstrumentsResource, LighterInstrumentsResource
|
|
|
6
6
|
from .funding import FundingResource
|
|
7
7
|
from .openinterest import OpenInterestResource
|
|
8
8
|
from .candles import CandlesResource
|
|
9
|
+
from .liquidations import LiquidationsResource
|
|
9
10
|
|
|
10
11
|
__all__ = [
|
|
11
12
|
"OrderBookResource",
|
|
@@ -15,4 +16,5 @@ __all__ = [
|
|
|
15
16
|
"FundingResource",
|
|
16
17
|
"OpenInterestResource",
|
|
17
18
|
"CandlesResource",
|
|
19
|
+
"LiquidationsResource",
|
|
18
20
|
]
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"""Liquidations API resource."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from ..http import HttpClient
|
|
9
|
+
from ..types import CursorResponse, Liquidation, Timestamp
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class LiquidationsResource:
|
|
13
|
+
"""
|
|
14
|
+
Liquidations API resource.
|
|
15
|
+
|
|
16
|
+
Retrieve historical liquidation events from Hyperliquid.
|
|
17
|
+
|
|
18
|
+
Note: Liquidation data is available from May 25, 2025 onwards.
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
>>> # Get recent liquidations
|
|
22
|
+
>>> liquidations = client.hyperliquid.liquidations.history(
|
|
23
|
+
... "BTC",
|
|
24
|
+
... start="2025-06-01",
|
|
25
|
+
... end="2025-06-02"
|
|
26
|
+
... )
|
|
27
|
+
>>>
|
|
28
|
+
>>> # Get liquidations for a specific user
|
|
29
|
+
>>> user_liquidations = client.hyperliquid.liquidations.by_user(
|
|
30
|
+
... "0x1234...",
|
|
31
|
+
... start="2025-06-01",
|
|
32
|
+
... end="2025-06-02"
|
|
33
|
+
... )
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, http: HttpClient, base_path: str = "/v1"):
|
|
37
|
+
self._http = http
|
|
38
|
+
self._base_path = base_path
|
|
39
|
+
|
|
40
|
+
def _convert_timestamp(self, ts: Optional[Timestamp]) -> Optional[int]:
|
|
41
|
+
"""Convert timestamp to Unix milliseconds."""
|
|
42
|
+
if ts is None:
|
|
43
|
+
return None
|
|
44
|
+
if isinstance(ts, int):
|
|
45
|
+
return ts
|
|
46
|
+
if isinstance(ts, datetime):
|
|
47
|
+
return int(ts.timestamp() * 1000)
|
|
48
|
+
if isinstance(ts, str):
|
|
49
|
+
try:
|
|
50
|
+
dt = datetime.fromisoformat(ts.replace("Z", "+00:00"))
|
|
51
|
+
return int(dt.timestamp() * 1000)
|
|
52
|
+
except ValueError:
|
|
53
|
+
return int(ts)
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
def history(
|
|
57
|
+
self,
|
|
58
|
+
coin: str,
|
|
59
|
+
*,
|
|
60
|
+
start: Timestamp,
|
|
61
|
+
end: Timestamp,
|
|
62
|
+
cursor: Optional[str] = None,
|
|
63
|
+
limit: Optional[int] = None,
|
|
64
|
+
) -> CursorResponse[list[Liquidation]]:
|
|
65
|
+
"""
|
|
66
|
+
Get liquidation history for a coin with cursor-based pagination.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
coin: The coin symbol (e.g., 'BTC', 'ETH')
|
|
70
|
+
start: Start timestamp (required)
|
|
71
|
+
end: End timestamp (required)
|
|
72
|
+
cursor: Cursor from previous response's next_cursor
|
|
73
|
+
limit: Maximum number of results (default: 100, max: 1000)
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
CursorResponse with liquidation records and next_cursor for pagination
|
|
77
|
+
|
|
78
|
+
Example:
|
|
79
|
+
>>> result = client.hyperliquid.liquidations.history("BTC", start=start, end=end, limit=1000)
|
|
80
|
+
>>> liquidations = result.data
|
|
81
|
+
>>> while result.next_cursor:
|
|
82
|
+
... result = client.hyperliquid.liquidations.history(
|
|
83
|
+
... "BTC", start=start, end=end, cursor=result.next_cursor, limit=1000
|
|
84
|
+
... )
|
|
85
|
+
... liquidations.extend(result.data)
|
|
86
|
+
"""
|
|
87
|
+
data = self._http.get(
|
|
88
|
+
f"{self._base_path}/liquidations/{coin.upper()}",
|
|
89
|
+
params={
|
|
90
|
+
"start": self._convert_timestamp(start),
|
|
91
|
+
"end": self._convert_timestamp(end),
|
|
92
|
+
"cursor": cursor,
|
|
93
|
+
"limit": limit,
|
|
94
|
+
},
|
|
95
|
+
)
|
|
96
|
+
return CursorResponse(
|
|
97
|
+
data=[Liquidation.model_validate(item) for item in data["data"]],
|
|
98
|
+
next_cursor=data.get("meta", {}).get("next_cursor"),
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
async def ahistory(
|
|
102
|
+
self,
|
|
103
|
+
coin: str,
|
|
104
|
+
*,
|
|
105
|
+
start: Timestamp,
|
|
106
|
+
end: Timestamp,
|
|
107
|
+
cursor: Optional[str] = None,
|
|
108
|
+
limit: Optional[int] = None,
|
|
109
|
+
) -> CursorResponse[list[Liquidation]]:
|
|
110
|
+
"""Async version of history(). start and end are required."""
|
|
111
|
+
data = await self._http.aget(
|
|
112
|
+
f"{self._base_path}/liquidations/{coin.upper()}",
|
|
113
|
+
params={
|
|
114
|
+
"start": self._convert_timestamp(start),
|
|
115
|
+
"end": self._convert_timestamp(end),
|
|
116
|
+
"cursor": cursor,
|
|
117
|
+
"limit": limit,
|
|
118
|
+
},
|
|
119
|
+
)
|
|
120
|
+
return CursorResponse(
|
|
121
|
+
data=[Liquidation.model_validate(item) for item in data["data"]],
|
|
122
|
+
next_cursor=data.get("meta", {}).get("next_cursor"),
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
def by_user(
|
|
126
|
+
self,
|
|
127
|
+
user_address: str,
|
|
128
|
+
*,
|
|
129
|
+
start: Timestamp,
|
|
130
|
+
end: Timestamp,
|
|
131
|
+
coin: Optional[str] = None,
|
|
132
|
+
cursor: Optional[str] = None,
|
|
133
|
+
limit: Optional[int] = None,
|
|
134
|
+
) -> CursorResponse[list[Liquidation]]:
|
|
135
|
+
"""
|
|
136
|
+
Get liquidation history for a specific user.
|
|
137
|
+
|
|
138
|
+
This returns liquidations where the user was either:
|
|
139
|
+
- The liquidated party (their position was liquidated)
|
|
140
|
+
- The liquidator (they executed the liquidation)
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
user_address: User's wallet address (e.g., '0x1234...')
|
|
144
|
+
start: Start timestamp (required)
|
|
145
|
+
end: End timestamp (required)
|
|
146
|
+
coin: Optional coin filter (e.g., 'BTC', 'ETH')
|
|
147
|
+
cursor: Cursor from previous response's next_cursor
|
|
148
|
+
limit: Maximum number of results (default: 100, max: 1000)
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
CursorResponse with liquidation records and next_cursor for pagination
|
|
152
|
+
"""
|
|
153
|
+
params = {
|
|
154
|
+
"start": self._convert_timestamp(start),
|
|
155
|
+
"end": self._convert_timestamp(end),
|
|
156
|
+
"cursor": cursor,
|
|
157
|
+
"limit": limit,
|
|
158
|
+
}
|
|
159
|
+
if coin:
|
|
160
|
+
params["coin"] = coin.upper()
|
|
161
|
+
|
|
162
|
+
data = self._http.get(
|
|
163
|
+
f"{self._base_path}/liquidations/user/{user_address}",
|
|
164
|
+
params=params,
|
|
165
|
+
)
|
|
166
|
+
return CursorResponse(
|
|
167
|
+
data=[Liquidation.model_validate(item) for item in data["data"]],
|
|
168
|
+
next_cursor=data.get("meta", {}).get("next_cursor"),
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
async def aby_user(
|
|
172
|
+
self,
|
|
173
|
+
user_address: str,
|
|
174
|
+
*,
|
|
175
|
+
start: Timestamp,
|
|
176
|
+
end: Timestamp,
|
|
177
|
+
coin: Optional[str] = None,
|
|
178
|
+
cursor: Optional[str] = None,
|
|
179
|
+
limit: Optional[int] = None,
|
|
180
|
+
) -> CursorResponse[list[Liquidation]]:
|
|
181
|
+
"""Async version of by_user()."""
|
|
182
|
+
params = {
|
|
183
|
+
"start": self._convert_timestamp(start),
|
|
184
|
+
"end": self._convert_timestamp(end),
|
|
185
|
+
"cursor": cursor,
|
|
186
|
+
"limit": limit,
|
|
187
|
+
}
|
|
188
|
+
if coin:
|
|
189
|
+
params["coin"] = coin.upper()
|
|
190
|
+
|
|
191
|
+
data = await self._http.aget(
|
|
192
|
+
f"{self._base_path}/liquidations/user/{user_address}",
|
|
193
|
+
params=params,
|
|
194
|
+
)
|
|
195
|
+
return CursorResponse(
|
|
196
|
+
data=[Liquidation.model_validate(item) for item in data["data"]],
|
|
197
|
+
next_cursor=data.get("meta", {}).get("next_cursor"),
|
|
198
|
+
)
|
oxarchive/resources/trades.py
CHANGED
|
@@ -14,17 +14,17 @@ class TradesResource:
|
|
|
14
14
|
Trades API resource.
|
|
15
15
|
|
|
16
16
|
Example:
|
|
17
|
-
>>> # Get recent trades
|
|
18
|
-
>>> trades = client.trades.recent("BTC")
|
|
19
|
-
>>>
|
|
20
17
|
>>> # Get trade history with cursor-based pagination (recommended)
|
|
21
|
-
>>> result = client.trades.list("BTC", start="2024-01-01", end="2024-01-02")
|
|
18
|
+
>>> result = client.hyperliquid.trades.list("BTC", start="2024-01-01", end="2024-01-02")
|
|
22
19
|
>>> trades = result.data
|
|
23
20
|
>>>
|
|
24
21
|
>>> # Get all pages
|
|
25
22
|
>>> while result.next_cursor:
|
|
26
|
-
... result = client.trades.list("BTC", start="2024-01-01", end="2024-01-02", cursor=result.next_cursor)
|
|
23
|
+
... result = client.hyperliquid.trades.list("BTC", start="2024-01-01", end="2024-01-02", cursor=result.next_cursor)
|
|
27
24
|
... trades.extend(result.data)
|
|
25
|
+
>>>
|
|
26
|
+
>>> # Get recent trades (Lighter only - has real-time data)
|
|
27
|
+
>>> recent = client.lighter.trades.recent("BTC")
|
|
28
28
|
"""
|
|
29
29
|
|
|
30
30
|
def __init__(self, http: HttpClient, base_path: str = "/v1"):
|
|
@@ -135,6 +135,10 @@ class TradesResource:
|
|
|
135
135
|
"""
|
|
136
136
|
Get most recent trades for a coin.
|
|
137
137
|
|
|
138
|
+
Note: This method is only available for Lighter (client.lighter.trades.recent())
|
|
139
|
+
which has real-time data ingestion. Hyperliquid uses hourly backfill so this
|
|
140
|
+
endpoint is not available for Hyperliquid.
|
|
141
|
+
|
|
138
142
|
Args:
|
|
139
143
|
coin: The coin symbol (e.g., 'BTC', 'ETH')
|
|
140
144
|
limit: Number of trades to return (default: 100)
|
oxarchive/types.py
CHANGED
|
@@ -270,6 +270,51 @@ class OpenInterest(BaseModel):
|
|
|
270
270
|
"""Impact ask price for liquidations."""
|
|
271
271
|
|
|
272
272
|
|
|
273
|
+
# =============================================================================
|
|
274
|
+
# Liquidation Types
|
|
275
|
+
# =============================================================================
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
class Liquidation(BaseModel):
|
|
279
|
+
"""Liquidation event record."""
|
|
280
|
+
|
|
281
|
+
coin: str
|
|
282
|
+
"""Trading pair symbol."""
|
|
283
|
+
|
|
284
|
+
timestamp: datetime
|
|
285
|
+
"""Liquidation timestamp (UTC)."""
|
|
286
|
+
|
|
287
|
+
liquidated_user: str
|
|
288
|
+
"""Address of the liquidated user."""
|
|
289
|
+
|
|
290
|
+
liquidator_user: str
|
|
291
|
+
"""Address of the liquidator."""
|
|
292
|
+
|
|
293
|
+
price: str
|
|
294
|
+
"""Liquidation execution price."""
|
|
295
|
+
|
|
296
|
+
size: str
|
|
297
|
+
"""Liquidation size."""
|
|
298
|
+
|
|
299
|
+
side: Literal["B", "S"]
|
|
300
|
+
"""Side: 'B' (buy) or 'S' (sell)."""
|
|
301
|
+
|
|
302
|
+
mark_price: Optional[str] = None
|
|
303
|
+
"""Mark price at time of liquidation."""
|
|
304
|
+
|
|
305
|
+
closed_pnl: Optional[str] = None
|
|
306
|
+
"""Realized PnL from the liquidation."""
|
|
307
|
+
|
|
308
|
+
direction: Optional[str] = None
|
|
309
|
+
"""Position direction (e.g., 'Open Long', 'Close Short')."""
|
|
310
|
+
|
|
311
|
+
trade_id: Optional[int] = None
|
|
312
|
+
"""Unique trade ID."""
|
|
313
|
+
|
|
314
|
+
tx_hash: Optional[str] = None
|
|
315
|
+
"""Blockchain transaction hash."""
|
|
316
|
+
|
|
317
|
+
|
|
273
318
|
# =============================================================================
|
|
274
319
|
# Candle Types
|
|
275
320
|
# =============================================================================
|
|
@@ -311,8 +356,8 @@ class Candle(BaseModel):
|
|
|
311
356
|
# WebSocket Types
|
|
312
357
|
# =============================================================================
|
|
313
358
|
|
|
314
|
-
WsChannel = Literal["orderbook", "trades", "candles", "ticker", "all_tickers"]
|
|
315
|
-
"""Available WebSocket channels. Note: ticker/all_tickers are real-time only."""
|
|
359
|
+
WsChannel = Literal["orderbook", "trades", "candles", "liquidations", "ticker", "all_tickers"]
|
|
360
|
+
"""Available WebSocket channels. Note: ticker/all_tickers are real-time only. Liquidations is historical only (May 2025+)."""
|
|
316
361
|
|
|
317
362
|
WsConnectionState = Literal["connecting", "connected", "disconnected", "reconnecting"]
|
|
318
363
|
"""WebSocket connection state."""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: oxarchive
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.3
|
|
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
|
|
@@ -211,9 +211,6 @@ history = client.lighter.orderbook.history(
|
|
|
211
211
|
The trades API uses cursor-based pagination for efficient retrieval of large datasets.
|
|
212
212
|
|
|
213
213
|
```python
|
|
214
|
-
# Get recent trades
|
|
215
|
-
recent = client.hyperliquid.trades.recent("BTC", limit=100)
|
|
216
|
-
|
|
217
214
|
# Get trade history with cursor-based pagination
|
|
218
215
|
result = client.hyperliquid.trades.list("ETH", start="2024-01-01", end="2024-01-02", limit=1000)
|
|
219
216
|
trades = result.data
|
|
@@ -232,8 +229,7 @@ while result.next_cursor:
|
|
|
232
229
|
# Filter by side
|
|
233
230
|
buys = client.hyperliquid.trades.list("BTC", start=..., end=..., side="buy")
|
|
234
231
|
|
|
235
|
-
# Async
|
|
236
|
-
recent = await client.hyperliquid.trades.arecent("BTC")
|
|
232
|
+
# Async version
|
|
237
233
|
result = await client.hyperliquid.trades.alist("ETH", start=..., end=...)
|
|
238
234
|
```
|
|
239
235
|
|
|
@@ -641,9 +637,11 @@ from oxarchive.resources.trades import CursorResponse
|
|
|
641
637
|
|
|
642
638
|
client = Client(api_key="ox_your_api_key")
|
|
643
639
|
|
|
644
|
-
orderbook: OrderBook = client.orderbook.get("BTC")
|
|
645
|
-
|
|
646
|
-
|
|
640
|
+
orderbook: OrderBook = client.hyperliquid.orderbook.get("BTC")
|
|
641
|
+
result: CursorResponse = client.hyperliquid.trades.list("BTC", start=..., end=...)
|
|
642
|
+
|
|
643
|
+
# Lighter has real-time data, so recent() is available
|
|
644
|
+
recent: list[Trade] = client.lighter.trades.recent("BTC")
|
|
647
645
|
|
|
648
646
|
# Lighter granularity type hint
|
|
649
647
|
granularity: LighterGranularity = "10s"
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
oxarchive/__init__.py,sha256
|
|
1
|
+
oxarchive/__init__.py,sha256=-TIv-IqajJ0h3W4QyZsnBKb-PLc3hrRcsz54ynKA_hw,2738
|
|
2
2
|
oxarchive/client.py,sha256=XWQ_VEBQy3UIAnmZQ-Z_FyzXnvMA3FITwtinBOf3o-Y,4453
|
|
3
|
-
oxarchive/exchanges.py,sha256=
|
|
3
|
+
oxarchive/exchanges.py,sha256=nTd0gRrgV2wDoptWWxwh38HXkCcunVNuNdNEwzNDsBM,2773
|
|
4
4
|
oxarchive/http.py,sha256=SY_o9Ag8ADo1HI3i3uAKW1xwkYjPE75gRAjnMsddAGs,4211
|
|
5
|
-
oxarchive/types.py,sha256=
|
|
5
|
+
oxarchive/types.py,sha256=tfL6QG2WTBe4cdgk5TAbgpTRGAQALvhPTADH9umqd4g,15463
|
|
6
6
|
oxarchive/websocket.py,sha256=sS-kLDKv2qS77-61hXChRePjL_hz-URcALM9UW5zxXU,30609
|
|
7
|
-
oxarchive/resources/__init__.py,sha256=
|
|
7
|
+
oxarchive/resources/__init__.py,sha256=JyNkR6dKBOGPQpYAuG6Qy1lDMCxJ68dXNLUAOua1Vs8,587
|
|
8
8
|
oxarchive/resources/candles.py,sha256=GI7-YSNFckEd1i49W9mlrLn1cl955sY8ki0u12TuLgw,4449
|
|
9
9
|
oxarchive/resources/funding.py,sha256=ybMWkpoccrkdwnd6W3oHgsaor7cBcA2nkYy4CbjmHUg,4485
|
|
10
10
|
oxarchive/resources/instruments.py,sha256=6q7rMdIaixXgFVXgwQsVd-YuO7RIXr1oGPT5jBsqI9A,3733
|
|
11
|
+
oxarchive/resources/liquidations.py,sha256=kX3mEX2u6uEvgk1aKfL4U0JE9gOjJOwsLVpkIj84arE,6741
|
|
11
12
|
oxarchive/resources/openinterest.py,sha256=whwo60KFNLGwvVrDmDYYc-rMZr35Fcizb5Iil-DSvD8,4553
|
|
12
13
|
oxarchive/resources/orderbook.py,sha256=NzgKH45MBb2oAHyPmy6BSikaYyq0nM-8GFjur9LIRN8,6661
|
|
13
|
-
oxarchive/resources/trades.py,sha256=
|
|
14
|
-
oxarchive-0.5.
|
|
15
|
-
oxarchive-0.5.
|
|
16
|
-
oxarchive-0.5.
|
|
14
|
+
oxarchive/resources/trades.py,sha256=RhOTbhqSSBAaekden6rLY8qJL-NDArGTqCempvUbX08,5801
|
|
15
|
+
oxarchive-0.5.3.dist-info/METADATA,sha256=a134ZFK2iEmo5UPDAI2sw3ZcqpqjzVphjSKGUS2-4LA,17924
|
|
16
|
+
oxarchive-0.5.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
17
|
+
oxarchive-0.5.3.dist-info/RECORD,,
|
|
File without changes
|