@pear-protocol/market-sdk 0.0.13 → 0.0.14

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.
Files changed (56) hide show
  1. package/dist/chart/cache/index.js +15 -3
  2. package/dist/chart/chart.js +14 -23
  3. package/dist/chart/collector/binance.js +4 -0
  4. package/dist/chart/collector/bybit.js +12 -1
  5. package/dist/chart/collector/helpers.js +3 -0
  6. package/dist/chart/collector/hyperliquid.js +12 -2
  7. package/dist/chart/collector/index.js +27 -12
  8. package/dist/chart/collector/lighter.js +4 -2
  9. package/dist/chart/collector/okx.js +12 -1
  10. package/dist/chart/compute/asset.js +4 -0
  11. package/dist/chart/compute/performance.js +20 -4
  12. package/dist/chart/compute/price-ratio.js +8 -1
  13. package/dist/chart/compute/weighted-ratio.js +9 -1
  14. package/dist/chart/utils.js +19 -11
  15. package/dist/chart/ws/base-candle.d.ts +11 -5
  16. package/dist/chart/ws/base-candle.js +6 -2
  17. package/dist/chart/ws/binance.d.ts +4 -6
  18. package/dist/chart/ws/binance.js +33 -19
  19. package/dist/chart/ws/bybit.d.ts +4 -6
  20. package/dist/chart/ws/bybit.js +40 -24
  21. package/dist/chart/ws/hyperliquid.d.ts +4 -6
  22. package/dist/chart/ws/hyperliquid.js +37 -19
  23. package/dist/chart/ws/index.js +3 -0
  24. package/dist/chart/ws/lighter.d.ts +20 -20
  25. package/dist/chart/ws/lighter.js +75 -196
  26. package/dist/chart/ws/okx.d.ts +4 -6
  27. package/dist/chart/ws/okx.js +47 -23
  28. package/dist/funding-rate/fetcher/binance.js +7 -1
  29. package/dist/funding-rate/fetcher/bybit.js +15 -13
  30. package/dist/funding-rate/fetcher/http-client.js +11 -8
  31. package/dist/funding-rate/fetcher/hyperliquid.js +7 -1
  32. package/dist/funding-rate/fetcher/index.js +3 -0
  33. package/dist/funding-rate/fetcher/lighter.js +7 -1
  34. package/dist/funding-rate/fetcher/okx.js +14 -12
  35. package/dist/funding-rate/funding-rate.js +29 -21
  36. package/dist/funding-rate/utils.js +26 -5
  37. package/dist/orderbook/book/aggregate.js +16 -3
  38. package/dist/orderbook/book/local-book.js +16 -5
  39. package/dist/orderbook/orderbook.js +29 -13
  40. package/dist/orderbook/types.js +7 -1
  41. package/dist/orderbook/utils.js +5 -0
  42. package/dist/orderbook/ws/base-depth.js +10 -1
  43. package/dist/orderbook/ws/binance.js +28 -7
  44. package/dist/orderbook/ws/bybit.js +20 -5
  45. package/dist/orderbook/ws/hyperliquid.js +31 -11
  46. package/dist/orderbook/ws/index.js +3 -0
  47. package/dist/orderbook/ws/lighter.js +22 -7
  48. package/dist/orderbook/ws/okx.js +23 -2
  49. package/dist/transport/base-transport.js +5 -0
  50. package/dist/transport/binance.js +5 -0
  51. package/dist/transport/bybit.js +8 -1
  52. package/dist/transport/hyperliquid.js +8 -1
  53. package/dist/transport/index.js +3 -0
  54. package/dist/transport/lighter.js +8 -1
  55. package/dist/transport/okx.js +5 -0
  56. package/package.json +2 -2
@@ -1,42 +1,56 @@
1
1
  import { BaseCandleWs } from './base-candle';
2
2
 
