@tradejs/connectors 1.0.4 → 1.0.6
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 +1 -1
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +329 -134
- package/dist/index.mjs +329 -134
- package/package.json +14 -6
package/README.md
CHANGED
package/dist/index.d.mts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -47,6 +47,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
47
47
|
// src/ByBit/index.ts
|
|
48
48
|
var import_lodash = __toESM(require("lodash"));
|
|
49
49
|
var import_chalk = __toESM(require("chalk"));
|
|
50
|
+
var import_async = require("@tradejs/core/async");
|
|
50
51
|
var import_constants2 = require("@tradejs/core/constants");
|
|
51
52
|
var import_data = require("@tradejs/core/data");
|
|
52
53
|
var import_data2 = require("@tradejs/core/data");
|
|
@@ -61,7 +62,13 @@ var import_bybit_api = require("bybit-api");
|
|
|
61
62
|
var import_logger = require("@tradejs/infra/logger");
|
|
62
63
|
var import_redis = require("@tradejs/infra/redis");
|
|
63
64
|
var useTestnet = false;
|
|
64
|
-
var getClient = async ({ userName }) => {
|
|
65
|
+
var getClient = async ({ userName }, access = "private") => {
|
|
66
|
+
if (access === "public") {
|
|
67
|
+
return new import_bybit_api.RestClientV5({
|
|
68
|
+
parseAPIRateLimits: true,
|
|
69
|
+
testnet: useTestnet
|
|
70
|
+
});
|
|
71
|
+
}
|
|
65
72
|
const user = await (0, import_redis.getData)(import_redis.redisKeys.user(userName));
|
|
66
73
|
if (!user) {
|
|
67
74
|
import_logger.logger.log("error", "connection config not found: %s", userName);
|
|
@@ -70,6 +77,7 @@ var getClient = async ({ userName }) => {
|
|
|
70
77
|
const client = new import_bybit_api.RestClientV5({
|
|
71
78
|
key: user.BYBIT_API_KEY,
|
|
72
79
|
secret: user.BYBIT_API_SECRET,
|
|
80
|
+
parseAPIRateLimits: true,
|
|
73
81
|
testnet: useTestnet
|
|
74
82
|
});
|
|
75
83
|
return client;
|
|
@@ -169,6 +177,12 @@ var mapPositionData = (data) => {
|
|
|
169
177
|
// src/ByBit/index.ts
|
|
170
178
|
var LIMIT = 1e3;
|
|
171
179
|
var CACHE_FALLBACK_WINDOW = 1e3;
|
|
180
|
+
var BYBIT_RATE_LIMIT_RETCODE = 10006;
|
|
181
|
+
var BYBIT_TRADING_STOP_NOT_MODIFIED_RETCODE = 34040;
|
|
182
|
+
var KLINE_RATE_LIMIT_MAX_ATTEMPTS = 3;
|
|
183
|
+
var KLINE_RATE_LIMIT_BASE_DELAY_MS = 800;
|
|
184
|
+
var KLINE_RATE_LIMIT_MAX_DELAY_MS = 1e4;
|
|
185
|
+
var KLINE_RATE_LIMIT_RESET_BUFFER_MS = 50;
|
|
172
186
|
var INTERVAL_TO_MINUTES = {
|
|
173
187
|
"1": 1,
|
|
174
188
|
"3": 3,
|
|
@@ -185,9 +199,37 @@ var INTERVAL_TO_MINUTES = {
|
|
|
185
199
|
M: 43200
|
|
186
200
|
};
|
|
187
201
|
var getLogLevel = (res) => res.retCode === 0 ? "info" : "error";
|
|
202
|
+
var isTradingStopNotModified = (res) => res?.retCode === BYBIT_TRADING_STOP_NOT_MODIFIED_RETCODE;
|
|
203
|
+
var isTradingStopAccepted = (res) => res?.retCode === 0 || isTradingStopNotModified(res);
|
|
204
|
+
var isKlineRateLimited = (res) => res?.retCode === BYBIT_RATE_LIMIT_RETCODE;
|
|
205
|
+
var resolveKlineRetryDelayMs = (res, attempt) => {
|
|
206
|
+
const resetAtRaw = Number(res?.rateLimitApi?.resetAtTimestamp);
|
|
207
|
+
if (Number.isFinite(resetAtRaw) && resetAtRaw > 0) {
|
|
208
|
+
return Math.max(
|
|
209
|
+
KLINE_RATE_LIMIT_RESET_BUFFER_MS,
|
|
210
|
+
resetAtRaw - Date.now() + KLINE_RATE_LIMIT_RESET_BUFFER_MS
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
const jitterMs = Math.round(Math.random() * 250);
|
|
214
|
+
const backoffMs = Math.min(
|
|
215
|
+
KLINE_RATE_LIMIT_MAX_DELAY_MS,
|
|
216
|
+
KLINE_RATE_LIMIT_BASE_DELAY_MS * 2 ** (attempt - 1)
|
|
217
|
+
);
|
|
218
|
+
return backoffMs + jitterMs;
|
|
219
|
+
};
|
|
188
220
|
var ByBitConnectorCreator = async (config) => {
|
|
189
221
|
let state = {};
|
|
190
222
|
let isTimescaleFallbackMode = false;
|
|
223
|
+
let publicClientPromise = null;
|
|
224
|
+
let privateClientPromise = null;
|
|
225
|
+
const getPublicClient = async () => {
|
|
226
|
+
publicClientPromise ??= getClient(config, "public");
|
|
227
|
+
return publicClientPromise;
|
|
228
|
+
};
|
|
229
|
+
const getPrivateClient = async () => {
|
|
230
|
+
privateClientPromise ??= getClient(config, "private");
|
|
231
|
+
return privateClientPromise;
|
|
232
|
+
};
|
|
191
233
|
const request = async ({
|
|
192
234
|
symbol,
|
|
193
235
|
interval,
|
|
@@ -201,34 +243,60 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
201
243
|
);
|
|
202
244
|
const normalizedEnd = (0, import_math.round)(end || Date.now());
|
|
203
245
|
try {
|
|
204
|
-
const client = await
|
|
246
|
+
const client = await getPublicClient();
|
|
205
247
|
if (!client) return [];
|
|
206
248
|
if (normalizedEnd <= normalizedStart) {
|
|
207
249
|
return [];
|
|
208
250
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
251
|
+
for (let attempt = 1; attempt <= KLINE_RATE_LIMIT_MAX_ATTEMPTS; attempt += 1) {
|
|
252
|
+
const kline = await client.getKline({
|
|
253
|
+
category: import_constants2.MARKET_CATEGORY,
|
|
254
|
+
symbol,
|
|
255
|
+
interval,
|
|
256
|
+
start: normalizedStart,
|
|
257
|
+
end: normalizedEnd,
|
|
258
|
+
limit: LIMIT
|
|
259
|
+
});
|
|
260
|
+
if (isKlineRateLimited(kline)) {
|
|
261
|
+
if (attempt < KLINE_RATE_LIMIT_MAX_ATTEMPTS) {
|
|
262
|
+
const waitMs = resolveKlineRetryDelayMs(kline, attempt);
|
|
263
|
+
import_logger2.logger.log(
|
|
264
|
+
"warn",
|
|
265
|
+
"kline rate limited for %s %s: attempt=%s/%s waitMs=%s",
|
|
266
|
+
symbol,
|
|
267
|
+
interval,
|
|
268
|
+
attempt,
|
|
269
|
+
KLINE_RATE_LIMIT_MAX_ATTEMPTS,
|
|
270
|
+
waitMs
|
|
271
|
+
);
|
|
272
|
+
await (0, import_async.delay)(waitMs);
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
if (!kline?.result?.list) {
|
|
277
|
+
const responseError = typeof kline?.retMsg === "string" && kline.retMsg !== "OK" ? `${kline.retMsg}${typeof kline?.retCode === "number" ? ` (retCode: ${kline.retCode})` : ""}` : typeof kline?.retCode === "number" && kline.retCode !== 0 ? `retCode: ${kline.retCode}` : "";
|
|
278
|
+
import_logger2.logger.log(
|
|
279
|
+
"error",
|
|
280
|
+
"empty kline.list for %s %s%s",
|
|
281
|
+
symbol,
|
|
282
|
+
interval,
|
|
283
|
+
responseError ? `: ${responseError}` : ""
|
|
284
|
+
);
|
|
285
|
+
return [];
|
|
286
|
+
}
|
|
287
|
+
if (!silent) {
|
|
288
|
+
import_logger2.logger.log(
|
|
289
|
+
"info",
|
|
290
|
+
"%s %s %s %s",
|
|
291
|
+
import_chalk.default.yellow((0, import_time2.formatUnix)(normalizedEnd)),
|
|
292
|
+
import_chalk.default.cyan(symbol),
|
|
293
|
+
import_chalk.default.cyan(interval),
|
|
294
|
+
import_chalk.default.yellow(kline.result.list.length)
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
return mapKlineToChartData(kline.result.list.reverse());
|
|
230
298
|
}
|
|
231
|
-
return
|
|
299
|
+
return [];
|
|
232
300
|
} catch (error) {
|
|
233
301
|
import_logger2.logger.log(
|
|
234
302
|
"error",
|
|
@@ -320,6 +388,190 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
320
388
|
await (0, import_timescale.upsertCandles)((0, import_timescale.toRows)(symbol, intMinutes, part));
|
|
321
389
|
}
|
|
322
390
|
};
|
|
391
|
+
const getPositionSnapshot = async (symbol) => {
|
|
392
|
+
const client = await getPrivateClient();
|
|
393
|
+
if (!client) {
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
const positionRes = await client.getPositionInfo({
|
|
397
|
+
symbol,
|
|
398
|
+
category: import_constants2.MARKET_CATEGORY
|
|
399
|
+
});
|
|
400
|
+
if (positionRes.retCode !== 0) {
|
|
401
|
+
import_logger2.logger.log(
|
|
402
|
+
getLogLevel(positionRes),
|
|
403
|
+
"position retCode: %s, %s",
|
|
404
|
+
symbol,
|
|
405
|
+
positionRes.retCode
|
|
406
|
+
);
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
const positions = mapPositionData(positionRes.result.list);
|
|
410
|
+
if (!positions || import_lodash.default.isEmpty(positions)) {
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
const position = positions[0];
|
|
414
|
+
import_logger2.logger.log(
|
|
415
|
+
"debug",
|
|
416
|
+
"position: %s %s qty=%s price=%s",
|
|
417
|
+
position.symbol,
|
|
418
|
+
position.direction,
|
|
419
|
+
position.qty,
|
|
420
|
+
position.price
|
|
421
|
+
);
|
|
422
|
+
return {
|
|
423
|
+
...position
|
|
424
|
+
};
|
|
425
|
+
};
|
|
426
|
+
const setTakeProfits = async ({
|
|
427
|
+
symbol,
|
|
428
|
+
direction,
|
|
429
|
+
qty,
|
|
430
|
+
takeProfits
|
|
431
|
+
}) => {
|
|
432
|
+
const client = await getPrivateClient();
|
|
433
|
+
const marketDataClient = await getPublicClient();
|
|
434
|
+
if (!client || !marketDataClient) {
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
if (!Array.isArray(takeProfits) || takeProfits.length === 0) {
|
|
438
|
+
return true;
|
|
439
|
+
}
|
|
440
|
+
const meta = await getSymbolMeta(marketDataClient, symbol);
|
|
441
|
+
const positionQty = qty ?? (await getPositionSnapshot(symbol))?.qty ?? 0;
|
|
442
|
+
if (!Number.isFinite(positionQty) || positionQty <= 0) {
|
|
443
|
+
import_logger2.logger.log(
|
|
444
|
+
"warn",
|
|
445
|
+
"setTakeProfits: missing position qty: %s",
|
|
446
|
+
(0, import_data2.toJson)({ symbol, qty, positionQty, takeProfits }, true)
|
|
447
|
+
);
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
const isLong = direction === "LONG";
|
|
451
|
+
for (const tp of takeProfits) {
|
|
452
|
+
const tpSizeRaw = positionQty * tp.rate;
|
|
453
|
+
const { qtyNum: tpSizeNum, qtyStr: tpSizeStr } = normalizeQty(
|
|
454
|
+
tpSizeRaw,
|
|
455
|
+
meta
|
|
456
|
+
);
|
|
457
|
+
if (!tpSizeNum || tpSizeNum < meta.minOrderQty) {
|
|
458
|
+
import_logger2.logger.log(
|
|
459
|
+
"warn",
|
|
460
|
+
"tp skipped: size too small %s",
|
|
461
|
+
(0, import_data2.toJson)(
|
|
462
|
+
{ symbol, tp, tpSizeNum, minOrderQty: meta.minOrderQty },
|
|
463
|
+
true
|
|
464
|
+
)
|
|
465
|
+
);
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
const tpPriceNorm = normalizePrice(
|
|
469
|
+
tp.price,
|
|
470
|
+
isLong ? "TP_LONG" : "TP_SHORT",
|
|
471
|
+
meta
|
|
472
|
+
);
|
|
473
|
+
const isFullMode = takeProfits.length === 1 && tp.rate === 1;
|
|
474
|
+
const tpRes = await client.setTradingStop({
|
|
475
|
+
category: import_constants2.MARKET_CATEGORY,
|
|
476
|
+
symbol,
|
|
477
|
+
tpSize: isFullMode ? void 0 : tpSizeStr,
|
|
478
|
+
tpslMode: isFullMode ? "Full" : "Partial",
|
|
479
|
+
takeProfit: tpPriceNorm.priceStr,
|
|
480
|
+
tpTriggerBy: "MarkPrice",
|
|
481
|
+
tpOrderType: "Market",
|
|
482
|
+
positionIdx: 0
|
|
483
|
+
});
|
|
484
|
+
if (isTradingStopNotModified(tpRes)) {
|
|
485
|
+
import_logger2.logger.log(
|
|
486
|
+
"debug",
|
|
487
|
+
"tp unchanged: %s %s price=%s rate=%s",
|
|
488
|
+
symbol,
|
|
489
|
+
direction,
|
|
490
|
+
tpPriceNorm.priceStr,
|
|
491
|
+
tp.rate
|
|
492
|
+
);
|
|
493
|
+
} else if (tpRes.retCode === 0) {
|
|
494
|
+
import_logger2.logger.log(
|
|
495
|
+
"info",
|
|
496
|
+
"tp updated: %s %s price=%s rate=%s",
|
|
497
|
+
symbol,
|
|
498
|
+
direction,
|
|
499
|
+
tpPriceNorm.priceStr,
|
|
500
|
+
tp.rate
|
|
501
|
+
);
|
|
502
|
+
} else {
|
|
503
|
+
import_logger2.logger.log(
|
|
504
|
+
"error",
|
|
505
|
+
"tp failed: %s %s price=%s rate=%s %s",
|
|
506
|
+
symbol,
|
|
507
|
+
direction,
|
|
508
|
+
tpPriceNorm.priceStr,
|
|
509
|
+
tp.rate,
|
|
510
|
+
(0, import_data2.toJson)(tpRes, true)
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
if (!isTradingStopAccepted(tpRes)) {
|
|
514
|
+
return false;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return true;
|
|
518
|
+
};
|
|
519
|
+
const setStopLoss = async ({
|
|
520
|
+
symbol,
|
|
521
|
+
direction,
|
|
522
|
+
stopLossPrice
|
|
523
|
+
}) => {
|
|
524
|
+
const client = await getPrivateClient();
|
|
525
|
+
const marketDataClient = await getPublicClient();
|
|
526
|
+
if (!client || !marketDataClient) {
|
|
527
|
+
return false;
|
|
528
|
+
}
|
|
529
|
+
if (typeof stopLossPrice !== "number" || !Number.isFinite(stopLossPrice)) {
|
|
530
|
+
return true;
|
|
531
|
+
}
|
|
532
|
+
const meta = await getSymbolMeta(marketDataClient, symbol);
|
|
533
|
+
const isLong = direction === "LONG";
|
|
534
|
+
const slNormalized = normalizePrice(
|
|
535
|
+
stopLossPrice,
|
|
536
|
+
isLong ? "SL_LONG" : "SL_SHORT",
|
|
537
|
+
meta
|
|
538
|
+
);
|
|
539
|
+
const slRes = await client.setTradingStop({
|
|
540
|
+
category: import_constants2.MARKET_CATEGORY,
|
|
541
|
+
symbol,
|
|
542
|
+
tpslMode: "Full",
|
|
543
|
+
stopLoss: slNormalized.priceStr,
|
|
544
|
+
slTriggerBy: "LastPrice",
|
|
545
|
+
positionIdx: 0
|
|
546
|
+
});
|
|
547
|
+
if (isTradingStopNotModified(slRes)) {
|
|
548
|
+
import_logger2.logger.log(
|
|
549
|
+
"debug",
|
|
550
|
+
"sl unchanged: %s %s stopLoss=%s",
|
|
551
|
+
symbol,
|
|
552
|
+
direction,
|
|
553
|
+
slNormalized.priceStr
|
|
554
|
+
);
|
|
555
|
+
} else if (slRes.retCode === 0) {
|
|
556
|
+
import_logger2.logger.log(
|
|
557
|
+
"info",
|
|
558
|
+
"sl updated: %s %s stopLoss=%s",
|
|
559
|
+
symbol,
|
|
560
|
+
direction,
|
|
561
|
+
slNormalized.priceStr
|
|
562
|
+
);
|
|
563
|
+
} else {
|
|
564
|
+
import_logger2.logger.log(
|
|
565
|
+
"error",
|
|
566
|
+
"sl failed: %s %s stopLoss=%s %s",
|
|
567
|
+
symbol,
|
|
568
|
+
direction,
|
|
569
|
+
slNormalized.priceStr,
|
|
570
|
+
(0, import_data2.toJson)(slRes, true)
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
return isTradingStopAccepted(slRes);
|
|
574
|
+
};
|
|
323
575
|
return {
|
|
324
576
|
getState: async () => state,
|
|
325
577
|
setState: async (newState) => {
|
|
@@ -331,7 +583,8 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
331
583
|
start: defaultStart,
|
|
332
584
|
end: defaultEnd,
|
|
333
585
|
silent = false,
|
|
334
|
-
cacheOnly = false
|
|
586
|
+
cacheOnly = false,
|
|
587
|
+
warmOnly = false
|
|
335
588
|
}) => {
|
|
336
589
|
const intMinutes = intervalToMinutes(interval);
|
|
337
590
|
if (!intMinutes) {
|
|
@@ -407,6 +660,16 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
407
660
|
if (!cacheOnly && isRightEdgeQuery) {
|
|
408
661
|
await refreshTail({ symbol, interval, silent });
|
|
409
662
|
}
|
|
663
|
+
if (warmOnly) {
|
|
664
|
+
if (isTimescaleFallbackMode) {
|
|
665
|
+
isTimescaleFallbackMode = false;
|
|
666
|
+
import_logger2.logger.log(
|
|
667
|
+
"info",
|
|
668
|
+
"TimescaleDB connection restored for kline cache"
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
return [];
|
|
672
|
+
}
|
|
410
673
|
const rangeStart = defaultStart ?? dataStart ?? 0;
|
|
411
674
|
const rangeEnd = defaultEnd ?? dataEnd ?? Date.now();
|
|
412
675
|
const { normStart: finalStart, normEnd: finalEnd } = normalizeRangeToClosed(intervalMs, rangeStart, rangeEnd);
|
|
@@ -432,7 +695,7 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
432
695
|
String(error)
|
|
433
696
|
);
|
|
434
697
|
}
|
|
435
|
-
if (cacheOnly) {
|
|
698
|
+
if (cacheOnly || warmOnly) {
|
|
436
699
|
return [];
|
|
437
700
|
}
|
|
438
701
|
return request({
|
|
@@ -444,41 +707,9 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
444
707
|
});
|
|
445
708
|
}
|
|
446
709
|
},
|
|
447
|
-
getPosition: async (symbol) =>
|
|
448
|
-
const client = await getClient(config);
|
|
449
|
-
if (!client) {
|
|
450
|
-
return null;
|
|
451
|
-
}
|
|
452
|
-
const positionRes = await client.getPositionInfo({
|
|
453
|
-
symbol,
|
|
454
|
-
category: import_constants2.MARKET_CATEGORY
|
|
455
|
-
});
|
|
456
|
-
import_logger2.logger.log(
|
|
457
|
-
getLogLevel(positionRes),
|
|
458
|
-
"position retCode: %s, %s",
|
|
459
|
-
symbol,
|
|
460
|
-
positionRes.retCode
|
|
461
|
-
);
|
|
462
|
-
if (positionRes.retCode !== 0) {
|
|
463
|
-
return null;
|
|
464
|
-
}
|
|
465
|
-
const positions = mapPositionData(positionRes.result.list);
|
|
466
|
-
if (!positions || import_lodash.default.isEmpty(positions)) {
|
|
467
|
-
return null;
|
|
468
|
-
}
|
|
469
|
-
const position = positions[0];
|
|
470
|
-
import_logger2.logger.log(
|
|
471
|
-
getLogLevel(positionRes),
|
|
472
|
-
"position: %s, %s",
|
|
473
|
-
symbol,
|
|
474
|
-
(0, import_data2.toJson)(positionRes, true)
|
|
475
|
-
);
|
|
476
|
-
return {
|
|
477
|
-
...position
|
|
478
|
-
};
|
|
479
|
-
},
|
|
710
|
+
getPosition: async (symbol) => getPositionSnapshot(symbol),
|
|
480
711
|
getPositions: async () => {
|
|
481
|
-
const client = await
|
|
712
|
+
const client = await getPrivateClient();
|
|
482
713
|
if (!client) {
|
|
483
714
|
return [];
|
|
484
715
|
}
|
|
@@ -486,12 +717,12 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
486
717
|
category: import_constants2.MARKET_CATEGORY,
|
|
487
718
|
settleCoin: "USDT"
|
|
488
719
|
});
|
|
489
|
-
import_logger2.logger.log(
|
|
490
|
-
getLogLevel(positionRes),
|
|
491
|
-
"positions retCode: %s, %s",
|
|
492
|
-
positionRes.retCode
|
|
493
|
-
);
|
|
494
720
|
if (positionRes.retCode !== 0) {
|
|
721
|
+
import_logger2.logger.log(
|
|
722
|
+
getLogLevel(positionRes),
|
|
723
|
+
"positions retCode: %s, %s",
|
|
724
|
+
positionRes.retCode
|
|
725
|
+
);
|
|
495
726
|
return [];
|
|
496
727
|
}
|
|
497
728
|
const positions = mapPositionData(positionRes.result.list);
|
|
@@ -500,13 +731,14 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
500
731
|
}
|
|
501
732
|
return positions;
|
|
502
733
|
},
|
|
503
|
-
placeOrder: async ({ symbol, price, qty, direction, isLimit }
|
|
504
|
-
const client = await
|
|
505
|
-
|
|
734
|
+
placeOrder: async ({ symbol, price, qty, direction, isLimit }) => {
|
|
735
|
+
const client = await getPrivateClient();
|
|
736
|
+
const marketDataClient = await getPublicClient();
|
|
737
|
+
if (!client || !marketDataClient) {
|
|
506
738
|
return false;
|
|
507
739
|
}
|
|
508
740
|
const isLong = direction === "LONG";
|
|
509
|
-
const meta = await getSymbolMeta(
|
|
741
|
+
const meta = await getSymbolMeta(marketDataClient, symbol);
|
|
510
742
|
const { qtyNum: orderQty, qtyStr: orderQtyStr } = normalizeQty(qty, meta);
|
|
511
743
|
if (orderQty < meta.minOrderQty) {
|
|
512
744
|
import_logger2.logger.log(
|
|
@@ -520,9 +752,6 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
520
752
|
return false;
|
|
521
753
|
}
|
|
522
754
|
const entryNormalized = isLimit ? normalizePrice(price, "ENTRY", meta) : void 0;
|
|
523
|
-
const slNormalized = slPrice ? normalizePrice(slPrice, isLong ? "SL_LONG" : "SL_SHORT", meta) : void 0;
|
|
524
|
-
const firstTP = TP?.[0];
|
|
525
|
-
const tpNormalized = firstTP && firstTP.rate === 1 ? normalizePrice(firstTP.price, isLong ? "TP_LONG" : "TP_SHORT", meta) : void 0;
|
|
526
755
|
import_logger2.logger.log(
|
|
527
756
|
"info",
|
|
528
757
|
"placeOrder: %s",
|
|
@@ -533,10 +762,7 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
533
762
|
qty,
|
|
534
763
|
direction,
|
|
535
764
|
orderQty,
|
|
536
|
-
orderQtyStr
|
|
537
|
-
slPrice,
|
|
538
|
-
slPriceNorm: slNormalized?.priceStr,
|
|
539
|
-
TP
|
|
765
|
+
orderQtyStr
|
|
540
766
|
},
|
|
541
767
|
true
|
|
542
768
|
)
|
|
@@ -551,10 +777,6 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
551
777
|
category: import_constants2.MARKET_CATEGORY,
|
|
552
778
|
symbol,
|
|
553
779
|
price: entryNormalized?.priceStr || void 0,
|
|
554
|
-
takeProfit: tpNormalized?.priceStr || void 0,
|
|
555
|
-
tpTriggerBy: "MarkPrice",
|
|
556
|
-
stopLoss: slNormalized?.priceStr || void 0,
|
|
557
|
-
slTriggerBy: "LastPrice",
|
|
558
780
|
side: isLong ? "Buy" : "Sell",
|
|
559
781
|
orderType: isLimit ? "Limit" : "Market",
|
|
560
782
|
qty: orderQtyStr,
|
|
@@ -568,53 +790,12 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
568
790
|
if (orderRes.retCode !== 0) {
|
|
569
791
|
return false;
|
|
570
792
|
}
|
|
571
|
-
if (!isLimit) {
|
|
572
|
-
for (const tp of TP) {
|
|
573
|
-
const tpSizeRaw = orderQty * tp.rate;
|
|
574
|
-
const { qtyNum: tpSizeNum, qtyStr: tpSizeStr } = normalizeQty(
|
|
575
|
-
tpSizeRaw,
|
|
576
|
-
meta
|
|
577
|
-
);
|
|
578
|
-
if (!tpSizeNum || tpSizeNum < meta.minOrderQty) {
|
|
579
|
-
import_logger2.logger.log(
|
|
580
|
-
"warn",
|
|
581
|
-
"tp skipped: size too small %s",
|
|
582
|
-
(0, import_data2.toJson)(
|
|
583
|
-
{ symbol, tp, tpSizeNum, minOrderQty: meta.minOrderQty },
|
|
584
|
-
true
|
|
585
|
-
)
|
|
586
|
-
);
|
|
587
|
-
continue;
|
|
588
|
-
}
|
|
589
|
-
const tpPriceNorm = normalizePrice(
|
|
590
|
-
tp.price,
|
|
591
|
-
isLong ? "TP_LONG" : "TP_SHORT",
|
|
592
|
-
meta
|
|
593
|
-
);
|
|
594
|
-
const isFullMode = TP.length === 1 && tp.rate === 1;
|
|
595
|
-
const tpRes = await client.setTradingStop({
|
|
596
|
-
category: import_constants2.MARKET_CATEGORY,
|
|
597
|
-
symbol,
|
|
598
|
-
tpSize: isFullMode ? void 0 : tpSizeStr,
|
|
599
|
-
tpslMode: isFullMode ? "Full" : "Partial",
|
|
600
|
-
takeProfit: tpPriceNorm.priceStr,
|
|
601
|
-
stopLoss: isFullMode && slNormalized ? slNormalized.priceStr : void 0,
|
|
602
|
-
slTriggerBy: "LastPrice",
|
|
603
|
-
tpOrderType: "Market",
|
|
604
|
-
positionIdx: 0
|
|
605
|
-
});
|
|
606
|
-
import_logger2.logger.log(
|
|
607
|
-
getLogLevel(tpRes),
|
|
608
|
-
"tp: %s %s",
|
|
609
|
-
(0, import_data2.toJson)(tp, true),
|
|
610
|
-
(0, import_data2.toJson)(tpRes, true)
|
|
611
|
-
);
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
793
|
return true;
|
|
615
794
|
},
|
|
795
|
+
setTakeProfits,
|
|
796
|
+
setStopLoss,
|
|
616
797
|
closePosition: async ({ symbol, direction }) => {
|
|
617
|
-
const client = await
|
|
798
|
+
const client = await getPrivateClient();
|
|
618
799
|
if (!client) {
|
|
619
800
|
return false;
|
|
620
801
|
}
|
|
@@ -639,7 +820,7 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
639
820
|
return true;
|
|
640
821
|
},
|
|
641
822
|
getTickers: async () => {
|
|
642
|
-
const client = await
|
|
823
|
+
const client = await getPublicClient();
|
|
643
824
|
if (!client) {
|
|
644
825
|
return [];
|
|
645
826
|
}
|
|
@@ -740,6 +921,8 @@ var BinanceConnectorCreator = async () => {
|
|
|
740
921
|
getPosition: async () => null,
|
|
741
922
|
getPositions: async () => [],
|
|
742
923
|
placeOrder: async () => false,
|
|
924
|
+
setTakeProfits: async () => false,
|
|
925
|
+
setStopLoss: async () => false,
|
|
743
926
|
closePosition: async () => false,
|
|
744
927
|
getTickers: async () => {
|
|
745
928
|
const baseUrl = process.env.BINANCE_BASE_URL?.trim() || "https://api.binance.com";
|
|
@@ -899,6 +1082,8 @@ var CoinbaseConnectorCreator = async () => {
|
|
|
899
1082
|
getPosition: async () => null,
|
|
900
1083
|
getPositions: async () => [],
|
|
901
1084
|
placeOrder: async () => false,
|
|
1085
|
+
setTakeProfits: async () => false,
|
|
1086
|
+
setStopLoss: async () => false,
|
|
902
1087
|
closePosition: async () => false,
|
|
903
1088
|
getTickers: async () => {
|
|
904
1089
|
const baseUrl = process.env.COINBASE_BASE_URL?.trim() || "https://api.exchange.coinbase.com";
|
|
@@ -1110,7 +1295,7 @@ var binanceCoinbaseSpreadProvider = {
|
|
|
1110
1295
|
};
|
|
1111
1296
|
|
|
1112
1297
|
// src/marketData/providers/coinalyze.ts
|
|
1113
|
-
var
|
|
1298
|
+
var import_async2 = require("@tradejs/core/async");
|
|
1114
1299
|
var import_indicators2 = require("@tradejs/core/indicators");
|
|
1115
1300
|
var coinalyzeIntervalMap = {
|
|
1116
1301
|
"15m": "15min",
|
|
@@ -1161,12 +1346,8 @@ var normalizeMetricPayload = (metric, raw) => {
|
|
|
1161
1346
|
}));
|
|
1162
1347
|
};
|
|
1163
1348
|
var fetchCoinalyzeSeries = async (params) => {
|
|
1164
|
-
const { endpoint, metric, symbol, interval, fromMs, toMs } = params;
|
|
1349
|
+
const { endpoint, metric, symbol, apiKey, interval, fromMs, toMs } = params;
|
|
1165
1350
|
const baseUrl = process.env.COINALYZE_BASE_URL?.trim() || "https://api.coinalyze.net/v1";
|
|
1166
|
-
const apiKey = process.env.COINALYZE_API_KEY?.trim();
|
|
1167
|
-
if (!apiKey) {
|
|
1168
|
-
throw new Error("Missing COINALYZE_API_KEY");
|
|
1169
|
-
}
|
|
1170
1351
|
const url = new URL(`${baseUrl}${endpoint}`);
|
|
1171
1352
|
url.searchParams.set("symbols", symbol);
|
|
1172
1353
|
url.searchParams.set("interval", coinalyzeIntervalMap[interval] || interval);
|
|
@@ -1184,7 +1365,7 @@ var fetchCoinalyzeSeries = async (params) => {
|
|
|
1184
1365
|
lastRequestTs + coinalyzeMinRequestDelayMs - now
|
|
1185
1366
|
);
|
|
1186
1367
|
if (waitMs > 0) {
|
|
1187
|
-
await (0,
|
|
1368
|
+
await (0, import_async2.delay)(waitMs);
|
|
1188
1369
|
}
|
|
1189
1370
|
lastRequestTs = Date.now();
|
|
1190
1371
|
const response = await fetch(url.toString(), { headers });
|
|
@@ -1198,7 +1379,7 @@ var fetchCoinalyzeSeries = async (params) => {
|
|
|
1198
1379
|
const transient = response.status === 429 || response.status >= 500;
|
|
1199
1380
|
if (attempt < coinalyzeMaxRetries && transient) {
|
|
1200
1381
|
const backoffMs = Math.min(1e4, 750 * 2 ** attempt);
|
|
1201
|
-
await (0,
|
|
1382
|
+
await (0, import_async2.delay)(retryAfterMs ?? backoffMs);
|
|
1202
1383
|
continue;
|
|
1203
1384
|
}
|
|
1204
1385
|
throw new Error(`Coinalyze ${endpoint} ${response.status}: ${text}`);
|
|
@@ -1207,15 +1388,27 @@ var fetchCoinalyzeSeries = async (params) => {
|
|
|
1207
1388
|
};
|
|
1208
1389
|
var coinalyzeProvider = {
|
|
1209
1390
|
name: "coinalyze",
|
|
1210
|
-
fetchWindow: async ({
|
|
1391
|
+
fetchWindow: async ({
|
|
1392
|
+
symbol,
|
|
1393
|
+
marketSymbol,
|
|
1394
|
+
apiKey,
|
|
1395
|
+
interval,
|
|
1396
|
+
fromMs,
|
|
1397
|
+
toMs
|
|
1398
|
+
}) => {
|
|
1399
|
+
if (!apiKey?.trim()) {
|
|
1400
|
+
throw new Error("Missing COINALYZE_API_KEY in user settings");
|
|
1401
|
+
}
|
|
1211
1402
|
const oiPath = process.env.COINALYZE_OI_PATH?.trim() || "/open-interest-history";
|
|
1212
1403
|
const fundingPath = process.env.COINALYZE_FUNDING_PATH?.trim() || "/funding-rate-history";
|
|
1213
1404
|
const liqPath = process.env.COINALYZE_LIQ_PATH?.trim() || "/liquidation-history";
|
|
1214
1405
|
const requestSymbol = (marketSymbol || symbol).trim().toUpperCase();
|
|
1406
|
+
const normalizedApiKey = apiKey.trim();
|
|
1215
1407
|
const oiRaw = await fetchCoinalyzeSeries({
|
|
1216
1408
|
endpoint: oiPath,
|
|
1217
1409
|
metric: "oi",
|
|
1218
1410
|
symbol: requestSymbol,
|
|
1411
|
+
apiKey: normalizedApiKey,
|
|
1219
1412
|
interval,
|
|
1220
1413
|
fromMs,
|
|
1221
1414
|
toMs
|
|
@@ -1224,6 +1417,7 @@ var coinalyzeProvider = {
|
|
|
1224
1417
|
endpoint: fundingPath,
|
|
1225
1418
|
metric: "funding",
|
|
1226
1419
|
symbol: requestSymbol,
|
|
1420
|
+
apiKey: normalizedApiKey,
|
|
1227
1421
|
interval,
|
|
1228
1422
|
fromMs,
|
|
1229
1423
|
toMs
|
|
@@ -1232,6 +1426,7 @@ var coinalyzeProvider = {
|
|
|
1232
1426
|
endpoint: liqPath,
|
|
1233
1427
|
metric: "liq",
|
|
1234
1428
|
symbol: requestSymbol,
|
|
1429
|
+
apiKey: normalizedApiKey,
|
|
1235
1430
|
interval,
|
|
1236
1431
|
fromMs,
|
|
1237
1432
|
toMs
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/ByBit/index.ts
|
|
2
2
|
import _ from "lodash";
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
+
import { delay } from "@tradejs/core/async";
|
|
4
5
|
import {
|
|
5
6
|
MARKET_CATEGORY as MARKET_CATEGORY2,
|
|
6
7
|
PRELOAD_FALLBACK_DAYS
|
|
@@ -23,7 +24,13 @@ import { RestClientV5 } from "bybit-api";
|
|
|
23
24
|
import { logger } from "@tradejs/infra/logger";
|
|
24
25
|
import { getData, redisKeys } from "@tradejs/infra/redis";
|
|
25
26
|
var useTestnet = false;
|
|
26
|
-
var getClient = async ({ userName }) => {
|
|
27
|
+
var getClient = async ({ userName }, access = "private") => {
|
|
28
|
+
if (access === "public") {
|
|
29
|
+
return new RestClientV5({
|
|
30
|
+
parseAPIRateLimits: true,
|
|
31
|
+
testnet: useTestnet
|
|
32
|
+
});
|
|
33
|
+
}
|
|
27
34
|
const user = await getData(redisKeys.user(userName));
|
|
28
35
|
if (!user) {
|
|
29
36
|
logger.log("error", "connection config not found: %s", userName);
|
|
@@ -32,6 +39,7 @@ var getClient = async ({ userName }) => {
|
|
|
32
39
|
const client = new RestClientV5({
|
|
33
40
|
key: user.BYBIT_API_KEY,
|
|
34
41
|
secret: user.BYBIT_API_SECRET,
|
|
42
|
+
parseAPIRateLimits: true,
|
|
35
43
|
testnet: useTestnet
|
|
36
44
|
});
|
|
37
45
|
return client;
|
|
@@ -131,6 +139,12 @@ var mapPositionData = (data) => {
|
|
|
131
139
|
// src/ByBit/index.ts
|
|
132
140
|
var LIMIT = 1e3;
|
|
133
141
|
var CACHE_FALLBACK_WINDOW = 1e3;
|
|
142
|
+
var BYBIT_RATE_LIMIT_RETCODE = 10006;
|
|
143
|
+
var BYBIT_TRADING_STOP_NOT_MODIFIED_RETCODE = 34040;
|
|
144
|
+
var KLINE_RATE_LIMIT_MAX_ATTEMPTS = 3;
|
|
145
|
+
var KLINE_RATE_LIMIT_BASE_DELAY_MS = 800;
|
|
146
|
+
var KLINE_RATE_LIMIT_MAX_DELAY_MS = 1e4;
|
|
147
|
+
var KLINE_RATE_LIMIT_RESET_BUFFER_MS = 50;
|
|
134
148
|
var INTERVAL_TO_MINUTES = {
|
|
135
149
|
"1": 1,
|
|
136
150
|
"3": 3,
|
|
@@ -147,9 +161,37 @@ var INTERVAL_TO_MINUTES = {
|
|
|
147
161
|
M: 43200
|
|
148
162
|
};
|
|
149
163
|
var getLogLevel = (res) => res.retCode === 0 ? "info" : "error";
|
|
164
|
+
var isTradingStopNotModified = (res) => res?.retCode === BYBIT_TRADING_STOP_NOT_MODIFIED_RETCODE;
|
|
165
|
+
var isTradingStopAccepted = (res) => res?.retCode === 0 || isTradingStopNotModified(res);
|
|
166
|
+
var isKlineRateLimited = (res) => res?.retCode === BYBIT_RATE_LIMIT_RETCODE;
|
|
167
|
+
var resolveKlineRetryDelayMs = (res, attempt) => {
|
|
168
|
+
const resetAtRaw = Number(res?.rateLimitApi?.resetAtTimestamp);
|
|
169
|
+
if (Number.isFinite(resetAtRaw) && resetAtRaw > 0) {
|
|
170
|
+
return Math.max(
|
|
171
|
+
KLINE_RATE_LIMIT_RESET_BUFFER_MS,
|
|
172
|
+
resetAtRaw - Date.now() + KLINE_RATE_LIMIT_RESET_BUFFER_MS
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
const jitterMs = Math.round(Math.random() * 250);
|
|
176
|
+
const backoffMs = Math.min(
|
|
177
|
+
KLINE_RATE_LIMIT_MAX_DELAY_MS,
|
|
178
|
+
KLINE_RATE_LIMIT_BASE_DELAY_MS * 2 ** (attempt - 1)
|
|
179
|
+
);
|
|
180
|
+
return backoffMs + jitterMs;
|
|
181
|
+
};
|
|
150
182
|
var ByBitConnectorCreator = async (config) => {
|
|
151
183
|
let state = {};
|
|
152
184
|
let isTimescaleFallbackMode = false;
|
|
185
|
+
let publicClientPromise = null;
|
|
186
|
+
let privateClientPromise = null;
|
|
187
|
+
const getPublicClient = async () => {
|
|
188
|
+
publicClientPromise ??= getClient(config, "public");
|
|
189
|
+
return publicClientPromise;
|
|
190
|
+
};
|
|
191
|
+
const getPrivateClient = async () => {
|
|
192
|
+
privateClientPromise ??= getClient(config, "private");
|
|
193
|
+
return privateClientPromise;
|
|
194
|
+
};
|
|
153
195
|
const request = async ({
|
|
154
196
|
symbol,
|
|
155
197
|
interval,
|
|
@@ -163,34 +205,60 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
163
205
|
);
|
|
164
206
|
const normalizedEnd = round(end || Date.now());
|
|
165
207
|
try {
|
|
166
|
-
const client = await
|
|
208
|
+
const client = await getPublicClient();
|
|
167
209
|
if (!client) return [];
|
|
168
210
|
if (normalizedEnd <= normalizedStart) {
|
|
169
211
|
return [];
|
|
170
212
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
213
|
+
for (let attempt = 1; attempt <= KLINE_RATE_LIMIT_MAX_ATTEMPTS; attempt += 1) {
|
|
214
|
+
const kline = await client.getKline({
|
|
215
|
+
category: MARKET_CATEGORY2,
|
|
216
|
+
symbol,
|
|
217
|
+
interval,
|
|
218
|
+
start: normalizedStart,
|
|
219
|
+
end: normalizedEnd,
|
|
220
|
+
limit: LIMIT
|
|
221
|
+
});
|
|
222
|
+
if (isKlineRateLimited(kline)) {
|
|
223
|
+
if (attempt < KLINE_RATE_LIMIT_MAX_ATTEMPTS) {
|
|
224
|
+
const waitMs = resolveKlineRetryDelayMs(kline, attempt);
|
|
225
|
+
logger2.log(
|
|
226
|
+
"warn",
|
|
227
|
+
"kline rate limited for %s %s: attempt=%s/%s waitMs=%s",
|
|
228
|
+
symbol,
|
|
229
|
+
interval,
|
|
230
|
+
attempt,
|
|
231
|
+
KLINE_RATE_LIMIT_MAX_ATTEMPTS,
|
|
232
|
+
waitMs
|
|
233
|
+
);
|
|
234
|
+
await delay(waitMs);
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (!kline?.result?.list) {
|
|
239
|
+
const responseError = typeof kline?.retMsg === "string" && kline.retMsg !== "OK" ? `${kline.retMsg}${typeof kline?.retCode === "number" ? ` (retCode: ${kline.retCode})` : ""}` : typeof kline?.retCode === "number" && kline.retCode !== 0 ? `retCode: ${kline.retCode}` : "";
|
|
240
|
+
logger2.log(
|
|
241
|
+
"error",
|
|
242
|
+
"empty kline.list for %s %s%s",
|
|
243
|
+
symbol,
|
|
244
|
+
interval,
|
|
245
|
+
responseError ? `: ${responseError}` : ""
|
|
246
|
+
);
|
|
247
|
+
return [];
|
|
248
|
+
}
|
|
249
|
+
if (!silent) {
|
|
250
|
+
logger2.log(
|
|
251
|
+
"info",
|
|
252
|
+
"%s %s %s %s",
|
|
253
|
+
chalk.yellow(formatUnix2(normalizedEnd)),
|
|
254
|
+
chalk.cyan(symbol),
|
|
255
|
+
chalk.cyan(interval),
|
|
256
|
+
chalk.yellow(kline.result.list.length)
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
return mapKlineToChartData(kline.result.list.reverse());
|
|
192
260
|
}
|
|
193
|
-
return
|
|
261
|
+
return [];
|
|
194
262
|
} catch (error) {
|
|
195
263
|
logger2.log(
|
|
196
264
|
"error",
|
|
@@ -282,6 +350,190 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
282
350
|
await upsertCandles(toRows(symbol, intMinutes, part));
|
|
283
351
|
}
|
|
284
352
|
};
|
|
353
|
+
const getPositionSnapshot = async (symbol) => {
|
|
354
|
+
const client = await getPrivateClient();
|
|
355
|
+
if (!client) {
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
const positionRes = await client.getPositionInfo({
|
|
359
|
+
symbol,
|
|
360
|
+
category: MARKET_CATEGORY2
|
|
361
|
+
});
|
|
362
|
+
if (positionRes.retCode !== 0) {
|
|
363
|
+
logger2.log(
|
|
364
|
+
getLogLevel(positionRes),
|
|
365
|
+
"position retCode: %s, %s",
|
|
366
|
+
symbol,
|
|
367
|
+
positionRes.retCode
|
|
368
|
+
);
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
const positions = mapPositionData(positionRes.result.list);
|
|
372
|
+
if (!positions || _.isEmpty(positions)) {
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
const position = positions[0];
|
|
376
|
+
logger2.log(
|
|
377
|
+
"debug",
|
|
378
|
+
"position: %s %s qty=%s price=%s",
|
|
379
|
+
position.symbol,
|
|
380
|
+
position.direction,
|
|
381
|
+
position.qty,
|
|
382
|
+
position.price
|
|
383
|
+
);
|
|
384
|
+
return {
|
|
385
|
+
...position
|
|
386
|
+
};
|
|
387
|
+
};
|
|
388
|
+
const setTakeProfits = async ({
|
|
389
|
+
symbol,
|
|
390
|
+
direction,
|
|
391
|
+
qty,
|
|
392
|
+
takeProfits
|
|
393
|
+
}) => {
|
|
394
|
+
const client = await getPrivateClient();
|
|
395
|
+
const marketDataClient = await getPublicClient();
|
|
396
|
+
if (!client || !marketDataClient) {
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
if (!Array.isArray(takeProfits) || takeProfits.length === 0) {
|
|
400
|
+
return true;
|
|
401
|
+
}
|
|
402
|
+
const meta = await getSymbolMeta(marketDataClient, symbol);
|
|
403
|
+
const positionQty = qty ?? (await getPositionSnapshot(symbol))?.qty ?? 0;
|
|
404
|
+
if (!Number.isFinite(positionQty) || positionQty <= 0) {
|
|
405
|
+
logger2.log(
|
|
406
|
+
"warn",
|
|
407
|
+
"setTakeProfits: missing position qty: %s",
|
|
408
|
+
toJson({ symbol, qty, positionQty, takeProfits }, true)
|
|
409
|
+
);
|
|
410
|
+
return false;
|
|
411
|
+
}
|
|
412
|
+
const isLong = direction === "LONG";
|
|
413
|
+
for (const tp of takeProfits) {
|
|
414
|
+
const tpSizeRaw = positionQty * tp.rate;
|
|
415
|
+
const { qtyNum: tpSizeNum, qtyStr: tpSizeStr } = normalizeQty(
|
|
416
|
+
tpSizeRaw,
|
|
417
|
+
meta
|
|
418
|
+
);
|
|
419
|
+
if (!tpSizeNum || tpSizeNum < meta.minOrderQty) {
|
|
420
|
+
logger2.log(
|
|
421
|
+
"warn",
|
|
422
|
+
"tp skipped: size too small %s",
|
|
423
|
+
toJson(
|
|
424
|
+
{ symbol, tp, tpSizeNum, minOrderQty: meta.minOrderQty },
|
|
425
|
+
true
|
|
426
|
+
)
|
|
427
|
+
);
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
const tpPriceNorm = normalizePrice(
|
|
431
|
+
tp.price,
|
|
432
|
+
isLong ? "TP_LONG" : "TP_SHORT",
|
|
433
|
+
meta
|
|
434
|
+
);
|
|
435
|
+
const isFullMode = takeProfits.length === 1 && tp.rate === 1;
|
|
436
|
+
const tpRes = await client.setTradingStop({
|
|
437
|
+
category: MARKET_CATEGORY2,
|
|
438
|
+
symbol,
|
|
439
|
+
tpSize: isFullMode ? void 0 : tpSizeStr,
|
|
440
|
+
tpslMode: isFullMode ? "Full" : "Partial",
|
|
441
|
+
takeProfit: tpPriceNorm.priceStr,
|
|
442
|
+
tpTriggerBy: "MarkPrice",
|
|
443
|
+
tpOrderType: "Market",
|
|
444
|
+
positionIdx: 0
|
|
445
|
+
});
|
|
446
|
+
if (isTradingStopNotModified(tpRes)) {
|
|
447
|
+
logger2.log(
|
|
448
|
+
"debug",
|
|
449
|
+
"tp unchanged: %s %s price=%s rate=%s",
|
|
450
|
+
symbol,
|
|
451
|
+
direction,
|
|
452
|
+
tpPriceNorm.priceStr,
|
|
453
|
+
tp.rate
|
|
454
|
+
);
|
|
455
|
+
} else if (tpRes.retCode === 0) {
|
|
456
|
+
logger2.log(
|
|
457
|
+
"info",
|
|
458
|
+
"tp updated: %s %s price=%s rate=%s",
|
|
459
|
+
symbol,
|
|
460
|
+
direction,
|
|
461
|
+
tpPriceNorm.priceStr,
|
|
462
|
+
tp.rate
|
|
463
|
+
);
|
|
464
|
+
} else {
|
|
465
|
+
logger2.log(
|
|
466
|
+
"error",
|
|
467
|
+
"tp failed: %s %s price=%s rate=%s %s",
|
|
468
|
+
symbol,
|
|
469
|
+
direction,
|
|
470
|
+
tpPriceNorm.priceStr,
|
|
471
|
+
tp.rate,
|
|
472
|
+
toJson(tpRes, true)
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
if (!isTradingStopAccepted(tpRes)) {
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
return true;
|
|
480
|
+
};
|
|
481
|
+
const setStopLoss = async ({
|
|
482
|
+
symbol,
|
|
483
|
+
direction,
|
|
484
|
+
stopLossPrice
|
|
485
|
+
}) => {
|
|
486
|
+
const client = await getPrivateClient();
|
|
487
|
+
const marketDataClient = await getPublicClient();
|
|
488
|
+
if (!client || !marketDataClient) {
|
|
489
|
+
return false;
|
|
490
|
+
}
|
|
491
|
+
if (typeof stopLossPrice !== "number" || !Number.isFinite(stopLossPrice)) {
|
|
492
|
+
return true;
|
|
493
|
+
}
|
|
494
|
+
const meta = await getSymbolMeta(marketDataClient, symbol);
|
|
495
|
+
const isLong = direction === "LONG";
|
|
496
|
+
const slNormalized = normalizePrice(
|
|
497
|
+
stopLossPrice,
|
|
498
|
+
isLong ? "SL_LONG" : "SL_SHORT",
|
|
499
|
+
meta
|
|
500
|
+
);
|
|
501
|
+
const slRes = await client.setTradingStop({
|
|
502
|
+
category: MARKET_CATEGORY2,
|
|
503
|
+
symbol,
|
|
504
|
+
tpslMode: "Full",
|
|
505
|
+
stopLoss: slNormalized.priceStr,
|
|
506
|
+
slTriggerBy: "LastPrice",
|
|
507
|
+
positionIdx: 0
|
|
508
|
+
});
|
|
509
|
+
if (isTradingStopNotModified(slRes)) {
|
|
510
|
+
logger2.log(
|
|
511
|
+
"debug",
|
|
512
|
+
"sl unchanged: %s %s stopLoss=%s",
|
|
513
|
+
symbol,
|
|
514
|
+
direction,
|
|
515
|
+
slNormalized.priceStr
|
|
516
|
+
);
|
|
517
|
+
} else if (slRes.retCode === 0) {
|
|
518
|
+
logger2.log(
|
|
519
|
+
"info",
|
|
520
|
+
"sl updated: %s %s stopLoss=%s",
|
|
521
|
+
symbol,
|
|
522
|
+
direction,
|
|
523
|
+
slNormalized.priceStr
|
|
524
|
+
);
|
|
525
|
+
} else {
|
|
526
|
+
logger2.log(
|
|
527
|
+
"error",
|
|
528
|
+
"sl failed: %s %s stopLoss=%s %s",
|
|
529
|
+
symbol,
|
|
530
|
+
direction,
|
|
531
|
+
slNormalized.priceStr,
|
|
532
|
+
toJson(slRes, true)
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
return isTradingStopAccepted(slRes);
|
|
536
|
+
};
|
|
285
537
|
return {
|
|
286
538
|
getState: async () => state,
|
|
287
539
|
setState: async (newState) => {
|
|
@@ -293,7 +545,8 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
293
545
|
start: defaultStart,
|
|
294
546
|
end: defaultEnd,
|
|
295
547
|
silent = false,
|
|
296
|
-
cacheOnly = false
|
|
548
|
+
cacheOnly = false,
|
|
549
|
+
warmOnly = false
|
|
297
550
|
}) => {
|
|
298
551
|
const intMinutes = intervalToMinutes(interval);
|
|
299
552
|
if (!intMinutes) {
|
|
@@ -369,6 +622,16 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
369
622
|
if (!cacheOnly && isRightEdgeQuery) {
|
|
370
623
|
await refreshTail({ symbol, interval, silent });
|
|
371
624
|
}
|
|
625
|
+
if (warmOnly) {
|
|
626
|
+
if (isTimescaleFallbackMode) {
|
|
627
|
+
isTimescaleFallbackMode = false;
|
|
628
|
+
logger2.log(
|
|
629
|
+
"info",
|
|
630
|
+
"TimescaleDB connection restored for kline cache"
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
return [];
|
|
634
|
+
}
|
|
372
635
|
const rangeStart = defaultStart ?? dataStart ?? 0;
|
|
373
636
|
const rangeEnd = defaultEnd ?? dataEnd ?? Date.now();
|
|
374
637
|
const { normStart: finalStart, normEnd: finalEnd } = normalizeRangeToClosed(intervalMs, rangeStart, rangeEnd);
|
|
@@ -394,7 +657,7 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
394
657
|
String(error)
|
|
395
658
|
);
|
|
396
659
|
}
|
|
397
|
-
if (cacheOnly) {
|
|
660
|
+
if (cacheOnly || warmOnly) {
|
|
398
661
|
return [];
|
|
399
662
|
}
|
|
400
663
|
return request({
|
|
@@ -406,41 +669,9 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
406
669
|
});
|
|
407
670
|
}
|
|
408
671
|
},
|
|
409
|
-
getPosition: async (symbol) =>
|
|
410
|
-
const client = await getClient(config);
|
|
411
|
-
if (!client) {
|
|
412
|
-
return null;
|
|
413
|
-
}
|
|
414
|
-
const positionRes = await client.getPositionInfo({
|
|
415
|
-
symbol,
|
|
416
|
-
category: MARKET_CATEGORY2
|
|
417
|
-
});
|
|
418
|
-
logger2.log(
|
|
419
|
-
getLogLevel(positionRes),
|
|
420
|
-
"position retCode: %s, %s",
|
|
421
|
-
symbol,
|
|
422
|
-
positionRes.retCode
|
|
423
|
-
);
|
|
424
|
-
if (positionRes.retCode !== 0) {
|
|
425
|
-
return null;
|
|
426
|
-
}
|
|
427
|
-
const positions = mapPositionData(positionRes.result.list);
|
|
428
|
-
if (!positions || _.isEmpty(positions)) {
|
|
429
|
-
return null;
|
|
430
|
-
}
|
|
431
|
-
const position = positions[0];
|
|
432
|
-
logger2.log(
|
|
433
|
-
getLogLevel(positionRes),
|
|
434
|
-
"position: %s, %s",
|
|
435
|
-
symbol,
|
|
436
|
-
toJson(positionRes, true)
|
|
437
|
-
);
|
|
438
|
-
return {
|
|
439
|
-
...position
|
|
440
|
-
};
|
|
441
|
-
},
|
|
672
|
+
getPosition: async (symbol) => getPositionSnapshot(symbol),
|
|
442
673
|
getPositions: async () => {
|
|
443
|
-
const client = await
|
|
674
|
+
const client = await getPrivateClient();
|
|
444
675
|
if (!client) {
|
|
445
676
|
return [];
|
|
446
677
|
}
|
|
@@ -448,12 +679,12 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
448
679
|
category: MARKET_CATEGORY2,
|
|
449
680
|
settleCoin: "USDT"
|
|
450
681
|
});
|
|
451
|
-
logger2.log(
|
|
452
|
-
getLogLevel(positionRes),
|
|
453
|
-
"positions retCode: %s, %s",
|
|
454
|
-
positionRes.retCode
|
|
455
|
-
);
|
|
456
682
|
if (positionRes.retCode !== 0) {
|
|
683
|
+
logger2.log(
|
|
684
|
+
getLogLevel(positionRes),
|
|
685
|
+
"positions retCode: %s, %s",
|
|
686
|
+
positionRes.retCode
|
|
687
|
+
);
|
|
457
688
|
return [];
|
|
458
689
|
}
|
|
459
690
|
const positions = mapPositionData(positionRes.result.list);
|
|
@@ -462,13 +693,14 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
462
693
|
}
|
|
463
694
|
return positions;
|
|
464
695
|
},
|
|
465
|
-
placeOrder: async ({ symbol, price, qty, direction, isLimit }
|
|
466
|
-
const client = await
|
|
467
|
-
|
|
696
|
+
placeOrder: async ({ symbol, price, qty, direction, isLimit }) => {
|
|
697
|
+
const client = await getPrivateClient();
|
|
698
|
+
const marketDataClient = await getPublicClient();
|
|
699
|
+
if (!client || !marketDataClient) {
|
|
468
700
|
return false;
|
|
469
701
|
}
|
|
470
702
|
const isLong = direction === "LONG";
|
|
471
|
-
const meta = await getSymbolMeta(
|
|
703
|
+
const meta = await getSymbolMeta(marketDataClient, symbol);
|
|
472
704
|
const { qtyNum: orderQty, qtyStr: orderQtyStr } = normalizeQty(qty, meta);
|
|
473
705
|
if (orderQty < meta.minOrderQty) {
|
|
474
706
|
logger2.log(
|
|
@@ -482,9 +714,6 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
482
714
|
return false;
|
|
483
715
|
}
|
|
484
716
|
const entryNormalized = isLimit ? normalizePrice(price, "ENTRY", meta) : void 0;
|
|
485
|
-
const slNormalized = slPrice ? normalizePrice(slPrice, isLong ? "SL_LONG" : "SL_SHORT", meta) : void 0;
|
|
486
|
-
const firstTP = TP?.[0];
|
|
487
|
-
const tpNormalized = firstTP && firstTP.rate === 1 ? normalizePrice(firstTP.price, isLong ? "TP_LONG" : "TP_SHORT", meta) : void 0;
|
|
488
717
|
logger2.log(
|
|
489
718
|
"info",
|
|
490
719
|
"placeOrder: %s",
|
|
@@ -495,10 +724,7 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
495
724
|
qty,
|
|
496
725
|
direction,
|
|
497
726
|
orderQty,
|
|
498
|
-
orderQtyStr
|
|
499
|
-
slPrice,
|
|
500
|
-
slPriceNorm: slNormalized?.priceStr,
|
|
501
|
-
TP
|
|
727
|
+
orderQtyStr
|
|
502
728
|
},
|
|
503
729
|
true
|
|
504
730
|
)
|
|
@@ -513,10 +739,6 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
513
739
|
category: MARKET_CATEGORY2,
|
|
514
740
|
symbol,
|
|
515
741
|
price: entryNormalized?.priceStr || void 0,
|
|
516
|
-
takeProfit: tpNormalized?.priceStr || void 0,
|
|
517
|
-
tpTriggerBy: "MarkPrice",
|
|
518
|
-
stopLoss: slNormalized?.priceStr || void 0,
|
|
519
|
-
slTriggerBy: "LastPrice",
|
|
520
742
|
side: isLong ? "Buy" : "Sell",
|
|
521
743
|
orderType: isLimit ? "Limit" : "Market",
|
|
522
744
|
qty: orderQtyStr,
|
|
@@ -530,53 +752,12 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
530
752
|
if (orderRes.retCode !== 0) {
|
|
531
753
|
return false;
|
|
532
754
|
}
|
|
533
|
-
if (!isLimit) {
|
|
534
|
-
for (const tp of TP) {
|
|
535
|
-
const tpSizeRaw = orderQty * tp.rate;
|
|
536
|
-
const { qtyNum: tpSizeNum, qtyStr: tpSizeStr } = normalizeQty(
|
|
537
|
-
tpSizeRaw,
|
|
538
|
-
meta
|
|
539
|
-
);
|
|
540
|
-
if (!tpSizeNum || tpSizeNum < meta.minOrderQty) {
|
|
541
|
-
logger2.log(
|
|
542
|
-
"warn",
|
|
543
|
-
"tp skipped: size too small %s",
|
|
544
|
-
toJson(
|
|
545
|
-
{ symbol, tp, tpSizeNum, minOrderQty: meta.minOrderQty },
|
|
546
|
-
true
|
|
547
|
-
)
|
|
548
|
-
);
|
|
549
|
-
continue;
|
|
550
|
-
}
|
|
551
|
-
const tpPriceNorm = normalizePrice(
|
|
552
|
-
tp.price,
|
|
553
|
-
isLong ? "TP_LONG" : "TP_SHORT",
|
|
554
|
-
meta
|
|
555
|
-
);
|
|
556
|
-
const isFullMode = TP.length === 1 && tp.rate === 1;
|
|
557
|
-
const tpRes = await client.setTradingStop({
|
|
558
|
-
category: MARKET_CATEGORY2,
|
|
559
|
-
symbol,
|
|
560
|
-
tpSize: isFullMode ? void 0 : tpSizeStr,
|
|
561
|
-
tpslMode: isFullMode ? "Full" : "Partial",
|
|
562
|
-
takeProfit: tpPriceNorm.priceStr,
|
|
563
|
-
stopLoss: isFullMode && slNormalized ? slNormalized.priceStr : void 0,
|
|
564
|
-
slTriggerBy: "LastPrice",
|
|
565
|
-
tpOrderType: "Market",
|
|
566
|
-
positionIdx: 0
|
|
567
|
-
});
|
|
568
|
-
logger2.log(
|
|
569
|
-
getLogLevel(tpRes),
|
|
570
|
-
"tp: %s %s",
|
|
571
|
-
toJson(tp, true),
|
|
572
|
-
toJson(tpRes, true)
|
|
573
|
-
);
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
755
|
return true;
|
|
577
756
|
},
|
|
757
|
+
setTakeProfits,
|
|
758
|
+
setStopLoss,
|
|
578
759
|
closePosition: async ({ symbol, direction }) => {
|
|
579
|
-
const client = await
|
|
760
|
+
const client = await getPrivateClient();
|
|
580
761
|
if (!client) {
|
|
581
762
|
return false;
|
|
582
763
|
}
|
|
@@ -601,7 +782,7 @@ var ByBitConnectorCreator = async (config) => {
|
|
|
601
782
|
return true;
|
|
602
783
|
},
|
|
603
784
|
getTickers: async () => {
|
|
604
|
-
const client = await
|
|
785
|
+
const client = await getPublicClient();
|
|
605
786
|
if (!client) {
|
|
606
787
|
return [];
|
|
607
788
|
}
|
|
@@ -702,6 +883,8 @@ var BinanceConnectorCreator = async () => {
|
|
|
702
883
|
getPosition: async () => null,
|
|
703
884
|
getPositions: async () => [],
|
|
704
885
|
placeOrder: async () => false,
|
|
886
|
+
setTakeProfits: async () => false,
|
|
887
|
+
setStopLoss: async () => false,
|
|
705
888
|
closePosition: async () => false,
|
|
706
889
|
getTickers: async () => {
|
|
707
890
|
const baseUrl = process.env.BINANCE_BASE_URL?.trim() || "https://api.binance.com";
|
|
@@ -861,6 +1044,8 @@ var CoinbaseConnectorCreator = async () => {
|
|
|
861
1044
|
getPosition: async () => null,
|
|
862
1045
|
getPositions: async () => [],
|
|
863
1046
|
placeOrder: async () => false,
|
|
1047
|
+
setTakeProfits: async () => false,
|
|
1048
|
+
setStopLoss: async () => false,
|
|
864
1049
|
closePosition: async () => false,
|
|
865
1050
|
getTickers: async () => {
|
|
866
1051
|
const baseUrl = process.env.COINBASE_BASE_URL?.trim() || "https://api.exchange.coinbase.com";
|
|
@@ -1076,7 +1261,7 @@ var binanceCoinbaseSpreadProvider = {
|
|
|
1076
1261
|
};
|
|
1077
1262
|
|
|
1078
1263
|
// src/marketData/providers/coinalyze.ts
|
|
1079
|
-
import { delay } from "@tradejs/core/async";
|
|
1264
|
+
import { delay as delay2 } from "@tradejs/core/async";
|
|
1080
1265
|
import {
|
|
1081
1266
|
coinalyzePointsToRows,
|
|
1082
1267
|
mergeCoinalyzeMetrics
|
|
@@ -1130,12 +1315,8 @@ var normalizeMetricPayload = (metric, raw) => {
|
|
|
1130
1315
|
}));
|
|
1131
1316
|
};
|
|
1132
1317
|
var fetchCoinalyzeSeries = async (params) => {
|
|
1133
|
-
const { endpoint, metric, symbol, interval, fromMs, toMs } = params;
|
|
1318
|
+
const { endpoint, metric, symbol, apiKey, interval, fromMs, toMs } = params;
|
|
1134
1319
|
const baseUrl = process.env.COINALYZE_BASE_URL?.trim() || "https://api.coinalyze.net/v1";
|
|
1135
|
-
const apiKey = process.env.COINALYZE_API_KEY?.trim();
|
|
1136
|
-
if (!apiKey) {
|
|
1137
|
-
throw new Error("Missing COINALYZE_API_KEY");
|
|
1138
|
-
}
|
|
1139
1320
|
const url = new URL(`${baseUrl}${endpoint}`);
|
|
1140
1321
|
url.searchParams.set("symbols", symbol);
|
|
1141
1322
|
url.searchParams.set("interval", coinalyzeIntervalMap[interval] || interval);
|
|
@@ -1153,7 +1334,7 @@ var fetchCoinalyzeSeries = async (params) => {
|
|
|
1153
1334
|
lastRequestTs + coinalyzeMinRequestDelayMs - now
|
|
1154
1335
|
);
|
|
1155
1336
|
if (waitMs > 0) {
|
|
1156
|
-
await
|
|
1337
|
+
await delay2(waitMs);
|
|
1157
1338
|
}
|
|
1158
1339
|
lastRequestTs = Date.now();
|
|
1159
1340
|
const response = await fetch(url.toString(), { headers });
|
|
@@ -1167,7 +1348,7 @@ var fetchCoinalyzeSeries = async (params) => {
|
|
|
1167
1348
|
const transient = response.status === 429 || response.status >= 500;
|
|
1168
1349
|
if (attempt < coinalyzeMaxRetries && transient) {
|
|
1169
1350
|
const backoffMs = Math.min(1e4, 750 * 2 ** attempt);
|
|
1170
|
-
await
|
|
1351
|
+
await delay2(retryAfterMs ?? backoffMs);
|
|
1171
1352
|
continue;
|
|
1172
1353
|
}
|
|
1173
1354
|
throw new Error(`Coinalyze ${endpoint} ${response.status}: ${text}`);
|
|
@@ -1176,15 +1357,27 @@ var fetchCoinalyzeSeries = async (params) => {
|
|
|
1176
1357
|
};
|
|
1177
1358
|
var coinalyzeProvider = {
|
|
1178
1359
|
name: "coinalyze",
|
|
1179
|
-
fetchWindow: async ({
|
|
1360
|
+
fetchWindow: async ({
|
|
1361
|
+
symbol,
|
|
1362
|
+
marketSymbol,
|
|
1363
|
+
apiKey,
|
|
1364
|
+
interval,
|
|
1365
|
+
fromMs,
|
|
1366
|
+
toMs
|
|
1367
|
+
}) => {
|
|
1368
|
+
if (!apiKey?.trim()) {
|
|
1369
|
+
throw new Error("Missing COINALYZE_API_KEY in user settings");
|
|
1370
|
+
}
|
|
1180
1371
|
const oiPath = process.env.COINALYZE_OI_PATH?.trim() || "/open-interest-history";
|
|
1181
1372
|
const fundingPath = process.env.COINALYZE_FUNDING_PATH?.trim() || "/funding-rate-history";
|
|
1182
1373
|
const liqPath = process.env.COINALYZE_LIQ_PATH?.trim() || "/liquidation-history";
|
|
1183
1374
|
const requestSymbol = (marketSymbol || symbol).trim().toUpperCase();
|
|
1375
|
+
const normalizedApiKey = apiKey.trim();
|
|
1184
1376
|
const oiRaw = await fetchCoinalyzeSeries({
|
|
1185
1377
|
endpoint: oiPath,
|
|
1186
1378
|
metric: "oi",
|
|
1187
1379
|
symbol: requestSymbol,
|
|
1380
|
+
apiKey: normalizedApiKey,
|
|
1188
1381
|
interval,
|
|
1189
1382
|
fromMs,
|
|
1190
1383
|
toMs
|
|
@@ -1193,6 +1386,7 @@ var coinalyzeProvider = {
|
|
|
1193
1386
|
endpoint: fundingPath,
|
|
1194
1387
|
metric: "funding",
|
|
1195
1388
|
symbol: requestSymbol,
|
|
1389
|
+
apiKey: normalizedApiKey,
|
|
1196
1390
|
interval,
|
|
1197
1391
|
fromMs,
|
|
1198
1392
|
toMs
|
|
@@ -1201,6 +1395,7 @@ var coinalyzeProvider = {
|
|
|
1201
1395
|
endpoint: liqPath,
|
|
1202
1396
|
metric: "liq",
|
|
1203
1397
|
symbol: requestSymbol,
|
|
1398
|
+
apiKey: normalizedApiKey,
|
|
1204
1399
|
interval,
|
|
1205
1400
|
fromMs,
|
|
1206
1401
|
toMs
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tradejs/connectors",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Built-in
|
|
3
|
+
"version": "1.0.6",
|
|
4
|
+
"description": "Built-in exchange connectors and market data providers for the TradeJS open-source framework.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tradejs",
|
|
7
7
|
"trading",
|
|
@@ -11,6 +11,14 @@
|
|
|
11
11
|
"market-data",
|
|
12
12
|
"bybit"
|
|
13
13
|
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/tradejs-dev/tradejs.git",
|
|
17
|
+
"directory": "packages/connectors"
|
|
18
|
+
},
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/tradejs-dev/tradejs/issues"
|
|
21
|
+
},
|
|
14
22
|
"main": "dist/index.js",
|
|
15
23
|
"module": "dist/index.mjs",
|
|
16
24
|
"types": "dist/index.d.ts",
|
|
@@ -25,10 +33,10 @@
|
|
|
25
33
|
}
|
|
26
34
|
},
|
|
27
35
|
"dependencies": {
|
|
28
|
-
"@tradejs/core": "^1.0.
|
|
29
|
-
"@tradejs/infra": "^1.0.
|
|
30
|
-
"@tradejs/node": "^1.0.
|
|
31
|
-
"@tradejs/types": "^1.0.
|
|
36
|
+
"@tradejs/core": "^1.0.6",
|
|
37
|
+
"@tradejs/infra": "^1.0.6",
|
|
38
|
+
"@tradejs/node": "^1.0.6",
|
|
39
|
+
"@tradejs/types": "^1.0.6",
|
|
32
40
|
"bybit-api": "^3.9.0",
|
|
33
41
|
"chalk": "4.1.2",
|
|
34
42
|
"lodash": "^4.17.21"
|