oxarchive 0.1.1__py3-none-any.whl → 0.2.2__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 +0 -4
- oxarchive/client.py +0 -4
- oxarchive/resources/__init__.py +0 -2
- oxarchive/resources/funding.py +7 -7
- oxarchive/resources/openinterest.py +7 -7
- oxarchive/resources/orderbook.py +7 -7
- oxarchive/resources/trades.py +7 -7
- oxarchive/types.py +167 -63
- oxarchive/websocket.py +81 -20
- {oxarchive-0.1.1.dist-info → oxarchive-0.2.2.dist-info}/METADATA +5 -19
- oxarchive-0.2.2.dist-info/RECORD +14 -0
- oxarchive/resources/candles.py +0 -100
- oxarchive-0.1.1.dist-info/RECORD +0 -15
- {oxarchive-0.1.1.dist-info → oxarchive-0.2.2.dist-info}/WHEEL +0 -0
oxarchive/__init__.py
CHANGED
|
@@ -20,8 +20,6 @@ from .client import Client
|
|
|
20
20
|
from .types import (
|
|
21
21
|
OrderBook,
|
|
22
22
|
Trade,
|
|
23
|
-
Candle,
|
|
24
|
-
CandleInterval,
|
|
25
23
|
Instrument,
|
|
26
24
|
FundingRate,
|
|
27
25
|
OpenInterest,
|
|
@@ -70,8 +68,6 @@ __all__ = [
|
|
|
70
68
|
# Types
|
|
71
69
|
"OrderBook",
|
|
72
70
|
"Trade",
|
|
73
|
-
"Candle",
|
|
74
|
-
"CandleInterval",
|
|
75
71
|
"Instrument",
|
|
76
72
|
"FundingRate",
|
|
77
73
|
"OpenInterest",
|
oxarchive/client.py
CHANGED
|
@@ -8,7 +8,6 @@ from .http import HttpClient
|
|
|
8
8
|
from .resources import (
|
|
9
9
|
OrderBookResource,
|
|
10
10
|
TradesResource,
|
|
11
|
-
CandlesResource,
|
|
12
11
|
InstrumentsResource,
|
|
13
12
|
FundingResource,
|
|
14
13
|
OpenInterestResource,
|
|
@@ -81,9 +80,6 @@ class Client:
|
|
|
81
80
|
self.trades = TradesResource(self._http)
|
|
82
81
|
"""Trade/fill history"""
|
|
83
82
|
|
|
84
|
-
self.candles = CandlesResource(self._http)
|
|
85
|
-
"""OHLCV candles"""
|
|
86
|
-
|
|
87
83
|
self.instruments = InstrumentsResource(self._http)
|
|
88
84
|
"""Trading instruments metadata"""
|
|
89
85
|
|
oxarchive/resources/__init__.py
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from .orderbook import OrderBookResource
|
|
4
4
|
from .trades import TradesResource
|
|
5
|
-
from .candles import CandlesResource
|
|
6
5
|
from .instruments import InstrumentsResource
|
|
7
6
|
from .funding import FundingResource
|
|
8
7
|
from .openinterest import OpenInterestResource
|
|
@@ -10,7 +9,6 @@ from .openinterest import OpenInterestResource
|
|
|
10
9
|
__all__ = [
|
|
11
10
|
"OrderBookResource",
|
|
12
11
|
"TradesResource",
|
|
13
|
-
"CandlesResource",
|
|
14
12
|
"InstrumentsResource",
|
|
15
13
|
"FundingResource",
|
|
16
14
|
"OpenInterestResource",
|
oxarchive/resources/funding.py
CHANGED
|
@@ -44,8 +44,8 @@ class FundingResource:
|
|
|
44
44
|
self,
|
|
45
45
|
coin: str,
|
|
46
46
|
*,
|
|
47
|
-
start:
|
|
48
|
-
end:
|
|
47
|
+
start: Timestamp,
|
|
48
|
+
end: Timestamp,
|
|
49
49
|
limit: Optional[int] = None,
|
|
50
50
|
offset: Optional[int] = None,
|
|
51
51
|
) -> list[FundingRate]:
|
|
@@ -54,8 +54,8 @@ class FundingResource:
|
|
|
54
54
|
|
|
55
55
|
Args:
|
|
56
56
|
coin: The coin symbol (e.g., 'BTC', 'ETH')
|
|
57
|
-
start: Start timestamp
|
|
58
|
-
end: End timestamp
|
|
57
|
+
start: Start timestamp (required)
|
|
58
|
+
end: End timestamp (required)
|
|
59
59
|
limit: Maximum number of results
|
|
60
60
|
offset: Number of results to skip
|
|
61
61
|
|
|
@@ -77,12 +77,12 @@ class FundingResource:
|
|
|
77
77
|
self,
|
|
78
78
|
coin: str,
|
|
79
79
|
*,
|
|
80
|
-
start:
|
|
81
|
-
end:
|
|
80
|
+
start: Timestamp,
|
|
81
|
+
end: Timestamp,
|
|
82
82
|
limit: Optional[int] = None,
|
|
83
83
|
offset: Optional[int] = None,
|
|
84
84
|
) -> list[FundingRate]:
|
|
85
|
-
"""Async version of history()."""
|
|
85
|
+
"""Async version of history(). start and end are required."""
|
|
86
86
|
data = await self._http.aget(
|
|
87
87
|
f"/v1/funding/{coin.upper()}",
|
|
88
88
|
params={
|
|
@@ -44,8 +44,8 @@ class OpenInterestResource:
|
|
|
44
44
|
self,
|
|
45
45
|
coin: str,
|
|
46
46
|
*,
|
|
47
|
-
start:
|
|
48
|
-
end:
|
|
47
|
+
start: Timestamp,
|
|
48
|
+
end: Timestamp,
|
|
49
49
|
limit: Optional[int] = None,
|
|
50
50
|
offset: Optional[int] = None,
|
|
51
51
|
) -> list[OpenInterest]:
|
|
@@ -54,8 +54,8 @@ class OpenInterestResource:
|
|
|
54
54
|
|
|
55
55
|
Args:
|
|
56
56
|
coin: The coin symbol (e.g., 'BTC', 'ETH')
|
|
57
|
-
start: Start timestamp
|
|
58
|
-
end: End timestamp
|
|
57
|
+
start: Start timestamp (required)
|
|
58
|
+
end: End timestamp (required)
|
|
59
59
|
limit: Maximum number of results
|
|
60
60
|
offset: Number of results to skip
|
|
61
61
|
|
|
@@ -77,12 +77,12 @@ class OpenInterestResource:
|
|
|
77
77
|
self,
|
|
78
78
|
coin: str,
|
|
79
79
|
*,
|
|
80
|
-
start:
|
|
81
|
-
end:
|
|
80
|
+
start: Timestamp,
|
|
81
|
+
end: Timestamp,
|
|
82
82
|
limit: Optional[int] = None,
|
|
83
83
|
offset: Optional[int] = None,
|
|
84
84
|
) -> list[OpenInterest]:
|
|
85
|
-
"""Async version of history()."""
|
|
85
|
+
"""Async version of history(). start and end are required."""
|
|
86
86
|
data = await self._http.aget(
|
|
87
87
|
f"/v1/openinterest/{coin.upper()}",
|
|
88
88
|
params={
|
oxarchive/resources/orderbook.py
CHANGED
|
@@ -92,8 +92,8 @@ class OrderBookResource:
|
|
|
92
92
|
self,
|
|
93
93
|
coin: str,
|
|
94
94
|
*,
|
|
95
|
-
start:
|
|
96
|
-
end:
|
|
95
|
+
start: Timestamp,
|
|
96
|
+
end: Timestamp,
|
|
97
97
|
limit: Optional[int] = None,
|
|
98
98
|
offset: Optional[int] = None,
|
|
99
99
|
depth: Optional[int] = None,
|
|
@@ -103,8 +103,8 @@ class OrderBookResource:
|
|
|
103
103
|
|
|
104
104
|
Args:
|
|
105
105
|
coin: The coin symbol (e.g., 'BTC', 'ETH')
|
|
106
|
-
start: Start timestamp
|
|
107
|
-
end: End timestamp
|
|
106
|
+
start: Start timestamp (required)
|
|
107
|
+
end: End timestamp (required)
|
|
108
108
|
limit: Maximum number of results
|
|
109
109
|
offset: Number of results to skip
|
|
110
110
|
depth: Number of price levels per side
|
|
@@ -128,13 +128,13 @@ class OrderBookResource:
|
|
|
128
128
|
self,
|
|
129
129
|
coin: str,
|
|
130
130
|
*,
|
|
131
|
-
start:
|
|
132
|
-
end:
|
|
131
|
+
start: Timestamp,
|
|
132
|
+
end: Timestamp,
|
|
133
133
|
limit: Optional[int] = None,
|
|
134
134
|
offset: Optional[int] = None,
|
|
135
135
|
depth: Optional[int] = None,
|
|
136
136
|
) -> list[OrderBook]:
|
|
137
|
-
"""Async version of history()."""
|
|
137
|
+
"""Async version of history(). start and end are required."""
|
|
138
138
|
data = await self._http.aget(
|
|
139
139
|
f"/v1/orderbook/{coin.upper()}/history",
|
|
140
140
|
params={
|
oxarchive/resources/trades.py
CHANGED
|
@@ -44,8 +44,8 @@ class TradesResource:
|
|
|
44
44
|
self,
|
|
45
45
|
coin: str,
|
|
46
46
|
*,
|
|
47
|
-
start:
|
|
48
|
-
end:
|
|
47
|
+
start: Timestamp,
|
|
48
|
+
end: Timestamp,
|
|
49
49
|
limit: Optional[int] = None,
|
|
50
50
|
offset: Optional[int] = None,
|
|
51
51
|
side: Optional[Literal["buy", "sell"]] = None,
|
|
@@ -55,8 +55,8 @@ class TradesResource:
|
|
|
55
55
|
|
|
56
56
|
Args:
|
|
57
57
|
coin: The coin symbol (e.g., 'BTC', 'ETH')
|
|
58
|
-
start: Start timestamp
|
|
59
|
-
end: End timestamp
|
|
58
|
+
start: Start timestamp (required)
|
|
59
|
+
end: End timestamp (required)
|
|
60
60
|
limit: Maximum number of results
|
|
61
61
|
offset: Number of results to skip
|
|
62
62
|
side: Filter by trade side
|
|
@@ -80,13 +80,13 @@ class TradesResource:
|
|
|
80
80
|
self,
|
|
81
81
|
coin: str,
|
|
82
82
|
*,
|
|
83
|
-
start:
|
|
84
|
-
end:
|
|
83
|
+
start: Timestamp,
|
|
84
|
+
end: Timestamp,
|
|
85
85
|
limit: Optional[int] = None,
|
|
86
86
|
offset: Optional[int] = None,
|
|
87
87
|
side: Optional[Literal["buy", "sell"]] = None,
|
|
88
88
|
) -> list[Trade]:
|
|
89
|
-
"""Async version of list()."""
|
|
89
|
+
"""Async version of list(). start and end are required."""
|
|
90
90
|
data = await self._http.aget(
|
|
91
91
|
f"/v1/trades/{coin.upper()}",
|
|
92
92
|
params={
|
oxarchive/types.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from datetime import datetime
|
|
6
|
-
from typing import Literal, Optional, Union
|
|
6
|
+
from typing import Any, Generic, Literal, Optional, TypeVar, Union
|
|
7
7
|
|
|
8
8
|
from pydantic import BaseModel, Field
|
|
9
9
|
|
|
@@ -12,69 +12,123 @@ from pydantic import BaseModel, Field
|
|
|
12
12
|
# Base Types
|
|
13
13
|
# =============================================================================
|
|
14
14
|
|
|
15
|
+
T = TypeVar("T")
|
|
15
16
|
|
|
16
|
-
class ApiResponse(BaseModel):
|
|
17
|
-
"""Standard API response wrapper."""
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
class ApiMeta(BaseModel):
|
|
19
|
+
"""Response metadata."""
|
|
20
|
+
|
|
20
21
|
count: int
|
|
22
|
+
next_cursor: Optional[str] = None
|
|
21
23
|
request_id: str
|
|
22
24
|
|
|
23
25
|
|
|
26
|
+
class ApiResponse(BaseModel, Generic[T]):
|
|
27
|
+
"""Standard API response wrapper."""
|
|
28
|
+
|
|
29
|
+
success: bool
|
|
30
|
+
data: T
|
|
31
|
+
meta: ApiMeta
|
|
32
|
+
|
|
33
|
+
|
|
24
34
|
# =============================================================================
|
|
25
35
|
# Order Book Types
|
|
26
36
|
# =============================================================================
|
|
27
37
|
|
|
28
38
|
|
|
39
|
+
class PriceLevel(BaseModel):
|
|
40
|
+
"""Single price level in the order book."""
|
|
41
|
+
|
|
42
|
+
px: str
|
|
43
|
+
"""Price at this level."""
|
|
44
|
+
|
|
45
|
+
sz: str
|
|
46
|
+
"""Total size at this price level."""
|
|
47
|
+
|
|
48
|
+
n: int
|
|
49
|
+
"""Number of orders at this level."""
|
|
50
|
+
|
|
51
|
+
|
|
29
52
|
class OrderBook(BaseModel):
|
|
30
|
-
"""
|
|
53
|
+
"""L2 order book snapshot."""
|
|
31
54
|
|
|
32
55
|
coin: str
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
56
|
+
"""Trading pair symbol (e.g., BTC, ETH)."""
|
|
57
|
+
|
|
58
|
+
timestamp: datetime
|
|
59
|
+
"""Snapshot timestamp (UTC)."""
|
|
60
|
+
|
|
61
|
+
bids: list[PriceLevel]
|
|
62
|
+
"""Bid price levels (best bid first)."""
|
|
63
|
+
|
|
64
|
+
asks: list[PriceLevel]
|
|
65
|
+
"""Ask price levels (best ask first)."""
|
|
66
|
+
|
|
67
|
+
mid_price: Optional[str] = None
|
|
68
|
+
"""Mid price (best bid + best ask) / 2."""
|
|
69
|
+
|
|
70
|
+
spread: Optional[str] = None
|
|
71
|
+
"""Spread in absolute terms (best ask - best bid)."""
|
|
72
|
+
|
|
73
|
+
spread_bps: Optional[str] = None
|
|
74
|
+
"""Spread in basis points."""
|
|
39
75
|
|
|
40
76
|
|
|
41
77
|
# =============================================================================
|
|
42
|
-
# Trade Types
|
|
78
|
+
# Trade/Fill Types
|
|
43
79
|
# =============================================================================
|
|
44
80
|
|
|
45
81
|
|
|
46
82
|
class Trade(BaseModel):
|
|
47
|
-
"""Trade/fill record."""
|
|
83
|
+
"""Trade/fill record with full execution details."""
|
|
48
84
|
|
|
49
|
-
id: str
|
|
50
85
|
coin: str
|
|
51
|
-
|
|
86
|
+
"""Trading pair symbol."""
|
|
87
|
+
|
|
88
|
+
side: Literal["A", "B"]
|
|
89
|
+
"""Trade side: 'B' (buy) or 'A' (sell/ask)."""
|
|
90
|
+
|
|
52
91
|
price: str
|
|
92
|
+
"""Execution price."""
|
|
93
|
+
|
|
53
94
|
size: str
|
|
54
|
-
|
|
55
|
-
timestamp: int
|
|
56
|
-
trade_type: str
|
|
95
|
+
"""Trade size."""
|
|
57
96
|
|
|
97
|
+
timestamp: datetime
|
|
98
|
+
"""Execution timestamp (UTC)."""
|
|
58
99
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
# =============================================================================
|
|
100
|
+
tx_hash: Optional[str] = None
|
|
101
|
+
"""Blockchain transaction hash."""
|
|
62
102
|
|
|
63
|
-
|
|
103
|
+
trade_id: Optional[int] = None
|
|
104
|
+
"""Unique trade ID."""
|
|
64
105
|
|
|
106
|
+
order_id: Optional[int] = None
|
|
107
|
+
"""Associated order ID."""
|
|
65
108
|
|
|
66
|
-
|
|
67
|
-
"""
|
|
109
|
+
crossed: Optional[bool] = None
|
|
110
|
+
"""True if taker (crossed the spread), false if maker."""
|
|
68
111
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
112
|
+
fee: Optional[str] = None
|
|
113
|
+
"""Trading fee amount."""
|
|
114
|
+
|
|
115
|
+
fee_token: Optional[str] = None
|
|
116
|
+
"""Fee denomination (e.g., USDC)."""
|
|
117
|
+
|
|
118
|
+
closed_pnl: Optional[str] = None
|
|
119
|
+
"""Realized PnL if closing a position."""
|
|
120
|
+
|
|
121
|
+
direction: Optional[Literal["Open Long", "Open Short", "Close Long", "Close Short"]] = None
|
|
122
|
+
"""Position direction."""
|
|
123
|
+
|
|
124
|
+
start_position: Optional[str] = None
|
|
125
|
+
"""Position size before this trade."""
|
|
126
|
+
|
|
127
|
+
source: Optional[Literal["s3", "ws", "api"]] = None
|
|
128
|
+
"""Data source."""
|
|
129
|
+
|
|
130
|
+
user_address: Optional[str] = None
|
|
131
|
+
"""User's wallet address."""
|
|
78
132
|
|
|
79
133
|
|
|
80
134
|
# =============================================================================
|
|
@@ -83,14 +137,27 @@ class Candle(BaseModel):
|
|
|
83
137
|
|
|
84
138
|
|
|
85
139
|
class Instrument(BaseModel):
|
|
86
|
-
"""Trading instrument
|
|
140
|
+
"""Trading instrument specification."""
|
|
141
|
+
|
|
142
|
+
model_config = {"populate_by_name": True}
|
|
87
143
|
|
|
88
|
-
coin: str
|
|
89
144
|
name: str
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
145
|
+
"""Instrument symbol (e.g., BTC)."""
|
|
146
|
+
|
|
147
|
+
sz_decimals: int = Field(alias="szDecimals")
|
|
148
|
+
"""Size decimal precision."""
|
|
149
|
+
|
|
150
|
+
max_leverage: Optional[int] = Field(default=None, alias="maxLeverage")
|
|
151
|
+
"""Maximum leverage allowed."""
|
|
152
|
+
|
|
153
|
+
only_isolated: Optional[bool] = Field(default=None, alias="onlyIsolated")
|
|
154
|
+
"""If true, only isolated margin mode is allowed."""
|
|
155
|
+
|
|
156
|
+
instrument_type: Optional[Literal["perp", "spot"]] = Field(default=None, alias="instrumentType")
|
|
157
|
+
"""Type of instrument."""
|
|
158
|
+
|
|
159
|
+
is_active: bool = Field(default=True, alias="isActive")
|
|
160
|
+
"""Whether the instrument is currently tradeable."""
|
|
94
161
|
|
|
95
162
|
|
|
96
163
|
# =============================================================================
|
|
@@ -102,9 +169,16 @@ class FundingRate(BaseModel):
|
|
|
102
169
|
"""Funding rate record."""
|
|
103
170
|
|
|
104
171
|
coin: str
|
|
172
|
+
"""Trading pair symbol."""
|
|
173
|
+
|
|
174
|
+
timestamp: datetime
|
|
175
|
+
"""Funding timestamp (UTC)."""
|
|
176
|
+
|
|
105
177
|
funding_rate: str
|
|
106
|
-
|
|
107
|
-
|
|
178
|
+
"""Funding rate as decimal (e.g., 0.0001 = 0.01%)."""
|
|
179
|
+
|
|
180
|
+
premium: Optional[str] = None
|
|
181
|
+
"""Premium component of funding rate."""
|
|
108
182
|
|
|
109
183
|
|
|
110
184
|
# =============================================================================
|
|
@@ -113,19 +187,48 @@ class FundingRate(BaseModel):
|
|
|
113
187
|
|
|
114
188
|
|
|
115
189
|
class OpenInterest(BaseModel):
|
|
116
|
-
"""Open interest
|
|
190
|
+
"""Open interest snapshot with market context."""
|
|
117
191
|
|
|
118
192
|
coin: str
|
|
193
|
+
"""Trading pair symbol."""
|
|
194
|
+
|
|
195
|
+
timestamp: datetime
|
|
196
|
+
"""Snapshot timestamp (UTC)."""
|
|
197
|
+
|
|
119
198
|
open_interest: str
|
|
120
|
-
|
|
199
|
+
"""Total open interest in contracts."""
|
|
200
|
+
|
|
201
|
+
mark_price: Optional[str] = None
|
|
202
|
+
"""Mark price used for liquidations."""
|
|
203
|
+
|
|
204
|
+
oracle_price: Optional[str] = None
|
|
205
|
+
"""Oracle price from external feed."""
|
|
206
|
+
|
|
207
|
+
day_ntl_volume: Optional[str] = None
|
|
208
|
+
"""24-hour notional volume."""
|
|
209
|
+
|
|
210
|
+
prev_day_price: Optional[str] = None
|
|
211
|
+
"""Price 24 hours ago."""
|
|
212
|
+
|
|
213
|
+
mid_price: Optional[str] = None
|
|
214
|
+
"""Current mid price."""
|
|
215
|
+
|
|
216
|
+
impact_bid_price: Optional[str] = None
|
|
217
|
+
"""Impact bid price for liquidations."""
|
|
218
|
+
|
|
219
|
+
impact_ask_price: Optional[str] = None
|
|
220
|
+
"""Impact ask price for liquidations."""
|
|
121
221
|
|
|
122
222
|
|
|
123
223
|
# =============================================================================
|
|
124
224
|
# WebSocket Types
|
|
125
225
|
# =============================================================================
|
|
126
226
|
|
|
127
|
-
WsChannel = Literal["orderbook", "trades", "ticker", "all_tickers"
|
|
227
|
+
WsChannel = Literal["orderbook", "trades", "ticker", "all_tickers"]
|
|
228
|
+
"""Available WebSocket channels. Note: ticker/all_tickers are real-time only."""
|
|
229
|
+
|
|
128
230
|
WsConnectionState = Literal["connecting", "connected", "disconnected", "reconnecting"]
|
|
231
|
+
"""WebSocket connection state."""
|
|
129
232
|
|
|
130
233
|
|
|
131
234
|
class WsSubscribed(BaseModel):
|
|
@@ -158,16 +261,16 @@ class WsError(BaseModel):
|
|
|
158
261
|
|
|
159
262
|
|
|
160
263
|
class WsData(BaseModel):
|
|
161
|
-
"""
|
|
264
|
+
"""Real-time data message from server."""
|
|
162
265
|
|
|
163
266
|
type: Literal["data"]
|
|
164
267
|
channel: WsChannel
|
|
165
268
|
coin: str
|
|
166
|
-
data: dict
|
|
269
|
+
data: dict[str, Any]
|
|
167
270
|
|
|
168
271
|
|
|
169
272
|
# =============================================================================
|
|
170
|
-
# WebSocket Replay Types (
|
|
273
|
+
# WebSocket Replay Types (Historical Replay Mode)
|
|
171
274
|
# =============================================================================
|
|
172
275
|
|
|
173
276
|
|
|
@@ -178,9 +281,11 @@ class WsReplayStarted(BaseModel):
|
|
|
178
281
|
channel: WsChannel
|
|
179
282
|
coin: str
|
|
180
283
|
start: int
|
|
284
|
+
"""Start timestamp in milliseconds."""
|
|
181
285
|
end: int
|
|
286
|
+
"""End timestamp in milliseconds."""
|
|
182
287
|
speed: float
|
|
183
|
-
|
|
288
|
+
"""Playback speed multiplier."""
|
|
184
289
|
|
|
185
290
|
|
|
186
291
|
class WsReplayPaused(BaseModel):
|
|
@@ -203,7 +308,7 @@ class WsReplayCompleted(BaseModel):
|
|
|
203
308
|
type: Literal["replay_completed"]
|
|
204
309
|
channel: WsChannel
|
|
205
310
|
coin: str
|
|
206
|
-
|
|
311
|
+
snapshots_sent: int
|
|
207
312
|
|
|
208
313
|
|
|
209
314
|
class WsReplayStopped(BaseModel):
|
|
@@ -219,11 +324,11 @@ class WsHistoricalData(BaseModel):
|
|
|
219
324
|
channel: WsChannel
|
|
220
325
|
coin: str
|
|
221
326
|
timestamp: int
|
|
222
|
-
data: dict
|
|
327
|
+
data: dict[str, Any]
|
|
223
328
|
|
|
224
329
|
|
|
225
330
|
# =============================================================================
|
|
226
|
-
# WebSocket Bulk Stream Types (
|
|
331
|
+
# WebSocket Bulk Stream Types (Bulk Download Mode)
|
|
227
332
|
# =============================================================================
|
|
228
333
|
|
|
229
334
|
|
|
@@ -234,35 +339,32 @@ class WsStreamStarted(BaseModel):
|
|
|
234
339
|
channel: WsChannel
|
|
235
340
|
coin: str
|
|
236
341
|
start: int
|
|
342
|
+
"""Start timestamp in milliseconds."""
|
|
237
343
|
end: int
|
|
238
|
-
|
|
239
|
-
total_records: int
|
|
344
|
+
"""End timestamp in milliseconds."""
|
|
240
345
|
|
|
241
346
|
|
|
242
347
|
class WsStreamProgress(BaseModel):
|
|
243
|
-
"""Stream progress response."""
|
|
348
|
+
"""Stream progress response (sent every ~2 seconds)."""
|
|
244
349
|
|
|
245
350
|
type: Literal["stream_progress"]
|
|
246
|
-
|
|
247
|
-
total_records: int
|
|
248
|
-
progress_pct: float
|
|
351
|
+
snapshots_sent: int
|
|
249
352
|
|
|
250
353
|
|
|
251
354
|
class TimestampedRecord(BaseModel):
|
|
252
|
-
"""A record with timestamp."""
|
|
355
|
+
"""A record with timestamp for batched data."""
|
|
253
356
|
|
|
254
357
|
timestamp: int
|
|
255
|
-
data: dict
|
|
358
|
+
data: dict[str, Any]
|
|
256
359
|
|
|
257
360
|
|
|
258
361
|
class WsHistoricalBatch(BaseModel):
|
|
259
|
-
"""
|
|
362
|
+
"""Batch of historical data (bulk streaming)."""
|
|
260
363
|
|
|
261
364
|
type: Literal["historical_batch"]
|
|
262
365
|
channel: WsChannel
|
|
263
366
|
coin: str
|
|
264
|
-
|
|
265
|
-
records: list[TimestampedRecord]
|
|
367
|
+
data: list[TimestampedRecord]
|
|
266
368
|
|
|
267
369
|
|
|
268
370
|
class WsStreamCompleted(BaseModel):
|
|
@@ -271,13 +373,14 @@ class WsStreamCompleted(BaseModel):
|
|
|
271
373
|
type: Literal["stream_completed"]
|
|
272
374
|
channel: WsChannel
|
|
273
375
|
coin: str
|
|
274
|
-
|
|
376
|
+
snapshots_sent: int
|
|
275
377
|
|
|
276
378
|
|
|
277
379
|
class WsStreamStopped(BaseModel):
|
|
278
380
|
"""Stream stopped response."""
|
|
279
381
|
|
|
280
382
|
type: Literal["stream_stopped"]
|
|
383
|
+
snapshots_sent: int
|
|
281
384
|
|
|
282
385
|
|
|
283
386
|
# =============================================================================
|
|
@@ -302,3 +405,4 @@ class OxArchiveError(Exception):
|
|
|
302
405
|
|
|
303
406
|
# Type alias for timestamp parameters
|
|
304
407
|
Timestamp = Union[int, str, datetime]
|
|
408
|
+
"""Timestamp can be Unix ms (int), ISO string, or datetime object."""
|
oxarchive/websocket.py
CHANGED
|
@@ -33,6 +33,7 @@ from typing import Any, Callable, Optional, Set, Union
|
|
|
33
33
|
try:
|
|
34
34
|
import websockets
|
|
35
35
|
from websockets.client import WebSocketClientProtocol
|
|
36
|
+
from websockets.protocol import State as WsState
|
|
36
37
|
except ImportError:
|
|
37
38
|
raise ImportError(
|
|
38
39
|
"WebSocket support requires the 'websockets' package. "
|
|
@@ -41,6 +42,7 @@ except ImportError:
|
|
|
41
42
|
|
|
42
43
|
from .types import (
|
|
43
44
|
OrderBook,
|
|
45
|
+
PriceLevel,
|
|
44
46
|
Trade,
|
|
45
47
|
WsChannel,
|
|
46
48
|
WsConnectionState,
|
|
@@ -108,14 +110,74 @@ ErrorHandler = Callable[[Exception], None]
|
|
|
108
110
|
|
|
109
111
|
# Replay handlers
|
|
110
112
|
HistoricalDataHandler = Callable[[str, int, dict], None]
|
|
111
|
-
ReplayStartHandler = Callable[[WsChannel, str, int, float], None]
|
|
112
|
-
ReplayCompleteHandler = Callable[[WsChannel, str, int], None]
|
|
113
|
+
ReplayStartHandler = Callable[[WsChannel, str, int, int, float], None] # channel, coin, start, end, speed
|
|
114
|
+
ReplayCompleteHandler = Callable[[WsChannel, str, int], None] # channel, coin, snapshots_sent
|
|
113
115
|
|
|
114
116
|
# Stream handlers
|
|
115
117
|
BatchHandler = Callable[[str, list[TimestampedRecord]], None]
|
|
116
|
-
StreamStartHandler = Callable[[WsChannel, str, int], None]
|
|
117
|
-
StreamProgressHandler = Callable[[int
|
|
118
|
-
StreamCompleteHandler = Callable[[WsChannel, str, int], None]
|
|
118
|
+
StreamStartHandler = Callable[[WsChannel, str, int, int], None] # channel, coin, start, end
|
|
119
|
+
StreamProgressHandler = Callable[[int], None] # snapshots_sent
|
|
120
|
+
StreamCompleteHandler = Callable[[WsChannel, str, int], None] # channel, coin, snapshots_sent
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _transform_orderbook(coin: str, raw: dict) -> OrderBook:
|
|
124
|
+
"""Transform raw Hyperliquid orderbook format to SDK OrderBook type.
|
|
125
|
+
|
|
126
|
+
Raw format: { coin, levels: [[{px, sz, n}, ...], [{px, sz, n}, ...]], time }
|
|
127
|
+
SDK format: { coin, timestamp, bids: [{px, sz, n}], asks: [{px, sz, n}], mid_price, spread, spread_bps }
|
|
128
|
+
"""
|
|
129
|
+
from datetime import datetime
|
|
130
|
+
|
|
131
|
+
# Check if already in SDK format (from REST API or historical replay)
|
|
132
|
+
if "bids" in raw and "asks" in raw:
|
|
133
|
+
return OrderBook(**raw)
|
|
134
|
+
|
|
135
|
+
# Transform from Hyperliquid raw format
|
|
136
|
+
# levels is [[{px, sz, n}, ...], [{px, sz, n}, ...]] where [0]=bids, [1]=asks
|
|
137
|
+
levels = raw.get("levels", [[], []])
|
|
138
|
+
time_ms = raw.get("time")
|
|
139
|
+
|
|
140
|
+
bids = []
|
|
141
|
+
asks = []
|
|
142
|
+
|
|
143
|
+
if len(levels) >= 2:
|
|
144
|
+
# levels[0] = bids, levels[1] = asks
|
|
145
|
+
# Each level is already {px, sz, n} object
|
|
146
|
+
for level in levels[0] or []:
|
|
147
|
+
if isinstance(level, dict):
|
|
148
|
+
bids.append(PriceLevel(px=str(level.get("px", "0")), sz=str(level.get("sz", "0")), n=int(level.get("n", 0))))
|
|
149
|
+
for level in levels[1] or []:
|
|
150
|
+
if isinstance(level, dict):
|
|
151
|
+
asks.append(PriceLevel(px=str(level.get("px", "0")), sz=str(level.get("sz", "0")), n=int(level.get("n", 0))))
|
|
152
|
+
|
|
153
|
+
# Calculate mid price and spread
|
|
154
|
+
mid_price = None
|
|
155
|
+
spread = None
|
|
156
|
+
spread_bps = None
|
|
157
|
+
|
|
158
|
+
if bids and asks:
|
|
159
|
+
best_bid = float(bids[0].px)
|
|
160
|
+
best_ask = float(asks[0].px)
|
|
161
|
+
mid = (best_bid + best_ask) / 2
|
|
162
|
+
mid_price = str(mid)
|
|
163
|
+
spread = str(best_ask - best_bid)
|
|
164
|
+
spread_bps = f"{((best_ask - best_bid) / mid * 10000):.2f}"
|
|
165
|
+
|
|
166
|
+
# Convert timestamp
|
|
167
|
+
if time_ms:
|
|
168
|
+
timestamp = datetime.utcfromtimestamp(time_ms / 1000).isoformat() + "Z"
|
|
169
|
+
else:
|
|
170
|
+
timestamp = datetime.utcnow().isoformat() + "Z"
|
|
171
|
+
|
|
172
|
+
return OrderBook(
|
|
173
|
+
coin=coin,
|
|
174
|
+
timestamp=timestamp,
|
|
175
|
+
bids=bids,
|
|
176
|
+
asks=asks,
|
|
177
|
+
mid_price=mid_price,
|
|
178
|
+
spread=spread,
|
|
179
|
+
spread_bps=spread_bps,
|
|
180
|
+
)
|
|
119
181
|
|
|
120
182
|
|
|
121
183
|
class OxArchiveWs:
|
|
@@ -164,7 +226,7 @@ class OxArchiveWs:
|
|
|
164
226
|
@property
|
|
165
227
|
def is_connected(self) -> bool:
|
|
166
228
|
"""Check if connected."""
|
|
167
|
-
return self._ws is not None and self._ws.
|
|
229
|
+
return self._ws is not None and self._ws.state == WsState.OPEN
|
|
168
230
|
|
|
169
231
|
async def connect(self) -> None:
|
|
170
232
|
"""Connect to the WebSocket server."""
|
|
@@ -421,14 +483,14 @@ class OxArchiveWs:
|
|
|
421
483
|
def on_replay_start(self, handler: ReplayStartHandler) -> None:
|
|
422
484
|
"""Set handler for replay started event.
|
|
423
485
|
|
|
424
|
-
Handler receives: (channel, coin,
|
|
486
|
+
Handler receives: (channel, coin, start, end, speed)
|
|
425
487
|
"""
|
|
426
488
|
self._on_replay_start = handler
|
|
427
489
|
|
|
428
490
|
def on_replay_complete(self, handler: ReplayCompleteHandler) -> None:
|
|
429
491
|
"""Set handler for replay completed event.
|
|
430
492
|
|
|
431
|
-
Handler receives: (channel, coin,
|
|
493
|
+
Handler receives: (channel, coin, snapshots_sent)
|
|
432
494
|
"""
|
|
433
495
|
self._on_replay_complete = handler
|
|
434
496
|
|
|
@@ -444,21 +506,21 @@ class OxArchiveWs:
|
|
|
444
506
|
def on_stream_start(self, handler: StreamStartHandler) -> None:
|
|
445
507
|
"""Set handler for stream started event.
|
|
446
508
|
|
|
447
|
-
Handler receives: (channel, coin,
|
|
509
|
+
Handler receives: (channel, coin, start, end)
|
|
448
510
|
"""
|
|
449
511
|
self._on_stream_start = handler
|
|
450
512
|
|
|
451
513
|
def on_stream_progress(self, handler: StreamProgressHandler) -> None:
|
|
452
514
|
"""Set handler for stream progress event.
|
|
453
515
|
|
|
454
|
-
Handler receives: (
|
|
516
|
+
Handler receives: (snapshots_sent)
|
|
455
517
|
"""
|
|
456
518
|
self._on_stream_progress = handler
|
|
457
519
|
|
|
458
520
|
def on_stream_complete(self, handler: StreamCompleteHandler) -> None:
|
|
459
521
|
"""Set handler for stream completed event.
|
|
460
522
|
|
|
461
|
-
Handler receives: (channel, coin,
|
|
523
|
+
Handler receives: (channel, coin, snapshots_sent)
|
|
462
524
|
"""
|
|
463
525
|
self._on_stream_complete = handler
|
|
464
526
|
|
|
@@ -575,7 +637,8 @@ class OxArchiveWs:
|
|
|
575
637
|
raw_data = data.get("data", {})
|
|
576
638
|
|
|
577
639
|
if channel == "orderbook" and self._on_orderbook:
|
|
578
|
-
|
|
640
|
+
# Transform raw Hyperliquid format to SDK OrderBook type
|
|
641
|
+
orderbook = _transform_orderbook(coin, raw_data)
|
|
579
642
|
self._on_orderbook(coin, orderbook)
|
|
580
643
|
|
|
581
644
|
elif channel == "trades" and self._on_trades:
|
|
@@ -585,30 +648,28 @@ class OxArchiveWs:
|
|
|
585
648
|
# Replay messages (Option B)
|
|
586
649
|
elif msg_type == "replay_started" and self._on_replay_start:
|
|
587
650
|
self._on_replay_start(
|
|
588
|
-
data["channel"], data["coin"], data["
|
|
651
|
+
data["channel"], data["coin"], data["start"], data["end"], data["speed"]
|
|
589
652
|
)
|
|
590
653
|
|
|
591
654
|
elif msg_type == "historical_data" and self._on_historical_data:
|
|
592
655
|
self._on_historical_data(data["coin"], data["timestamp"], data["data"])
|
|
593
656
|
|
|
594
657
|
elif msg_type == "replay_completed" and self._on_replay_complete:
|
|
595
|
-
self._on_replay_complete(data["channel"], data["coin"], data["
|
|
658
|
+
self._on_replay_complete(data["channel"], data["coin"], data["snapshots_sent"])
|
|
596
659
|
|
|
597
660
|
# Stream messages (Option D)
|
|
598
661
|
elif msg_type == "stream_started" and self._on_stream_start:
|
|
599
|
-
self._on_stream_start(data["channel"], data["coin"], data["
|
|
662
|
+
self._on_stream_start(data["channel"], data["coin"], data["start"], data["end"])
|
|
600
663
|
|
|
601
664
|
elif msg_type == "stream_progress" and self._on_stream_progress:
|
|
602
|
-
self._on_stream_progress(
|
|
603
|
-
data["records_sent"], data["total_records"], data["progress_pct"]
|
|
604
|
-
)
|
|
665
|
+
self._on_stream_progress(data["snapshots_sent"])
|
|
605
666
|
|
|
606
667
|
elif msg_type == "historical_batch" and self._on_batch:
|
|
607
|
-
records = [TimestampedRecord(**r) for r in data["
|
|
668
|
+
records = [TimestampedRecord(**r) for r in data["data"]]
|
|
608
669
|
self._on_batch(data["coin"], records)
|
|
609
670
|
|
|
610
671
|
elif msg_type == "stream_completed" and self._on_stream_complete:
|
|
611
|
-
self._on_stream_complete(data["channel"], data["coin"], data["
|
|
672
|
+
self._on_stream_complete(data["channel"], data["coin"], data["snapshots_sent"])
|
|
612
673
|
|
|
613
674
|
except Exception as e:
|
|
614
675
|
logger.error(f"Error handling message: {e}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: oxarchive
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Official Python SDK for 0xarchive - Hyperliquid Historical Data API
|
|
5
5
|
Project-URL: Homepage, https://0xarchive.io
|
|
6
6
|
Project-URL: Documentation, https://0xarchive.io/docs/sdks
|
|
@@ -140,20 +140,6 @@ trades = client.trades.list(
|
|
|
140
140
|
)
|
|
141
141
|
```
|
|
142
142
|
|
|
143
|
-
### Candles (OHLCV)
|
|
144
|
-
|
|
145
|
-
```python
|
|
146
|
-
# Get hourly candles
|
|
147
|
-
candles = client.candles.list(
|
|
148
|
-
"BTC",
|
|
149
|
-
interval="1h",
|
|
150
|
-
start="2024-01-01",
|
|
151
|
-
end="2024-01-02"
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
# Available intervals: '1m', '5m', '15m', '1h', '4h', '1d'
|
|
155
|
-
```
|
|
156
|
-
|
|
157
143
|
### Instruments
|
|
158
144
|
|
|
159
145
|
```python
|
|
@@ -272,8 +258,8 @@ async def main():
|
|
|
272
258
|
print(f"{ts}: {data['mid_price']}")
|
|
273
259
|
)
|
|
274
260
|
|
|
275
|
-
ws.on_replay_start(lambda ch, coin,
|
|
276
|
-
print(f"Starting replay
|
|
261
|
+
ws.on_replay_start(lambda ch, coin, start, end, speed:
|
|
262
|
+
print(f"Starting replay from {start} to {end} at {speed}x")
|
|
277
263
|
)
|
|
278
264
|
|
|
279
265
|
ws.on_replay_complete(lambda ch, coin, sent:
|
|
@@ -316,8 +302,8 @@ async def main():
|
|
|
316
302
|
all_data.extend([r.data for r in records])
|
|
317
303
|
)
|
|
318
304
|
|
|
319
|
-
ws.on_stream_progress(lambda
|
|
320
|
-
print(f"
|
|
305
|
+
ws.on_stream_progress(lambda snapshots_sent:
|
|
306
|
+
print(f"Sent: {snapshots_sent} snapshots")
|
|
321
307
|
)
|
|
322
308
|
|
|
323
309
|
ws.on_stream_complete(lambda ch, coin, sent:
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
oxarchive/__init__.py,sha256=QqBnxRIGEBXE2wHdmynznUEBoJsePN_xObo_v79lPZI,2181
|
|
2
|
+
oxarchive/client.py,sha256=3P0fvOcyM5BWppkVV4054NduDHKvRg-cWeluoGymmRk,3163
|
|
3
|
+
oxarchive/http.py,sha256=LJgw488_nNb14ixRoDv5ChuhUvyzTGtQ4aTSdEEwXww,3577
|
|
4
|
+
oxarchive/types.py,sha256=8n_R41IjdNnshPscY5zAfrHa855EbvSTSdbi-GZExKo,10434
|
|
5
|
+
oxarchive/websocket.py,sha256=BM6CmUbRdYptNnlicmAVPKBdqlyZBSI1l5iUt_OzWwo,25126
|
|
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=rAXfQ-Qq57FQphXBsdYUn03wr3_C4uEKa1wUB7uUse0,3905
|
|
12
|
+
oxarchive-0.2.2.dist-info/METADATA,sha256=wkuwm2X2mJYYkrcJ4Kw_EjJwk9Sb7et6kM2V4tfypFU,8749
|
|
13
|
+
oxarchive-0.2.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
14
|
+
oxarchive-0.2.2.dist-info/RECORD,,
|
oxarchive/resources/candles.py
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
"""Candles API resource."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from datetime import datetime
|
|
6
|
-
from typing import Optional
|
|
7
|
-
|
|
8
|
-
from ..http import HttpClient
|
|
9
|
-
from ..types import Candle, CandleInterval, Timestamp
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class CandlesResource:
|
|
13
|
-
"""
|
|
14
|
-
Candles (OHLCV) API resource.
|
|
15
|
-
|
|
16
|
-
Example:
|
|
17
|
-
>>> # Get hourly candles
|
|
18
|
-
>>> candles = client.candles.list("BTC", interval="1h")
|
|
19
|
-
>>>
|
|
20
|
-
>>> # Get daily candles for a date range
|
|
21
|
-
>>> daily = client.candles.list("ETH", interval="1d", start="2024-01-01", end="2024-01-31")
|
|
22
|
-
"""
|
|
23
|
-
|
|
24
|
-
def __init__(self, http: HttpClient):
|
|
25
|
-
self._http = http
|
|
26
|
-
|
|
27
|
-
def _convert_timestamp(self, ts: Optional[Timestamp]) -> Optional[int]:
|
|
28
|
-
"""Convert timestamp to Unix milliseconds."""
|
|
29
|
-
if ts is None:
|
|
30
|
-
return None
|
|
31
|
-
if isinstance(ts, int):
|
|
32
|
-
return ts
|
|
33
|
-
if isinstance(ts, datetime):
|
|
34
|
-
return int(ts.timestamp() * 1000)
|
|
35
|
-
if isinstance(ts, str):
|
|
36
|
-
try:
|
|
37
|
-
dt = datetime.fromisoformat(ts.replace("Z", "+00:00"))
|
|
38
|
-
return int(dt.timestamp() * 1000)
|
|
39
|
-
except ValueError:
|
|
40
|
-
return int(ts)
|
|
41
|
-
return None
|
|
42
|
-
|
|
43
|
-
def list(
|
|
44
|
-
self,
|
|
45
|
-
coin: str,
|
|
46
|
-
*,
|
|
47
|
-
interval: Optional[CandleInterval] = None,
|
|
48
|
-
start: Optional[Timestamp] = None,
|
|
49
|
-
end: Optional[Timestamp] = None,
|
|
50
|
-
limit: Optional[int] = None,
|
|
51
|
-
offset: Optional[int] = None,
|
|
52
|
-
) -> list[Candle]:
|
|
53
|
-
"""
|
|
54
|
-
Get OHLCV candles for a coin.
|
|
55
|
-
|
|
56
|
-
Args:
|
|
57
|
-
coin: The coin symbol (e.g., 'BTC', 'ETH')
|
|
58
|
-
interval: Candle interval ('1m', '5m', '15m', '1h', '4h', '1d')
|
|
59
|
-
start: Start timestamp
|
|
60
|
-
end: End timestamp
|
|
61
|
-
limit: Maximum number of results
|
|
62
|
-
offset: Number of results to skip
|
|
63
|
-
|
|
64
|
-
Returns:
|
|
65
|
-
List of candles
|
|
66
|
-
"""
|
|
67
|
-
data = self._http.get(
|
|
68
|
-
f"/v1/candles/{coin.upper()}",
|
|
69
|
-
params={
|
|
70
|
-
"interval": interval,
|
|
71
|
-
"start": self._convert_timestamp(start),
|
|
72
|
-
"end": self._convert_timestamp(end),
|
|
73
|
-
"limit": limit,
|
|
74
|
-
"offset": offset,
|
|
75
|
-
},
|
|
76
|
-
)
|
|
77
|
-
return [Candle.model_validate(item) for item in data["data"]]
|
|
78
|
-
|
|
79
|
-
async def alist(
|
|
80
|
-
self,
|
|
81
|
-
coin: str,
|
|
82
|
-
*,
|
|
83
|
-
interval: Optional[CandleInterval] = None,
|
|
84
|
-
start: Optional[Timestamp] = None,
|
|
85
|
-
end: Optional[Timestamp] = None,
|
|
86
|
-
limit: Optional[int] = None,
|
|
87
|
-
offset: Optional[int] = None,
|
|
88
|
-
) -> list[Candle]:
|
|
89
|
-
"""Async version of list()."""
|
|
90
|
-
data = await self._http.aget(
|
|
91
|
-
f"/v1/candles/{coin.upper()}",
|
|
92
|
-
params={
|
|
93
|
-
"interval": interval,
|
|
94
|
-
"start": self._convert_timestamp(start),
|
|
95
|
-
"end": self._convert_timestamp(end),
|
|
96
|
-
"limit": limit,
|
|
97
|
-
"offset": offset,
|
|
98
|
-
},
|
|
99
|
-
)
|
|
100
|
-
return [Candle.model_validate(item) for item in data["data"]]
|
oxarchive-0.1.1.dist-info/RECORD
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
oxarchive/__init__.py,sha256=hRUWIJhKmm8d4XBt2gWCSQ6j-9VNaC52y89rXpAKvH0,2253
|
|
2
|
-
oxarchive/client.py,sha256=tIvsxR9qE92xIeGwazJdwqY7bawFe_ZlzEWG0NsNxsw,3268
|
|
3
|
-
oxarchive/http.py,sha256=LJgw488_nNb14ixRoDv5ChuhUvyzTGtQ4aTSdEEwXww,3577
|
|
4
|
-
oxarchive/types.py,sha256=9eCETwCVE_cewap8El2GNSJcYN3BXaeImpyqLneyPUM,7135
|
|
5
|
-
oxarchive/websocket.py,sha256=yByFOfeU6yrHhmd5Eo8FFs4hNBHuL1O65ZH-wsmG1vE,22806
|
|
6
|
-
oxarchive/resources/__init__.py,sha256=4gt5xVlnPhLUjk2n03ldJ9VCnodgdR75U9d1H88a7BE,447
|
|
7
|
-
oxarchive/resources/candles.py,sha256=IhyBCm65CZz4Nsm6IPqfMZHQl_3K4H3jHgChfigc3QU,3147
|
|
8
|
-
oxarchive/resources/funding.py,sha256=JrJ1LqXy8GX6U70zOI89lLIX_d5vM_Rq0qFJy0YRof0,3518
|
|
9
|
-
oxarchive/resources/instruments.py,sha256=flD1sH6x3P3CTqV1ZwkfwbranVacmhsHn5Dhr7lGQhM,1606
|
|
10
|
-
oxarchive/resources/openinterest.py,sha256=trMFr6TEEV88WQkpptQCUEJTy0aaoAy3QnBx9opNZH8,3570
|
|
11
|
-
oxarchive/resources/orderbook.py,sha256=ZfK3HhYrpVpELz6GDb9qpGcWzQ3p4-uxUyEY2Emhr0o,4575
|
|
12
|
-
oxarchive/resources/trades.py,sha256=4sXgyBVaq6RHFJe0jeOvFBIX-bDCe8FwgoeeeM_He3Q,3923
|
|
13
|
-
oxarchive-0.1.1.dist-info/METADATA,sha256=MLL3mweHZ7l9MA0TsdTRvY3qj0V4-_64b5vRGyd0txU,8956
|
|
14
|
-
oxarchive-0.1.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
15
|
-
oxarchive-0.1.1.dist-info/RECORD,,
|
|
File without changes
|