oxarchive 0.3.6__py3-none-any.whl → 0.4.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 +19 -6
- oxarchive/client.py +39 -17
- oxarchive/exchanges.py +79 -0
- oxarchive/resources/__init__.py +2 -1
- oxarchive/resources/funding.py +34 -18
- oxarchive/resources/instruments.py +61 -6
- oxarchive/resources/openinterest.py +34 -18
- oxarchive/resources/orderbook.py +59 -23
- oxarchive/resources/trades.py +9 -151
- oxarchive/types.py +63 -4
- oxarchive/websocket.py +6 -7
- {oxarchive-0.3.6.dist-info → oxarchive-0.4.3.dist-info}/METADATA +140 -39
- oxarchive-0.4.3.dist-info/RECORD +15 -0
- oxarchive-0.3.6.dist-info/RECORD +0 -14
- {oxarchive-0.3.6.dist-info → oxarchive-0.4.3.dist-info}/WHEEL +0 -0
oxarchive/__init__.py
CHANGED
|
@@ -1,26 +1,34 @@
|
|
|
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
|
|
26
|
+
from .resources.orderbook import LighterGranularity
|
|
20
27
|
from .types import (
|
|
21
28
|
OrderBook,
|
|
22
29
|
Trade,
|
|
23
30
|
Instrument,
|
|
31
|
+
LighterInstrument,
|
|
24
32
|
FundingRate,
|
|
25
33
|
OpenInterest,
|
|
26
34
|
OxArchiveError,
|
|
@@ -57,11 +65,14 @@ except ImportError:
|
|
|
57
65
|
OxArchiveWs = None # type: ignore
|
|
58
66
|
WsOptions = None # type: ignore
|
|
59
67
|
|
|
60
|
-
__version__ = "0.3
|
|
68
|
+
__version__ = "0.4.3"
|
|
61
69
|
|
|
62
70
|
__all__ = [
|
|
63
71
|
# Client
|
|
64
72
|
"Client",
|
|
73
|
+
# Exchange Clients
|
|
74
|
+
"HyperliquidClient",
|
|
75
|
+
"LighterClient",
|
|
65
76
|
# WebSocket Client
|
|
66
77
|
"OxArchiveWs",
|
|
67
78
|
"WsOptions",
|
|
@@ -69,6 +80,8 @@ __all__ = [
|
|
|
69
80
|
"OrderBook",
|
|
70
81
|
"Trade",
|
|
71
82
|
"Instrument",
|
|
83
|
+
"LighterInstrument",
|
|
84
|
+
"LighterGranularity",
|
|
72
85
|
"FundingRate",
|
|
73
86
|
"OpenInterest",
|
|
74
87
|
"OxArchiveError",
|
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 (August 2025+)"""
|
|
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,79 @@
|
|
|
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
|
+
LighterInstrumentsResource,
|
|
11
|
+
FundingResource,
|
|
12
|
+
OpenInterestResource,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class HyperliquidClient:
|
|
17
|
+
"""
|
|
18
|
+
Hyperliquid exchange client.
|
|
19
|
+
|
|
20
|
+
Access Hyperliquid market data through the 0xarchive API.
|
|
21
|
+
|
|
22
|
+
Example:
|
|
23
|
+
>>> client = oxarchive.Client(api_key="...")
|
|
24
|
+
>>> orderbook = client.hyperliquid.orderbook.get("BTC")
|
|
25
|
+
>>> trades = client.hyperliquid.trades.list("ETH", start=..., end=...)
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, http: HttpClient):
|
|
29
|
+
self._http = http
|
|
30
|
+
base_path = "/v1/hyperliquid"
|
|
31
|
+
|
|
32
|
+
self.orderbook = OrderBookResource(http, base_path)
|
|
33
|
+
"""Order book data (L2 snapshots from April 2023)"""
|
|
34
|
+
|
|
35
|
+
self.trades = TradesResource(http, base_path)
|
|
36
|
+
"""Trade/fill history"""
|
|
37
|
+
|
|
38
|
+
self.instruments = InstrumentsResource(http, base_path)
|
|
39
|
+
"""Trading instruments metadata"""
|
|
40
|
+
|
|
41
|
+
self.funding = FundingResource(http, base_path)
|
|
42
|
+
"""Funding rates"""
|
|
43
|
+
|
|
44
|
+
self.open_interest = OpenInterestResource(http, base_path)
|
|
45
|
+
"""Open interest"""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class LighterClient:
|
|
49
|
+
"""
|
|
50
|
+
Lighter.xyz exchange client.
|
|
51
|
+
|
|
52
|
+
Access Lighter.xyz market data through the 0xarchive API.
|
|
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
|
+
>>> instruments = client.lighter.instruments.list()
|
|
59
|
+
>>> print(f"ETH taker fee: {instruments[0].taker_fee}")
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(self, http: HttpClient):
|
|
63
|
+
self._http = http
|
|
64
|
+
base_path = "/v1/lighter"
|
|
65
|
+
|
|
66
|
+
self.orderbook = OrderBookResource(http, base_path)
|
|
67
|
+
"""Order book data (L2 snapshots)"""
|
|
68
|
+
|
|
69
|
+
self.trades = TradesResource(http, base_path)
|
|
70
|
+
"""Trade/fill history"""
|
|
71
|
+
|
|
72
|
+
self.instruments = LighterInstrumentsResource(http, base_path)
|
|
73
|
+
"""Trading instruments metadata (returns LighterInstrument with fees, min amounts, etc.)"""
|
|
74
|
+
|
|
75
|
+
self.funding = FundingResource(http, base_path)
|
|
76
|
+
"""Funding rates"""
|
|
77
|
+
|
|
78
|
+
self.open_interest = OpenInterestResource(http, base_path)
|
|
79
|
+
"""Open interest"""
|
oxarchive/resources/__init__.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from .orderbook import OrderBookResource
|
|
4
4
|
from .trades import TradesResource
|
|
5
|
-
from .instruments import InstrumentsResource
|
|
5
|
+
from .instruments import InstrumentsResource, LighterInstrumentsResource
|
|
6
6
|
from .funding import FundingResource
|
|
7
7
|
from .openinterest import OpenInterestResource
|
|
8
8
|
|
|
@@ -10,6 +10,7 @@ __all__ = [
|
|
|
10
10
|
"OrderBookResource",
|
|
11
11
|
"TradesResource",
|
|
12
12
|
"InstrumentsResource",
|
|
13
|
+
"LighterInstrumentsResource",
|
|
13
14
|
"FundingResource",
|
|
14
15
|
"OpenInterestResource",
|
|
15
16
|
]
|
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"])
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from ..http import HttpClient
|
|
6
|
-
from ..types import Instrument
|
|
6
|
+
from ..types import Instrument, LighterInstrument
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class InstrumentsResource:
|
|
@@ -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,64 @@ 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"])
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class LighterInstrumentsResource:
|
|
60
|
+
"""
|
|
61
|
+
Lighter.xyz Instruments API resource.
|
|
62
|
+
|
|
63
|
+
Lighter instruments have a different schema than Hyperliquid with more
|
|
64
|
+
detailed market configuration including fees and minimum amounts.
|
|
65
|
+
|
|
66
|
+
Example:
|
|
67
|
+
>>> # List all Lighter instruments
|
|
68
|
+
>>> instruments = client.lighter.instruments.list()
|
|
69
|
+
>>>
|
|
70
|
+
>>> # Get specific instrument
|
|
71
|
+
>>> btc = client.lighter.instruments.get("BTC")
|
|
72
|
+
>>> print(f"Taker fee: {btc.taker_fee}")
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
def __init__(self, http: HttpClient, base_path: str = "/v1/lighter"):
|
|
76
|
+
self._http = http
|
|
77
|
+
self._base_path = base_path
|
|
78
|
+
|
|
79
|
+
def list(self) -> list[LighterInstrument]:
|
|
80
|
+
"""
|
|
81
|
+
List all available Lighter trading instruments.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
List of Lighter instruments with full market configuration
|
|
85
|
+
"""
|
|
86
|
+
data = self._http.get(f"{self._base_path}/instruments")
|
|
87
|
+
return [LighterInstrument.model_validate(item) for item in data["data"]]
|
|
88
|
+
|
|
89
|
+
async def alist(self) -> list[LighterInstrument]:
|
|
90
|
+
"""Async version of list()."""
|
|
91
|
+
data = await self._http.aget(f"{self._base_path}/instruments")
|
|
92
|
+
return [LighterInstrument.model_validate(item) for item in data["data"]]
|
|
93
|
+
|
|
94
|
+
def get(self, coin: str) -> LighterInstrument:
|
|
95
|
+
"""
|
|
96
|
+
Get a specific Lighter instrument by coin symbol.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
coin: The coin symbol (e.g., 'BTC', 'ETH')
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
Lighter instrument details with full market configuration
|
|
103
|
+
"""
|
|
104
|
+
data = self._http.get(f"{self._base_path}/instruments/{coin.upper()}")
|
|
105
|
+
return LighterInstrument.model_validate(data["data"])
|
|
106
|
+
|
|
107
|
+
async def aget(self, coin: str) -> LighterInstrument:
|
|
108
|
+
"""Async version of get()."""
|
|
109
|
+
data = await self._http.aget(f"{self._base_path}/instruments/{coin.upper()}")
|
|
110
|
+
return LighterInstrument.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"])
|