oxarchive 0.3.10__py3-none-any.whl → 0.4.0__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 +15 -6
- oxarchive/client.py +39 -17
- oxarchive/exchanges.py +77 -0
- oxarchive/resources/funding.py +34 -18
- oxarchive/resources/instruments.py +6 -5
- oxarchive/resources/openinterest.py +34 -18
- oxarchive/resources/orderbook.py +41 -22
- oxarchive/resources/trades.py +9 -151
- oxarchive/types.py +15 -0
- {oxarchive-0.3.10.dist-info → oxarchive-0.4.0.dist-info}/METADATA +62 -34
- oxarchive-0.4.0.dist-info/RECORD +15 -0
- oxarchive-0.3.10.dist-info/RECORD +0 -14
- {oxarchive-0.3.10.dist-info → oxarchive-0.4.0.dist-info}/WHEEL +0 -0
oxarchive/__init__.py
CHANGED
|
@@ -1,22 +1,28 @@
|
|
|
1
1
|
"""
|
|
2
2
|
oxarchive - Official Python SDK for 0xarchive
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
Historical Market Data API for multiple exchanges:
|
|
5
|
+
- Hyperliquid (perpetuals data from April 2023)
|
|
6
|
+
- Lighter.xyz (perpetuals data)
|
|
5
7
|
|
|
6
8
|
Example:
|
|
7
9
|
>>> from oxarchive import Client
|
|
8
10
|
>>>
|
|
9
11
|
>>> client = Client(api_key="ox_your_api_key")
|
|
10
12
|
>>>
|
|
11
|
-
>>> #
|
|
12
|
-
>>>
|
|
13
|
-
>>> print(f"BTC mid price: {
|
|
13
|
+
>>> # Hyperliquid data
|
|
14
|
+
>>> hl_orderbook = client.hyperliquid.orderbook.get("BTC")
|
|
15
|
+
>>> print(f"BTC mid price: {hl_orderbook.mid_price}")
|
|
16
|
+
>>>
|
|
17
|
+
>>> # Lighter.xyz data
|
|
18
|
+
>>> lighter_orderbook = client.lighter.orderbook.get("BTC")
|
|
14
19
|
>>>
|
|
15
20
|
>>> # Get historical snapshots
|
|
16
|
-
>>> history = client.orderbook.history("ETH", start="2024-01-01", end="2024-01-02")
|
|
21
|
+
>>> history = client.hyperliquid.orderbook.history("ETH", start="2024-01-01", end="2024-01-02")
|
|
17
22
|
"""
|
|
18
23
|
|
|
19
24
|
from .client import Client
|
|
25
|
+
from .exchanges import HyperliquidClient, LighterClient
|
|
20
26
|
from .types import (
|
|
21
27
|
OrderBook,
|
|
22
28
|
Trade,
|
|
@@ -57,11 +63,14 @@ except ImportError:
|
|
|
57
63
|
OxArchiveWs = None # type: ignore
|
|
58
64
|
WsOptions = None # type: ignore
|
|
59
65
|
|
|
60
|
-
__version__ = "0.
|
|
66
|
+
__version__ = "0.4.0"
|
|
61
67
|
|
|
62
68
|
__all__ = [
|
|
63
69
|
# Client
|
|
64
70
|
"Client",
|
|
71
|
+
# Exchange Clients
|
|
72
|
+
"HyperliquidClient",
|
|
73
|
+
"LighterClient",
|
|
65
74
|
# WebSocket Client
|
|
66
75
|
"OxArchiveWs",
|
|
67
76
|
"WsOptions",
|
oxarchive/client.py
CHANGED
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
from typing import Optional
|
|
6
6
|
|
|
7
7
|
from .http import HttpClient
|
|
8
|
+
from .exchanges import HyperliquidClient, LighterClient
|
|
8
9
|
from .resources import (
|
|
9
10
|
OrderBookResource,
|
|
10
11
|
TradesResource,
|
|
@@ -21,20 +22,27 @@ class Client:
|
|
|
21
22
|
"""
|
|
22
23
|
0xarchive API client.
|
|
23
24
|
|
|
25
|
+
Supports multiple exchanges:
|
|
26
|
+
- `client.hyperliquid` - Hyperliquid perpetuals (April 2023+)
|
|
27
|
+
- `client.lighter` - Lighter.xyz perpetuals
|
|
28
|
+
|
|
24
29
|
Example:
|
|
25
30
|
>>> from oxarchive import Client
|
|
26
31
|
>>>
|
|
27
32
|
>>> client = Client(api_key="ox_your_api_key")
|
|
28
33
|
>>>
|
|
29
|
-
>>> #
|
|
30
|
-
>>>
|
|
31
|
-
>>> print(f"BTC mid price: {
|
|
34
|
+
>>> # Hyperliquid data
|
|
35
|
+
>>> hl_orderbook = client.hyperliquid.orderbook.get("BTC")
|
|
36
|
+
>>> print(f"BTC mid price: {hl_orderbook.mid_price}")
|
|
37
|
+
>>>
|
|
38
|
+
>>> # Lighter.xyz data
|
|
39
|
+
>>> lighter_orderbook = client.lighter.orderbook.get("BTC")
|
|
32
40
|
>>>
|
|
33
41
|
>>> # Get historical snapshots
|
|
34
|
-
>>> history = client.orderbook.history("ETH", start="2024-01-01", end="2024-01-02")
|
|
42
|
+
>>> history = client.hyperliquid.orderbook.history("ETH", start="2024-01-01", end="2024-01-02")
|
|
35
43
|
>>>
|
|
36
44
|
>>> # List all instruments
|
|
37
|
-
>>> instruments = client.instruments.list()
|
|
45
|
+
>>> instruments = client.hyperliquid.instruments.list()
|
|
38
46
|
|
|
39
47
|
Async example:
|
|
40
48
|
>>> import asyncio
|
|
@@ -42,11 +50,15 @@ class Client:
|
|
|
42
50
|
>>>
|
|
43
51
|
>>> async def main():
|
|
44
52
|
... client = Client(api_key="ox_your_api_key")
|
|
45
|
-
... orderbook = await client.orderbook.aget("BTC")
|
|
53
|
+
... orderbook = await client.hyperliquid.orderbook.aget("BTC")
|
|
46
54
|
... print(f"BTC mid price: {orderbook.mid_price}")
|
|
47
55
|
... await client.aclose()
|
|
48
56
|
>>>
|
|
49
57
|
>>> asyncio.run(main())
|
|
58
|
+
|
|
59
|
+
Legacy usage (deprecated, will be removed in v2.0):
|
|
60
|
+
>>> # These still work but use client.hyperliquid.* instead
|
|
61
|
+
>>> orderbook = client.orderbook.get("BTC") # deprecated
|
|
50
62
|
"""
|
|
51
63
|
|
|
52
64
|
def __init__(
|
|
@@ -73,21 +85,31 @@ class Client:
|
|
|
73
85
|
timeout=timeout or DEFAULT_TIMEOUT,
|
|
74
86
|
)
|
|
75
87
|
|
|
76
|
-
#
|
|
77
|
-
self.
|
|
78
|
-
"""
|
|
88
|
+
# Exchange-specific clients (recommended)
|
|
89
|
+
self.hyperliquid = HyperliquidClient(self._http)
|
|
90
|
+
"""Hyperliquid exchange data (orderbook, trades, funding, OI from April 2023)"""
|
|
91
|
+
|
|
92
|
+
self.lighter = LighterClient(self._http)
|
|
93
|
+
"""Lighter.xyz exchange data (orderbook reconstructed from checkpoints + deltas)"""
|
|
94
|
+
|
|
95
|
+
# Legacy resource namespaces (deprecated - use client.hyperliquid.* instead)
|
|
96
|
+
# These will be removed in v2.0
|
|
97
|
+
# Note: Using /v1/hyperliquid base path for backward compatibility
|
|
98
|
+
legacy_base = "/v1/hyperliquid"
|
|
99
|
+
self.orderbook = OrderBookResource(self._http, legacy_base)
|
|
100
|
+
"""[DEPRECATED] Use client.hyperliquid.orderbook instead"""
|
|
79
101
|
|
|
80
|
-
self.trades = TradesResource(self._http)
|
|
81
|
-
"""
|
|
102
|
+
self.trades = TradesResource(self._http, legacy_base)
|
|
103
|
+
"""[DEPRECATED] Use client.hyperliquid.trades instead"""
|
|
82
104
|
|
|
83
|
-
self.instruments = InstrumentsResource(self._http)
|
|
84
|
-
"""
|
|
105
|
+
self.instruments = InstrumentsResource(self._http, legacy_base)
|
|
106
|
+
"""[DEPRECATED] Use client.hyperliquid.instruments instead"""
|
|
85
107
|
|
|
86
|
-
self.funding = FundingResource(self._http)
|
|
87
|
-
"""
|
|
108
|
+
self.funding = FundingResource(self._http, legacy_base)
|
|
109
|
+
"""[DEPRECATED] Use client.hyperliquid.funding instead"""
|
|
88
110
|
|
|
89
|
-
self.open_interest = OpenInterestResource(self._http)
|
|
90
|
-
"""
|
|
111
|
+
self.open_interest = OpenInterestResource(self._http, legacy_base)
|
|
112
|
+
"""[DEPRECATED] Use client.hyperliquid.open_interest instead"""
|
|
91
113
|
|
|
92
114
|
def close(self) -> None:
|
|
93
115
|
"""Close the HTTP client and release resources."""
|
oxarchive/exchanges.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Exchange-specific client classes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .http import HttpClient
|
|
6
|
+
from .resources import (
|
|
7
|
+
OrderBookResource,
|
|
8
|
+
TradesResource,
|
|
9
|
+
InstrumentsResource,
|
|
10
|
+
FundingResource,
|
|
11
|
+
OpenInterestResource,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class HyperliquidClient:
|
|
16
|
+
"""
|
|
17
|
+
Hyperliquid exchange client.
|
|
18
|
+
|
|
19
|
+
Access Hyperliquid market data through the 0xarchive API.
|
|
20
|
+
|
|
21
|
+
Example:
|
|
22
|
+
>>> client = oxarchive.Client(api_key="...")
|
|
23
|
+
>>> orderbook = client.hyperliquid.orderbook.get("BTC")
|
|
24
|
+
>>> trades = client.hyperliquid.trades.list("ETH", start=..., end=...)
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, http: HttpClient):
|
|
28
|
+
self._http = http
|
|
29
|
+
base_path = "/v1/hyperliquid"
|
|
30
|
+
|
|
31
|
+
self.orderbook = OrderBookResource(http, base_path)
|
|
32
|
+
"""Order book data (L2 snapshots from April 2023)"""
|
|
33
|
+
|
|
34
|
+
self.trades = TradesResource(http, base_path)
|
|
35
|
+
"""Trade/fill history"""
|
|
36
|
+
|
|
37
|
+
self.instruments = InstrumentsResource(http, base_path)
|
|
38
|
+
"""Trading instruments metadata"""
|
|
39
|
+
|
|
40
|
+
self.funding = FundingResource(http, base_path)
|
|
41
|
+
"""Funding rates"""
|
|
42
|
+
|
|
43
|
+
self.open_interest = OpenInterestResource(http, base_path)
|
|
44
|
+
"""Open interest"""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class LighterClient:
|
|
48
|
+
"""
|
|
49
|
+
Lighter.xyz exchange client.
|
|
50
|
+
|
|
51
|
+
Access Lighter.xyz market data through the 0xarchive API.
|
|
52
|
+
Lighter orderbooks are reconstructed from checkpoint + delta data.
|
|
53
|
+
|
|
54
|
+
Example:
|
|
55
|
+
>>> client = oxarchive.Client(api_key="...")
|
|
56
|
+
>>> orderbook = client.lighter.orderbook.get("BTC")
|
|
57
|
+
>>> trades = client.lighter.trades.list("ETH", start=..., end=...)
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
def __init__(self, http: HttpClient):
|
|
61
|
+
self._http = http
|
|
62
|
+
base_path = "/v1/lighter"
|
|
63
|
+
|
|
64
|
+
self.orderbook = OrderBookResource(http, base_path)
|
|
65
|
+
"""Order book data (reconstructed from checkpoints + deltas)"""
|
|
66
|
+
|
|
67
|
+
self.trades = TradesResource(http, base_path)
|
|
68
|
+
"""Trade/fill history"""
|
|
69
|
+
|
|
70
|
+
self.instruments = InstrumentsResource(http, base_path)
|
|
71
|
+
"""Trading instruments metadata"""
|
|
72
|
+
|
|
73
|
+
self.funding = FundingResource(http, base_path)
|
|
74
|
+
"""Funding rates"""
|
|
75
|
+
|
|
76
|
+
self.open_interest = OpenInterestResource(http, base_path)
|
|
77
|
+
"""Open interest"""
|
oxarchive/resources/funding.py
CHANGED
|
@@ -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:
|
|
@@ -21,8 +21,9 @@ class FundingResource:
|
|
|
21
21
|
>>> history = client.funding.history("ETH", start="2024-01-01", end="2024-01-07")
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
|
-
def __init__(self, http: HttpClient):
|
|
24
|
+
def __init__(self, http: HttpClient, base_path: str = "/v1"):
|
|
25
25
|
self._http = http
|
|
26
|
+
self._base_path = base_path
|
|
26
27
|
|
|
27
28
|
def _convert_timestamp(self, ts: Optional[Timestamp]) -> Optional[int]:
|
|
28
29
|
"""Convert timestamp to Unix milliseconds."""
|
|
@@ -46,32 +47,44 @@ class FundingResource:
|
|
|
46
47
|
*,
|
|
47
48
|
start: Timestamp,
|
|
48
49
|
end: Timestamp,
|
|
50
|
+
cursor: Optional[Timestamp] = None,
|
|
49
51
|
limit: Optional[int] = None,
|
|
50
|
-
|
|
51
|
-
) -> list[FundingRate]:
|
|
52
|
+
) -> CursorResponse[list[FundingRate]]:
|
|
52
53
|
"""
|
|
53
|
-
Get funding rate history for a coin.
|
|
54
|
+
Get funding rate history for a coin with cursor-based pagination.
|
|
54
55
|
|
|
55
56
|
Args:
|
|
56
57
|
coin: The coin symbol (e.g., 'BTC', 'ETH')
|
|
57
58
|
start: Start timestamp (required)
|
|
58
59
|
end: End timestamp (required)
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
cursor: Cursor from previous response's next_cursor (timestamp)
|
|
61
|
+
limit: Maximum number of results (default: 100, max: 1000)
|
|
61
62
|
|
|
62
63
|
Returns:
|
|
63
|
-
|
|
64
|
+
CursorResponse with funding rate records and next_cursor for pagination
|
|
65
|
+
|
|
66
|
+
Example:
|
|
67
|
+
>>> result = client.funding.history("BTC", start=start, end=end, limit=1000)
|
|
68
|
+
>>> rates = result.data
|
|
69
|
+
>>> while result.next_cursor:
|
|
70
|
+
... result = client.funding.history(
|
|
71
|
+
... "BTC", start=start, end=end, cursor=result.next_cursor, limit=1000
|
|
72
|
+
... )
|
|
73
|
+
... rates.extend(result.data)
|
|
64
74
|
"""
|
|
65
75
|
data = self._http.get(
|
|
66
|
-
f"/
|
|
76
|
+
f"{self._base_path}/funding/{coin.upper()}",
|
|
67
77
|
params={
|
|
68
78
|
"start": self._convert_timestamp(start),
|
|
69
79
|
"end": self._convert_timestamp(end),
|
|
80
|
+
"cursor": self._convert_timestamp(cursor),
|
|
70
81
|
"limit": limit,
|
|
71
|
-
"offset": offset,
|
|
72
82
|
},
|
|
73
83
|
)
|
|
74
|
-
return
|
|
84
|
+
return CursorResponse(
|
|
85
|
+
data=[FundingRate.model_validate(item) for item in data["data"]],
|
|
86
|
+
next_cursor=data.get("meta", {}).get("next_cursor"),
|
|
87
|
+
)
|
|
75
88
|
|
|
76
89
|
async def ahistory(
|
|
77
90
|
self,
|
|
@@ -79,20 +92,23 @@ class FundingResource:
|
|
|
79
92
|
*,
|
|
80
93
|
start: Timestamp,
|
|
81
94
|
end: Timestamp,
|
|
95
|
+
cursor: Optional[Timestamp] = None,
|
|
82
96
|
limit: Optional[int] = None,
|
|
83
|
-
|
|
84
|
-
) -> list[FundingRate]:
|
|
97
|
+
) -> CursorResponse[list[FundingRate]]:
|
|
85
98
|
"""Async version of history(). start and end are required."""
|
|
86
99
|
data = await self._http.aget(
|
|
87
|
-
f"/
|
|
100
|
+
f"{self._base_path}/funding/{coin.upper()}",
|
|
88
101
|
params={
|
|
89
102
|
"start": self._convert_timestamp(start),
|
|
90
103
|
"end": self._convert_timestamp(end),
|
|
104
|
+
"cursor": self._convert_timestamp(cursor),
|
|
91
105
|
"limit": limit,
|
|
92
|
-
"offset": offset,
|
|
93
106
|
},
|
|
94
107
|
)
|
|
95
|
-
return
|
|
108
|
+
return CursorResponse(
|
|
109
|
+
data=[FundingRate.model_validate(item) for item in data["data"]],
|
|
110
|
+
next_cursor=data.get("meta", {}).get("next_cursor"),
|
|
111
|
+
)
|
|
96
112
|
|
|
97
113
|
def current(self, coin: str) -> FundingRate:
|
|
98
114
|
"""
|
|
@@ -104,10 +120,10 @@ class FundingResource:
|
|
|
104
120
|
Returns:
|
|
105
121
|
Current funding rate
|
|
106
122
|
"""
|
|
107
|
-
data = self._http.get(f"/
|
|
123
|
+
data = self._http.get(f"{self._base_path}/funding/{coin.upper()}/current")
|
|
108
124
|
return FundingRate.model_validate(data["data"])
|
|
109
125
|
|
|
110
126
|
async def acurrent(self, coin: str) -> FundingRate:
|
|
111
127
|
"""Async version of current()."""
|
|
112
|
-
data = await self._http.aget(f"/
|
|
128
|
+
data = await self._http.aget(f"{self._base_path}/funding/{coin.upper()}/current")
|
|
113
129
|
return FundingRate.model_validate(data["data"])
|
|
@@ -18,8 +18,9 @@ class InstrumentsResource:
|
|
|
18
18
|
>>> btc = client.instruments.get("BTC")
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
|
-
def __init__(self, http: HttpClient):
|
|
21
|
+
def __init__(self, http: HttpClient, base_path: str = "/v1"):
|
|
22
22
|
self._http = http
|
|
23
|
+
self._base_path = base_path
|
|
23
24
|
|
|
24
25
|
def list(self) -> list[Instrument]:
|
|
25
26
|
"""
|
|
@@ -28,12 +29,12 @@ class InstrumentsResource:
|
|
|
28
29
|
Returns:
|
|
29
30
|
List of instruments
|
|
30
31
|
"""
|
|
31
|
-
data = self._http.get("/
|
|
32
|
+
data = self._http.get(f"{self._base_path}/instruments")
|
|
32
33
|
return [Instrument.model_validate(item) for item in data["data"]]
|
|
33
34
|
|
|
34
35
|
async def alist(self) -> list[Instrument]:
|
|
35
36
|
"""Async version of list()."""
|
|
36
|
-
data = await self._http.aget("/
|
|
37
|
+
data = await self._http.aget(f"{self._base_path}/instruments")
|
|
37
38
|
return [Instrument.model_validate(item) for item in data["data"]]
|
|
38
39
|
|
|
39
40
|
def get(self, coin: str) -> Instrument:
|
|
@@ -46,10 +47,10 @@ class InstrumentsResource:
|
|
|
46
47
|
Returns:
|
|
47
48
|
Instrument details
|
|
48
49
|
"""
|
|
49
|
-
data = self._http.get(f"/
|
|
50
|
+
data = self._http.get(f"{self._base_path}/instruments/{coin.upper()}")
|
|
50
51
|
return Instrument.model_validate(data["data"])
|
|
51
52
|
|
|
52
53
|
async def aget(self, coin: str) -> Instrument:
|
|
53
54
|
"""Async version of get()."""
|
|
54
|
-
data = await self._http.aget(f"/
|
|
55
|
+
data = await self._http.aget(f"{self._base_path}/instruments/{coin.upper()}")
|
|
55
56
|
return Instrument.model_validate(data["data"])
|
|
@@ -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:
|
|
@@ -21,8 +21,9 @@ class OpenInterestResource:
|
|
|
21
21
|
>>> history = client.open_interest.history("ETH", start="2024-01-01", end="2024-01-07")
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
|
-
def __init__(self, http: HttpClient):
|
|
24
|
+
def __init__(self, http: HttpClient, base_path: str = "/v1"):
|
|
25
25
|
self._http = http
|
|
26
|
+
self._base_path = base_path
|
|
26
27
|
|
|
27
28
|
def _convert_timestamp(self, ts: Optional[Timestamp]) -> Optional[int]:
|
|
28
29
|
"""Convert timestamp to Unix milliseconds."""
|
|
@@ -46,32 +47,44 @@ class OpenInterestResource:
|
|
|
46
47
|
*,
|
|
47
48
|
start: Timestamp,
|
|
48
49
|
end: Timestamp,
|
|
50
|
+
cursor: Optional[Timestamp] = None,
|
|
49
51
|
limit: Optional[int] = None,
|
|
50
|
-
|
|
51
|
-
) -> list[OpenInterest]:
|
|
52
|
+
) -> CursorResponse[list[OpenInterest]]:
|
|
52
53
|
"""
|
|
53
|
-
Get open interest history for a coin.
|
|
54
|
+
Get open interest history for a coin with cursor-based pagination.
|
|
54
55
|
|
|
55
56
|
Args:
|
|
56
57
|
coin: The coin symbol (e.g., 'BTC', 'ETH')
|
|
57
58
|
start: Start timestamp (required)
|
|
58
59
|
end: End timestamp (required)
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
cursor: Cursor from previous response's next_cursor (timestamp)
|
|
61
|
+
limit: Maximum number of results (default: 100, max: 1000)
|
|
61
62
|
|
|
62
63
|
Returns:
|
|
63
|
-
|
|
64
|
+
CursorResponse with open interest records and next_cursor for pagination
|
|
65
|
+
|
|
66
|
+
Example:
|
|
67
|
+
>>> result = client.open_interest.history("BTC", start=start, end=end, limit=1000)
|
|
68
|
+
>>> records = result.data
|
|
69
|
+
>>> while result.next_cursor:
|
|
70
|
+
... result = client.open_interest.history(
|
|
71
|
+
... "BTC", start=start, end=end, cursor=result.next_cursor, limit=1000
|
|
72
|
+
... )
|
|
73
|
+
... records.extend(result.data)
|
|
64
74
|
"""
|
|
65
75
|
data = self._http.get(
|
|
66
|
-
f"/
|
|
76
|
+
f"{self._base_path}/openinterest/{coin.upper()}",
|
|
67
77
|
params={
|
|
68
78
|
"start": self._convert_timestamp(start),
|
|
69
79
|
"end": self._convert_timestamp(end),
|
|
80
|
+
"cursor": self._convert_timestamp(cursor),
|
|
70
81
|
"limit": limit,
|
|
71
|
-
"offset": offset,
|
|
72
82
|
},
|
|
73
83
|
)
|
|
74
|
-
return
|
|
84
|
+
return CursorResponse(
|
|
85
|
+
data=[OpenInterest.model_validate(item) for item in data["data"]],
|
|
86
|
+
next_cursor=data.get("meta", {}).get("next_cursor"),
|
|
87
|
+
)
|
|
75
88
|
|
|
76
89
|
async def ahistory(
|
|
77
90
|
self,
|
|
@@ -79,20 +92,23 @@ class OpenInterestResource:
|
|
|
79
92
|
*,
|
|
80
93
|
start: Timestamp,
|
|
81
94
|
end: Timestamp,
|
|
95
|
+
cursor: Optional[Timestamp] = None,
|
|
82
96
|
limit: Optional[int] = None,
|
|
83
|
-
|
|
84
|
-
) -> list[OpenInterest]:
|
|
97
|
+
) -> CursorResponse[list[OpenInterest]]:
|
|
85
98
|
"""Async version of history(). start and end are required."""
|
|
86
99
|
data = await self._http.aget(
|
|
87
|
-
f"/
|
|
100
|
+
f"{self._base_path}/openinterest/{coin.upper()}",
|
|
88
101
|
params={
|
|
89
102
|
"start": self._convert_timestamp(start),
|
|
90
103
|
"end": self._convert_timestamp(end),
|
|
104
|
+
"cursor": self._convert_timestamp(cursor),
|
|
91
105
|
"limit": limit,
|
|
92
|
-
"offset": offset,
|
|
93
106
|
},
|
|
94
107
|
)
|
|
95
|
-
return
|
|
108
|
+
return CursorResponse(
|
|
109
|
+
data=[OpenInterest.model_validate(item) for item in data["data"]],
|
|
110
|
+
next_cursor=data.get("meta", {}).get("next_cursor"),
|
|
111
|
+
)
|
|
96
112
|
|
|
97
113
|
def current(self, coin: str) -> OpenInterest:
|
|
98
114
|
"""
|
|
@@ -104,10 +120,10 @@ class OpenInterestResource:
|
|
|
104
120
|
Returns:
|
|
105
121
|
Current open interest
|
|
106
122
|
"""
|
|
107
|
-
data = self._http.get(f"/
|
|
123
|
+
data = self._http.get(f"{self._base_path}/openinterest/{coin.upper()}/current")
|
|
108
124
|
return OpenInterest.model_validate(data["data"])
|
|
109
125
|
|
|
110
126
|
async def acurrent(self, coin: str) -> OpenInterest:
|
|
111
127
|
"""Async version of current()."""
|
|
112
|
-
data = await self._http.aget(f"/
|
|
128
|
+
data = await self._http.aget(f"{self._base_path}/openinterest/{coin.upper()}/current")
|
|
113
129
|
return OpenInterest.model_validate(data["data"])
|
oxarchive/resources/orderbook.py
CHANGED
|
@@ -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:
|
|
@@ -14,18 +14,22 @@ class OrderBookResource:
|
|
|
14
14
|
Order book API resource.
|
|
15
15
|
|
|
16
16
|
Example:
|
|
17
|
-
>>> # Get current order book
|
|
18
|
-
>>> orderbook = client.orderbook.get("BTC")
|
|
17
|
+
>>> # Get current order book (Hyperliquid)
|
|
18
|
+
>>> orderbook = client.hyperliquid.orderbook.get("BTC")
|
|
19
19
|
>>>
|
|
20
20
|
>>> # Get order book at specific timestamp
|
|
21
|
-
>>> historical = client.orderbook.get("ETH", timestamp=1704067200000)
|
|
21
|
+
>>> historical = client.hyperliquid.orderbook.get("ETH", timestamp=1704067200000)
|
|
22
22
|
>>>
|
|
23
23
|
>>> # Get order book history
|
|
24
|
-
>>> history = client.orderbook.history("BTC", start="2024-01-01", end="2024-01-02")
|
|
24
|
+
>>> history = client.hyperliquid.orderbook.history("BTC", start="2024-01-01", end="2024-01-02")
|
|
25
|
+
>>>
|
|
26
|
+
>>> # Lighter.xyz order book
|
|
27
|
+
>>> lighter_ob = client.lighter.orderbook.get("BTC")
|
|
25
28
|
"""
|
|
26
29
|
|
|
27
|
-
def __init__(self, http: HttpClient):
|
|
30
|
+
def __init__(self, http: HttpClient, base_path: str = "/v1"):
|
|
28
31
|
self._http = http
|
|
32
|
+
self._base_path = base_path
|
|
29
33
|
|
|
30
34
|
def _convert_timestamp(self, ts: Optional[Timestamp]) -> Optional[int]:
|
|
31
35
|
"""Convert timestamp to Unix milliseconds."""
|
|
@@ -63,7 +67,7 @@ class OrderBookResource:
|
|
|
63
67
|
Order book snapshot
|
|
64
68
|
"""
|
|
65
69
|
data = self._http.get(
|
|
66
|
-
f"/
|
|
70
|
+
f"{self._base_path}/orderbook/{coin.upper()}",
|
|
67
71
|
params={
|
|
68
72
|
"timestamp": self._convert_timestamp(timestamp),
|
|
69
73
|
"depth": depth,
|
|
@@ -80,7 +84,7 @@ class OrderBookResource:
|
|
|
80
84
|
) -> OrderBook:
|
|
81
85
|
"""Async version of get()."""
|
|
82
86
|
data = await self._http.aget(
|
|
83
|
-
f"/
|
|
87
|
+
f"{self._base_path}/orderbook/{coin.upper()}",
|
|
84
88
|
params={
|
|
85
89
|
"timestamp": self._convert_timestamp(timestamp),
|
|
86
90
|
"depth": depth,
|
|
@@ -94,35 +98,47 @@ class OrderBookResource:
|
|
|
94
98
|
*,
|
|
95
99
|
start: Timestamp,
|
|
96
100
|
end: Timestamp,
|
|
101
|
+
cursor: Optional[Timestamp] = None,
|
|
97
102
|
limit: Optional[int] = None,
|
|
98
|
-
offset: Optional[int] = None,
|
|
99
103
|
depth: Optional[int] = None,
|
|
100
|
-
) -> list[OrderBook]:
|
|
104
|
+
) -> CursorResponse[list[OrderBook]]:
|
|
101
105
|
"""
|
|
102
|
-
Get historical order book snapshots.
|
|
106
|
+
Get historical order book snapshots with cursor-based pagination.
|
|
103
107
|
|
|
104
108
|
Args:
|
|
105
109
|
coin: The coin symbol (e.g., 'BTC', 'ETH')
|
|
106
110
|
start: Start timestamp (required)
|
|
107
111
|
end: End timestamp (required)
|
|
108
|
-
|
|
109
|
-
|
|
112
|
+
cursor: Cursor from previous response's next_cursor (timestamp)
|
|
113
|
+
limit: Maximum number of results (default: 100, max: 1000)
|
|
110
114
|
depth: Number of price levels per side
|
|
111
115
|
|
|
112
116
|
Returns:
|
|
113
|
-
|
|
117
|
+
CursorResponse with order book snapshots and next_cursor for pagination
|
|
118
|
+
|
|
119
|
+
Example:
|
|
120
|
+
>>> result = client.orderbook.history("BTC", start=start, end=end, limit=1000)
|
|
121
|
+
>>> snapshots = result.data
|
|
122
|
+
>>> while result.next_cursor:
|
|
123
|
+
... result = client.orderbook.history(
|
|
124
|
+
... "BTC", start=start, end=end, cursor=result.next_cursor, limit=1000
|
|
125
|
+
... )
|
|
126
|
+
... snapshots.extend(result.data)
|
|
114
127
|
"""
|
|
115
128
|
data = self._http.get(
|
|
116
|
-
f"/
|
|
129
|
+
f"{self._base_path}/orderbook/{coin.upper()}/history",
|
|
117
130
|
params={
|
|
118
131
|
"start": self._convert_timestamp(start),
|
|
119
132
|
"end": self._convert_timestamp(end),
|
|
133
|
+
"cursor": self._convert_timestamp(cursor),
|
|
120
134
|
"limit": limit,
|
|
121
|
-
"offset": offset,
|
|
122
135
|
"depth": depth,
|
|
123
136
|
},
|
|
124
137
|
)
|
|
125
|
-
return
|
|
138
|
+
return CursorResponse(
|
|
139
|
+
data=[OrderBook.model_validate(item) for item in data["data"]],
|
|
140
|
+
next_cursor=data.get("meta", {}).get("next_cursor"),
|
|
141
|
+
)
|
|
126
142
|
|
|
127
143
|
async def ahistory(
|
|
128
144
|
self,
|
|
@@ -130,19 +146,22 @@ class OrderBookResource:
|
|
|
130
146
|
*,
|
|
131
147
|
start: Timestamp,
|
|
132
148
|
end: Timestamp,
|
|
149
|
+
cursor: Optional[Timestamp] = None,
|
|
133
150
|
limit: Optional[int] = None,
|
|
134
|
-
offset: Optional[int] = None,
|
|
135
151
|
depth: Optional[int] = None,
|
|
136
|
-
) -> list[OrderBook]:
|
|
152
|
+
) -> CursorResponse[list[OrderBook]]:
|
|
137
153
|
"""Async version of history(). start and end are required."""
|
|
138
154
|
data = await self._http.aget(
|
|
139
|
-
f"/
|
|
155
|
+
f"{self._base_path}/orderbook/{coin.upper()}/history",
|
|
140
156
|
params={
|
|
141
157
|
"start": self._convert_timestamp(start),
|
|
142
158
|
"end": self._convert_timestamp(end),
|
|
159
|
+
"cursor": self._convert_timestamp(cursor),
|
|
143
160
|
"limit": limit,
|
|
144
|
-
"offset": offset,
|
|
145
161
|
"depth": depth,
|
|
146
162
|
},
|
|
147
163
|
)
|
|
148
|
-
return
|
|
164
|
+
return CursorResponse(
|
|
165
|
+
data=[OrderBook.model_validate(item) for item in data["data"]],
|
|
166
|
+
next_cursor=data.get("meta", {}).get("next_cursor"),
|
|
167
|
+
)
|
oxarchive/resources/trades.py
CHANGED
|
@@ -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:
|
|
@@ -36,8 +27,9 @@ class TradesResource:
|
|
|
36
27
|
... trades.extend(result.data)
|
|
37
28
|
"""
|
|
38
29
|
|
|
39
|
-
def __init__(self, http: HttpClient):
|
|
30
|
+
def __init__(self, http: HttpClient, base_path: str = "/v1"):
|
|
40
31
|
self._http = http
|
|
32
|
+
self._base_path = base_path
|
|
41
33
|
|
|
42
34
|
def _convert_timestamp(self, ts: Optional[Timestamp]) -> Optional[int]:
|
|
43
35
|
"""Convert timestamp to Unix milliseconds."""
|
|
@@ -64,7 +56,7 @@ class TradesResource:
|
|
|
64
56
|
cursor: Optional[Timestamp] = None,
|
|
65
57
|
limit: Optional[int] = None,
|
|
66
58
|
side: Optional[Literal["buy", "sell"]] = None,
|
|
67
|
-
) -> CursorResponse:
|
|
59
|
+
) -> CursorResponse[list[Trade]]:
|
|
68
60
|
"""
|
|
69
61
|
Get trade history for a coin using cursor-based pagination.
|
|
70
62
|
|
|
@@ -95,7 +87,7 @@ class TradesResource:
|
|
|
95
87
|
... trades.extend(result.data)
|
|
96
88
|
"""
|
|
97
89
|
data = self._http.get(
|
|
98
|
-
f"/
|
|
90
|
+
f"{self._base_path}/trades/{coin.upper()}",
|
|
99
91
|
params={
|
|
100
92
|
"start": self._convert_timestamp(start),
|
|
101
93
|
"end": self._convert_timestamp(end),
|
|
@@ -118,14 +110,14 @@ class TradesResource:
|
|
|
118
110
|
cursor: Optional[Timestamp] = None,
|
|
119
111
|
limit: Optional[int] = None,
|
|
120
112
|
side: Optional[Literal["buy", "sell"]] = None,
|
|
121
|
-
) -> CursorResponse:
|
|
113
|
+
) -> CursorResponse[list[Trade]]:
|
|
122
114
|
"""
|
|
123
115
|
Async version of list().
|
|
124
116
|
|
|
125
117
|
Uses cursor-based pagination by default.
|
|
126
118
|
"""
|
|
127
119
|
data = await self._http.aget(
|
|
128
|
-
f"/
|
|
120
|
+
f"{self._base_path}/trades/{coin.upper()}",
|
|
129
121
|
params={
|
|
130
122
|
"start": self._convert_timestamp(start),
|
|
131
123
|
"end": self._convert_timestamp(end),
|
|
@@ -139,83 +131,6 @@ class TradesResource:
|
|
|
139
131
|
next_cursor=data.get("meta", {}).get("next_cursor"),
|
|
140
132
|
)
|
|
141
133
|
|
|
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
134
|
def recent(self, coin: str, limit: Optional[int] = None) -> list[Trade]:
|
|
220
135
|
"""
|
|
221
136
|
Get most recent trades for a coin.
|
|
@@ -228,7 +143,7 @@ class TradesResource:
|
|
|
228
143
|
List of recent trades
|
|
229
144
|
"""
|
|
230
145
|
data = self._http.get(
|
|
231
|
-
f"/
|
|
146
|
+
f"{self._base_path}/trades/{coin.upper()}/recent",
|
|
232
147
|
params={"limit": limit},
|
|
233
148
|
)
|
|
234
149
|
return [Trade.model_validate(item) for item in data["data"]]
|
|
@@ -236,64 +151,7 @@ class TradesResource:
|
|
|
236
151
|
async def arecent(self, coin: str, limit: Optional[int] = None) -> list[Trade]:
|
|
237
152
|
"""Async version of recent()."""
|
|
238
153
|
data = await self._http.aget(
|
|
239
|
-
f"/
|
|
154
|
+
f"{self._base_path}/trades/{coin.upper()}/recent",
|
|
240
155
|
params={"limit": limit},
|
|
241
156
|
)
|
|
242
157
|
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."""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: oxarchive
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
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
|
|
@@ -37,7 +37,11 @@ Description-Content-Type: text/markdown
|
|
|
37
37
|
|
|
38
38
|
# oxarchive
|
|
39
39
|
|
|
40
|
-
Official Python SDK for [0xarchive](https://0xarchive.io) -
|
|
40
|
+
Official Python SDK for [0xarchive](https://0xarchive.io) - Historical Market Data API.
|
|
41
|
+
|
|
42
|
+
Supports multiple exchanges:
|
|
43
|
+
- **Hyperliquid** - Perpetuals data from April 2023
|
|
44
|
+
- **Lighter.xyz** - Perpetuals data with orderbook reconstruction
|
|
41
45
|
|
|
42
46
|
## Installation
|
|
43
47
|
|
|
@@ -58,12 +62,16 @@ from oxarchive import Client
|
|
|
58
62
|
|
|
59
63
|
client = Client(api_key="ox_your_api_key")
|
|
60
64
|
|
|
61
|
-
#
|
|
62
|
-
|
|
63
|
-
print(f"BTC mid price: {
|
|
65
|
+
# Hyperliquid data
|
|
66
|
+
hl_orderbook = client.hyperliquid.orderbook.get("BTC")
|
|
67
|
+
print(f"Hyperliquid BTC mid price: {hl_orderbook.mid_price}")
|
|
68
|
+
|
|
69
|
+
# Lighter.xyz data
|
|
70
|
+
lighter_orderbook = client.lighter.orderbook.get("BTC")
|
|
71
|
+
print(f"Lighter BTC mid price: {lighter_orderbook.mid_price}")
|
|
64
72
|
|
|
65
73
|
# Get historical order book snapshots
|
|
66
|
-
history = client.orderbook.history(
|
|
74
|
+
history = client.hyperliquid.orderbook.history(
|
|
67
75
|
"ETH",
|
|
68
76
|
start="2024-01-01",
|
|
69
77
|
end="2024-01-02",
|
|
@@ -82,10 +90,13 @@ from oxarchive import Client
|
|
|
82
90
|
async def main():
|
|
83
91
|
client = Client(api_key="ox_your_api_key")
|
|
84
92
|
|
|
85
|
-
# Async get
|
|
86
|
-
orderbook = await client.orderbook.aget("BTC")
|
|
93
|
+
# Async get (Hyperliquid)
|
|
94
|
+
orderbook = await client.hyperliquid.orderbook.aget("BTC")
|
|
87
95
|
print(f"BTC mid price: {orderbook.mid_price}")
|
|
88
96
|
|
|
97
|
+
# Async get (Lighter.xyz)
|
|
98
|
+
lighter_ob = await client.lighter.orderbook.aget("BTC")
|
|
99
|
+
|
|
89
100
|
# Don't forget to close the client
|
|
90
101
|
await client.aclose()
|
|
91
102
|
|
|
@@ -96,7 +107,7 @@ Or use as async context manager:
|
|
|
96
107
|
|
|
97
108
|
```python
|
|
98
109
|
async with Client(api_key="ox_your_api_key") as client:
|
|
99
|
-
orderbook = await client.orderbook.aget("BTC")
|
|
110
|
+
orderbook = await client.hyperliquid.orderbook.aget("BTC")
|
|
100
111
|
```
|
|
101
112
|
|
|
102
113
|
## Configuration
|
|
@@ -111,20 +122,25 @@ client = Client(
|
|
|
111
122
|
|
|
112
123
|
## REST API Reference
|
|
113
124
|
|
|
125
|
+
All examples use `client.hyperliquid.*` but the same methods are available on `client.lighter.*` for Lighter.xyz data.
|
|
126
|
+
|
|
114
127
|
### Order Book
|
|
115
128
|
|
|
116
129
|
```python
|
|
117
|
-
# Get current order book
|
|
118
|
-
orderbook = client.orderbook.get("BTC")
|
|
130
|
+
# Get current order book (Hyperliquid)
|
|
131
|
+
orderbook = client.hyperliquid.orderbook.get("BTC")
|
|
132
|
+
|
|
133
|
+
# Get current order book (Lighter.xyz)
|
|
134
|
+
orderbook = client.lighter.orderbook.get("BTC")
|
|
119
135
|
|
|
120
136
|
# Get order book at specific timestamp
|
|
121
|
-
historical = client.orderbook.get("BTC", timestamp=1704067200000)
|
|
137
|
+
historical = client.hyperliquid.orderbook.get("BTC", timestamp=1704067200000)
|
|
122
138
|
|
|
123
139
|
# Get with limited depth
|
|
124
|
-
shallow = client.orderbook.get("BTC", depth=10)
|
|
140
|
+
shallow = client.hyperliquid.orderbook.get("BTC", depth=10)
|
|
125
141
|
|
|
126
142
|
# Get historical snapshots (start and end are required)
|
|
127
|
-
history = client.orderbook.history(
|
|
143
|
+
history = client.hyperliquid.orderbook.history(
|
|
128
144
|
"BTC",
|
|
129
145
|
start="2024-01-01",
|
|
130
146
|
end="2024-01-02",
|
|
@@ -133,8 +149,8 @@ history = client.orderbook.history(
|
|
|
133
149
|
)
|
|
134
150
|
|
|
135
151
|
# Async versions
|
|
136
|
-
orderbook = await client.orderbook.aget("BTC")
|
|
137
|
-
history = await client.orderbook.ahistory("BTC", start=..., end=...)
|
|
152
|
+
orderbook = await client.hyperliquid.orderbook.aget("BTC")
|
|
153
|
+
history = await client.hyperliquid.orderbook.ahistory("BTC", start=..., end=...)
|
|
138
154
|
```
|
|
139
155
|
|
|
140
156
|
### Trades
|
|
@@ -143,15 +159,15 @@ The trades API uses cursor-based pagination for efficient retrieval of large dat
|
|
|
143
159
|
|
|
144
160
|
```python
|
|
145
161
|
# Get recent trades
|
|
146
|
-
recent = client.trades.recent("BTC", limit=100)
|
|
162
|
+
recent = client.hyperliquid.trades.recent("BTC", limit=100)
|
|
147
163
|
|
|
148
164
|
# Get trade history with cursor-based pagination
|
|
149
|
-
result = client.trades.list("ETH", start="2024-01-01", end="2024-01-02", limit=1000)
|
|
165
|
+
result = client.hyperliquid.trades.list("ETH", start="2024-01-01", end="2024-01-02", limit=1000)
|
|
150
166
|
trades = result.data
|
|
151
167
|
|
|
152
168
|
# Paginate through all results
|
|
153
169
|
while result.next_cursor:
|
|
154
|
-
result = client.trades.list(
|
|
170
|
+
result = client.hyperliquid.trades.list(
|
|
155
171
|
"ETH",
|
|
156
172
|
start="2024-01-01",
|
|
157
173
|
end="2024-01-02",
|
|
@@ -161,61 +177,73 @@ while result.next_cursor:
|
|
|
161
177
|
trades.extend(result.data)
|
|
162
178
|
|
|
163
179
|
# Filter by side
|
|
164
|
-
buys = client.trades.list("BTC", start=..., end=..., side="buy")
|
|
180
|
+
buys = client.hyperliquid.trades.list("BTC", start=..., end=..., side="buy")
|
|
165
181
|
|
|
166
182
|
# Async versions
|
|
167
|
-
recent = await client.trades.arecent("BTC")
|
|
168
|
-
result = await client.trades.alist("ETH", start=..., end=...)
|
|
183
|
+
recent = await client.hyperliquid.trades.arecent("BTC")
|
|
184
|
+
result = await client.hyperliquid.trades.alist("ETH", start=..., end=...)
|
|
169
185
|
```
|
|
170
186
|
|
|
171
187
|
### Instruments
|
|
172
188
|
|
|
173
189
|
```python
|
|
174
190
|
# List all trading instruments
|
|
175
|
-
instruments = client.instruments.list()
|
|
191
|
+
instruments = client.hyperliquid.instruments.list()
|
|
176
192
|
|
|
177
193
|
# Get specific instrument details
|
|
178
|
-
btc = client.instruments.get("BTC")
|
|
194
|
+
btc = client.hyperliquid.instruments.get("BTC")
|
|
179
195
|
|
|
180
196
|
# Async versions
|
|
181
|
-
instruments = await client.instruments.alist()
|
|
182
|
-
btc = await client.instruments.aget("BTC")
|
|
197
|
+
instruments = await client.hyperliquid.instruments.alist()
|
|
198
|
+
btc = await client.hyperliquid.instruments.aget("BTC")
|
|
183
199
|
```
|
|
184
200
|
|
|
185
201
|
### Funding Rates
|
|
186
202
|
|
|
187
203
|
```python
|
|
188
204
|
# Get current funding rate
|
|
189
|
-
current = client.funding.current("BTC")
|
|
205
|
+
current = client.hyperliquid.funding.current("BTC")
|
|
190
206
|
|
|
191
207
|
# Get funding rate history (start is required)
|
|
192
|
-
history = client.funding.history(
|
|
208
|
+
history = client.hyperliquid.funding.history(
|
|
193
209
|
"ETH",
|
|
194
210
|
start="2024-01-01",
|
|
195
211
|
end="2024-01-07"
|
|
196
212
|
)
|
|
197
213
|
|
|
198
214
|
# Async versions
|
|
199
|
-
current = await client.funding.acurrent("BTC")
|
|
200
|
-
history = await client.funding.ahistory("ETH", start=..., end=...)
|
|
215
|
+
current = await client.hyperliquid.funding.acurrent("BTC")
|
|
216
|
+
history = await client.hyperliquid.funding.ahistory("ETH", start=..., end=...)
|
|
201
217
|
```
|
|
202
218
|
|
|
203
219
|
### Open Interest
|
|
204
220
|
|
|
205
221
|
```python
|
|
206
222
|
# Get current open interest
|
|
207
|
-
current = client.open_interest.current("BTC")
|
|
223
|
+
current = client.hyperliquid.open_interest.current("BTC")
|
|
208
224
|
|
|
209
225
|
# Get open interest history (start is required)
|
|
210
|
-
history = client.open_interest.history(
|
|
226
|
+
history = client.hyperliquid.open_interest.history(
|
|
211
227
|
"ETH",
|
|
212
228
|
start="2024-01-01",
|
|
213
229
|
end="2024-01-07"
|
|
214
230
|
)
|
|
215
231
|
|
|
216
232
|
# Async versions
|
|
217
|
-
current = await client.open_interest.acurrent("BTC")
|
|
218
|
-
history = await client.open_interest.ahistory("ETH", start=..., end=...)
|
|
233
|
+
current = await client.hyperliquid.open_interest.acurrent("BTC")
|
|
234
|
+
history = await client.hyperliquid.open_interest.ahistory("ETH", start=..., end=...)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Legacy API (Deprecated)
|
|
238
|
+
|
|
239
|
+
The following legacy methods are deprecated and will be removed in v2.0. They default to Hyperliquid data:
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
# Deprecated - use client.hyperliquid.orderbook.get() instead
|
|
243
|
+
orderbook = client.orderbook.get("BTC")
|
|
244
|
+
|
|
245
|
+
# Deprecated - use client.hyperliquid.trades.list() instead
|
|
246
|
+
trades = client.trades.list("BTC", start=..., end=...)
|
|
219
247
|
```
|
|
220
248
|
|
|
221
249
|
## WebSocket Client
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
oxarchive/__init__.py,sha256=lBRtHH7NMlNqEN783KdsvFgmd3mT1B8UDcfFo2TW-_w,2536
|
|
2
|
+
oxarchive/client.py,sha256=uoZ_1oxL13M-TvysQdd4qiyRM5MkrMhdSXTCzr5rtw4,4490
|
|
3
|
+
oxarchive/exchanges.py,sha256=fujxX--IKVgJbuJpxrmYZhgMyjkOIPY3TiX1oX9RhZE,2301
|
|
4
|
+
oxarchive/http.py,sha256=SY_o9Ag8ADo1HI3i3uAKW1xwkYjPE75gRAjnMsddAGs,4211
|
|
5
|
+
oxarchive/types.py,sha256=RUnni6RMGHxK3-tTajcDVtT3Rk_sJqlOJLiG5SMc3Do,11278
|
|
6
|
+
oxarchive/websocket.py,sha256=MyffJxaabDBonECcJg9vF4hAQ_4thjP4DaS7MaR4uno,28381
|
|
7
|
+
oxarchive/resources/__init__.py,sha256=WQ4GYQ8p3L0D2Isk4IV4h1DRpvyZlt6tOF1t_CJr6ls,385
|
|
8
|
+
oxarchive/resources/funding.py,sha256=ybMWkpoccrkdwnd6W3oHgsaor7cBcA2nkYy4CbjmHUg,4485
|
|
9
|
+
oxarchive/resources/instruments.py,sha256=9XAqYcwiJ1UXh17AgfKHYC-ExOLjn4hkmxmOTQdhcY4,1725
|
|
10
|
+
oxarchive/resources/openinterest.py,sha256=whwo60KFNLGwvVrDmDYYc-rMZr35Fcizb5Iil-DSvD8,4553
|
|
11
|
+
oxarchive/resources/orderbook.py,sha256=-paXBdGYBCMX-LK-MeXIKlcFz97iW2UWn9Am2XCgg2U,5717
|
|
12
|
+
oxarchive/resources/trades.py,sha256=t4iicyi1waaHzC7Q_cC-c7O4yVx1vqS4eMH8F8_5hxk,5503
|
|
13
|
+
oxarchive-0.4.0.dist-info/METADATA,sha256=yNo8AmElD6sG8Fb2c3qKf8Tg01CS041-9wnFxWb9CFQ,12397
|
|
14
|
+
oxarchive-0.4.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
15
|
+
oxarchive-0.4.0.dist-info/RECORD,,
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
oxarchive/__init__.py,sha256=AQVwSQ6yMPb6Dm5mqRyLuM0gk4XvaqffLMTdUaSFhdg,2182
|
|
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=MyffJxaabDBonECcJg9vF4hAQ_4thjP4DaS7MaR4uno,28381
|
|
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.10.dist-info/METADATA,sha256=rPdO6lBAEQfApyJFllhU6vcuBEYpj7i3iLyR2-NaP9c,11071
|
|
13
|
-
oxarchive-0.3.10.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
14
|
-
oxarchive-0.3.10.dist-info/RECORD,,
|
|
File without changes
|