3
- const toStreamName = (symbol, interval) => `${symbol.toLowerCase()}@kline_${interval}`;
3
+ var __defProp = Object.defineProperty;
4
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
5
+ const toStreamName = /* @__PURE__ */ __name((symbol, interval) => `${symbol.toLowerCase()}@kline_${interval}`, "toStreamName");
4
6
  class BinanceCandleWs extends BaseCandleWs {
7
+ static {
8
+ __name(this, "BinanceCandleWs");
9
+ }
5
10
  idCounter = 1;
6
11
  buildSubscribeMessage(symbol, interval) {
7
12
  return {
8
13
  method: "SUBSCRIBE",
9
- params: [toStreamName(symbol, interval)],
14
+ params: [
15
+ toStreamName(symbol, interval)
16
+ ],
10
17
  id: this.idCounter++
11
18
  };
12
19
  }
13
20
  buildUnsubscribeMessage(symbol, interval) {
14
21
  return {
15
22
  method: "UNSUBSCRIBE",
16
- params: [toStreamName(symbol, interval)],
23
+ params: [
24
+ toStreamName(symbol, interval)
25
+ ],
17
26
  id: this.idCounter++
18
27
  };
19
28
  }
20
- parseMessage(data) {
21
- let msg;
29
+ /** Translates one Binance kline frame into a single-candle batch. */
30
+ parseMessages(data) {
31
+ let message;
22
32
  try {
23
- msg = JSON.parse(data);
33
+ message = JSON.parse(data);
24
34
  } catch {
25
- return null;
35
+ return [];
26
36
  }
27
- if (msg.e !== "kline" || !msg.k) return null;
28
- const k = msg.k;
29
- return {
30
- symbol: k.s,
31
- candle: {
32
- t: k.t,
33
- T: k.T,
34
- o: Number(k.o),
35
- h: Number(k.h),
36
- l: Number(k.l),
37
- c: Number(k.c)
37
+ if (message.e !== "kline" || !message.k) {
38
+ return [];
39
+ }
40
+ const kline = message.k;
41
+ return [
42
+ {
43
+ symbol: kline.s,
44
+ candle: {
45
+ t: kline.t,
46
+ T: kline.T,
47
+ o: Number(kline.o),
48
+ h: Number(kline.h),
49
+ l: Number(kline.l),
50
+ c: Number(kline.c)
51
+ }
38
52
  }
39
- };
53
+ ];
40
54
  }
41
55
  }
42
56
 
@@ -1,6 +1,6 @@
1
1
  import { WsMessage } from '../../shared/types.js';
2
- import { CandleInterval, CandleData } from '../types.js';
3
- import { BaseCandleWs } from './base-candle.js';
2
+ import { CandleInterval } from '../types.js';
3
+ import { BaseCandleWs, ParsedCandle } from './base-candle.js';
4
4
  import '../../transport/index.js';
5
5
  import '@pear-protocol/types';
6
6
  import '../../transport/base-transport.js';
@@ -9,10 +9,8 @@ import 'partysocket';
9
9
  declare class BybitCandleWs extends BaseCandleWs {
10
10
  protected buildSubscribeMessage(symbol: string, interval: CandleInterval): WsMessage;
11
11
  protected buildUnsubscribeMessage(symbol: string, interval: CandleInterval): WsMessage;
12
- protected parseMessage(data: string): {
13
- symbol: string;
14
- candle: CandleData;
15
- } | null;
12
+ /** Translates one Bybit kline frame into a single-candle batch. */
13
+ protected parseMessages(data: string): ParsedCandle[];
16
14
  }
17
15
 
18
16
  export { BybitCandleWs };
@@ -1,5 +1,7 @@
1
1
  import { BaseCandleWs } from './base-candle';
2
2
 
3
+ var __defProp = Object.defineProperty;
4
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
5
  const intervalMap = {
4
6
  "1m": "1",
5
7
  "3m": "3",
@@ -16,47 +18,61 @@ const intervalMap = {
16
18
  "1w": "W",
17
19
  "1M": "M"
18
20
  };
19
- const toTopic = (symbol, interval) => `kline.${intervalMap[interval]}.${symbol}`;
20
- const fromTopic = (topic) => {
21
+ const toTopic = /* @__PURE__ */ __name((symbol, interval) => `kline.${intervalMap[interval]}.${symbol}`, "toTopic");
22
+ const fromTopic = /* @__PURE__ */ __name((topic) => {
21
23
  const parts = topic.split(".");
22
24
  return parts[2] ?? "";
23
- };
25
+ }, "fromTopic");
24
26
  class BybitCandleWs extends BaseCandleWs {
27
+ static {
28
+ __name(this, "BybitCandleWs");
29
+ }
25
30
  buildSubscribeMessage(symbol, interval) {
26
31
  return {
27
32
  op: "subscribe",
28
- args: [toTopic(symbol, interval)]
33
+ args: [
34
+ toTopic(symbol, interval)
35
+ ]
29
36
  };
30
37
  }
31
38
  buildUnsubscribeMessage(symbol, interval) {
32
39
  return {
33
40
  op: "unsubscribe",
34
- args: [toTopic(symbol, interval)]
41
+ args: [
42
+ toTopic(symbol, interval)
43
+ ]
35
44
  };
36
45
  }
37
- parseMessage(data) {
38
- let msg;
46
+ /** Translates one Bybit kline frame into a single-candle batch. */
47
+ parseMessages(data) {
48
+ let message;
39
49
  try {
40
- msg = JSON.parse(data);
50
+ message = JSON.parse(data);
41
51
  } catch {
42
- return null;
52
+ return [];
43
53
  }
44
- const topic = msg.topic;
45
- const msgData = msg.data;
46
- if (!topic || !msgData || !Array.isArray(msgData) || msgData.length === 0) return null;
47
- if (!topic.startsWith("kline.")) return null;
48
- const d = msgData[0];
49
- return {
50
- symbol: fromTopic(topic),
51
- candle: {
52
- t: d.start,
53
- T: d.end,
54
- o: Number(d.open),
55
- h: Number(d.high),
56
- l: Number(d.low),
57
- c: Number(d.close)
54
+ const topic = message.topic;
55
+ const klines = message.data;
56
+ if (!topic || !klines || !Array.isArray(klines) || klines.length === 0) {
57
+ return [];
58
+ }
59
+ if (!topic.startsWith("kline.")) {
60
+ return [];
61
+ }
62
+ const kline = klines[0];
63
+ return [
64
+ {
65
+ symbol: fromTopic(topic),
66
+ candle: {
67
+ t: kline.start,
68
+ T: kline.end,
69
+ o: Number(kline.open),
70
+ h: Number(kline.high),
71
+ l: Number(kline.low),
72
+ c: Number(kline.close)
73
+ }
58
74
  }
59
- };
75
+ ];
60
76
  }
61
77
  }
62
78
 
@@ -1,6 +1,6 @@
1
1
  import { WsMessage } from '../../shared/types.js';
2
- import { CandleInterval, CandleData } from '../types.js';
3
- import { BaseCandleWs } from './base-candle.js';
2
+ import { CandleInterval } from '../types.js';
3
+ import { BaseCandleWs, ParsedCandle } from './base-candle.js';
4
4
  import '../../transport/index.js';
5
5
  import '@pear-protocol/types';
6
6
  import '../../transport/base-transport.js';
@@ -22,10 +22,8 @@ interface HyperliquidUnsubscribeMessage extends WsMessage {
22
22
  declare class HyperliquidCandleWs extends BaseCandleWs {
23
23
  protected buildSubscribeMessage(symbol: string, interval: CandleInterval): HyperliquidSubscribeMessage;
24
24
  protected buildUnsubscribeMessage(symbol: string, interval: CandleInterval): HyperliquidUnsubscribeMessage;
25
- protected parseMessage(data: string): {
26
- symbol: string;
27
- candle: CandleData;
28
- } | null;
25
+ /** Translates one Hyperliquid candle frame into a single-candle batch. */
26
+ protected parseMessages(data: string): ParsedCandle[];
29
27
  }
30
28
 
31
29
  export { HyperliquidCandleWs };
@@ -1,39 +1,57 @@
1
1
  import { BaseCandleWs } from './base-candle';
2
2
 
3
+ var __defProp = Object.defineProperty;
4
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
5
  class HyperliquidCandleWs extends BaseCandleWs {
6
+ static {
7
+ __name(this, "HyperliquidCandleWs");
8
+ }
4
9
  buildSubscribeMessage(symbol, interval) {
5
10
  return {
6
11
  method: "subscribe",
7
- subscription: { type: "candle", coin: symbol, interval }
12
+ subscription: {
13
+ type: "candle",
14
+ coin: symbol,
15
+ interval
16
+ }
8
17
  };
9
18
  }
10
19
  buildUnsubscribeMessage(symbol, interval) {
11
20
  return {
12
21
  method: "unsubscribe",
13
- subscription: { type: "candle", coin: symbol, interval }
22
+ subscription: {
23
+ type: "candle",
24
+ coin: symbol,
25
+ interval
26
+ }
14
27
  };
15
28
  }
16
- parseMessage(data) {
17
- let msg;
29
+ /** Translates one Hyperliquid candle frame into a single-candle batch. */
30
+ parseMessages(data) {
31
+ let message;
18
32
  try {
19
- msg = JSON.parse(data);
33
+ message = JSON.parse(data);
20
34
  } catch {
21
- return null;
35
+ return [];
22
36
  }
23
- if (msg.channel !== "candle" || !msg.data) return null;
24
- const d = msg.data;
25
- return {
26
- symbol: d.s,
27
- candle: {
28
- s: d.s,
29
- t: d.t,
30
- T: d.T,
31
- o: Number(d.o),
32
- h: Number(d.h),
33
- l: Number(d.l),
34
- c: Number(d.c)
37
+ if (message.channel !== "candle" || !message.data) {
38
+ return [];
39
+ }
40
+ const payload = message.data;
41
+ return [
42
+ {
43
+ symbol: payload.s,
44
+ candle: {
45
+ s: payload.s,
46
+ t: payload.t,
47
+ T: payload.T,
48
+ o: Number(payload.o),
49
+ h: Number(payload.h),
50
+ l: Number(payload.l),
51
+ c: Number(payload.c)
52
+ }
35
53
  }
36
- };
54
+ ];
37
55
  }
38
56
  }
39
57
 
@@ -5,6 +5,8 @@ import { HyperliquidCandleWs } from './hyperliquid';
5
5
  import { LighterCandleWs } from './lighter';
6
6
  import { OkxCandleWs } from './okx';
7
7
 
8
+ var __defProp = Object.defineProperty;
9
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
10
  function createCandleWs(transport, connector, onCandle) {
9
11
  switch (connector) {
10
12
  case "hyperliquid":
@@ -23,5 +25,6 @@ function createCandleWs(transport, connector, onCandle) {
23
25
  }
24
26
  }
25
27
  }
28
+ __name(createCandleWs, "createCandleWs");
26
29
 
27
30
  export { createCandleWs };
@@ -1,30 +1,30 @@
1
1
  import { WsMessage } from '../../shared/types.js';
2
- import { CandleInterval, CandleData } from '../types.js';
3
- import { BaseCandleWs } from './base-candle.js';
2
+ import { CandleInterval } from '../types.js';
3
+ import { BaseCandleWs, ParsedCandle } from './base-candle.js';
4
4
  import '../../transport/index.js';
5
5
  import '@pear-protocol/types';
6
6
  import '../../transport/base-transport.js';
7
7
  import 'partysocket';
8
8
 
9
+ /**
10
+ * Lighter publishes ready-made OHLC candles on a dedicated channel, so this class only translates
11
+ * them onto CandleData — it never aggregates the trade tape and never seeds itself over REST, because
12
+ * the subscribe acknowledgement already carries the current live candle.
13
+ * Docs: https://apidocs.lighter.xyz/docs/websocket-reference#candlesticks
14
+ */
15
+
9
16
  declare class LighterCandleWs extends BaseCandleWs {
10
- private symbolIntervals;
11
- private candleState;
12
- private ownMessageListenerId;
13
- private ownOpenListenerId;
14
- protected buildSubscribeMessage(symbol: string, _interval: CandleInterval): WsMessage;
15
- protected buildUnsubscribeMessage(symbol: string, _interval: CandleInterval): WsMessage;
16
- protected parseMessage(_data: string): {
17
- symbol: string;
18
- candle: CandleData;
19
- } | null;
20
- start(): void;
21
- stop(): void;
22
- subscribe(symbol: string, interval: CandleInterval): void;
23
- unsubscribe(symbol: string, interval: CandleInterval): void;
24
- onHistoricalDataFetched(symbol: string, interval: CandleInterval, candles: CandleData[]): void;
25
- private applyCandleState;
26
- private seedFromHistory;
27
- private handleTradeMessage;
17
+ /** Subscribes to a market's candle stream; `symbol` is Lighter's numeric market id. */
18
+ protected buildSubscribeMessage(symbol: string, interval: CandleInterval): WsMessage;
19
+ /** Mirrors the subscribe channel — Lighter keys unsubscribes by the same slash-delimited name. */
20
+ protected buildUnsubscribeMessage(symbol: string, interval: CandleInterval): WsMessage;
21
+ /**
22
+ * Translates one candle message into CandleData, oldest first.
23
+ *
24
+ * Lighter batches updates every 500ms and, when a period rolls over, sends the just-closed candle
25
+ * followed by the newly opened one — both are emitted so the closed bar keeps its final trades.
26
+ */
27
+ protected parseMessages(data: string): ParsedCandle[];
28
28
  }
29
29
 
30
30
  export { LighterCandleWs };
@@ -1,214 +1,93 @@
1
1
  import { getIntervalSeconds } from '../utils';
2
2
  import { BaseCandleWs } from './base-candle';
3
3
 
4
- const LIGHTER_BASE_URL = "https://mainnet.zklighter.elliot.ai";
5
- const RESOLUTION_MAP = {
6
- "1m": "1m",
7
- "5m": "5m",
8
- "15m": "15m",
9
- "30m": "30m",
10
- "1h": "1h",
11
- "4h": "4h",
12
- "12h": "12h",
13
- "1d": "1d",
14
- "1w": "1w"
15
- };
16
- function toIntervalOpenTime(ts, intervalMs) {
17
- return Math.floor(ts / intervalMs) * intervalMs;
18
- }
4
+ var __defProp = Object.defineProperty;
5
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
6
+ const SUPPORTED_RESOLUTIONS = /* @__PURE__ */ new Set([
7
+ "1m",
8
+ "5m",
9
+ "15m",
10
+ "30m",
11
+ "1h",
12
+ "4h",
13
+ "12h",
14
+ "1d"
15
+ ]);
19
16
  class LighterCandleWs extends BaseCandleWs {
20
- symbolIntervals = /* @__PURE__ */ new Map();
21
- candleState = /* @__PURE__ */ new Map();
22
- ownMessageListenerId = null;
23
- ownOpenListenerId = null;
24
- buildSubscribeMessage(symbol, _interval) {
25
- return { type: "subscribe", channel: `trade/${symbol}` };
26
- }
27
- buildUnsubscribeMessage(symbol, _interval) {
28
- return { type: "unsubscribe", channel: `trade/${symbol}` };
17
+ static {
18
+ __name(this, "LighterCandleWs");
29
19
  }
30
- parseMessage(_data) {
31
- return null;
20
+ /** Subscribes to a market's candle stream; `symbol` is Lighter's numeric market id. */
21
+ buildSubscribeMessage(symbol, interval) {
22
+ return {
23
+ type: "subscribe",
24
+ channel: `candle/${symbol}/${interval}`
25
+ };
32
26
  }
33
- start() {
34
- if (this.ownMessageListenerId) return;
35
- this.ownMessageListenerId = this.transport.addMessageListener((data) => {
36
- this.handleTradeMessage(data);
37
- });
38
- this.ownOpenListenerId = this.transport.addOpenListener(() => {
39
- this.candleState.clear();
40
- for (const [symbol, intervals] of this.symbolIntervals) {
41
- this.transport.send(this.buildSubscribeMessage(symbol, "1m"));
42
- for (const interval of intervals) {
43
- void this.seedFromHistory(symbol, interval);
44
- }
45
- }
46
- });
47
- if (!this.transport.connected) {
48
- this.transport.connect();
49
- } else {
50
- for (const symbol of this.symbolIntervals.keys()) {
51
- this.transport.send(this.buildSubscribeMessage(symbol, "1m"));
52
- }
53
- }
27
+ /** Mirrors the subscribe channel — Lighter keys unsubscribes by the same slash-delimited name. */
28
+ buildUnsubscribeMessage(symbol, interval) {
29
+ return {
30
+ type: "unsubscribe",
31
+ channel: `candle/${symbol}/${interval}`
32
+ };
54
33
  }
55
- stop() {
56
- if (this.ownMessageListenerId) {
57
- this.transport.removeMessageListener(this.ownMessageListenerId);
58
- this.ownMessageListenerId = null;
59
- }
60
- if (this.ownOpenListenerId) {
61
- this.transport.removeOpenListener(this.ownOpenListenerId);
62
- this.ownOpenListenerId = null;
63
- }
64
- if (this.transport.connected) {
65
- for (const symbol of this.symbolIntervals.keys()) {
66
- this.transport.send(this.buildUnsubscribeMessage(symbol, "1m"));
67
- }
34
+ /**
35
+ * Translates one candle message into CandleData, oldest first.
36
+ *
37
+ * Lighter batches updates every 500ms and, when a period rolls over, sends the just-closed candle
38
+ * followed by the newly opened one — both are emitted so the closed bar keeps its final trades.
39
+ */
40
+ parseMessages(data) {
41
+ let message;
42
+ try {
43
+ message = JSON.parse(data);
44
+ } catch {
45
+ return [];
68
46
  }
69
- this.symbolIntervals.clear();
70
- this.candleState.clear();
71
- super.stop();
72
- }
73
- subscribe(symbol, interval) {
74
- let intervals = this.symbolIntervals.get(symbol);
75
- if (!intervals) {
76
- intervals = /* @__PURE__ */ new Set();
77
- this.symbolIntervals.set(symbol, intervals);
47
+ if (message.type !== "subscribed/candle" && message.type !== "update/candle") {
48
+ return [];
78
49
  }
79
- const isFirstForSymbol = intervals.size === 0;
80
- intervals.add(interval);
81
- if (isFirstForSymbol && this.transport.connected) {
82
- this.transport.send(this.buildSubscribeMessage(symbol, interval));
50
+ if (!Array.isArray(message.candles) || message.candles.length === 0) {
51
+ return [];
83
52
  }
84
- void this.seedFromHistory(symbol, interval);
85
- }
86
- unsubscribe(symbol, interval) {
87
- const intervals = this.symbolIntervals.get(symbol);
88
- if (!intervals) return;
89
- intervals.delete(interval);
90
- if (intervals.size === 0) {
91
- this.symbolIntervals.delete(symbol);
92
- this.candleState.delete(symbol);
93
- if (this.transport.connected) {
94
- this.transport.send(this.buildUnsubscribeMessage(symbol, interval));
95
- }
53
+ const route = parseChannel(message.channel);
54
+ if (!route) {
55
+ return [];
96
56
  }
57
+ const intervalMs = getIntervalSeconds(route.interval) * 1e3;
58
+ return message.candles.map((candle) => ({
59
+ symbol: route.symbol,
60
+ candle: toCandleData(candle, intervalMs)
61
+ }));
97
62
  }
98
- // Called before subscribe() in attachCandleWs so seedFromHistory can skip its fetch when
99
- // the current period is already covered by historical data.
100
- onHistoricalDataFetched(symbol, interval, candles) {
101
- if (candles.length === 0) return;
102
- const lastCandle = candles[candles.length - 1];
103
- if (!lastCandle) return;
104
- const intervalMs = getIntervalSeconds(interval) * 1e3;
105
- if (lastCandle.t !== toIntervalOpenTime(Date.now(), intervalMs)) return;
106
- this.applyCandleState(symbol, interval, {
107
- openTime: lastCandle.t,
108
- closeTime: lastCandle.T,
109
- open: lastCandle.o,
110
- high: lastCandle.h,
111
- low: lastCandle.l,
112
- close: lastCandle.c
113
- });
114
- }
115
- applyCandleState(symbol, interval, state) {
116
- let symbolState = this.candleState.get(symbol);
117
- if (!symbolState) {
118
- symbolState = /* @__PURE__ */ new Map();
119
- this.candleState.set(symbol, symbolState);
120
- }
121
- const existing = symbolState.get(interval);
122
- if (!existing || existing.openTime !== state.openTime) {
123
- symbolState.set(interval, state);
124
- }
63
+ }
64
+ function parseChannel(channel) {
65
+ if (!channel) {
66
+ return null;
125
67
  }
126
- async seedFromHistory(symbol, interval) {
127
- const resolution = RESOLUTION_MAP[interval];
128
- if (!resolution) return;
129
- const intervalMs = getIntervalSeconds(interval) * 1e3;
130
- const now = Date.now();
131
- const openTime = toIntervalOpenTime(now, intervalMs);
132
- if (this.candleState.get(symbol)?.get(interval)?.openTime === openTime) return;
133
- try {
134
- const params = new URLSearchParams({
135
- market_id: symbol,
136
- resolution,
137
- start_timestamp: String(openTime),
138
- end_timestamp: String(now),
139
- count_back: "1"
140
- });
141
- const response = await fetch(`${LIGHTER_BASE_URL}/api/v1/candles?${params}`);
142
- if (!response.ok) return;
143
- const data = await response.json();
144
- if (data.code !== 200 || !data.c?.length) return;
145
- const candle = data.c[data.c.length - 1];
146
- if (!this.symbolIntervals.get(symbol)?.has(interval)) return;
147
- this.applyCandleState(symbol, interval, {
148
- openTime: candle.t,
149
- closeTime: candle.t + intervalMs - 1,
150
- open: candle.o ?? 0,
151
- high: candle.h ?? 0,
152
- low: candle.l ?? 0,
153
- close: candle.c ?? 0
154
- });
155
- } catch {
156
- }
68
+ const [prefix, symbol, resolution] = channel.split(":");
69
+ if (prefix !== "candle" || !symbol || !resolution) {
70
+ return null;
157
71
  }
158
- handleTradeMessage(data) {
159
- let msg;
160
- try {
161
- msg = JSON.parse(data);
162
- } catch {
163
- console.log("Received non-JSON message from Lighter WS, ignoring:", data);
164
- return;
165
- }
166
- if (msg.type !== "update/trade") return;
167
- if (!Array.isArray(msg.trades) || msg.trades.length === 0) return;
168
- const colonIdx = msg.channel.indexOf(":");
169
- if (colonIdx === -1 || !msg.channel.startsWith("trade")) return;
170
- const symbol = msg.channel.slice(colonIdx + 1);
171
- const intervals = this.symbolIntervals.get(symbol);
172
- if (!intervals || intervals.size === 0) return;
173
- let symbolState = this.candleState.get(symbol);
174
- if (!symbolState) {
175
- symbolState = /* @__PURE__ */ new Map();
176
- this.candleState.set(symbol, symbolState);
177
- }
178
- const intervalMsMap = /* @__PURE__ */ new Map();
179
- for (const interval of intervals) {
180
- intervalMsMap.set(interval, getIntervalSeconds(interval) * 1e3);
181
- }
182
- for (const trade of msg.trades) {
183
- const price = Number(trade.price);
184
- const ts = trade.timestamp;
185
- for (const interval of intervals) {
186
- const intervalMs = intervalMsMap.get(interval);
187
- if (intervalMs === void 0) continue;
188
- const openTime = toIntervalOpenTime(ts, intervalMs);
189
- const closeTime = openTime + intervalMs - 1;
190
- const existing = symbolState.get(interval);
191
- let state;
192
- if (!existing || existing.openTime !== openTime) {
193
- state = { openTime, closeTime, open: price, high: price, low: price, close: price };
194
- symbolState.set(interval, state);
195
- } else {
196
- if (price > existing.high) existing.high = price;
197
- if (price < existing.low) existing.low = price;
198
- existing.close = price;
199
- state = existing;
200
- }
201
- this.onCandle(symbol, {
202
- t: state.openTime,
203
- T: state.closeTime,
204
- o: state.open,
205
- h: state.high,
206
- l: state.low,
207
- c: state.close
208
- });
209
- }
210
- }
72
+ if (!SUPPORTED_RESOLUTIONS.has(resolution)) {
73
+ return null;
211
74
  }
75
+ return {
76
+ symbol,
77
+ interval: resolution
78
+ };
79
+ }
80
+ __name(parseChannel, "parseChannel");
81
+ function toCandleData(candle, intervalMs) {
82
+ return {
83
+ t: candle.t,
84
+ T: candle.t + intervalMs - 1,
85
+ o: candle.o ?? 0,
86
+ h: candle.h ?? 0,
87
+ l: candle.l ?? 0,
88
+ c: candle.c ?? 0
89
+ };
212
90
  }
91
+ __name(toCandleData, "toCandleData");
213
92
 
214
93
  export { LighterCandleWs };
@@ -1,6 +1,6 @@
1
1
  import { WsMessage } from '../../shared/types.js';
2
- import { CandleInterval, CandleData } from '../types.js';
3
- import { BaseCandleWs } from './base-candle.js';
2
+ import { CandleInterval } from '../types.js';
3
+ import { BaseCandleWs, ParsedCandle } from './base-candle.js';
4
4
  import '../../transport/index.js';
5
5
  import '@pear-protocol/types';
6
6
  import '../../transport/base-transport.js';
@@ -9,10 +9,8 @@ import 'partysocket';
9
9
  declare class OkxCandleWs extends BaseCandleWs {
10
10
  protected buildSubscribeMessage(symbol: string, interval: CandleInterval): WsMessage;
11
11
  protected buildUnsubscribeMessage(symbol: string, interval: CandleInterval): WsMessage;
12
- protected parseMessage(data: string): {
13
- symbol: string;
14
- candle: CandleData;
15
- } | null;
12
+ /** Translates one OKX candle frame into a single-candle batch. */
13
+ protected parseMessages(data: string): ParsedCandle[];
16
14
  }
17
15
 
18
16
  export { OkxCandleWs };