@backtest-kit/cli 15.5.0 → 16.1.0
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/build/index.cjs +134 -127
- package/build/index.mjs +136 -129
- package/docker/package.json +6 -6
- package/package.json +13 -13
- package/template/project/package.mustache +5 -5
package/build/index.cjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
var BacktestKit = require('backtest-kit');
|
|
5
5
|
var functoolsKit = require('functools-kit');
|
|
6
|
+
var child_process = require('child_process');
|
|
6
7
|
var fs = require('fs');
|
|
7
8
|
var stackTrace = require('stack-trace');
|
|
8
9
|
var path = require('path');
|
|
@@ -11,7 +12,6 @@ var dotenv = require('dotenv');
|
|
|
11
12
|
var diKit = require('di-kit');
|
|
12
13
|
var url = require('url');
|
|
13
14
|
var util = require('util');
|
|
14
|
-
var child_process = require('child_process');
|
|
15
15
|
var BacktestKitUi = require('@backtest-kit/ui');
|
|
16
16
|
var QuickChart = require('quickchart-js');
|
|
17
17
|
var telegraf = require('telegraf');
|
|
@@ -57,6 +57,129 @@ var BacktestKitOllama__namespace = /*#__PURE__*/_interopNamespaceDefault(Backtes
|
|
|
57
57
|
var BacktestKitPinets__namespace = /*#__PURE__*/_interopNamespaceDefault(BacktestKitPinets);
|
|
58
58
|
var BacktestKitSignals__namespace = /*#__PURE__*/_interopNamespaceDefault(BacktestKitSignals);
|
|
59
59
|
|
|
60
|
+
function killPid(pid, signal) {
|
|
61
|
+
try {
|
|
62
|
+
process.kill(pid, signal);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
if (err.code !== "ESRCH")
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function killAll(tree, signal, callback) {
|
|
70
|
+
const killed = {};
|
|
71
|
+
try {
|
|
72
|
+
for (const pid of Object.keys(tree).map(Number)) {
|
|
73
|
+
for (const child of tree[pid]) {
|
|
74
|
+
if (!killed[child]) {
|
|
75
|
+
killPid(child, signal);
|
|
76
|
+
killed[child] = true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (!killed[pid]) {
|
|
80
|
+
killPid(pid, signal);
|
|
81
|
+
killed[pid] = true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
if (callback)
|
|
87
|
+
return callback(err);
|
|
88
|
+
throw err;
|
|
89
|
+
}
|
|
90
|
+
callback?.();
|
|
91
|
+
}
|
|
92
|
+
function buildProcessTree(parentPid, tree, pidsToProcess, spawnList, cb) {
|
|
93
|
+
const ps = spawnList(parentPid);
|
|
94
|
+
let allData = "";
|
|
95
|
+
ps.stdout.on("data", (data) => {
|
|
96
|
+
allData += data.toString("ascii");
|
|
97
|
+
});
|
|
98
|
+
ps.on("close", (code) => {
|
|
99
|
+
delete pidsToProcess[parentPid];
|
|
100
|
+
if (code !== 0) {
|
|
101
|
+
if (Object.keys(pidsToProcess).length === 0)
|
|
102
|
+
cb();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const matches = allData.match(/\d+/g);
|
|
106
|
+
if (matches) {
|
|
107
|
+
for (const pidStr of matches) {
|
|
108
|
+
const pid = parseInt(pidStr, 10);
|
|
109
|
+
tree[parentPid].push(pid);
|
|
110
|
+
tree[pid] = [];
|
|
111
|
+
pidsToProcess[pid] = 1;
|
|
112
|
+
buildProcessTree(pid, tree, pidsToProcess, spawnList, cb);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
function treeKill(pid, signalOrCallback, callback) {
|
|
118
|
+
let signal;
|
|
119
|
+
if (typeof signalOrCallback === "function") {
|
|
120
|
+
callback = signalOrCallback;
|
|
121
|
+
signal = undefined;
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
signal = signalOrCallback;
|
|
125
|
+
}
|
|
126
|
+
pid = parseInt(pid, 10);
|
|
127
|
+
if (Number.isNaN(pid)) {
|
|
128
|
+
const err = new Error("pid must be a number");
|
|
129
|
+
if (callback)
|
|
130
|
+
return callback(err);
|
|
131
|
+
throw err;
|
|
132
|
+
}
|
|
133
|
+
const tree = { [pid]: [] };
|
|
134
|
+
const pidsToProcess = { [pid]: 1 };
|
|
135
|
+
switch (process.platform) {
|
|
136
|
+
case "win32":
|
|
137
|
+
child_process.exec(`taskkill /pid ${pid} /T /F`, (err) => callback?.(err ?? undefined));
|
|
138
|
+
break;
|
|
139
|
+
case "darwin":
|
|
140
|
+
buildProcessTree(pid, tree, pidsToProcess, (parentPid) => child_process.spawn("pgrep", ["-P", String(parentPid)]), () => killAll(tree, signal, callback));
|
|
141
|
+
break;
|
|
142
|
+
default: // Linux
|
|
143
|
+
buildProcessTree(pid, tree, pidsToProcess, (parentPid) => child_process.spawn("ps", ["-o", "pid", "--no-headers", "--ppid", String(parentPid)]), () => killAll(tree, signal, callback));
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const DRAIN_MAX_AWAIT = 250;
|
|
149
|
+
const drainStream = (stream) => new Promise((resolve) => {
|
|
150
|
+
if (stream.writableLength === 0) {
|
|
151
|
+
stream.write("", () => resolve());
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
stream.once("drain", () => {
|
|
155
|
+
stream.write("", () => resolve());
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
const flushStream = (stream) => {
|
|
159
|
+
const handle = stream._handle;
|
|
160
|
+
if (handle && typeof handle.setBlocking === "function") {
|
|
161
|
+
handle.setBlocking(true);
|
|
162
|
+
return new Promise((resolve) => stream.write("", () => resolve()));
|
|
163
|
+
}
|
|
164
|
+
return drainStream(stream);
|
|
165
|
+
};
|
|
166
|
+
const kill = functoolsKit.singleshot(async (code = -1) => {
|
|
167
|
+
await Promise.race([
|
|
168
|
+
Promise.all([
|
|
169
|
+
flushStream(process.stdout),
|
|
170
|
+
flushStream(process.stderr)
|
|
171
|
+
]),
|
|
172
|
+
functoolsKit.sleep(DRAIN_MAX_AWAIT),
|
|
173
|
+
]);
|
|
174
|
+
treeKill(process.pid, "SIGKILL", () => {
|
|
175
|
+
process.exit(code);
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
const notifyKill = functoolsKit.singleshot(() => {
|
|
179
|
+
console.log("Press Ctrl+C again to force quit.");
|
|
180
|
+
process.on("SIGINT", () => kill());
|
|
181
|
+
});
|
|
182
|
+
|
|
60
183
|
BacktestKit.setConfig({
|
|
61
184
|
CC_MAX_NOTIFICATIONS: 5000,
|
|
62
185
|
CC_MAX_SIGNALS: 750,
|
|
@@ -91,6 +214,13 @@ BacktestKit.setConfig({
|
|
|
91
214
|
BacktestKit.setConfig({
|
|
92
215
|
CC_WALKER_MARKDOWN_TOP_N: 10,
|
|
93
216
|
});
|
|
217
|
+
BacktestKit.listenExit((error) => {
|
|
218
|
+
console.error("Exiting due to error:", {
|
|
219
|
+
errorMessage: functoolsKit.getErrorMessage(error),
|
|
220
|
+
errorData: functoolsKit.errorData(error),
|
|
221
|
+
});
|
|
222
|
+
kill();
|
|
223
|
+
});
|
|
94
224
|
|
|
95
225
|
const getEntrySubject = functoolsKit.singleshot(() => {
|
|
96
226
|
const entrySubject = BacktestKit.emitters["entrySubject"];
|
|
@@ -903,129 +1033,6 @@ const notifyVerbose = functoolsKit.singleshot(() => {
|
|
|
903
1033
|
});
|
|
904
1034
|
});
|
|
905
1035
|
|
|
906
|
-
function killPid(pid, signal) {
|
|
907
|
-
try {
|
|
908
|
-
process.kill(pid, signal);
|
|
909
|
-
}
|
|
910
|
-
catch (err) {
|
|
911
|
-
if (err.code !== "ESRCH")
|
|
912
|
-
throw err;
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
function killAll(tree, signal, callback) {
|
|
916
|
-
const killed = {};
|
|
917
|
-
try {
|
|
918
|
-
for (const pid of Object.keys(tree).map(Number)) {
|
|
919
|
-
for (const child of tree[pid]) {
|
|
920
|
-
if (!killed[child]) {
|
|
921
|
-
killPid(child, signal);
|
|
922
|
-
killed[child] = true;
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
if (!killed[pid]) {
|
|
926
|
-
killPid(pid, signal);
|
|
927
|
-
killed[pid] = true;
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
catch (err) {
|
|
932
|
-
if (callback)
|
|
933
|
-
return callback(err);
|
|
934
|
-
throw err;
|
|
935
|
-
}
|
|
936
|
-
callback?.();
|
|
937
|
-
}
|
|
938
|
-
function buildProcessTree(parentPid, tree, pidsToProcess, spawnList, cb) {
|
|
939
|
-
const ps = spawnList(parentPid);
|
|
940
|
-
let allData = "";
|
|
941
|
-
ps.stdout.on("data", (data) => {
|
|
942
|
-
allData += data.toString("ascii");
|
|
943
|
-
});
|
|
944
|
-
ps.on("close", (code) => {
|
|
945
|
-
delete pidsToProcess[parentPid];
|
|
946
|
-
if (code !== 0) {
|
|
947
|
-
if (Object.keys(pidsToProcess).length === 0)
|
|
948
|
-
cb();
|
|
949
|
-
return;
|
|
950
|
-
}
|
|
951
|
-
const matches = allData.match(/\d+/g);
|
|
952
|
-
if (matches) {
|
|
953
|
-
for (const pidStr of matches) {
|
|
954
|
-
const pid = parseInt(pidStr, 10);
|
|
955
|
-
tree[parentPid].push(pid);
|
|
956
|
-
tree[pid] = [];
|
|
957
|
-
pidsToProcess[pid] = 1;
|
|
958
|
-
buildProcessTree(pid, tree, pidsToProcess, spawnList, cb);
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
});
|
|
962
|
-
}
|
|
963
|
-
function treeKill(pid, signalOrCallback, callback) {
|
|
964
|
-
let signal;
|
|
965
|
-
if (typeof signalOrCallback === "function") {
|
|
966
|
-
callback = signalOrCallback;
|
|
967
|
-
signal = undefined;
|
|
968
|
-
}
|
|
969
|
-
else {
|
|
970
|
-
signal = signalOrCallback;
|
|
971
|
-
}
|
|
972
|
-
pid = parseInt(pid, 10);
|
|
973
|
-
if (Number.isNaN(pid)) {
|
|
974
|
-
const err = new Error("pid must be a number");
|
|
975
|
-
if (callback)
|
|
976
|
-
return callback(err);
|
|
977
|
-
throw err;
|
|
978
|
-
}
|
|
979
|
-
const tree = { [pid]: [] };
|
|
980
|
-
const pidsToProcess = { [pid]: 1 };
|
|
981
|
-
switch (process.platform) {
|
|
982
|
-
case "win32":
|
|
983
|
-
child_process.exec(`taskkill /pid ${pid} /T /F`, (err) => callback?.(err ?? undefined));
|
|
984
|
-
break;
|
|
985
|
-
case "darwin":
|
|
986
|
-
buildProcessTree(pid, tree, pidsToProcess, (parentPid) => child_process.spawn("pgrep", ["-P", String(parentPid)]), () => killAll(tree, signal, callback));
|
|
987
|
-
break;
|
|
988
|
-
default: // Linux
|
|
989
|
-
buildProcessTree(pid, tree, pidsToProcess, (parentPid) => child_process.spawn("ps", ["-o", "pid", "--no-headers", "--ppid", String(parentPid)]), () => killAll(tree, signal, callback));
|
|
990
|
-
break;
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
const DRAIN_MAX_AWAIT = 250;
|
|
995
|
-
const drainStream = (stream) => new Promise((resolve) => {
|
|
996
|
-
if (stream.writableLength === 0) {
|
|
997
|
-
stream.write("", () => resolve());
|
|
998
|
-
return;
|
|
999
|
-
}
|
|
1000
|
-
stream.once("drain", () => {
|
|
1001
|
-
stream.write("", () => resolve());
|
|
1002
|
-
});
|
|
1003
|
-
});
|
|
1004
|
-
const flushStream = (stream) => {
|
|
1005
|
-
const handle = stream._handle;
|
|
1006
|
-
if (handle && typeof handle.setBlocking === "function") {
|
|
1007
|
-
handle.setBlocking(true);
|
|
1008
|
-
return new Promise((resolve) => stream.write("", () => resolve()));
|
|
1009
|
-
}
|
|
1010
|
-
return drainStream(stream);
|
|
1011
|
-
};
|
|
1012
|
-
const kill = functoolsKit.singleshot(async (code = -1) => {
|
|
1013
|
-
await Promise.race([
|
|
1014
|
-
Promise.all([
|
|
1015
|
-
flushStream(process.stdout),
|
|
1016
|
-
flushStream(process.stderr)
|
|
1017
|
-
]),
|
|
1018
|
-
functoolsKit.sleep(DRAIN_MAX_AWAIT),
|
|
1019
|
-
]);
|
|
1020
|
-
treeKill(process.pid, "SIGKILL", () => {
|
|
1021
|
-
process.exit(code);
|
|
1022
|
-
});
|
|
1023
|
-
});
|
|
1024
|
-
const notifyKill = functoolsKit.singleshot(() => {
|
|
1025
|
-
console.log("Press Ctrl+C again to force quit.");
|
|
1026
|
-
process.on("SIGINT", () => kill());
|
|
1027
|
-
});
|
|
1028
|
-
|
|
1029
1036
|
// Must match the getArgs --cacheInterval default ("1m, 15m, 30m, 1h, 4h"):
|
|
1030
1037
|
// this fallback is only reachable with an explicit --cacheInterval ""
|
|
1031
1038
|
const DEFAULT_CACHE_LIST$1 = ["1m", "15m", "30m", "1h", "4h"];
|
|
@@ -3542,7 +3549,7 @@ const main$h = async () => {
|
|
|
3542
3549
|
if (MODES.some((mode) => values[mode])) {
|
|
3543
3550
|
return;
|
|
3544
3551
|
}
|
|
3545
|
-
process.stdout.write(`@backtest-kit/cli ${"
|
|
3552
|
+
process.stdout.write(`@backtest-kit/cli ${"16.1.0"}\n`);
|
|
3546
3553
|
process.stdout.write("\n");
|
|
3547
3554
|
process.stdout.write(`Run with --help to see available commands.\n`);
|
|
3548
3555
|
process.stdout.write("\n");
|
|
@@ -4852,7 +4859,7 @@ const main$1 = async () => {
|
|
|
4852
4859
|
if (!values.help) {
|
|
4853
4860
|
return;
|
|
4854
4861
|
}
|
|
4855
|
-
process.stdout.write(`@backtest-kit/cli ${"
|
|
4862
|
+
process.stdout.write(`@backtest-kit/cli ${"16.1.0"}\n\n`);
|
|
4856
4863
|
process.stdout.write(HELP_TEXT);
|
|
4857
4864
|
process.exit(0);
|
|
4858
4865
|
};
|
|
@@ -4866,7 +4873,7 @@ const main = async () => {
|
|
|
4866
4873
|
if (!values.version) {
|
|
4867
4874
|
return;
|
|
4868
4875
|
}
|
|
4869
|
-
process.stdout.write(`@backtest-kit/cli ${"
|
|
4876
|
+
process.stdout.write(`@backtest-kit/cli ${"16.1.0"}\n`);
|
|
4870
4877
|
process.exit(0);
|
|
4871
4878
|
};
|
|
4872
4879
|
main();
|
package/build/index.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as BacktestKit from 'backtest-kit';
|
|
3
|
-
import { setConfig, emitters, Notification, Cron, Recent, Storage, Markdown, Report, Dump, State, Memory, MarkdownWriter, ReportWriter, PersistSignalAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistPartialAdapter, PersistBreakevenAdapter, PersistCandleAdapter, PersistStorageAdapter, PersistNotificationAdapter, PersistLogAdapter, PersistMeasureAdapter, PersistIntervalAdapter, PersistMemoryAdapter, PersistRecentAdapter, PersistStateAdapter, PersistSessionAdapter, PersistStrategyAdapter, Log, StorageLive, StorageBacktest, NotificationLive, NotificationBacktest, RecentLive, RecentBacktest, Broker, SessionLive, SessionBacktest, MemoryLive, MemoryBacktest, StateLive, StateBacktest, listExchangeSchema, addExchangeSchema, roundTicks, listFrameSchema, addFrameSchema, listenDoneLive, listenDoneBacktest, shutdown, listenSignal, listStrategySchema, overrideExchangeSchema, Backtest, System, Cache, Interval, alignToInterval, addWalkerSchema, overrideWalkerSchema, Walker, listenDoneWalker, Live, getCandles, checkCandles, warmCandles, listenRisk, listenStrategyCommit, listenSync, listenSignalNotify, Exchange } from 'backtest-kit';
|
|
4
|
-
import { singleshot,
|
|
3
|
+
import { setConfig, listenExit, emitters, Notification, Cron, Recent, Storage, Markdown, Report, Dump, State, Memory, MarkdownWriter, ReportWriter, PersistSignalAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistPartialAdapter, PersistBreakevenAdapter, PersistCandleAdapter, PersistStorageAdapter, PersistNotificationAdapter, PersistLogAdapter, PersistMeasureAdapter, PersistIntervalAdapter, PersistMemoryAdapter, PersistRecentAdapter, PersistStateAdapter, PersistSessionAdapter, PersistStrategyAdapter, Log, StorageLive, StorageBacktest, NotificationLive, NotificationBacktest, RecentLive, RecentBacktest, Broker, SessionLive, SessionBacktest, MemoryLive, MemoryBacktest, StateLive, StateBacktest, listExchangeSchema, addExchangeSchema, roundTicks, listFrameSchema, addFrameSchema, listenDoneLive, listenDoneBacktest, shutdown, listenSignal, listStrategySchema, overrideExchangeSchema, Backtest, System, Cache, Interval, alignToInterval, addWalkerSchema, overrideWalkerSchema, Walker, listenDoneWalker, Live, getCandles, checkCandles, warmCandles, listenRisk, listenStrategyCommit, listenSync, listenSignalNotify, Exchange } from 'backtest-kit';
|
|
4
|
+
import { singleshot, sleep, getErrorMessage, errorData, BehaviorSubject, Subject, str, compose, createAwaiter, execpool, queued, randomString, TIMEOUT_SYMBOL, typo, retry, trycatch, memoize, isObject } from 'functools-kit';
|
|
5
|
+
import { exec, spawn } from 'child_process';
|
|
5
6
|
import fs, { constants, realpathSync } from 'fs';
|
|
6
7
|
import * as stackTrace from 'stack-trace';
|
|
7
8
|
import path, { join, resolve, dirname, basename, extname } from 'path';
|
|
@@ -10,7 +11,6 @@ import dotenv from 'dotenv';
|
|
|
10
11
|
import { createActivator } from 'di-kit';
|
|
11
12
|
import { fileURLToPath } from 'url';
|
|
12
13
|
import { parseArgs } from 'util';
|
|
13
|
-
import { exec, spawn } from 'child_process';
|
|
14
14
|
import * as BacktestKitUi from '@backtest-kit/ui';
|
|
15
15
|
import { lib, serve } from '@backtest-kit/ui';
|
|
16
16
|
import QuickChart from 'quickchart-js';
|
|
@@ -32,6 +32,129 @@ import { run as run$1, Code, toMarkdown } from '@backtest-kit/pinets';
|
|
|
32
32
|
import * as BacktestKitSignals from '@backtest-kit/signals';
|
|
33
33
|
import open from 'open';
|
|
34
34
|
|
|
35
|
+
function killPid(pid, signal) {
|
|
36
|
+
try {
|
|
37
|
+
process.kill(pid, signal);
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
if (err.code !== "ESRCH")
|
|
41
|
+
throw err;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function killAll(tree, signal, callback) {
|
|
45
|
+
const killed = {};
|
|
46
|
+
try {
|
|
47
|
+
for (const pid of Object.keys(tree).map(Number)) {
|
|
48
|
+
for (const child of tree[pid]) {
|
|
49
|
+
if (!killed[child]) {
|
|
50
|
+
killPid(child, signal);
|
|
51
|
+
killed[child] = true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (!killed[pid]) {
|
|
55
|
+
killPid(pid, signal);
|
|
56
|
+
killed[pid] = true;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
if (callback)
|
|
62
|
+
return callback(err);
|
|
63
|
+
throw err;
|
|
64
|
+
}
|
|
65
|
+
callback?.();
|
|
66
|
+
}
|
|
67
|
+
function buildProcessTree(parentPid, tree, pidsToProcess, spawnList, cb) {
|
|
68
|
+
const ps = spawnList(parentPid);
|
|
69
|
+
let allData = "";
|
|
70
|
+
ps.stdout.on("data", (data) => {
|
|
71
|
+
allData += data.toString("ascii");
|
|
72
|
+
});
|
|
73
|
+
ps.on("close", (code) => {
|
|
74
|
+
delete pidsToProcess[parentPid];
|
|
75
|
+
if (code !== 0) {
|
|
76
|
+
if (Object.keys(pidsToProcess).length === 0)
|
|
77
|
+
cb();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const matches = allData.match(/\d+/g);
|
|
81
|
+
if (matches) {
|
|
82
|
+
for (const pidStr of matches) {
|
|
83
|
+
const pid = parseInt(pidStr, 10);
|
|
84
|
+
tree[parentPid].push(pid);
|
|
85
|
+
tree[pid] = [];
|
|
86
|
+
pidsToProcess[pid] = 1;
|
|
87
|
+
buildProcessTree(pid, tree, pidsToProcess, spawnList, cb);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function treeKill(pid, signalOrCallback, callback) {
|
|
93
|
+
let signal;
|
|
94
|
+
if (typeof signalOrCallback === "function") {
|
|
95
|
+
callback = signalOrCallback;
|
|
96
|
+
signal = undefined;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
signal = signalOrCallback;
|
|
100
|
+
}
|
|
101
|
+
pid = parseInt(pid, 10);
|
|
102
|
+
if (Number.isNaN(pid)) {
|
|
103
|
+
const err = new Error("pid must be a number");
|
|
104
|
+
if (callback)
|
|
105
|
+
return callback(err);
|
|
106
|
+
throw err;
|
|
107
|
+
}
|
|
108
|
+
const tree = { [pid]: [] };
|
|
109
|
+
const pidsToProcess = { [pid]: 1 };
|
|
110
|
+
switch (process.platform) {
|
|
111
|
+
case "win32":
|
|
112
|
+
exec(`taskkill /pid ${pid} /T /F`, (err) => callback?.(err ?? undefined));
|
|
113
|
+
break;
|
|
114
|
+
case "darwin":
|
|
115
|
+
buildProcessTree(pid, tree, pidsToProcess, (parentPid) => spawn("pgrep", ["-P", String(parentPid)]), () => killAll(tree, signal, callback));
|
|
116
|
+
break;
|
|
117
|
+
default: // Linux
|
|
118
|
+
buildProcessTree(pid, tree, pidsToProcess, (parentPid) => spawn("ps", ["-o", "pid", "--no-headers", "--ppid", String(parentPid)]), () => killAll(tree, signal, callback));
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const DRAIN_MAX_AWAIT = 250;
|
|
124
|
+
const drainStream = (stream) => new Promise((resolve) => {
|
|
125
|
+
if (stream.writableLength === 0) {
|
|
126
|
+
stream.write("", () => resolve());
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
stream.once("drain", () => {
|
|
130
|
+
stream.write("", () => resolve());
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
const flushStream = (stream) => {
|
|
134
|
+
const handle = stream._handle;
|
|
135
|
+
if (handle && typeof handle.setBlocking === "function") {
|
|
136
|
+
handle.setBlocking(true);
|
|
137
|
+
return new Promise((resolve) => stream.write("", () => resolve()));
|
|
138
|
+
}
|
|
139
|
+
return drainStream(stream);
|
|
140
|
+
};
|
|
141
|
+
const kill = singleshot(async (code = -1) => {
|
|
142
|
+
await Promise.race([
|
|
143
|
+
Promise.all([
|
|
144
|
+
flushStream(process.stdout),
|
|
145
|
+
flushStream(process.stderr)
|
|
146
|
+
]),
|
|
147
|
+
sleep(DRAIN_MAX_AWAIT),
|
|
148
|
+
]);
|
|
149
|
+
treeKill(process.pid, "SIGKILL", () => {
|
|
150
|
+
process.exit(code);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
const notifyKill = singleshot(() => {
|
|
154
|
+
console.log("Press Ctrl+C again to force quit.");
|
|
155
|
+
process.on("SIGINT", () => kill());
|
|
156
|
+
});
|
|
157
|
+
|
|
35
158
|
setConfig({
|
|
36
159
|
CC_MAX_NOTIFICATIONS: 5000,
|
|
37
160
|
CC_MAX_SIGNALS: 750,
|
|
@@ -66,6 +189,13 @@ setConfig({
|
|
|
66
189
|
setConfig({
|
|
67
190
|
CC_WALKER_MARKDOWN_TOP_N: 10,
|
|
68
191
|
});
|
|
192
|
+
listenExit((error) => {
|
|
193
|
+
console.error("Exiting due to error:", {
|
|
194
|
+
errorMessage: getErrorMessage(error),
|
|
195
|
+
errorData: errorData(error),
|
|
196
|
+
});
|
|
197
|
+
kill();
|
|
198
|
+
});
|
|
69
199
|
|
|
70
200
|
const getEntrySubject = singleshot(() => {
|
|
71
201
|
const entrySubject = emitters["entrySubject"];
|
|
@@ -878,129 +1008,6 @@ const notifyVerbose = singleshot(() => {
|
|
|
878
1008
|
});
|
|
879
1009
|
});
|
|
880
1010
|
|
|
881
|
-
function killPid(pid, signal) {
|
|
882
|
-
try {
|
|
883
|
-
process.kill(pid, signal);
|
|
884
|
-
}
|
|
885
|
-
catch (err) {
|
|
886
|
-
if (err.code !== "ESRCH")
|
|
887
|
-
throw err;
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
function killAll(tree, signal, callback) {
|
|
891
|
-
const killed = {};
|
|
892
|
-
try {
|
|
893
|
-
for (const pid of Object.keys(tree).map(Number)) {
|
|
894
|
-
for (const child of tree[pid]) {
|
|
895
|
-
if (!killed[child]) {
|
|
896
|
-
killPid(child, signal);
|
|
897
|
-
killed[child] = true;
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
if (!killed[pid]) {
|
|
901
|
-
killPid(pid, signal);
|
|
902
|
-
killed[pid] = true;
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
catch (err) {
|
|
907
|
-
if (callback)
|
|
908
|
-
return callback(err);
|
|
909
|
-
throw err;
|
|
910
|
-
}
|
|
911
|
-
callback?.();
|
|
912
|
-
}
|
|
913
|
-
function buildProcessTree(parentPid, tree, pidsToProcess, spawnList, cb) {
|
|
914
|
-
const ps = spawnList(parentPid);
|
|
915
|
-
let allData = "";
|
|
916
|
-
ps.stdout.on("data", (data) => {
|
|
917
|
-
allData += data.toString("ascii");
|
|
918
|
-
});
|
|
919
|
-
ps.on("close", (code) => {
|
|
920
|
-
delete pidsToProcess[parentPid];
|
|
921
|
-
if (code !== 0) {
|
|
922
|
-
if (Object.keys(pidsToProcess).length === 0)
|
|
923
|
-
cb();
|
|
924
|
-
return;
|
|
925
|
-
}
|
|
926
|
-
const matches = allData.match(/\d+/g);
|
|
927
|
-
if (matches) {
|
|
928
|
-
for (const pidStr of matches) {
|
|
929
|
-
const pid = parseInt(pidStr, 10);
|
|
930
|
-
tree[parentPid].push(pid);
|
|
931
|
-
tree[pid] = [];
|
|
932
|
-
pidsToProcess[pid] = 1;
|
|
933
|
-
buildProcessTree(pid, tree, pidsToProcess, spawnList, cb);
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
});
|
|
937
|
-
}
|
|
938
|
-
function treeKill(pid, signalOrCallback, callback) {
|
|
939
|
-
let signal;
|
|
940
|
-
if (typeof signalOrCallback === "function") {
|
|
941
|
-
callback = signalOrCallback;
|
|
942
|
-
signal = undefined;
|
|
943
|
-
}
|
|
944
|
-
else {
|
|
945
|
-
signal = signalOrCallback;
|
|
946
|
-
}
|
|
947
|
-
pid = parseInt(pid, 10);
|
|
948
|
-
if (Number.isNaN(pid)) {
|
|
949
|
-
const err = new Error("pid must be a number");
|
|
950
|
-
if (callback)
|
|
951
|
-
return callback(err);
|
|
952
|
-
throw err;
|
|
953
|
-
}
|
|
954
|
-
const tree = { [pid]: [] };
|
|
955
|
-
const pidsToProcess = { [pid]: 1 };
|
|
956
|
-
switch (process.platform) {
|
|
957
|
-
case "win32":
|
|
958
|
-
exec(`taskkill /pid ${pid} /T /F`, (err) => callback?.(err ?? undefined));
|
|
959
|
-
break;
|
|
960
|
-
case "darwin":
|
|
961
|
-
buildProcessTree(pid, tree, pidsToProcess, (parentPid) => spawn("pgrep", ["-P", String(parentPid)]), () => killAll(tree, signal, callback));
|
|
962
|
-
break;
|
|
963
|
-
default: // Linux
|
|
964
|
-
buildProcessTree(pid, tree, pidsToProcess, (parentPid) => spawn("ps", ["-o", "pid", "--no-headers", "--ppid", String(parentPid)]), () => killAll(tree, signal, callback));
|
|
965
|
-
break;
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
const DRAIN_MAX_AWAIT = 250;
|
|
970
|
-
const drainStream = (stream) => new Promise((resolve) => {
|
|
971
|
-
if (stream.writableLength === 0) {
|
|
972
|
-
stream.write("", () => resolve());
|
|
973
|
-
return;
|
|
974
|
-
}
|
|
975
|
-
stream.once("drain", () => {
|
|
976
|
-
stream.write("", () => resolve());
|
|
977
|
-
});
|
|
978
|
-
});
|
|
979
|
-
const flushStream = (stream) => {
|
|
980
|
-
const handle = stream._handle;
|
|
981
|
-
if (handle && typeof handle.setBlocking === "function") {
|
|
982
|
-
handle.setBlocking(true);
|
|
983
|
-
return new Promise((resolve) => stream.write("", () => resolve()));
|
|
984
|
-
}
|
|
985
|
-
return drainStream(stream);
|
|
986
|
-
};
|
|
987
|
-
const kill = singleshot(async (code = -1) => {
|
|
988
|
-
await Promise.race([
|
|
989
|
-
Promise.all([
|
|
990
|
-
flushStream(process.stdout),
|
|
991
|
-
flushStream(process.stderr)
|
|
992
|
-
]),
|
|
993
|
-
sleep(DRAIN_MAX_AWAIT),
|
|
994
|
-
]);
|
|
995
|
-
treeKill(process.pid, "SIGKILL", () => {
|
|
996
|
-
process.exit(code);
|
|
997
|
-
});
|
|
998
|
-
});
|
|
999
|
-
const notifyKill = singleshot(() => {
|
|
1000
|
-
console.log("Press Ctrl+C again to force quit.");
|
|
1001
|
-
process.on("SIGINT", () => kill());
|
|
1002
|
-
});
|
|
1003
|
-
|
|
1004
1011
|
// Must match the getArgs --cacheInterval default ("1m, 15m, 30m, 1h, 4h"):
|
|
1005
1012
|
// this fallback is only reachable with an explicit --cacheInterval ""
|
|
1006
1013
|
const DEFAULT_CACHE_LIST$1 = ["1m", "15m", "30m", "1h", "4h"];
|
|
@@ -3510,7 +3517,7 @@ const main$h = async () => {
|
|
|
3510
3517
|
if (MODES.some((mode) => values[mode])) {
|
|
3511
3518
|
return;
|
|
3512
3519
|
}
|
|
3513
|
-
process.stdout.write(`@backtest-kit/cli ${"
|
|
3520
|
+
process.stdout.write(`@backtest-kit/cli ${"16.1.0"}\n`);
|
|
3514
3521
|
process.stdout.write("\n");
|
|
3515
3522
|
process.stdout.write(`Run with --help to see available commands.\n`);
|
|
3516
3523
|
process.stdout.write("\n");
|
|
@@ -4820,7 +4827,7 @@ const main$1 = async () => {
|
|
|
4820
4827
|
if (!values.help) {
|
|
4821
4828
|
return;
|
|
4822
4829
|
}
|
|
4823
|
-
process.stdout.write(`@backtest-kit/cli ${"
|
|
4830
|
+
process.stdout.write(`@backtest-kit/cli ${"16.1.0"}\n\n`);
|
|
4824
4831
|
process.stdout.write(HELP_TEXT);
|
|
4825
4832
|
process.exit(0);
|
|
4826
4833
|
};
|
|
@@ -4834,7 +4841,7 @@ const main = async () => {
|
|
|
4834
4841
|
if (!values.version) {
|
|
4835
4842
|
return;
|
|
4836
4843
|
}
|
|
4837
|
-
process.stdout.write(`@backtest-kit/cli ${"
|
|
4844
|
+
process.stdout.write(`@backtest-kit/cli ${"16.1.0"}\n`);
|
|
4838
4845
|
process.exit(0);
|
|
4839
4846
|
};
|
|
4840
4847
|
main();
|
package/docker/package.json
CHANGED
|
@@ -15,17 +15,17 @@
|
|
|
15
15
|
"@types/node": "25.6.0"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@backtest-kit/cli": "
|
|
19
|
-
"@backtest-kit/graph": "
|
|
20
|
-
"@backtest-kit/pinets": "
|
|
21
|
-
"@backtest-kit/signals": "
|
|
22
|
-
"@backtest-kit/ui": "
|
|
18
|
+
"@backtest-kit/cli": "16.1.0",
|
|
19
|
+
"@backtest-kit/graph": "16.1.0",
|
|
20
|
+
"@backtest-kit/pinets": "16.1.0",
|
|
21
|
+
"@backtest-kit/signals": "16.1.0",
|
|
22
|
+
"@backtest-kit/ui": "16.1.0",
|
|
23
23
|
"@tavily/core": "0.7.2",
|
|
24
24
|
"@tensorflow/tfjs": "4.22.0",
|
|
25
25
|
"@tensorflow/tfjs-backend-wasm": "4.22.0",
|
|
26
26
|
"@tensorflow/tfjs-core": "4.22.0",
|
|
27
27
|
"agent-swarm-kit": "5.1.0",
|
|
28
|
-
"backtest-kit": "
|
|
28
|
+
"backtest-kit": "16.1.0",
|
|
29
29
|
"dayjs": "1.11.20",
|
|
30
30
|
"functools-kit": "4.0.0",
|
|
31
31
|
"garch": "2.0.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backtest-kit/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "16.1.0",
|
|
4
4
|
"description": "Zero-boilerplate CLI runner for backtest-kit strategies. Run backtests, paper trading, and live bots with candle cache warming, web dashboard, and Telegram notifications — no setup code required.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Petr Tripolsky",
|
|
@@ -63,11 +63,11 @@
|
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@babel/plugin-transform-modules-umd": "7.27.1",
|
|
65
65
|
"@babel/standalone": "7.29.1",
|
|
66
|
-
"@backtest-kit/graph": "
|
|
67
|
-
"@backtest-kit/ollama": "
|
|
68
|
-
"@backtest-kit/pinets": "
|
|
69
|
-
"@backtest-kit/signals": "
|
|
70
|
-
"@backtest-kit/ui": "
|
|
66
|
+
"@backtest-kit/graph": "16.1.0",
|
|
67
|
+
"@backtest-kit/ollama": "16.1.0",
|
|
68
|
+
"@backtest-kit/pinets": "16.1.0",
|
|
69
|
+
"@backtest-kit/signals": "16.1.0",
|
|
70
|
+
"@backtest-kit/ui": "16.1.0",
|
|
71
71
|
"@rollup/plugin-replace": "6.0.3",
|
|
72
72
|
"@rollup/plugin-typescript": "11.1.6",
|
|
73
73
|
"@types/image-size": "0.7.0",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"@types/mustache": "4.2.6",
|
|
76
76
|
"@types/node": "22.9.0",
|
|
77
77
|
"@types/stack-trace": "0.0.33",
|
|
78
|
-
"backtest-kit": "
|
|
78
|
+
"backtest-kit": "16.1.0",
|
|
79
79
|
"glob": "11.0.1",
|
|
80
80
|
"markdown-it": "14.1.1",
|
|
81
81
|
"rimraf": "6.0.1",
|
|
@@ -92,14 +92,14 @@
|
|
|
92
92
|
"peerDependencies": {
|
|
93
93
|
"@babel/plugin-transform-modules-umd": "^7.27.1",
|
|
94
94
|
"@babel/standalone": "^7.29.1",
|
|
95
|
-
"@backtest-kit/graph": "^
|
|
96
|
-
"@backtest-kit/ollama": "^
|
|
97
|
-
"@backtest-kit/pinets": "^
|
|
98
|
-
"@backtest-kit/signals": "^
|
|
99
|
-
"@backtest-kit/ui": "^
|
|
95
|
+
"@backtest-kit/graph": "^16.1.0",
|
|
96
|
+
"@backtest-kit/ollama": "^16.1.0",
|
|
97
|
+
"@backtest-kit/pinets": "^16.1.0",
|
|
98
|
+
"@backtest-kit/signals": "^16.1.0",
|
|
99
|
+
"@backtest-kit/ui": "^16.1.0",
|
|
100
100
|
"pinets": "^0.9.29",
|
|
101
101
|
"ccxt": "^4.5.39",
|
|
102
|
-
"backtest-kit": "^
|
|
102
|
+
"backtest-kit": "^16.1.0",
|
|
103
103
|
"markdown-it": "^14.1.1",
|
|
104
104
|
"typescript": "^5.0.0"
|
|
105
105
|
},
|
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
"license": "ISC",
|
|
14
14
|
"type": "commonjs",
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@backtest-kit/cli": "^
|
|
17
|
-
"@backtest-kit/graph": "^
|
|
18
|
-
"@backtest-kit/pinets": "^
|
|
19
|
-
"@backtest-kit/ui": "^
|
|
16
|
+
"@backtest-kit/cli": "^16.1.0",
|
|
17
|
+
"@backtest-kit/graph": "^16.1.0",
|
|
18
|
+
"@backtest-kit/pinets": "^16.1.0",
|
|
19
|
+
"@backtest-kit/ui": "^16.1.0",
|
|
20
20
|
"agent-swarm-kit": "^5.1.0",
|
|
21
|
-
"backtest-kit": "^
|
|
21
|
+
"backtest-kit": "^16.1.0",
|
|
22
22
|
"functools-kit": "^4.0.0",
|
|
23
23
|
"garch": "^2.0.0",
|
|
24
24
|
"get-moment-stamp": "^3.0.0",
|