@0xarchive/sdk 0.1.0
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.
- package/README.md +301 -0
- package/dist/index.d.mts +856 -0
- package/dist/index.d.ts +856 -0
- package/dist/index.js +763 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +734 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +59 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,734 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
var OxArchiveError = class extends Error {
|
|
3
|
+
code;
|
|
4
|
+
requestId;
|
|
5
|
+
constructor(message, code, requestId) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "OxArchiveError";
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.requestId = requestId;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// src/http.ts
|
|
14
|
+
var HttpClient = class {
|
|
15
|
+
baseUrl;
|
|
16
|
+
apiKey;
|
|
17
|
+
timeout;
|
|
18
|
+
constructor(options) {
|
|
19
|
+
this.baseUrl = options.baseUrl.replace(/\/$/, "");
|
|
20
|
+
this.apiKey = options.apiKey;
|
|
21
|
+
this.timeout = options.timeout;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Make a GET request to the API
|
|
25
|
+
*/
|
|
26
|
+
async get(path, params) {
|
|
27
|
+
const url = new URL(`${this.baseUrl}${path}`);
|
|
28
|
+
if (params) {
|
|
29
|
+
for (const [key, value] of Object.entries(params)) {
|
|
30
|
+
if (value !== void 0 && value !== null) {
|
|
31
|
+
url.searchParams.set(key, String(value));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const controller = new AbortController();
|
|
36
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
37
|
+
try {
|
|
38
|
+
const response = await fetch(url.toString(), {
|
|
39
|
+
method: "GET",
|
|
40
|
+
headers: {
|
|
41
|
+
"X-API-Key": this.apiKey,
|
|
42
|
+
"Content-Type": "application/json"
|
|
43
|
+
},
|
|
44
|
+
signal: controller.signal
|
|
45
|
+
});
|
|
46
|
+
clearTimeout(timeoutId);
|
|
47
|
+
const data = await response.json();
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
const error = data;
|
|
50
|
+
throw new OxArchiveError(
|
|
51
|
+
error.error || `Request failed with status ${response.status}`,
|
|
52
|
+
response.status,
|
|
53
|
+
data.request_id
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
return data;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
clearTimeout(timeoutId);
|
|
59
|
+
if (error instanceof OxArchiveError) {
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
63
|
+
throw new OxArchiveError(`Request timeout after ${this.timeout}ms`, 408);
|
|
64
|
+
}
|
|
65
|
+
throw new OxArchiveError(
|
|
66
|
+
error instanceof Error ? error.message : "Unknown error",
|
|
67
|
+
500
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// src/resources/orderbook.ts
|
|
74
|
+
var OrderBookResource = class {
|
|
75
|
+
constructor(http) {
|
|
76
|
+
this.http = http;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get order book snapshot for a coin
|
|
80
|
+
*
|
|
81
|
+
* @param coin - The coin symbol (e.g., 'BTC', 'ETH')
|
|
82
|
+
* @param params - Optional parameters
|
|
83
|
+
* @returns Order book snapshot
|
|
84
|
+
*/
|
|
85
|
+
async get(coin, params) {
|
|
86
|
+
const response = await this.http.get(
|
|
87
|
+
`/v1/orderbook/${coin.toUpperCase()}`,
|
|
88
|
+
params
|
|
89
|
+
);
|
|
90
|
+
return response.data;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get historical order book snapshots
|
|
94
|
+
*
|
|
95
|
+
* @param coin - The coin symbol (e.g., 'BTC', 'ETH')
|
|
96
|
+
* @param params - Time range and pagination parameters
|
|
97
|
+
* @returns Array of order book snapshots
|
|
98
|
+
*/
|
|
99
|
+
async history(coin, params) {
|
|
100
|
+
const response = await this.http.get(
|
|
101
|
+
`/v1/orderbook/${coin.toUpperCase()}/history`,
|
|
102
|
+
params
|
|
103
|
+
);
|
|
104
|
+
return response.data;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// src/resources/trades.ts
|
|
109
|
+
var TradesResource = class {
|
|
110
|
+
constructor(http) {
|
|
111
|
+
this.http = http;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Get trade history for a coin
|
|
115
|
+
*
|
|
116
|
+
* @param coin - The coin symbol (e.g., 'BTC', 'ETH')
|
|
117
|
+
* @param params - Time range and pagination parameters
|
|
118
|
+
* @returns Array of trades
|
|
119
|
+
*/
|
|
120
|
+
async list(coin, params) {
|
|
121
|
+
const response = await this.http.get(
|
|
122
|
+
`/v1/trades/${coin.toUpperCase()}`,
|
|
123
|
+
params
|
|
124
|
+
);
|
|
125
|
+
return response.data;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Get most recent trades for a coin
|
|
129
|
+
*
|
|
130
|
+
* @param coin - The coin symbol (e.g., 'BTC', 'ETH')
|
|
131
|
+
* @param limit - Number of trades to return (default: 100)
|
|
132
|
+
* @returns Array of recent trades
|
|
133
|
+
*/
|
|
134
|
+
async recent(coin, limit) {
|
|
135
|
+
const response = await this.http.get(
|
|
136
|
+
`/v1/trades/${coin.toUpperCase()}/recent`,
|
|
137
|
+
{ limit }
|
|
138
|
+
);
|
|
139
|
+
return response.data;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// src/resources/candles.ts
|
|
144
|
+
var CandlesResource = class {
|
|
145
|
+
constructor(http) {
|
|
146
|
+
this.http = http;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Get OHLCV candles for a coin
|
|
150
|
+
*
|
|
151
|
+
* @param coin - The coin symbol (e.g., 'BTC', 'ETH')
|
|
152
|
+
* @param params - Interval, time range, and pagination parameters
|
|
153
|
+
* @returns Array of candles
|
|
154
|
+
*/
|
|
155
|
+
async list(coin, params) {
|
|
156
|
+
const response = await this.http.get(
|
|
157
|
+
`/v1/candles/${coin.toUpperCase()}`,
|
|
158
|
+
params
|
|
159
|
+
);
|
|
160
|
+
return response.data;
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// src/resources/instruments.ts
|
|
165
|
+
var InstrumentsResource = class {
|
|
166
|
+
constructor(http) {
|
|
167
|
+
this.http = http;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* List all available trading instruments
|
|
171
|
+
*
|
|
172
|
+
* @returns Array of instruments
|
|
173
|
+
*/
|
|
174
|
+
async list() {
|
|
175
|
+
const response = await this.http.get(
|
|
176
|
+
"/v1/instruments"
|
|
177
|
+
);
|
|
178
|
+
return response.data;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Get a specific instrument by coin symbol
|
|
182
|
+
*
|
|
183
|
+
* @param coin - The coin symbol (e.g., 'BTC', 'ETH')
|
|
184
|
+
* @returns Instrument details
|
|
185
|
+
*/
|
|
186
|
+
async get(coin) {
|
|
187
|
+
const response = await this.http.get(
|
|
188
|
+
`/v1/instruments/${coin.toUpperCase()}`
|
|
189
|
+
);
|
|
190
|
+
return response.data;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// src/resources/funding.ts
|
|
195
|
+
var FundingResource = class {
|
|
196
|
+
constructor(http) {
|
|
197
|
+
this.http = http;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Get funding rate history for a coin
|
|
201
|
+
*
|
|
202
|
+
* @param coin - The coin symbol (e.g., 'BTC', 'ETH')
|
|
203
|
+
* @param params - Time range and pagination parameters
|
|
204
|
+
* @returns Array of funding rate records
|
|
205
|
+
*/
|
|
206
|
+
async history(coin, params) {
|
|
207
|
+
const response = await this.http.get(
|
|
208
|
+
`/v1/funding/${coin.toUpperCase()}`,
|
|
209
|
+
params
|
|
210
|
+
);
|
|
211
|
+
return response.data;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Get current funding rate for a coin
|
|
215
|
+
*
|
|
216
|
+
* @param coin - The coin symbol (e.g., 'BTC', 'ETH')
|
|
217
|
+
* @returns Current funding rate
|
|
218
|
+
*/
|
|
219
|
+
async current(coin) {
|
|
220
|
+
const response = await this.http.get(
|
|
221
|
+
`/v1/funding/${coin.toUpperCase()}/current`
|
|
222
|
+
);
|
|
223
|
+
return response.data;
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// src/resources/openinterest.ts
|
|
228
|
+
var OpenInterestResource = class {
|
|
229
|
+
constructor(http) {
|
|
230
|
+
this.http = http;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Get open interest history for a coin
|
|
234
|
+
*
|
|
235
|
+
* @param coin - The coin symbol (e.g., 'BTC', 'ETH')
|
|
236
|
+
* @param params - Time range and pagination parameters
|
|
237
|
+
* @returns Array of open interest records
|
|
238
|
+
*/
|
|
239
|
+
async history(coin, params) {
|
|
240
|
+
const response = await this.http.get(
|
|
241
|
+
`/v1/openinterest/${coin.toUpperCase()}`,
|
|
242
|
+
params
|
|
243
|
+
);
|
|
244
|
+
return response.data;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Get current open interest for a coin
|
|
248
|
+
*
|
|
249
|
+
* @param coin - The coin symbol (e.g., 'BTC', 'ETH')
|
|
250
|
+
* @returns Current open interest
|
|
251
|
+
*/
|
|
252
|
+
async current(coin) {
|
|
253
|
+
const response = await this.http.get(
|
|
254
|
+
`/v1/openinterest/${coin.toUpperCase()}/current`
|
|
255
|
+
);
|
|
256
|
+
return response.data;
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
// src/client.ts
|
|
261
|
+
var DEFAULT_BASE_URL = "https://api.0xarchive.io";
|
|
262
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
263
|
+
var OxArchive = class {
|
|
264
|
+
http;
|
|
265
|
+
/**
|
|
266
|
+
* Order book data (L2 snapshots from April 2023)
|
|
267
|
+
*/
|
|
268
|
+
orderbook;
|
|
269
|
+
/**
|
|
270
|
+
* Trade/fill history
|
|
271
|
+
*/
|
|
272
|
+
trades;
|
|
273
|
+
/**
|
|
274
|
+
* OHLCV candles
|
|
275
|
+
*/
|
|
276
|
+
candles;
|
|
277
|
+
/**
|
|
278
|
+
* Trading instruments metadata
|
|
279
|
+
*/
|
|
280
|
+
instruments;
|
|
281
|
+
/**
|
|
282
|
+
* Funding rates
|
|
283
|
+
*/
|
|
284
|
+
funding;
|
|
285
|
+
/**
|
|
286
|
+
* Open interest
|
|
287
|
+
*/
|
|
288
|
+
openInterest;
|
|
289
|
+
/**
|
|
290
|
+
* Create a new 0xarchive client
|
|
291
|
+
*
|
|
292
|
+
* @param options - Client configuration options
|
|
293
|
+
*/
|
|
294
|
+
constructor(options) {
|
|
295
|
+
if (!options.apiKey) {
|
|
296
|
+
throw new Error("API key is required. Get one at https://0xarchive.io/signup");
|
|
297
|
+
}
|
|
298
|
+
this.http = new HttpClient({
|
|
299
|
+
baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
|
|
300
|
+
apiKey: options.apiKey,
|
|
301
|
+
timeout: options.timeout ?? DEFAULT_TIMEOUT
|
|
302
|
+
});
|
|
303
|
+
this.orderbook = new OrderBookResource(this.http);
|
|
304
|
+
this.trades = new TradesResource(this.http);
|
|
305
|
+
this.candles = new CandlesResource(this.http);
|
|
306
|
+
this.instruments = new InstrumentsResource(this.http);
|
|
307
|
+
this.funding = new FundingResource(this.http);
|
|
308
|
+
this.openInterest = new OpenInterestResource(this.http);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// src/websocket.ts
|
|
313
|
+
var DEFAULT_WS_URL = "wss://ws.0xarchive.io";
|
|
314
|
+
var DEFAULT_PING_INTERVAL = 3e4;
|
|
315
|
+
var DEFAULT_RECONNECT_DELAY = 1e3;
|
|
316
|
+
var DEFAULT_MAX_RECONNECT_ATTEMPTS = 10;
|
|
317
|
+
var OxArchiveWs = class {
|
|
318
|
+
ws = null;
|
|
319
|
+
options;
|
|
320
|
+
handlers = {};
|
|
321
|
+
subscriptions = /* @__PURE__ */ new Set();
|
|
322
|
+
state = "disconnected";
|
|
323
|
+
reconnectAttempts = 0;
|
|
324
|
+
pingTimer = null;
|
|
325
|
+
reconnectTimer = null;
|
|
326
|
+
constructor(options) {
|
|
327
|
+
this.options = {
|
|
328
|
+
apiKey: options.apiKey,
|
|
329
|
+
wsUrl: options.wsUrl ?? DEFAULT_WS_URL,
|
|
330
|
+
autoReconnect: options.autoReconnect ?? true,
|
|
331
|
+
reconnectDelay: options.reconnectDelay ?? DEFAULT_RECONNECT_DELAY,
|
|
332
|
+
maxReconnectAttempts: options.maxReconnectAttempts ?? DEFAULT_MAX_RECONNECT_ATTEMPTS,
|
|
333
|
+
pingInterval: options.pingInterval ?? DEFAULT_PING_INTERVAL
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Connect to the WebSocket server
|
|
338
|
+
*/
|
|
339
|
+
connect(handlers) {
|
|
340
|
+
if (handlers) {
|
|
341
|
+
this.handlers = handlers;
|
|
342
|
+
}
|
|
343
|
+
this.setState("connecting");
|
|
344
|
+
const url = `${this.options.wsUrl}?apiKey=${encodeURIComponent(this.options.apiKey)}`;
|
|
345
|
+
this.ws = new WebSocket(url);
|
|
346
|
+
this.ws.onopen = () => {
|
|
347
|
+
this.reconnectAttempts = 0;
|
|
348
|
+
this.setState("connected");
|
|
349
|
+
this.startPing();
|
|
350
|
+
this.resubscribe();
|
|
351
|
+
this.handlers.onOpen?.();
|
|
352
|
+
};
|
|
353
|
+
this.ws.onclose = (event) => {
|
|
354
|
+
this.stopPing();
|
|
355
|
+
this.handlers.onClose?.(event.code, event.reason);
|
|
356
|
+
if (this.options.autoReconnect && this.state !== "disconnected") {
|
|
357
|
+
this.scheduleReconnect();
|
|
358
|
+
} else {
|
|
359
|
+
this.setState("disconnected");
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
this.ws.onerror = () => {
|
|
363
|
+
const error = new Error("WebSocket connection error");
|
|
364
|
+
this.handlers.onError?.(error);
|
|
365
|
+
};
|
|
366
|
+
this.ws.onmessage = (event) => {
|
|
367
|
+
try {
|
|
368
|
+
const message = JSON.parse(event.data);
|
|
369
|
+
this.handlers.onMessage?.(message);
|
|
370
|
+
} catch {
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Disconnect from the WebSocket server
|
|
376
|
+
*/
|
|
377
|
+
disconnect() {
|
|
378
|
+
this.setState("disconnected");
|
|
379
|
+
this.stopPing();
|
|
380
|
+
this.clearReconnectTimer();
|
|
381
|
+
if (this.ws) {
|
|
382
|
+
this.ws.close(1e3, "Client disconnect");
|
|
383
|
+
this.ws = null;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Subscribe to a channel
|
|
388
|
+
*/
|
|
389
|
+
subscribe(channel, coin) {
|
|
390
|
+
const key = this.subscriptionKey(channel, coin);
|
|
391
|
+
this.subscriptions.add(key);
|
|
392
|
+
if (this.isConnected()) {
|
|
393
|
+
this.send({ op: "subscribe", channel, coin });
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Subscribe to order book updates for a coin
|
|
398
|
+
*/
|
|
399
|
+
subscribeOrderbook(coin) {
|
|
400
|
+
this.subscribe("orderbook", coin);
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Subscribe to trades for a coin
|
|
404
|
+
*/
|
|
405
|
+
subscribeTrades(coin) {
|
|
406
|
+
this.subscribe("trades", coin);
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Subscribe to ticker updates for a coin
|
|
410
|
+
*/
|
|
411
|
+
subscribeTicker(coin) {
|
|
412
|
+
this.subscribe("ticker", coin);
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Subscribe to all tickers
|
|
416
|
+
*/
|
|
417
|
+
subscribeAllTickers() {
|
|
418
|
+
this.subscribe("all_tickers");
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Unsubscribe from a channel
|
|
422
|
+
*/
|
|
423
|
+
unsubscribe(channel, coin) {
|
|
424
|
+
const key = this.subscriptionKey(channel, coin);
|
|
425
|
+
this.subscriptions.delete(key);
|
|
426
|
+
if (this.isConnected()) {
|
|
427
|
+
this.send({ op: "unsubscribe", channel, coin });
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Unsubscribe from order book updates for a coin
|
|
432
|
+
*/
|
|
433
|
+
unsubscribeOrderbook(coin) {
|
|
434
|
+
this.unsubscribe("orderbook", coin);
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Unsubscribe from trades for a coin
|
|
438
|
+
*/
|
|
439
|
+
unsubscribeTrades(coin) {
|
|
440
|
+
this.unsubscribe("trades", coin);
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Unsubscribe from ticker updates for a coin
|
|
444
|
+
*/
|
|
445
|
+
unsubscribeTicker(coin) {
|
|
446
|
+
this.unsubscribe("ticker", coin);
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Unsubscribe from all tickers
|
|
450
|
+
*/
|
|
451
|
+
unsubscribeAllTickers() {
|
|
452
|
+
this.unsubscribe("all_tickers");
|
|
453
|
+
}
|
|
454
|
+
// ==========================================================================
|
|
455
|
+
// Historical Replay (Option B) - Like Tardis.dev
|
|
456
|
+
// ==========================================================================
|
|
457
|
+
/**
|
|
458
|
+
* Start historical replay with timing preserved
|
|
459
|
+
*
|
|
460
|
+
* @param channel - Data channel to replay
|
|
461
|
+
* @param coin - Trading pair (e.g., 'BTC', 'ETH')
|
|
462
|
+
* @param options - Replay options
|
|
463
|
+
*
|
|
464
|
+
* @example
|
|
465
|
+
* ```typescript
|
|
466
|
+
* ws.replay('orderbook', 'BTC', {
|
|
467
|
+
* start: Date.now() - 86400000, // 24 hours ago
|
|
468
|
+
* speed: 10 // 10x faster than real-time
|
|
469
|
+
* });
|
|
470
|
+
* ```
|
|
471
|
+
*/
|
|
472
|
+
replay(channel, coin, options) {
|
|
473
|
+
this.send({
|
|
474
|
+
op: "replay",
|
|
475
|
+
channel,
|
|
476
|
+
coin,
|
|
477
|
+
start: options.start,
|
|
478
|
+
end: options.end,
|
|
479
|
+
speed: options.speed ?? 1
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Pause the current replay
|
|
484
|
+
*/
|
|
485
|
+
replayPause() {
|
|
486
|
+
this.send({ op: "replay.pause" });
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Resume a paused replay
|
|
490
|
+
*/
|
|
491
|
+
replayResume() {
|
|
492
|
+
this.send({ op: "replay.resume" });
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Seek to a specific timestamp in the replay
|
|
496
|
+
* @param timestamp - Unix timestamp in milliseconds
|
|
497
|
+
*/
|
|
498
|
+
replaySeek(timestamp) {
|
|
499
|
+
this.send({ op: "replay.seek", timestamp });
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Stop the current replay
|
|
503
|
+
*/
|
|
504
|
+
replayStop() {
|
|
505
|
+
this.send({ op: "replay.stop" });
|
|
506
|
+
}
|
|
507
|
+
// ==========================================================================
|
|
508
|
+
// Bulk Streaming (Option D) - Like Databento
|
|
509
|
+
// ==========================================================================
|
|
510
|
+
/**
|
|
511
|
+
* Start bulk streaming for fast data download
|
|
512
|
+
*
|
|
513
|
+
* @param channel - Data channel to stream
|
|
514
|
+
* @param coin - Trading pair (e.g., 'BTC', 'ETH')
|
|
515
|
+
* @param options - Stream options
|
|
516
|
+
*
|
|
517
|
+
* @example
|
|
518
|
+
* ```typescript
|
|
519
|
+
* ws.stream('orderbook', 'ETH', {
|
|
520
|
+
* start: Date.now() - 3600000, // 1 hour ago
|
|
521
|
+
* end: Date.now(),
|
|
522
|
+
* batchSize: 1000
|
|
523
|
+
* });
|
|
524
|
+
* ```
|
|
525
|
+
*/
|
|
526
|
+
stream(channel, coin, options) {
|
|
527
|
+
this.send({
|
|
528
|
+
op: "stream",
|
|
529
|
+
channel,
|
|
530
|
+
coin,
|
|
531
|
+
start: options.start,
|
|
532
|
+
end: options.end,
|
|
533
|
+
batch_size: options.batchSize ?? 1e3
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Stop the current bulk stream
|
|
538
|
+
*/
|
|
539
|
+
streamStop() {
|
|
540
|
+
this.send({ op: "stream.stop" });
|
|
541
|
+
}
|
|
542
|
+
// ==========================================================================
|
|
543
|
+
// Event Handlers for Replay/Stream
|
|
544
|
+
// ==========================================================================
|
|
545
|
+
/**
|
|
546
|
+
* Handle historical data points (replay mode)
|
|
547
|
+
*/
|
|
548
|
+
onHistoricalData(handler) {
|
|
549
|
+
const originalHandler = this.handlers.onMessage;
|
|
550
|
+
this.handlers.onMessage = (message) => {
|
|
551
|
+
if (message.type === "historical_data") {
|
|
552
|
+
const msg = message;
|
|
553
|
+
handler(msg.coin, msg.timestamp, msg.data);
|
|
554
|
+
}
|
|
555
|
+
originalHandler?.(message);
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Handle batched data (bulk stream mode)
|
|
560
|
+
*/
|
|
561
|
+
onBatch(handler) {
|
|
562
|
+
const originalHandler = this.handlers.onMessage;
|
|
563
|
+
this.handlers.onMessage = (message) => {
|
|
564
|
+
if (message.type === "historical_batch") {
|
|
565
|
+
const msg = message;
|
|
566
|
+
handler(msg.coin, msg.records);
|
|
567
|
+
}
|
|
568
|
+
originalHandler?.(message);
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Handle replay started event
|
|
573
|
+
*/
|
|
574
|
+
onReplayStart(handler) {
|
|
575
|
+
const originalHandler = this.handlers.onMessage;
|
|
576
|
+
this.handlers.onMessage = (message) => {
|
|
577
|
+
if (message.type === "replay_started") {
|
|
578
|
+
const msg = message;
|
|
579
|
+
handler(msg.channel, msg.coin, msg.total_records, msg.speed);
|
|
580
|
+
}
|
|
581
|
+
originalHandler?.(message);
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Handle replay completed event
|
|
586
|
+
*/
|
|
587
|
+
onReplayComplete(handler) {
|
|
588
|
+
const originalHandler = this.handlers.onMessage;
|
|
589
|
+
this.handlers.onMessage = (message) => {
|
|
590
|
+
if (message.type === "replay_completed") {
|
|
591
|
+
const msg = message;
|
|
592
|
+
handler(msg.channel, msg.coin, msg.records_sent);
|
|
593
|
+
}
|
|
594
|
+
originalHandler?.(message);
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Handle stream started event
|
|
599
|
+
*/
|
|
600
|
+
onStreamStart(handler) {
|
|
601
|
+
const originalHandler = this.handlers.onMessage;
|
|
602
|
+
this.handlers.onMessage = (message) => {
|
|
603
|
+
if (message.type === "stream_started") {
|
|
604
|
+
const msg = message;
|
|
605
|
+
handler(msg.channel, msg.coin, msg.total_records);
|
|
606
|
+
}
|
|
607
|
+
originalHandler?.(message);
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Handle stream progress event
|
|
612
|
+
*/
|
|
613
|
+
onStreamProgress(handler) {
|
|
614
|
+
const originalHandler = this.handlers.onMessage;
|
|
615
|
+
this.handlers.onMessage = (message) => {
|
|
616
|
+
if (message.type === "stream_progress") {
|
|
617
|
+
const msg = message;
|
|
618
|
+
handler(msg.records_sent, msg.total_records, msg.progress_pct);
|
|
619
|
+
}
|
|
620
|
+
originalHandler?.(message);
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Handle stream completed event
|
|
625
|
+
*/
|
|
626
|
+
onStreamComplete(handler) {
|
|
627
|
+
const originalHandler = this.handlers.onMessage;
|
|
628
|
+
this.handlers.onMessage = (message) => {
|
|
629
|
+
if (message.type === "stream_completed") {
|
|
630
|
+
const msg = message;
|
|
631
|
+
handler(msg.channel, msg.coin, msg.records_sent);
|
|
632
|
+
}
|
|
633
|
+
originalHandler?.(message);
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Get current connection state
|
|
638
|
+
*/
|
|
639
|
+
getState() {
|
|
640
|
+
return this.state;
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Check if connected
|
|
644
|
+
*/
|
|
645
|
+
isConnected() {
|
|
646
|
+
return this.ws?.readyState === WebSocket.OPEN;
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Set event handlers after construction
|
|
650
|
+
*/
|
|
651
|
+
on(event, handler) {
|
|
652
|
+
this.handlers[event] = handler;
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Helper to handle typed orderbook data
|
|
656
|
+
*/
|
|
657
|
+
onOrderbook(handler) {
|
|
658
|
+
const originalHandler = this.handlers.onMessage;
|
|
659
|
+
this.handlers.onMessage = (message) => {
|
|
660
|
+
if (message.type === "data" && message.channel === "orderbook") {
|
|
661
|
+
handler(message.coin, message.data);
|
|
662
|
+
}
|
|
663
|
+
originalHandler?.(message);
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Helper to handle typed trade data
|
|
668
|
+
*/
|
|
669
|
+
onTrades(handler) {
|
|
670
|
+
const originalHandler = this.handlers.onMessage;
|
|
671
|
+
this.handlers.onMessage = (message) => {
|
|
672
|
+
if (message.type === "data" && message.channel === "trades") {
|
|
673
|
+
handler(message.coin, message.data);
|
|
674
|
+
}
|
|
675
|
+
originalHandler?.(message);
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
// Private methods
|
|
679
|
+
send(message) {
|
|
680
|
+
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
681
|
+
this.ws.send(JSON.stringify(message));
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
setState(state) {
|
|
685
|
+
this.state = state;
|
|
686
|
+
this.handlers.onStateChange?.(state);
|
|
687
|
+
}
|
|
688
|
+
startPing() {
|
|
689
|
+
this.stopPing();
|
|
690
|
+
this.pingTimer = setInterval(() => {
|
|
691
|
+
this.send({ op: "ping" });
|
|
692
|
+
}, this.options.pingInterval);
|
|
693
|
+
}
|
|
694
|
+
stopPing() {
|
|
695
|
+
if (this.pingTimer) {
|
|
696
|
+
clearInterval(this.pingTimer);
|
|
697
|
+
this.pingTimer = null;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
subscriptionKey(channel, coin) {
|
|
701
|
+
return coin ? `${channel}:${coin}` : channel;
|
|
702
|
+
}
|
|
703
|
+
resubscribe() {
|
|
704
|
+
for (const key of this.subscriptions) {
|
|
705
|
+
const [channel, coin] = key.split(":");
|
|
706
|
+
this.send({ op: "subscribe", channel, coin });
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
scheduleReconnect() {
|
|
710
|
+
if (this.reconnectAttempts >= this.options.maxReconnectAttempts) {
|
|
711
|
+
this.setState("disconnected");
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
this.setState("reconnecting");
|
|
715
|
+
this.reconnectAttempts++;
|
|
716
|
+
const delay = this.options.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
|
|
717
|
+
this.reconnectTimer = setTimeout(() => {
|
|
718
|
+
this.connect();
|
|
719
|
+
}, delay);
|
|
720
|
+
}
|
|
721
|
+
clearReconnectTimer() {
|
|
722
|
+
if (this.reconnectTimer) {
|
|
723
|
+
clearTimeout(this.reconnectTimer);
|
|
724
|
+
this.reconnectTimer = null;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
export {
|
|
729
|
+
OxArchive,
|
|
730
|
+
OxArchiveError,
|
|
731
|
+
OxArchiveWs,
|
|
732
|
+
OxArchive as default
|
|
733
|
+
};
|
|
734
|
+
//# sourceMappingURL=index.mjs.map
|