@pear-protocol/market-sdk 0.0.12 → 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/okx.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": "1m",
|
|
5
7
|
"3m": "3m",
|
|
@@ -16,44 +18,66 @@ const intervalMap = {
|
|
|
16
18
|
"1w": "1W",
|
|
17
19
|
"1M": "1M"
|
|
18
20
|
};
|
|
19
|
-
const toChannel = (interval) => `candle${intervalMap[interval]}
|
|
21
|
+
const toChannel = /* @__PURE__ */ __name((interval) => `candle${intervalMap[interval]}`, "toChannel");
|
|
20
22
|
class OkxCandleWs extends BaseCandleWs {
|
|
23
|
+
static {
|
|
24
|
+
__name(this, "OkxCandleWs");
|
|
25
|
+
}
|
|
21
26
|
buildSubscribeMessage(symbol, interval) {
|
|
22
27
|
return {
|
|
23
28
|
op: "subscribe",
|
|
24
|
-
args: [
|
|
29
|
+
args: [
|
|
30
|
+
{
|
|
31
|
+
channel: toChannel(interval),
|
|
32
|
+
instId: symbol
|
|
33
|
+
}
|
|
34
|
+
]
|
|
25
35
|
};
|
|
26
36
|
}
|
|
27
37
|
buildUnsubscribeMessage(symbol, interval) {
|
|
28
38
|
return {
|
|
29
39
|
op: "unsubscribe",
|
|
30
|
-
args: [
|
|
40
|
+
args: [
|
|
41
|
+
{
|
|
42
|
+
channel: toChannel(interval),
|
|
43
|
+
instId: symbol
|
|
44
|
+
}
|
|
45
|
+
]
|
|
31
46
|
};
|
|
32
47
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
48
|
+
/** Translates one OKX candle frame into a single-candle batch. */
|
|
49
|
+
parseMessages(data) {
|
|
50
|
+
if (data === "pong") {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
let message;
|
|
36
54
|
try {
|
|
37
|
-
|
|
55
|
+
message = JSON.parse(data);
|
|
38
56
|
} catch {
|
|
39
|
-
return
|
|
57
|
+
return [];
|
|
40
58
|
}
|
|
41
|
-
const arg =
|
|
42
|
-
const candleData =
|
|
43
|
-
if (!arg || !candleData || !Array.isArray(candleData) || candleData.length === 0)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
59
|
+
const arg = message.arg;
|
|
60
|
+
const candleData = message.data;
|
|
61
|
+
if (!arg || !candleData || !Array.isArray(candleData) || candleData.length === 0) {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
if (!arg.channel.startsWith("candle")) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
const tuple = candleData[0];
|
|
68
|
+
return [
|
|
69
|
+
{
|
|
70
|
+
symbol: arg.instId,
|
|
71
|
+
candle: {
|
|
72
|
+
t: Number(tuple[0]),
|
|
73
|
+
T: Number(tuple[0]),
|
|
74
|
+
o: Number(tuple[1]),
|
|
75
|
+
h: Number(tuple[2]),
|
|
76
|
+
l: Number(tuple[3]),
|
|
77
|
+
c: Number(tuple[4])
|
|
78
|
+
}
|
|
55
79
|
}
|
|
56
|
-
|
|
80
|
+
];
|
|
57
81
|
}
|
|
58
82
|
}
|
|
59
83
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { httpGet } from './http-client';
|
|
2
2
|
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
5
|
const BINANCE_FUTURES_URL = "https://fapi.binance.com/fapi/v1/fundingRate";
|
|
4
6
|
const MAX_LIMIT = 1e3;
|
|
5
7
|
async function fetchHistoricalFundingRates(symbol, startTime, endTime) {
|
|
@@ -14,7 +16,10 @@ async function fetchHistoricalFundingRates(symbol, startTime, endTime) {
|
|
|
14
16
|
});
|
|
15
17
|
if (data.length === 0) break;
|
|
16
18
|
for (const entry of data) {
|
|
17
|
-
allRates.push({
|
|
19
|
+
allRates.push({
|
|
20
|
+
time: entry.fundingTime,
|
|
21
|
+
rate: Number(entry.fundingRate)
|
|
22
|
+
});
|
|
18
23
|
}
|
|
19
24
|
const lastTime = data[data.length - 1]?.fundingTime;
|
|
20
25
|
if (!lastTime || lastTime <= cursor) break;
|
|
@@ -22,5 +27,6 @@ async function fetchHistoricalFundingRates(symbol, startTime, endTime) {
|
|
|
22
27
|
}
|
|
23
28
|
return allRates.sort((a, b) => a.time - b.time);
|
|
24
29
|
}
|
|
30
|
+
__name(fetchHistoricalFundingRates, "fetchHistoricalFundingRates");
|
|
25
31
|
|
|
26
32
|
export { fetchHistoricalFundingRates };
|
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
import { httpGet } from './http-client';
|
|
2
2
|
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
5
|
const BYBIT_URL = "https://api.bybit.com/v5/market/funding/history";
|
|
4
6
|
const MAX_LIMIT = 200;
|
|
5
7
|
async function fetchHistoricalFundingRates(symbol, startTime, endTime) {
|
|
6
8
|
const allRates = [];
|
|
7
9
|
let cursor = endTime;
|
|
8
|
-
const validate = (d) => d.retCode !== 0 ? `Bybit API error: ${d.retMsg}` : void 0;
|
|
10
|
+
const validate = /* @__PURE__ */ __name((d) => d.retCode !== 0 ? `Bybit API error: ${d.retMsg}` : void 0, "validate");
|
|
9
11
|
while (cursor > startTime) {
|
|
10
|
-
const data = await httpGet(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
limit: String(MAX_LIMIT)
|
|
18
|
-
},
|
|
19
|
-
validate
|
|
20
|
-
);
|
|
12
|
+
const data = await httpGet(BYBIT_URL, {
|
|
13
|
+
category: "linear",
|
|
14
|
+
symbol,
|
|
15
|
+
startTime: String(startTime),
|
|
16
|
+
endTime: String(cursor),
|
|
17
|
+
limit: String(MAX_LIMIT)
|
|
18
|
+
}, validate);
|
|
21
19
|
if (data.result.list.length === 0) break;
|
|
22
20
|
for (const entry of data.result.list) {
|
|
23
|
-
allRates.push({
|
|
21
|
+
allRates.push({
|
|
22
|
+
time: Number(entry.fundingRateTimestamp),
|
|
23
|
+
rate: Number(entry.fundingRate)
|
|
24
|
+
});
|
|
24
25
|
}
|
|
25
26
|
const oldestTime = Number(data.result.list[data.result.list.length - 1]?.fundingRateTimestamp);
|
|
26
27
|
if (!oldestTime || oldestTime >= cursor) break;
|
|
@@ -28,5 +29,6 @@ async function fetchHistoricalFundingRates(symbol, startTime, endTime) {
|
|
|
28
29
|
}
|
|
29
30
|
return allRates.sort((a, b) => a.time - b.time);
|
|
30
31
|
}
|
|
32
|
+
__name(fetchHistoricalFundingRates, "fetchHistoricalFundingRates");
|
|
31
33
|
|
|
32
34
|
export { fetchHistoricalFundingRates };
|
|
@@ -1,20 +1,22 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
1
3
|
const BASE_DELAY_MS = 1e3;
|
|
2
4
|
const MAX_DELAY_MS = 6e4;
|
|
3
5
|
async function httpGet(url, params, validate) {
|
|
4
6
|
const qs = new URLSearchParams(params).toString();
|
|
5
7
|
return httpFetch(`${url}?${qs}`, void 0, validate);
|
|
6
8
|
}
|
|
9
|
+
__name(httpGet, "httpGet");
|
|
7
10
|
async function httpPost(url, body, validate) {
|
|
8
|
-
return httpFetch(
|
|
9
|
-
|
|
10
|
-
{
|
|
11
|
-
|
|
12
|
-
headers: { "Content-Type": "application/json" },
|
|
13
|
-
body: JSON.stringify(body)
|
|
11
|
+
return httpFetch(url, {
|
|
12
|
+
method: "POST",
|
|
13
|
+
headers: {
|
|
14
|
+
"Content-Type": "application/json"
|
|
14
15
|
},
|
|
15
|
-
|
|
16
|
-
);
|
|
16
|
+
body: JSON.stringify(body)
|
|
17
|
+
}, validate);
|
|
17
18
|
}
|
|
19
|
+
__name(httpPost, "httpPost");
|
|
18
20
|
async function httpFetch(url, init, validate) {
|
|
19
21
|
let attempt = 0;
|
|
20
22
|
while (true) {
|
|
@@ -36,5 +38,6 @@ async function httpFetch(url, init, validate) {
|
|
|
36
38
|
return data;
|
|
37
39
|
}
|
|
38
40
|
}
|
|
41
|
+
__name(httpFetch, "httpFetch");
|
|
39
42
|
|
|
40
43
|
export { httpGet, httpPost };
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { httpPost } from './http-client';
|
|
2
2
|
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
5
|
async function fetchHistoricalFundingRates(symbol, startTime, endTime) {
|
|
4
6
|
const allRates = [];
|
|
5
7
|
let cursor = startTime;
|
|
@@ -13,7 +15,10 @@ async function fetchHistoricalFundingRates(symbol, startTime, endTime) {
|
|
|
13
15
|
if (data.length === 0) break;
|
|
14
16
|
for (const entry of data) {
|
|
15
17
|
if (entry.time >= endTime) continue;
|
|
16
|
-
allRates.push({
|
|
18
|
+
allRates.push({
|
|
19
|
+
time: entry.time,
|
|
20
|
+
rate: Number(entry.fundingRate)
|
|
21
|
+
});
|
|
17
22
|
}
|
|
18
23
|
const lastTime = data[data.length - 1]?.time;
|
|
19
24
|
if (!lastTime || lastTime <= cursor) break;
|
|
@@ -21,5 +26,6 @@ async function fetchHistoricalFundingRates(symbol, startTime, endTime) {
|
|
|
21
26
|
}
|
|
22
27
|
return allRates.sort((a, b) => a.time - b.time);
|
|
23
28
|
}
|
|
29
|
+
__name(fetchHistoricalFundingRates, "fetchHistoricalFundingRates");
|
|
24
30
|
|
|
25
31
|
export { fetchHistoricalFundingRates };
|
|
@@ -4,6 +4,8 @@ import { fetchHistoricalFundingRates as fetchHistoricalFundingRates$5 } from './
|
|
|
4
4
|
import { fetchHistoricalFundingRates as fetchHistoricalFundingRates$1 } from './lighter';
|
|
5
5
|
import { fetchHistoricalFundingRates as fetchHistoricalFundingRates$2 } from './okx';
|
|
6
6
|
|
|
7
|
+
var __defProp = Object.defineProperty;
|
|
8
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
9
|
async function fetchHistoricalFundingRates(connector, symbol, startTime, endTime, duration) {
|
|
8
10
|
switch (connector) {
|
|
9
11
|
case "hyperliquid":
|
|
@@ -20,5 +22,6 @@ async function fetchHistoricalFundingRates(connector, symbol, startTime, endTime
|
|
|
20
22
|
throw new Error(`Unsupported connector: ${connector}`);
|
|
21
23
|
}
|
|
22
24
|
}
|
|
25
|
+
__name(fetchHistoricalFundingRates, "fetchHistoricalFundingRates");
|
|
23
26
|
|
|
24
27
|
export { fetchHistoricalFundingRates };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import BigNumber from 'bignumber.js';
|
|
2
2
|
import { httpGet } from './http-client';
|
|
3
3
|
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
4
6
|
const BASE_URL = "https://mainnet.zklighter.elliot.ai";
|
|
5
7
|
const MAX_LIMIT = 500;
|
|
6
8
|
async function fetchHistoricalFundingRates(symbol, startTime, endTime, _duration) {
|
|
@@ -21,7 +23,10 @@ async function fetchHistoricalFundingRates(symbol, startTime, endTime, _duration
|
|
|
21
23
|
for (const entry of entries) {
|
|
22
24
|
const timeMs = entry.timestamp * 1e3;
|
|
23
25
|
const rate = entry.rate ? new BigNumber(entry.rate).dividedBy(100).times(entry.direction === "long" ? -1 : 1).toNumber() : 0;
|
|
24
|
-
allRates.push({
|
|
26
|
+
allRates.push({
|
|
27
|
+
time: timeMs,
|
|
28
|
+
rate
|
|
29
|
+
});
|
|
25
30
|
}
|
|
26
31
|
const lastTimestamp = entries[entries.length - 1]?.timestamp;
|
|
27
32
|
if (!lastTimestamp) break;
|
|
@@ -31,5 +36,6 @@ async function fetchHistoricalFundingRates(symbol, startTime, endTime, _duration
|
|
|
31
36
|
}
|
|
32
37
|
return allRates.sort((a, b) => a.time - b.time);
|
|
33
38
|
}
|
|
39
|
+
__name(fetchHistoricalFundingRates, "fetchHistoricalFundingRates");
|
|
34
40
|
|
|
35
41
|
export { fetchHistoricalFundingRates };
|
|
@@ -1,27 +1,28 @@
|
|
|
1
1
|
import { httpGet } from './http-client';
|
|
2
2
|
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
5
|
const OKX_URL = "https://www.okx.com/api/v5/public/funding-rate-history";
|
|
4
6
|
const MAX_LIMIT = 400;
|
|
5
7
|
async function fetchHistoricalFundingRates(instId, startTime, endTime) {
|
|
6
8
|
const allRates = [];
|
|
7
9
|
let cursor = endTime;
|
|
8
|
-
const validate = (d) => d.code !== "0" ? `OKX API error: ${d.msg}` : void 0;
|
|
10
|
+
const validate = /* @__PURE__ */ __name((d) => d.code !== "0" ? `OKX API error: ${d.msg}` : void 0, "validate");
|
|
9
11
|
while (cursor > startTime) {
|
|
10
|
-
const data = await httpGet(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
limit: String(MAX_LIMIT)
|
|
17
|
-
},
|
|
18
|
-
validate
|
|
19
|
-
);
|
|
12
|
+
const data = await httpGet(OKX_URL, {
|
|
13
|
+
instId,
|
|
14
|
+
before: String(startTime),
|
|
15
|
+
after: String(cursor),
|
|
16
|
+
limit: String(MAX_LIMIT)
|
|
17
|
+
}, validate);
|
|
20
18
|
if (data.data.length === 0) break;
|
|
21
19
|
let oldestTime = Infinity;
|
|
22
20
|
for (const entry of data.data) {
|
|
23
21
|
const time = Number(entry.fundingTime);
|
|
24
|
-
allRates.push({
|
|
22
|
+
allRates.push({
|
|
23
|
+
time,
|
|
24
|
+
rate: Number(entry.realizedRate)
|
|
25
|
+
});
|
|
25
26
|
if (time < oldestTime) oldestTime = time;
|
|
26
27
|
}
|
|
27
28
|
if (oldestTime >= cursor) break;
|
|
@@ -29,5 +30,6 @@ async function fetchHistoricalFundingRates(instId, startTime, endTime) {
|
|
|
29
30
|
}
|
|
30
31
|
return allRates.filter((r) => r.time >= startTime && r.time < endTime).sort((a, b) => a.time - b.time);
|
|
31
32
|
}
|
|
33
|
+
__name(fetchHistoricalFundingRates, "fetchHistoricalFundingRates");
|
|
32
34
|
|
|
33
35
|
export { fetchHistoricalFundingRates };
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { fetchHistoricalFundingRates } from './fetcher';
|
|
2
2
|
import { aggregateRates, annualizeRates, computeBasketRates, computeStartTime } from './utils';
|
|
3
3
|
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
4
6
|
const DEFAULT_AGGREGATION = "1d";
|
|
5
7
|
const DEFAULT_DURATION = "1m";
|
|
6
8
|
class FundingRate {
|
|
9
|
+
static {
|
|
10
|
+
__name(this, "FundingRate");
|
|
11
|
+
}
|
|
7
12
|
transport;
|
|
8
13
|
longTokens;
|
|
9
14
|
shortTokens;
|
|
@@ -83,29 +88,32 @@ class FundingRate {
|
|
|
83
88
|
}
|
|
84
89
|
async refetch(tokens, start, end, duration) {
|
|
85
90
|
if (tokens.length === 0) return;
|
|
86
|
-
await Promise.all(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
);
|
|
91
|
+
await Promise.all(tokens.map(async (token) => {
|
|
92
|
+
const existing = this.cache[token.symbol];
|
|
93
|
+
const fetchStart = existing ? Math.min(start, existing.start) : start;
|
|
94
|
+
const fetchEnd = existing ? Math.max(end, existing.end) : end;
|
|
95
|
+
try {
|
|
96
|
+
const rates = await fetchHistoricalFundingRates(this.transport.connector, token.symbol, fetchStart, fetchEnd, duration);
|
|
97
|
+
this.cache[token.symbol] = {
|
|
98
|
+
entries: rates,
|
|
99
|
+
start: fetchStart,
|
|
100
|
+
end: fetchEnd
|
|
101
|
+
};
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.warn(`Failed to fetch funding rate data for ${token.symbol}:`, error);
|
|
104
|
+
this.cache[token.symbol] = {
|
|
105
|
+
entries: [],
|
|
106
|
+
start: fetchStart,
|
|
107
|
+
end: fetchEnd
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}));
|
|
106
111
|
}
|
|
107
112
|
getAllTokens() {
|
|
108
|
-
return [
|
|
113
|
+
return [
|
|
114
|
+
...this.longTokens,
|
|
115
|
+
...this.shortTokens
|
|
116
|
+
];
|
|
109
117
|
}
|
|
110
118
|
assertTokenExists(symbol) {
|
|
111
119
|
if (!this.getAllTokens().some((t) => t.symbol === symbol)) {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import BigNumber from 'bignumber.js';
|
|
2
2
|
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
5
|
function computeStartTime(endTime, duration) {
|
|
4
6
|
const date = new Date(endTime);
|
|
5
7
|
switch (duration) {
|
|
@@ -13,6 +15,7 @@ function computeStartTime(endTime, duration) {
|
|
|
13
15
|
return date.getTime();
|
|
14
16
|
}
|
|
15
17
|
}
|
|
18
|
+
__name(computeStartTime, "computeStartTime");
|
|
16
19
|
function getBucketStart(timestamp, aggregation) {
|
|
17
20
|
const date = new Date(timestamp);
|
|
18
21
|
switch (aggregation) {
|
|
@@ -28,20 +31,29 @@ function getBucketStart(timestamp, aggregation) {
|
|
|
28
31
|
return Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1);
|
|
29
32
|
}
|
|
30
33
|
}
|
|
34
|
+
__name(getBucketStart, "getBucketStart");
|
|
31
35
|
function aggregateRates(rawRates, aggregation) {
|
|
32
36
|
if (aggregation === "none") {
|
|
33
|
-
return [
|
|
37
|
+
return [
|
|
38
|
+
...rawRates
|
|
39
|
+
].sort((a, b) => a.time - b.time);
|
|
34
40
|
}
|
|
35
41
|
const buckets = /* @__PURE__ */ new Map();
|
|
36
42
|
for (const rate of rawRates) {
|
|
37
43
|
const key = getBucketStart(rate.time, aggregation);
|
|
38
44
|
buckets.set(key, (buckets.get(key) ?? new BigNumber(0)).plus(rate.rate));
|
|
39
45
|
}
|
|
40
|
-
return Array.from(buckets.entries()).map(([time, rate]) => ({
|
|
46
|
+
return Array.from(buckets.entries()).map(([time, rate]) => ({
|
|
47
|
+
time,
|
|
48
|
+
rate: rate.toNumber()
|
|
49
|
+
})).sort((a, b) => a.time - b.time);
|
|
41
50
|
}
|
|
51
|
+
__name(aggregateRates, "aggregateRates");
|
|
42
52
|
function inferAvgIntervalMs(entries) {
|
|
43
53
|
if (entries.length < 2) return null;
|
|
44
|
-
const sorted = [
|
|
54
|
+
const sorted = [
|
|
55
|
+
...entries
|
|
56
|
+
].sort((a, b) => a.time - b.time);
|
|
45
57
|
let totalGap = new BigNumber(0);
|
|
46
58
|
let count = 0;
|
|
47
59
|
for (let i = 1; i < sorted.length; i++) {
|
|
@@ -56,6 +68,7 @@ function inferAvgIntervalMs(entries) {
|
|
|
56
68
|
}
|
|
57
69
|
return count > 0 ? totalGap.div(count) : null;
|
|
58
70
|
}
|
|
71
|
+
__name(inferAvgIntervalMs, "inferAvgIntervalMs");
|
|
59
72
|
const MS_PER_YEAR = 365 * 864e5;
|
|
60
73
|
function annualizeRates(entries, aggregation, rawEntriesForInference) {
|
|
61
74
|
let multiplier;
|
|
@@ -77,8 +90,12 @@ function annualizeRates(entries, aggregation, rawEntriesForInference) {
|
|
|
77
90
|
annualizedRate: new BigNumber(e.rate).times(multiplier).toNumber()
|
|
78
91
|
}));
|
|
79
92
|
}
|
|
93
|
+
__name(annualizeRates, "annualizeRates");
|
|
80
94
|
function computeBasketRates(longTokens, shortTokens, tokenRates) {
|
|
81
|
-
const allSymbols = [
|
|
95
|
+
const allSymbols = [
|
|
96
|
+
...longTokens,
|
|
97
|
+
...shortTokens
|
|
98
|
+
].map((t) => t.symbol);
|
|
82
99
|
if (allSymbols.length === 0) return [];
|
|
83
100
|
const lookups = {};
|
|
84
101
|
const allTimestamps = /* @__PURE__ */ new Set();
|
|
@@ -111,10 +128,14 @@ function computeBasketRates(longTokens, shortTokens, tokenRates) {
|
|
|
111
128
|
}
|
|
112
129
|
}
|
|
113
130
|
if (count > 0) {
|
|
114
|
-
result.push({
|
|
131
|
+
result.push({
|
|
132
|
+
time,
|
|
133
|
+
rate: total.toNumber()
|
|
134
|
+
});
|
|
115
135
|
}
|
|
116
136
|
}
|
|
117
137
|
return result;
|
|
118
138
|
}
|
|
139
|
+
__name(computeBasketRates, "computeBasketRates");
|
|
119
140
|
|
|
120
141
|
export { aggregateRates, annualizeRates, computeBasketRates, computeStartTime };
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import BigNumber from 'bignumber.js';
|
|
2
2
|
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
5
|
function aggregateBook(localBook, bucketSize, depth) {
|
|
4
6
|
const bucket = new BigNumber(bucketSize);
|
|
5
|
-
if (!bucket.isFinite() || bucket.lte(0)) return {
|
|
7
|
+
if (!bucket.isFinite() || bucket.lte(0)) return {
|
|
8
|
+
bids: [],
|
|
9
|
+
asks: []
|
|
10
|
+
};
|
|
6
11
|
return {
|
|
7
12
|
bids: walk(localBook.sortedBids(), bucket, "bid", depth),
|
|
8
13
|
asks: walk(localBook.sortedAsks(), bucket, "ask", depth)
|
|
9
14
|
};
|
|
10
15
|
}
|
|
16
|
+
__name(aggregateBook, "aggregateBook");
|
|
11
17
|
function walk(sorted, bucket, side, depth) {
|
|
12
18
|
const result = [];
|
|
13
19
|
let currentBoundary = null;
|
|
@@ -21,7 +27,10 @@ function walk(sorted, bucket, side, depth) {
|
|
|
21
27
|
const boundary = side === "bid" ? p.div(bucket).integerValue(BigNumber.ROUND_FLOOR).times(bucket) : p.div(bucket).integerValue(BigNumber.ROUND_CEIL).times(bucket);
|
|
22
28
|
if (currentBoundary === null || !boundary.eq(currentBoundary)) {
|
|
23
29
|
if (currentBoundary !== null) {
|
|
24
|
-
result.push({
|
|
30
|
+
result.push({
|
|
31
|
+
price: currentBoundary.toNumber(),
|
|
32
|
+
size: currentSize.toNumber()
|
|
33
|
+
});
|
|
25
34
|
if (result.length >= depth) return result;
|
|
26
35
|
}
|
|
27
36
|
currentBoundary = boundary;
|
|
@@ -30,9 +39,13 @@ function walk(sorted, bucket, side, depth) {
|
|
|
30
39
|
currentSize = currentSize.plus(size);
|
|
31
40
|
}
|
|
32
41
|
if (currentBoundary !== null && result.length < depth) {
|
|
33
|
-
result.push({
|
|
42
|
+
result.push({
|
|
43
|
+
price: currentBoundary.toNumber(),
|
|
44
|
+
size: currentSize.toNumber()
|
|
45
|
+
});
|
|
34
46
|
}
|
|
35
47
|
return result;
|
|
36
48
|
}
|
|
49
|
+
__name(walk, "walk");
|
|
37
50
|
|
|
38
51
|
export { aggregateBook };
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import BigNumber from 'bignumber.js';
|
|
2
2
|
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
5
|
class LocalBook {
|
|
6
|
+
static {
|
|
7
|
+
__name(this, "LocalBook");
|
|
8
|
+
}
|
|
4
9
|
_bids = [];
|
|
5
10
|
_asks = [];
|
|
6
11
|
lastUpdateTs = 0;
|
|
@@ -60,9 +65,9 @@ class LocalBook {
|
|
|
60
65
|
this.isLoaded = false;
|
|
61
66
|
}
|
|
62
67
|
/**
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
68
|
+
* Insert, update, or remove a level in a sorted array.
|
|
69
|
+
* @param dir -1 for descending (bids), 1 for ascending (asks)
|
|
70
|
+
*/
|
|
66
71
|
upsert(levels, price, size, dir) {
|
|
67
72
|
const numPrice = Number(price);
|
|
68
73
|
let lo = 0;
|
|
@@ -77,12 +82,18 @@ class LocalBook {
|
|
|
77
82
|
if (size === 0) {
|
|
78
83
|
levels.splice(lo, 1);
|
|
79
84
|
} else {
|
|
80
|
-
levels[lo] = [
|
|
85
|
+
levels[lo] = [
|
|
86
|
+
price,
|
|
87
|
+
size
|
|
88
|
+
];
|
|
81
89
|
}
|
|
82
90
|
return;
|
|
83
91
|
}
|
|
84
92
|
if (size > 0) {
|
|
85
|
-
levels.splice(lo, 0, [
|
|
93
|
+
levels.splice(lo, 0, [
|
|
94
|
+
price,
|
|
95
|
+
size
|
|
96
|
+
]);
|
|
86
97
|
}
|
|
87
98
|
}
|
|
88
99
|
}
|
|
@@ -3,7 +3,12 @@ import { LocalBook } from './book/local-book';
|
|
|
3
3
|
import { DEFAULT_DEPTH } from './types';
|
|
4
4
|
import { createDepthWs } from './ws';
|
|
5
5
|
|
|
6
|
+
var __defProp = Object.defineProperty;
|
|
7
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
6
8
|
class Orderbook {
|
|
9
|
+
static {
|
|
10
|
+
__name(this, "Orderbook");
|
|
11
|
+
}
|
|
7
12
|
depthWs;
|
|
8
13
|
symbol;
|
|
9
14
|
book = new LocalBook();
|
|
@@ -22,23 +27,23 @@ class Orderbook {
|
|
|
22
27
|
this.depthWs.subscribe(this.symbol);
|
|
23
28
|
}
|
|
24
29
|
/**
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
* Subscribe to orderbook updates. Returns the symbol as the subscription ID.
|
|
31
|
+
*/
|
|
27
32
|
subscribe(cb) {
|
|
28
33
|
this.listener = cb;
|
|
29
34
|
return this.symbol;
|
|
30
35
|
}
|
|
31
36
|
/**
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
* Unsubscribe from orderbook updates.
|
|
38
|
+
*/
|
|
34
39
|
unsubscribe(id) {
|
|
35
40
|
if (id !== this.symbol) throw new Error(`Subscription "${id}" not found`);
|
|
36
41
|
this.listener = null;
|
|
37
42
|
}
|
|
38
43
|
/**
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
* Set the aggregation bucket size (e.g. 0.1, 1, 10, 100).
|
|
45
|
+
* Set to 0 to disable aggregation.
|
|
46
|
+
*/
|
|
42
47
|
setAggregation(aggregation) {
|
|
43
48
|
if (this.aggregation === aggregation) return;
|
|
44
49
|
this.aggregation = aggregation;
|
|
@@ -51,9 +56,9 @@ class Orderbook {
|
|
|
51
56
|
return this.aggregation;
|
|
52
57
|
}
|
|
53
58
|
/**
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
* Update the snapshotted mid-price used for server-side aggregation params
|
|
60
|
+
* (e.g. Hyperliquid's nSigFigs/mantissa).
|
|
61
|
+
*/
|
|
57
62
|
setSnapshottedPrice(price) {
|
|
58
63
|
this.depthWs.setSnapshottedPrice(price);
|
|
59
64
|
}
|
|
@@ -62,7 +67,12 @@ class Orderbook {
|
|
|
62
67
|
return this.buildSnapshot();
|
|
63
68
|
}
|
|
64
69
|
get bbo() {
|
|
65
|
-
if (!this.book.isLoaded) return {
|
|
70
|
+
if (!this.book.isLoaded) return {
|
|
71
|
+
bestBid: null,
|
|
72
|
+
bestAsk: null,
|
|
73
|
+
spread: null,
|
|
74
|
+
spreadPct: null
|
|
75
|
+
};
|
|
66
76
|
return this.book.bbo();
|
|
67
77
|
}
|
|
68
78
|
destroy() {
|
|
@@ -87,8 +97,14 @@ class Orderbook {
|
|
|
87
97
|
bids = aggregated.bids;
|
|
88
98
|
asks = aggregated.asks;
|
|
89
99
|
} else {
|
|
90
|
-
bids = this.book.topBids(this.depth).map(([price, size]) => ({
|
|
91
|
-
|
|
100
|
+
bids = this.book.topBids(this.depth).map(([price, size]) => ({
|
|
101
|
+
price: Number(price),
|
|
102
|
+
size
|
|
103
|
+
}));
|
|
104
|
+
asks = this.book.topAsks(this.depth).map(([price, size]) => ({
|
|
105
|
+
price: Number(price),
|
|
106
|
+
size
|
|
107
|
+
}));
|
|
92
108
|
}
|
|
93
109
|
return {
|
|
94
110
|
symbol: this.symbol,
|