@tradejs/node 1.0.5 → 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/dist/ai.d.mts +31 -0
- package/dist/ai.d.ts +31 -0
- package/dist/ai.js +926 -0
- package/dist/ai.mjs +35 -0
- package/dist/backtest.js +408 -5687
- package/dist/backtest.mjs +331 -175
- package/dist/chunk-72FKXJ2I.mjs +473 -0
- package/dist/{chunk-P2ZUWONT.mjs → chunk-CGJ2UU6H.mjs} +1 -1
- package/dist/{chunk-ZY6ULOWK.mjs → chunk-EOZSJKUM.mjs} +1 -1
- package/dist/{chunk-7ICOZAKA.mjs → chunk-H6LIYHU4.mjs} +29 -11
- package/dist/{chunk-RBE4PZER.mjs → chunk-PQH5PAFC.mjs} +1 -1
- package/dist/{chunk-CIY64D57.mjs → chunk-XW5L327F.mjs} +1 -1
- package/dist/cli.d.mts +4 -4
- package/dist/cli.d.ts +4 -4
- package/dist/cli.js +711 -125
- package/dist/cli.mjs +199 -71
- package/dist/connectors.js +1 -1
- package/dist/connectors.mjs +2 -2
- package/dist/pine.d.mts +10 -6
- package/dist/pine.d.ts +10 -6
- package/dist/pine.js +40 -18
- package/dist/pine.mjs +21 -13
- package/dist/registry.js +1 -1
- package/dist/registry.mjs +2 -2
- package/dist/strategies.d.mts +6 -12
- package/dist/strategies.d.ts +6 -12
- package/dist/strategies.js +426 -83
- package/dist/strategies.mjs +200 -34
- package/package.json +9 -4
- package/dist/ai-G7ATN4YL.mjs +0 -19
- package/dist/chunk-LMAKIC3C.mjs +0 -294
package/dist/cli.mjs
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AI_CONCURRENCY_LIMIT,
|
|
3
3
|
KLINE_CONCURRENCY_LIMIT,
|
|
4
|
-
SCREENSHOT_CONCURRENCY_LIMIT
|
|
5
|
-
TG_CONCURRENCY_LIMIT
|
|
4
|
+
SCREENSHOT_CONCURRENCY_LIMIT
|
|
6
5
|
} from "./chunk-SCMBUEGK.mjs";
|
|
7
6
|
import {
|
|
8
7
|
require_lodash
|
|
9
8
|
} from "./chunk-GKDBAF3A.mjs";
|
|
10
9
|
import {
|
|
11
10
|
askAI
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
11
|
+
} from "./chunk-72FKXJ2I.mjs";
|
|
12
|
+
import "./chunk-EOZSJKUM.mjs";
|
|
14
13
|
import {
|
|
15
14
|
getTradejsProjectCwd
|
|
16
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-CGJ2UU6H.mjs";
|
|
17
16
|
import {
|
|
18
17
|
__toESM
|
|
19
18
|
} from "./chunk-6DZX6EAA.mjs";
|
|
@@ -33,8 +32,8 @@ import {
|
|
|
33
32
|
RedisWriteBlockedError,
|
|
34
33
|
delKeyWithOptions,
|
|
35
34
|
getKeys,
|
|
36
|
-
getData
|
|
37
|
-
redisKeys
|
|
35
|
+
getData,
|
|
36
|
+
redisKeys
|
|
38
37
|
} from "@tradejs/infra/redis";
|
|
39
38
|
import { logger as logger3 } from "@tradejs/infra/logger";
|
|
40
39
|
|
|
@@ -44,7 +43,7 @@ import path from "path";
|
|
|
44
43
|
import puppeteer from "puppeteer";
|
|
45
44
|
import { delay } from "@tradejs/core/async";
|
|
46
45
|
import { logger } from "@tradejs/infra/logger";
|
|
47
|
-
import {
|
|
46
|
+
import { getUserSettings } from "@tradejs/infra/userSettings";
|
|
48
47
|
var { APP_URL } = process.env;
|
|
49
48
|
var SCREENSHOT_NAVIGATION_ATTEMPTS = 3;
|
|
50
49
|
var SCREENSHOT_NAVIGATION_RETRY_DELAY_MS = 2e3;
|
|
@@ -64,13 +63,59 @@ var SCREENSHOT_VIEWPORT = {
|
|
|
64
63
|
var getProjectRoot = (projectRoot) => path.resolve(getTradejsProjectCwd(projectRoot));
|
|
65
64
|
var getScreenshotsDir = (projectRoot) => path.join(getProjectRoot(projectRoot), "data", "screenshots");
|
|
66
65
|
var maskTokenInUrl = (url) => url.replace(/([?&]token=)[^&]+/i, "$1<hidden>");
|
|
66
|
+
var getErrorFields = (value) => {
|
|
67
|
+
if (!value || typeof value !== "object") {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
const record = value;
|
|
71
|
+
const fields = [
|
|
72
|
+
["name", record.name],
|
|
73
|
+
["code", record.code],
|
|
74
|
+
["errno", record.errno],
|
|
75
|
+
["type", record.type],
|
|
76
|
+
["syscall", record.syscall],
|
|
77
|
+
["hostname", record.hostname],
|
|
78
|
+
["host", record.host],
|
|
79
|
+
["address", record.address],
|
|
80
|
+
["port", record.port]
|
|
81
|
+
];
|
|
82
|
+
return fields.filter(([, fieldValue]) => fieldValue != null && String(fieldValue).trim()).map(([key, fieldValue]) => `${key}=${String(fieldValue)}`);
|
|
83
|
+
};
|
|
84
|
+
var describeErrorValue = (value) => {
|
|
85
|
+
if (value == null) {
|
|
86
|
+
return "";
|
|
87
|
+
}
|
|
88
|
+
if (typeof value === "string") {
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
if (value instanceof Error) {
|
|
92
|
+
const message = value.message?.trim() || "";
|
|
93
|
+
const fields = getErrorFields(value);
|
|
94
|
+
return [message, ...fields].filter(Boolean).join(" ");
|
|
95
|
+
}
|
|
96
|
+
if (typeof value === "object") {
|
|
97
|
+
const fields = getErrorFields(value);
|
|
98
|
+
if (fields.length) {
|
|
99
|
+
return fields.join(" ");
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
return JSON.stringify(value);
|
|
103
|
+
} catch {
|
|
104
|
+
return String(value);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return String(value);
|
|
108
|
+
};
|
|
67
109
|
var getErrorMessage = (error) => {
|
|
68
110
|
const maybeError = error;
|
|
69
|
-
const message =
|
|
111
|
+
const message = describeErrorValue(error) || String(error);
|
|
70
112
|
if (maybeError?.cause == null) {
|
|
71
113
|
return message;
|
|
72
114
|
}
|
|
73
|
-
const cause =
|
|
115
|
+
const cause = describeErrorValue(maybeError.cause);
|
|
116
|
+
if (!cause) {
|
|
117
|
+
return message;
|
|
118
|
+
}
|
|
74
119
|
return `${message}; cause: ${cause}`;
|
|
75
120
|
};
|
|
76
121
|
var truncateText = (value, maxLength = SCREENSHOT_CONSOLE_TEXT_LIMIT) => value.length > maxLength ? `${value.slice(0, maxLength)}...` : value;
|
|
@@ -97,13 +142,13 @@ var getScreenshotPath = ({ symbol, signalId, interval }, projectRoot) => {
|
|
|
97
142
|
getScreenshotFilename({ symbol, signalId, interval })
|
|
98
143
|
);
|
|
99
144
|
};
|
|
100
|
-
var screenDashboard = async (signal, projectRoot) => {
|
|
145
|
+
var screenDashboard = async (signal, projectRoot, userName = "root") => {
|
|
101
146
|
const { symbol, signalId, interval } = signal;
|
|
102
147
|
const screenshotBaseUrl = getScreenshotRenderBaseUrl();
|
|
103
148
|
const screenshotPath = getScreenshotPath(signal, projectRoot);
|
|
104
|
-
const
|
|
105
|
-
const
|
|
106
|
-
const tokenParam = typeof
|
|
149
|
+
const settings = await getUserSettings(userName);
|
|
150
|
+
const token = settings.token;
|
|
151
|
+
const tokenParam = typeof token === "string" && token.length > 0 ? `&token=${encodeURIComponent(token)}` : "";
|
|
107
152
|
const dashboardUrl = `${screenshotBaseUrl}/routes/dashboard/bybit/${symbol}/${interval}/?signalId=${signalId}&autoZoom=true&screenshot=1${tokenParam}`;
|
|
108
153
|
const maskedDashboardUrl = maskTokenInUrl(dashboardUrl);
|
|
109
154
|
logger.info(
|
|
@@ -128,6 +173,7 @@ var screenDashboard = async (signal, projectRoot) => {
|
|
|
128
173
|
]
|
|
129
174
|
});
|
|
130
175
|
const browserProcess = browser.process();
|
|
176
|
+
let browserCloseRequested = false;
|
|
131
177
|
try {
|
|
132
178
|
if (browserProcess) {
|
|
133
179
|
logger.info(
|
|
@@ -147,6 +193,9 @@ var screenDashboard = async (signal, projectRoot) => {
|
|
|
147
193
|
await browser.version()
|
|
148
194
|
);
|
|
149
195
|
browser.on("disconnected", () => {
|
|
196
|
+
if (browserCloseRequested) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
150
199
|
logger.error(
|
|
151
200
|
"screenshot browser disconnected: %s %sm captureAttempt=%d",
|
|
152
201
|
symbol,
|
|
@@ -345,6 +394,7 @@ var screenDashboard = async (signal, projectRoot) => {
|
|
|
345
394
|
await delay(SCREENSHOT_CAPTURE_RETRY_DELAY_MS);
|
|
346
395
|
}
|
|
347
396
|
} finally {
|
|
397
|
+
browserCloseRequested = true;
|
|
348
398
|
await browser.close().catch(() => void 0);
|
|
349
399
|
}
|
|
350
400
|
}
|
|
@@ -355,14 +405,65 @@ var screenDashboard = async (signal, projectRoot) => {
|
|
|
355
405
|
import { delay as delay2 } from "@tradejs/core/async";
|
|
356
406
|
import { formatNumber } from "@tradejs/core/math";
|
|
357
407
|
import { logger as logger2 } from "@tradejs/infra/logger";
|
|
408
|
+
import { getUserSettings as getUserSettings2 } from "@tradejs/infra/userSettings";
|
|
358
409
|
var escapeHtml = (s) => {
|
|
359
410
|
if (!s) return "";
|
|
360
411
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
361
412
|
};
|
|
362
|
-
var { APP_URL: APP_URL2
|
|
413
|
+
var { APP_URL: APP_URL2 } = process.env;
|
|
363
414
|
var TG_REQUEST_ATTEMPTS = 3;
|
|
364
415
|
var TG_REQUEST_RETRY_DELAY_MS = 2e3;
|
|
416
|
+
var getErrorFields2 = (value) => {
|
|
417
|
+
if (!value || typeof value !== "object") {
|
|
418
|
+
return [];
|
|
419
|
+
}
|
|
420
|
+
const record = value;
|
|
421
|
+
const fields = [
|
|
422
|
+
["name", record.name],
|
|
423
|
+
["code", record.code],
|
|
424
|
+
["errno", record.errno],
|
|
425
|
+
["type", record.type],
|
|
426
|
+
["syscall", record.syscall],
|
|
427
|
+
["hostname", record.hostname],
|
|
428
|
+
["host", record.host],
|
|
429
|
+
["address", record.address],
|
|
430
|
+
["port", record.port]
|
|
431
|
+
];
|
|
432
|
+
return fields.filter(([, fieldValue]) => fieldValue != null && String(fieldValue).trim()).map(([key, fieldValue]) => `${key}=${String(fieldValue)}`);
|
|
433
|
+
};
|
|
434
|
+
var describeErrorValue2 = (value) => {
|
|
435
|
+
if (value == null) {
|
|
436
|
+
return "";
|
|
437
|
+
}
|
|
438
|
+
if (typeof value === "string") {
|
|
439
|
+
return value;
|
|
440
|
+
}
|
|
441
|
+
if (value instanceof Error) {
|
|
442
|
+
const message = value.message?.trim() || "";
|
|
443
|
+
const fields = getErrorFields2(value);
|
|
444
|
+
return [message, ...fields].filter(Boolean).join(" ");
|
|
445
|
+
}
|
|
446
|
+
if (typeof value === "object") {
|
|
447
|
+
const fields = getErrorFields2(value);
|
|
448
|
+
if (fields.length) {
|
|
449
|
+
return fields.join(" ");
|
|
450
|
+
}
|
|
451
|
+
try {
|
|
452
|
+
return JSON.stringify(value);
|
|
453
|
+
} catch {
|
|
454
|
+
return String(value);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return String(value);
|
|
458
|
+
};
|
|
365
459
|
var normalizeQuality = (value) => typeof value === "number" ? Math.max(1, Math.min(5, Math.round(value))) : null;
|
|
460
|
+
var formatOrderSkipReason = (reason) => {
|
|
461
|
+
if (!reason) return "";
|
|
462
|
+
if (reason.startsWith("AI_QUALITY_BELOW_MIN")) {
|
|
463
|
+
return "AI_QUALITY_BELOW_MIN";
|
|
464
|
+
}
|
|
465
|
+
return reason;
|
|
466
|
+
};
|
|
366
467
|
var getLastNumber = (value) => {
|
|
367
468
|
if (Array.isArray(value)) {
|
|
368
469
|
const last = value[value.length - 1];
|
|
@@ -402,11 +503,14 @@ var getTelegramErrorReason = (data) => {
|
|
|
402
503
|
};
|
|
403
504
|
var getErrorMessage2 = (error) => {
|
|
404
505
|
const maybeError = error;
|
|
405
|
-
const message =
|
|
506
|
+
const message = describeErrorValue2(error) || String(error);
|
|
406
507
|
if (maybeError?.cause == null) {
|
|
407
508
|
return message;
|
|
408
509
|
}
|
|
409
|
-
const cause =
|
|
510
|
+
const cause = describeErrorValue2(maybeError.cause);
|
|
511
|
+
if (!cause) {
|
|
512
|
+
return message;
|
|
513
|
+
}
|
|
410
514
|
return `${message}; cause: ${cause}`;
|
|
411
515
|
};
|
|
412
516
|
var parseTelegramResponse = async (response) => {
|
|
@@ -429,7 +533,20 @@ var parseTelegramResponse = async (response) => {
|
|
|
429
533
|
}
|
|
430
534
|
}
|
|
431
535
|
};
|
|
432
|
-
var
|
|
536
|
+
var getTelegramSettings = async (userName = "root") => {
|
|
537
|
+
const settings = await getUserSettings2(userName);
|
|
538
|
+
const token = settings.TG_BOT_TOKEN;
|
|
539
|
+
const chatId = settings.TG_CHAT_ID;
|
|
540
|
+
if (!token || !chatId) {
|
|
541
|
+
throw new Error(`Telegram settings are incomplete for user ${userName}`);
|
|
542
|
+
}
|
|
543
|
+
return { token, chatId };
|
|
544
|
+
};
|
|
545
|
+
var requestTelegram = async ({
|
|
546
|
+
method,
|
|
547
|
+
token,
|
|
548
|
+
init
|
|
549
|
+
}) => {
|
|
433
550
|
let lastError = null;
|
|
434
551
|
for (let attempt = 1; attempt <= TG_REQUEST_ATTEMPTS; attempt += 1) {
|
|
435
552
|
try {
|
|
@@ -457,17 +574,23 @@ var requestTelegram = async (method, init) => {
|
|
|
457
574
|
};
|
|
458
575
|
var sendTelegramMessage = async ({
|
|
459
576
|
message,
|
|
460
|
-
markup
|
|
577
|
+
markup,
|
|
578
|
+
token,
|
|
579
|
+
chatId
|
|
461
580
|
}) => {
|
|
462
|
-
const data = await requestTelegram(
|
|
463
|
-
method: "
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
581
|
+
const data = await requestTelegram({
|
|
582
|
+
method: "sendMessage",
|
|
583
|
+
token,
|
|
584
|
+
init: {
|
|
585
|
+
method: "POST",
|
|
586
|
+
headers: { "Content-Type": "application/json" },
|
|
587
|
+
body: JSON.stringify({
|
|
588
|
+
chat_id: chatId,
|
|
589
|
+
text: message,
|
|
590
|
+
reply_markup: markup,
|
|
591
|
+
parse_mode: "HTML"
|
|
592
|
+
})
|
|
593
|
+
}
|
|
471
594
|
});
|
|
472
595
|
logger2.info(
|
|
473
596
|
"tg sendMessage: %s",
|
|
@@ -526,7 +649,9 @@ var formatMessage = (signal, analysis) => {
|
|
|
526
649
|
}
|
|
527
650
|
lines.push(orderStatusText);
|
|
528
651
|
if ((orderStatus === "skipped" || orderStatus === "canceled") && orderSkipReason) {
|
|
529
|
-
lines.push(
|
|
652
|
+
lines.push(
|
|
653
|
+
`Skip reason: <b>${escapeHtml(formatOrderSkipReason(orderSkipReason))}</b>`
|
|
654
|
+
);
|
|
530
655
|
}
|
|
531
656
|
}
|
|
532
657
|
if (isConfigFromBacktest) {
|
|
@@ -544,21 +669,9 @@ var formatMessage = (signal, analysis) => {
|
|
|
544
669
|
lines.push(aiQualityLine);
|
|
545
670
|
}
|
|
546
671
|
lines.push("");
|
|
547
|
-
if (touches) {
|
|
548
|
-
lines.push(`Points: ${touches}`);
|
|
549
|
-
}
|
|
550
|
-
if (atrPct != null && Number.isFinite(atrPct)) {
|
|
551
|
-
lines.push(`ATR: ${atrPct.toFixed(2)}`);
|
|
552
|
-
}
|
|
553
|
-
if (distance) {
|
|
554
|
-
lines.push(`Distance: ${distance}`);
|
|
555
|
-
}
|
|
556
672
|
if (correlation) {
|
|
557
673
|
lines.push(`BTC correlation: ${correlation}`);
|
|
558
674
|
}
|
|
559
|
-
if (spread != null && Number.isFinite(spread)) {
|
|
560
|
-
lines.push(`BTC spread (CB-BN)/BN: ${spread.toFixed(6)}`);
|
|
561
|
-
}
|
|
562
675
|
const prices = formatPrices();
|
|
563
676
|
if (prices) {
|
|
564
677
|
lines.push("");
|
|
@@ -572,8 +685,9 @@ var formatMessage = (signal, analysis) => {
|
|
|
572
685
|
\u0414\u0435\u0442\u0430\u043B\u0438: ${err.message || String(err)}`;
|
|
573
686
|
}
|
|
574
687
|
};
|
|
575
|
-
var sendSignal = async (signal, imgInterval, analysis) => {
|
|
688
|
+
var sendSignal = async (signal, imgInterval, analysis, options = {}) => {
|
|
576
689
|
const { symbol, signalId, interval } = signal;
|
|
690
|
+
const { token, chatId } = await getTelegramSettings(options.userName);
|
|
577
691
|
const message = formatMessage(signal, analysis);
|
|
578
692
|
const publicAppUrl = APP_URL2?.startsWith("https") ? APP_URL2 : null;
|
|
579
693
|
const dashboardUrl = publicAppUrl ? `${APP_URL2}/routes/dashboard/bybit/${symbol}/${interval}/?signalId=${signalId}` : null;
|
|
@@ -599,7 +713,7 @@ var sendSignal = async (signal, imgInterval, analysis) => {
|
|
|
599
713
|
symbol,
|
|
600
714
|
error?.message || String(error)
|
|
601
715
|
);
|
|
602
|
-
await sendTelegramMessage({ message, markup });
|
|
716
|
+
await sendTelegramMessage({ message, markup, token, chatId });
|
|
603
717
|
return;
|
|
604
718
|
}
|
|
605
719
|
const photoBody = new FormData();
|
|
@@ -616,9 +730,13 @@ var sendSignal = async (signal, imgInterval, analysis) => {
|
|
|
616
730
|
}
|
|
617
731
|
let data;
|
|
618
732
|
try {
|
|
619
|
-
data = await requestTelegram(
|
|
620
|
-
method: "
|
|
621
|
-
|
|
733
|
+
data = await requestTelegram({
|
|
734
|
+
method: "sendPhoto",
|
|
735
|
+
token,
|
|
736
|
+
init: {
|
|
737
|
+
method: "POST",
|
|
738
|
+
body: photoBody
|
|
739
|
+
}
|
|
622
740
|
});
|
|
623
741
|
} catch (error) {
|
|
624
742
|
const reason = getErrorMessage2(error);
|
|
@@ -628,7 +746,9 @@ var sendSignal = async (signal, imgInterval, analysis) => {
|
|
|
628
746
|
|
|
629
747
|
\u26A0\uFE0F <b>Photo delivery failed</b>
|
|
630
748
|
Reason: <code>${escapeHtml(reason)}</code>`,
|
|
631
|
-
markup
|
|
749
|
+
markup,
|
|
750
|
+
token,
|
|
751
|
+
chatId
|
|
632
752
|
});
|
|
633
753
|
return;
|
|
634
754
|
}
|
|
@@ -640,7 +760,9 @@ Reason: <code>${escapeHtml(reason)}</code>`,
|
|
|
640
760
|
|
|
641
761
|
\u26A0\uFE0F <b>Photo delivery failed</b>
|
|
642
762
|
Reason: <code>${escapeHtml(reason)}</code>`,
|
|
643
|
-
markup
|
|
763
|
+
markup,
|
|
764
|
+
token,
|
|
765
|
+
chatId
|
|
644
766
|
});
|
|
645
767
|
return;
|
|
646
768
|
}
|
|
@@ -688,16 +810,21 @@ ${escapeHtml(clean)}`);
|
|
|
688
810
|
}
|
|
689
811
|
return lines.join("\n");
|
|
690
812
|
};
|
|
691
|
-
var sendSignalAnalysis = async (signal, analysis) => {
|
|
813
|
+
var sendSignalAnalysis = async (signal, analysis, options = {}) => {
|
|
814
|
+
const { token, chatId } = await getTelegramSettings(options.userName);
|
|
692
815
|
const message = formatAnalysisMessage(signal, analysis);
|
|
693
|
-
const data = await requestTelegram(
|
|
694
|
-
method: "
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
816
|
+
const data = await requestTelegram({
|
|
817
|
+
method: "sendMessage",
|
|
818
|
+
token,
|
|
819
|
+
init: {
|
|
820
|
+
method: "POST",
|
|
821
|
+
headers: { "Content-Type": "application/json" },
|
|
822
|
+
body: JSON.stringify({
|
|
823
|
+
chat_id: chatId,
|
|
824
|
+
text: message,
|
|
825
|
+
parse_mode: "HTML"
|
|
826
|
+
})
|
|
827
|
+
}
|
|
701
828
|
});
|
|
702
829
|
logger2.info(
|
|
703
830
|
"tg sendMessage (analysis): %s",
|
|
@@ -762,8 +889,8 @@ var cleanRedis = async (area) => {
|
|
|
762
889
|
}
|
|
763
890
|
logger3.info("");
|
|
764
891
|
};
|
|
765
|
-
var update = async (connector, interval, tickers) => {
|
|
766
|
-
const PRELOAD_START = getTimestamp(
|
|
892
|
+
var update = async (connector, interval, tickers, preloadDays = PRELOAD_DAYS) => {
|
|
893
|
+
const PRELOAD_START = getTimestamp(preloadDays);
|
|
767
894
|
const PRELOAD_END = getTimestamp();
|
|
768
895
|
const bar = new ProgressBar(
|
|
769
896
|
":current/:total [:bar][:percent] :eta(s) :symbol",
|
|
@@ -774,7 +901,7 @@ var update = async (connector, interval, tickers) => {
|
|
|
774
901
|
);
|
|
775
902
|
logger3.info(
|
|
776
903
|
chalk.yellow(
|
|
777
|
-
`update: ${tickers.length} (klineConcurrency=${KLINE_CONCURRENCY_LIMIT})`
|
|
904
|
+
`update: ${tickers.length} (klineConcurrency=${KLINE_CONCURRENCY_LIMIT}, preloadDays=${preloadDays})`
|
|
778
905
|
)
|
|
779
906
|
);
|
|
780
907
|
const queue = tickers.slice();
|
|
@@ -788,7 +915,8 @@ var update = async (connector, interval, tickers) => {
|
|
|
788
915
|
start: PRELOAD_START,
|
|
789
916
|
end: PRELOAD_END,
|
|
790
917
|
interval,
|
|
791
|
-
silent: true
|
|
918
|
+
silent: true,
|
|
919
|
+
warmOnly: true
|
|
792
920
|
});
|
|
793
921
|
} catch {
|
|
794
922
|
logger3.error("Failed loading: %s", symbol);
|
|
@@ -841,7 +969,7 @@ var getTickers = async (connector, include = "", exclude = "", limit, chunk) =>
|
|
|
841
969
|
}
|
|
842
970
|
return tickers.filter((t) => !excludeTickers.includes(t));
|
|
843
971
|
};
|
|
844
|
-
var makeScreenshots = async (signals, interval) => {
|
|
972
|
+
var makeScreenshots = async (signals, interval, userName = "root") => {
|
|
845
973
|
const projectRoot = getProjectRoot2();
|
|
846
974
|
const bar = new ProgressBar(
|
|
847
975
|
":current/:total [:bar][:percent] :eta(s) :symbol",
|
|
@@ -856,7 +984,7 @@ var makeScreenshots = async (signals, interval) => {
|
|
|
856
984
|
SCREENSHOT_CONCURRENCY_LIMIT,
|
|
857
985
|
async (signal) => {
|
|
858
986
|
try {
|
|
859
|
-
await screenDashboard({ ...signal, interval }, projectRoot);
|
|
987
|
+
await screenDashboard({ ...signal, interval }, projectRoot, userName);
|
|
860
988
|
} catch (error) {
|
|
861
989
|
logger3.error(
|
|
862
990
|
"Failed screenshot: %s (%s)",
|
|
@@ -870,7 +998,7 @@ var makeScreenshots = async (signals, interval) => {
|
|
|
870
998
|
);
|
|
871
999
|
logger3.info("");
|
|
872
1000
|
};
|
|
873
|
-
var sendToAI = async (signals) => {
|
|
1001
|
+
var sendToAI = async (signals, userName = "root") => {
|
|
874
1002
|
const bar = new ProgressBar(
|
|
875
1003
|
":current/:total [:bar][:percent] :eta(s) :symbol",
|
|
876
1004
|
{
|
|
@@ -881,7 +1009,7 @@ var sendToAI = async (signals) => {
|
|
|
881
1009
|
logger3.info(chalk.yellow("AI:", signals.length));
|
|
882
1010
|
await runWithConcurrency(signals, AI_CONCURRENCY_LIMIT, async (signal) => {
|
|
883
1011
|
try {
|
|
884
|
-
await askAI(signal);
|
|
1012
|
+
await askAI(signal, { userName });
|
|
885
1013
|
} catch {
|
|
886
1014
|
logger3.error("Failed ask: %s", signal.symbol);
|
|
887
1015
|
} finally {
|
|
@@ -890,7 +1018,7 @@ var sendToAI = async (signals) => {
|
|
|
890
1018
|
});
|
|
891
1019
|
logger3.info("");
|
|
892
1020
|
};
|
|
893
|
-
var sendToTG = async (signals, imgInterval) => {
|
|
1021
|
+
var sendToTG = async (signals, imgInterval, userName = "root") => {
|
|
894
1022
|
const bar = new ProgressBar(
|
|
895
1023
|
":current/:total [:bar][:percent] :eta(s) :symbol",
|
|
896
1024
|
{
|
|
@@ -899,15 +1027,15 @@ var sendToTG = async (signals, imgInterval) => {
|
|
|
899
1027
|
}
|
|
900
1028
|
);
|
|
901
1029
|
logger3.info(chalk.yellow("messages:", signals.length));
|
|
902
|
-
await runWithConcurrency(signals,
|
|
1030
|
+
await runWithConcurrency(signals, 1, async (signal) => {
|
|
903
1031
|
try {
|
|
904
|
-
const analysis = await
|
|
905
|
-
|
|
1032
|
+
const analysis = await getData(
|
|
1033
|
+
redisKeys.analysis(signal.symbol, signal.signalId),
|
|
906
1034
|
null
|
|
907
1035
|
);
|
|
908
|
-
await sendSignal(signal, imgInterval, analysis);
|
|
1036
|
+
await sendSignal(signal, imgInterval, analysis, { userName });
|
|
909
1037
|
if (analysis && typeof analysis === "object" && Object.keys(analysis).length > 0) {
|
|
910
|
-
await sendSignalAnalysis(signal, analysis);
|
|
1038
|
+
await sendSignalAnalysis(signal, analysis, { userName });
|
|
911
1039
|
}
|
|
912
1040
|
} catch (err) {
|
|
913
1041
|
logger3.error(
|
package/dist/connectors.js
CHANGED
|
@@ -258,7 +258,7 @@ var loadTradejsConfig = async (cwd = getTradejsProjectCwd()) => {
|
|
|
258
258
|
cachedByCwd.set(cwd, config);
|
|
259
259
|
if (!announcedConfigFile.has(configFilePath)) {
|
|
260
260
|
announcedConfigFile.add(configFilePath);
|
|
261
|
-
import_logger.logger.log("
|
|
261
|
+
import_logger.logger.log("debug", "Loaded TradeJS config: %s", configFilePath);
|
|
262
262
|
}
|
|
263
263
|
return config;
|
|
264
264
|
} catch (error) {
|
package/dist/connectors.mjs
CHANGED
|
@@ -10,8 +10,8 @@ import {
|
|
|
10
10
|
registerConnectorEntries,
|
|
11
11
|
resetConnectorRegistryCache,
|
|
12
12
|
resolveConnectorName
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import "./chunk-
|
|
13
|
+
} from "./chunk-XW5L327F.mjs";
|
|
14
|
+
import "./chunk-CGJ2UU6H.mjs";
|
|
15
15
|
import "./chunk-6DZX6EAA.mjs";
|
|
16
16
|
export {
|
|
17
17
|
BUILTIN_CONNECTOR_NAMES,
|
package/dist/pine.d.mts
CHANGED
|
@@ -22,11 +22,15 @@ interface RunPineScriptParams {
|
|
|
22
22
|
limit?: number;
|
|
23
23
|
}
|
|
24
24
|
declare const getPinePlotSeries: (context: PineContextLike, plotName: string) => PinePlotPoint[];
|
|
25
|
-
declare const
|
|
26
|
-
declare const
|
|
27
|
-
declare const
|
|
28
|
-
declare const
|
|
29
|
-
declare const
|
|
25
|
+
declare const getLatestPineRawPlotValue: (context: PineContextLike, plotName: string) => unknown;
|
|
26
|
+
declare const getLatestPineNumberPlotValue: (context: PineContextLike, plotName: string) => number | null;
|
|
27
|
+
declare const getLatestPineBooleanPlotValue: (context: PineContextLike, plotName: string) => boolean;
|
|
28
|
+
declare const getLatestPineNumberPlotValues: <TPlotName extends string>(context: PineContextLike, plotNames: readonly TPlotName[]) => Record<TPlotName, number | null>;
|
|
29
|
+
declare const getLatestPineBooleanPlotValues: <TPlotName extends string>(context: PineContextLike, plotNames: readonly TPlotName[]) => Record<TPlotName, boolean>;
|
|
30
|
+
declare const toFiniteNumber: (value: unknown) => number | undefined;
|
|
31
|
+
declare const toPineBoolean: (value: unknown) => boolean;
|
|
32
|
+
declare const loadPineScriptFile: (filePath: string, fallback?: string) => string;
|
|
33
|
+
declare const createPineScriptLoader: (baseDir: string) => ((fileNameOrPath: string, fallback?: string) => string);
|
|
30
34
|
declare const runPineScript: ({ candles, script, symbol, timeframe, inputs, limit, }: RunPineScriptParams) => Promise<PineContextLike>;
|
|
31
35
|
|
|
32
|
-
export { type PineContextLike, type PinePlotPoint, type RunPineScriptParams,
|
|
36
|
+
export { type PineContextLike, type PinePlotPoint, type RunPineScriptParams, createPineScriptLoader, getLatestPineBooleanPlotValue, getLatestPineBooleanPlotValues, getLatestPineNumberPlotValue, getLatestPineNumberPlotValues, getLatestPineRawPlotValue, getPinePlotSeries, loadPineScriptFile, runPineScript, toFiniteNumber, toPineBoolean };
|
package/dist/pine.d.ts
CHANGED
|
@@ -22,11 +22,15 @@ interface RunPineScriptParams {
|
|
|
22
22
|
limit?: number;
|
|
23
23
|
}
|
|
24
24
|
declare const getPinePlotSeries: (context: PineContextLike, plotName: string) => PinePlotPoint[];
|
|
25
|
-
declare const
|
|
26
|
-
declare const
|
|
27
|
-
declare const
|
|
28
|
-
declare const
|
|
29
|
-
declare const
|
|
25
|
+
declare const getLatestPineRawPlotValue: (context: PineContextLike, plotName: string) => unknown;
|
|
26
|
+
declare const getLatestPineNumberPlotValue: (context: PineContextLike, plotName: string) => number | null;
|
|
27
|
+
declare const getLatestPineBooleanPlotValue: (context: PineContextLike, plotName: string) => boolean;
|
|
28
|
+
declare const getLatestPineNumberPlotValues: <TPlotName extends string>(context: PineContextLike, plotNames: readonly TPlotName[]) => Record<TPlotName, number | null>;
|
|
29
|
+
declare const getLatestPineBooleanPlotValues: <TPlotName extends string>(context: PineContextLike, plotNames: readonly TPlotName[]) => Record<TPlotName, boolean>;
|
|
30
|
+
declare const toFiniteNumber: (value: unknown) => number | undefined;
|
|
31
|
+
declare const toPineBoolean: (value: unknown) => boolean;
|
|
32
|
+
declare const loadPineScriptFile: (filePath: string, fallback?: string) => string;
|
|
33
|
+
declare const createPineScriptLoader: (baseDir: string) => ((fileNameOrPath: string, fallback?: string) => string);
|
|
30
34
|
declare const runPineScript: ({ candles, script, symbol, timeframe, inputs, limit, }: RunPineScriptParams) => Promise<PineContextLike>;
|
|
31
35
|
|
|
32
|
-
export { type PineContextLike, type PinePlotPoint, type RunPineScriptParams,
|
|
36
|
+
export { type PineContextLike, type PinePlotPoint, type RunPineScriptParams, createPineScriptLoader, getLatestPineBooleanPlotValue, getLatestPineBooleanPlotValues, getLatestPineNumberPlotValue, getLatestPineNumberPlotValues, getLatestPineRawPlotValue, getPinePlotSeries, loadPineScriptFile, runPineScript, toFiniteNumber, toPineBoolean };
|
package/dist/pine.js
CHANGED
|
@@ -30,13 +30,17 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/pine.ts
|
|
31
31
|
var pine_exports = {};
|
|
32
32
|
__export(pine_exports, {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
createPineScriptLoader: () => createPineScriptLoader,
|
|
34
|
+
getLatestPineBooleanPlotValue: () => getLatestPineBooleanPlotValue,
|
|
35
|
+
getLatestPineBooleanPlotValues: () => getLatestPineBooleanPlotValues,
|
|
36
|
+
getLatestPineNumberPlotValue: () => getLatestPineNumberPlotValue,
|
|
37
|
+
getLatestPineNumberPlotValues: () => getLatestPineNumberPlotValues,
|
|
38
|
+
getLatestPineRawPlotValue: () => getLatestPineRawPlotValue,
|
|
37
39
|
getPinePlotSeries: () => getPinePlotSeries,
|
|
38
|
-
|
|
39
|
-
runPineScript: () => runPineScript
|
|
40
|
+
loadPineScriptFile: () => loadPineScriptFile,
|
|
41
|
+
runPineScript: () => runPineScript,
|
|
42
|
+
toFiniteNumber: () => toFiniteNumber,
|
|
43
|
+
toPineBoolean: () => toPineBoolean
|
|
40
44
|
});
|
|
41
45
|
module.exports = __toCommonJS(pine_exports);
|
|
42
46
|
var import_node_fs = __toESM(require("fs"));
|
|
@@ -47,18 +51,32 @@ var getPinePlotSeries = (context, plotName) => {
|
|
|
47
51
|
const data = context?.plots?.[name]?.data;
|
|
48
52
|
return Array.isArray(data) ? data : [];
|
|
49
53
|
};
|
|
50
|
-
var
|
|
54
|
+
var getLatestPineRawPlotValue = (context, plotName) => {
|
|
51
55
|
const series = getPinePlotSeries(context, plotName);
|
|
52
56
|
if (!series.length) return void 0;
|
|
53
57
|
return series[series.length - 1]?.value;
|
|
54
58
|
};
|
|
55
|
-
var
|
|
59
|
+
var getLatestPineNumberPlotValue = (context, plotName) => toFiniteNumber(getLatestPineRawPlotValue(context, plotName)) ?? null;
|
|
60
|
+
var getLatestPineBooleanPlotValue = (context, plotName) => toPineBoolean(getLatestPineRawPlotValue(context, plotName));
|
|
61
|
+
var getLatestPineNumberPlotValues = (context, plotNames) => Object.fromEntries(
|
|
62
|
+
plotNames.map((plotName) => [
|
|
63
|
+
plotName,
|
|
64
|
+
getLatestPineNumberPlotValue(context, plotName)
|
|
65
|
+
])
|
|
66
|
+
);
|
|
67
|
+
var getLatestPineBooleanPlotValues = (context, plotNames) => Object.fromEntries(
|
|
68
|
+
plotNames.map((plotName) => [
|
|
69
|
+
plotName,
|
|
70
|
+
getLatestPineBooleanPlotValue(context, plotName)
|
|
71
|
+
])
|
|
72
|
+
);
|
|
73
|
+
var toFiniteNumber = (value) => {
|
|
56
74
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
57
75
|
return void 0;
|
|
58
76
|
}
|
|
59
77
|
return value;
|
|
60
78
|
};
|
|
61
|
-
var
|
|
79
|
+
var toPineBoolean = (value) => {
|
|
62
80
|
if (typeof value === "boolean") return value;
|
|
63
81
|
if (typeof value === "number") return Number.isFinite(value) && value !== 0;
|
|
64
82
|
return false;
|
|
@@ -68,7 +86,7 @@ var loadPinets = () => {
|
|
|
68
86
|
const cjsPath = resolvedPath.includes("pinets.min.browser") ? resolvedPath.replace(/pinets\.min\.browser(\.es)?\.js$/, "pinets.min.cjs") : resolvedPath;
|
|
69
87
|
return require(cjsPath);
|
|
70
88
|
};
|
|
71
|
-
var
|
|
89
|
+
var loadPineScriptFile = (filePath, fallback = "") => {
|
|
72
90
|
const resolvedPath = String(filePath || "").trim();
|
|
73
91
|
if (!resolvedPath) {
|
|
74
92
|
return fallback;
|
|
@@ -79,7 +97,7 @@ var loadPineScript = (filePath, fallback = "") => {
|
|
|
79
97
|
return fallback;
|
|
80
98
|
}
|
|
81
99
|
};
|
|
82
|
-
var
|
|
100
|
+
var createPineScriptLoader = (baseDir) => {
|
|
83
101
|
const resolvedBaseDir = import_node_path.default.resolve(baseDir);
|
|
84
102
|
return (fileNameOrPath, fallback = "") => {
|
|
85
103
|
const rawPath = String(fileNameOrPath || "").trim();
|
|
@@ -87,7 +105,7 @@ var createLoadPineScript = (baseDir) => {
|
|
|
87
105
|
return fallback;
|
|
88
106
|
}
|
|
89
107
|
const resolvedPath = import_node_path.default.isAbsolute(rawPath) ? rawPath : import_node_path.default.resolve(resolvedBaseDir, rawPath);
|
|
90
|
-
return
|
|
108
|
+
return loadPineScriptFile(resolvedPath, fallback);
|
|
91
109
|
};
|
|
92
110
|
};
|
|
93
111
|
var MINUTE_MS = 6e4;
|
|
@@ -145,11 +163,15 @@ var runPineScript = async ({
|
|
|
145
163
|
};
|
|
146
164
|
// Annotate the CommonJS export names for ESM import in node:
|
|
147
165
|
0 && (module.exports = {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
166
|
+
createPineScriptLoader,
|
|
167
|
+
getLatestPineBooleanPlotValue,
|
|
168
|
+
getLatestPineBooleanPlotValues,
|
|
169
|
+
getLatestPineNumberPlotValue,
|
|
170
|
+
getLatestPineNumberPlotValues,
|
|
171
|
+
getLatestPineRawPlotValue,
|
|
152
172
|
getPinePlotSeries,
|
|
153
|
-
|
|
154
|
-
runPineScript
|
|
173
|
+
loadPineScriptFile,
|
|
174
|
+
runPineScript,
|
|
175
|
+
toFiniteNumber,
|
|
176
|
+
toPineBoolean
|
|
155
177
|
});
|