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 CHANGED
@@ -1,22 +1,28 @@
1
1
  """
2
2
  oxarchive - Official Python SDK for 0xarchive
3
3
 
4
- Hyperliquid Historical Data API.
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
- >>> # Get current order book
12
- >>> orderbook = client.orderbook.get("BTC")
13
- >>> print(f"BTC mid price: {orderbook.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.3.10"
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
- >>> # Get current order book
30
- >>> orderbook = client.orderbook.get("BTC")
31
- >>> print(f"BTC mid price: {orderbook.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
- # Initialize resource namespaces
77
- self.orderbook = OrderBookResource(self._http)
78
- """Order book data (L2 snapshots from April 2023)"""
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
- """Trade/fill history"""
102
+ self.trades = TradesResource(self._http, legacy_base)
103
+ """[DEPRECATED] Use client.hyperliquid.trades instead"""
82
104
 
83
- self.instruments = InstrumentsResource(self._http)
84
- """Trading instruments metadata"""
105
+ self.instruments = InstrumentsResource(self._http, legacy_base)
106
+ """[DEPRECATED] Use client.hyperliquid.instruments instead"""
85
107
 
86
- self.funding = FundingResource(self._http)
87
- """Funding rates"""
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
- """Open interest"""
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"""
@@ -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
- offset: Optional[int] = None,
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
- limit: Maximum number of results
60
- offset: Number of results to skip
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
- List of funding rate records
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"/v1/funding/{coin.upper()}",
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 [FundingRate.model_validate(item) for item in data["data"]]
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
- offset: Optional[int] = None,
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"/v1/funding/{coin.upper()}",
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 [FundingRate.model_validate(item) for item in data["data"]]
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"/v1/funding/{coin.upper()}/current")
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"/v1/funding/{coin.upper()}/current")
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("/v1/instruments")
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("/v1/instruments")
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"/v1/instruments/{coin.upper()}")
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"/v1/instruments/{coin.upper()}")
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
- offset: Optional[int] = None,
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
- limit: Maximum number of results
60
- offset: Number of results to skip
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
- List of open interest records
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"/v1/openinterest/{coin.upper()}",
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 [OpenInterest.model_validate(item) for item in data["data"]]
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
- offset: Optional[int] = None,
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"/v1/openinterest/{coin.upper()}",
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 [OpenInterest.model_validate(item) for item in data["data"]]
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"/v1/openinterest/{coin.upper()}/current")
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"/v1/openinterest/{coin.upper()}/current")
128
+ data = await self._http.aget(f"{self._base_path}/openinterest/{coin.upper()}/current")
113
129
  return OpenInterest.model_validate(data["data"])
@@ -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"/v1/orderbook/{coin.upper()}",
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"/v1/orderbook/{coin.upper()}",
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
- limit: Maximum number of results
109
- offset: Number of results to skip
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
- List of order book snapshots
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"/v1/orderbook/{coin.upper()}/history",
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 [OrderBook.model_validate(item) for item in data["data"]]
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"/v1/orderbook/{coin.upper()}/history",
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 [OrderBook.model_validate(item) for item in data["data"]]
164
+ return CursorResponse(
165
+ data=[OrderBook.model_validate(item) for item in data["data"]],
166
+ next_cursor=data.get("meta", {}).get("next_cursor"),
167
+ )
@@ -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"/v1/trades/{coin.upper()}",
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"/v1/trades/{coin.upper()}",
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"/v1/trades/{coin.upper()}/recent",
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"/v1/trades/{coin.upper()}/recent",
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.10
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) - Hyperliquid Historical Data API.
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
- # Get current order book
62
- orderbook = client.orderbook.get("BTC")
63
- print(f"BTC mid price: {orderbook.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,,