@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.
- package/dist/chart/cache/index.js +15 -3
- package/dist/chart/chart.js +14 -23
- package/dist/chart/collector/binance.js +4 -0
- package/dist/chart/collector/bybit.js +12 -1
- package/dist/chart/collector/helpers.js +3 -0
- package/dist/chart/collector/hyperliquid.js +12 -2
- package/dist/chart/collector/index.js +27 -12
- package/dist/chart/collector/lighter.js +4 -2
- package/dist/chart/collector/okx.js +12 -1
- package/dist/chart/compute/asset.js +4 -0
- package/dist/chart/compute/performance.js +20 -4
- package/dist/chart/compute/price-ratio.js +8 -1
- package/dist/chart/compute/weighted-ratio.js +9 -1
- package/dist/chart/utils.js +19 -11
- package/dist/chart/ws/base-candle.d.ts +11 -5
- package/dist/chart/ws/base-candle.js +6 -2
- package/dist/chart/ws/binance.d.ts +4 -6
- package/dist/chart/ws/binance.js +33 -19
- package/dist/chart/ws/bybit.d.ts +4 -6
- package/dist/chart/ws/bybit.js +40 -24
- package/dist/chart/ws/hyperliquid.d.ts +4 -6
- package/dist/chart/ws/hyperliquid.js +37 -19
- package/dist/chart/ws/index.js +3 -0
- package/dist/chart/ws/lighter.d.ts +20 -20
- package/dist/chart/ws/lighter.js +75 -196
- package/dist/chart/ws/okx.d.ts +4 -6
- package/dist/chart/ws/okx.js +47 -23
- package/dist/funding-rate/fetcher/binance.js +7 -1
- package/dist/funding-rate/fetcher/bybit.js +15 -13
- package/dist/funding-rate/fetcher/http-client.js +11 -8
- package/dist/funding-rate/fetcher/hyperliquid.js +7 -1
- package/dist/funding-rate/fetcher/index.js +3 -0
- package/dist/funding-rate/fetcher/lighter.js +7 -1
- package/dist/funding-rate/fetcher/okx.js +14 -12
- package/dist/funding-rate/funding-rate.js +29 -21
- package/dist/funding-rate/utils.js +26 -5
- package/dist/orderbook/book/aggregate.js +16 -3
- package/dist/orderbook/book/local-book.js +16 -5
- package/dist/orderbook/orderbook.js +29 -13
- package/dist/orderbook/types.js +7 -1
- package/dist/orderbook/utils.js +5 -0
- package/dist/orderbook/ws/base-depth.js +10 -1
- package/dist/orderbook/ws/binance.js +28 -7
- package/dist/orderbook/ws/bybit.js +20 -5
- package/dist/orderbook/ws/hyperliquid.js +31 -11
- package/dist/orderbook/ws/index.js +3 -0
- package/dist/orderbook/ws/lighter.js +22 -7
- package/dist/orderbook/ws/okx.js +23 -2
- package/dist/transport/base-transport.js +5 -0
- package/dist/transport/binance.js +5 -0
- package/dist/transport/bybit.js +8 -1
- package/dist/transport/hyperliquid.js +8 -1
- package/dist/transport/index.js +3 -0
- package/dist/transport/lighter.js +8 -1
- package/dist/transport/okx.js +5 -0
- package/package.json +2 -2
package/dist/chart/ws/binance.js
CHANGED
|
@@ -1,42 +1,56 @@
|
|
|
1
1
|
import { BaseCandleWs } from './base-candle';
|
|
2
2
|
|
|
3
|
-
|
|
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: [
|
|
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: [
|
|
23
|
+
params: [
|
|
24
|
+
toStreamName(symbol, interval)
|
|
25
|
+
],
|
|
17
26
|
id: this.idCounter++
|
|
18
27
|
};
|
|
19
28
|
}
|
|
20
|
-
|
|
21
|
-
|
|
29
|
+
/** Translates one Binance kline frame into a single-candle batch. */
|
|
30
|
+
parseMessages(data) {
|
|
31
|
+
let message;
|
|
22
32
|
try {
|
|
23
|
-
|
|
33
|
+
message = JSON.parse(data);
|
|
24
34
|
} catch {
|
|
25
|
-
return
|
|
35
|
+
return [];
|
|
26
36
|
}
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
package/dist/chart/ws/bybit.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { WsMessage } from '../../shared/types.js';
|
|
2
|
-
import { CandleInterval
|
|
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
|
-
|
|
13
|
-
|
|
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 };
|
package/dist/chart/ws/bybit.js
CHANGED
|
@@ -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: [
|
|
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: [
|
|
41
|
+
args: [
|
|
42
|
+
toTopic(symbol, interval)
|
|
43
|
+
]
|
|
35
44
|
};
|
|
36
45
|
}
|
|
37
|
-
|
|
38
|
-
|
|
46
|
+
/** Translates one Bybit kline frame into a single-candle batch. */
|
|
47
|
+
parseMessages(data) {
|
|
48
|
+
let message;
|
|
39
49
|
try {
|
|
40
|
-
|
|
50
|
+
message = JSON.parse(data);
|
|
41
51
|
} catch {
|
|
42
|
-
return
|
|
52
|
+
return [];
|
|
43
53
|
}
|
|
44
|
-
const topic =
|
|
45
|
-
const
|
|
46
|
-
if (!topic || !
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
|
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
|
-
|
|
26
|
-
|
|
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: {
|
|
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: {
|
|
22
|
+
subscription: {
|
|
23
|
+
type: "candle",
|
|
24
|
+
coin: symbol,
|
|
25
|
+
interval
|
|
26
|
+
}
|
|
14
27
|
};
|
|
15
28
|
}
|
|
16
|
-
|
|
17
|
-
|
|
29
|
+
/** Translates one Hyperliquid candle frame into a single-candle batch. */
|
|
30
|
+
parseMessages(data) {
|
|
31
|
+
let message;
|
|
18
32
|
try {
|
|
19
|
-
|
|
33
|
+
message = JSON.parse(data);
|
|
20
34
|
} catch {
|
|
21
|
-
return
|
|
35
|
+
return [];
|
|
22
36
|
}
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
|
package/dist/chart/ws/index.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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 };
|
package/dist/chart/ws/lighter.js
CHANGED
|
@@ -1,214 +1,93 @@
|
|
|
1
1
|
import { getIntervalSeconds } from '../utils';
|
|
2
2
|
import { BaseCandleWs } from './base-candle';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
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
|
-
|
|
21
|
-
|
|
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
|
-
|
|
31
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
70
|
-
|
|
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
|
-
|
|
80
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
159
|
-
|
|
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 };
|
package/dist/chart/ws/okx.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { WsMessage } from '../../shared/types.js';
|
|
2
|
-
import { CandleInterval
|
|
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
|
-
|
|
13
|
-
|
|
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 };
|