@tradejs/cli 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.
@@ -5,6 +5,10 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
8
12
  var __copyProps = (to, from, except, desc) => {
9
13
  if (from && typeof from === "object" || typeof from === "function") {
10
14
  for (let key of __getOwnPropNames(from))
@@ -21,8 +25,14 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
25
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
26
  mod
23
27
  ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
24
29
 
25
30
  // src/scripts/signals.ts
31
+ var signals_exports = {};
32
+ __export(signals_exports, {
33
+ signals: () => signals
34
+ });
35
+ module.exports = __toCommonJS(signals_exports);
26
36
  var import_config = require("dotenv/config");
27
37
  var import_args = __toESM(require("args"));
28
38
  var import_progress = __toESM(require("progress"));
@@ -46,6 +56,11 @@ import_args.default.option(["S", "skipScreenshots"], "Skip screenshot generation
46
56
  import_args.default.option(["u", "updateOnly"], "Only update tickers history", false);
47
57
  import_args.default.option(["C", "cacheOnly"], "Do not update tickers history", false);
48
58
  import_args.default.option(["L", "showTickersList"], "Just show only ticker list", false);
59
+ import_args.default.option(
60
+ ["R", "showSkipStats"],
61
+ "Show aggregated skip stats by strategy",
62
+ false
63
+ );
49
64
  import_args.default.option(["c", "chunk"], "Split by chunks, ex. 1/3");
50
65
  import_args.default.option(["U", "user"], "Use user confg", "root");
51
66
  import_args.default.option(
@@ -53,12 +68,11 @@ import_args.default.option(
53
68
  "Connector provider or name for signals (e.g. bybit, binance, coinbase, custom)",
54
69
  "bybit"
55
70
  );
56
- var PRELOAD_START = (0, import_time.getTimestamp)(import_constants.SIGNALS_PRELOAD_DAYS);
71
+ var PRELOAD_START = (0, import_time.getTimestamp)(import_constants.SIGNALS_CLI_PRELOAD_DAYS);
57
72
  var projectRoot = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
58
73
  var flags = import_args.default.parse(process.argv);
59
- var minTouches = parseInt(flags.points);
60
- var offset = parseInt(flags.offset);
61
74
  var interval = flags.timeframe.toString();
75
+ var formatElapsed = (startedAt) => `${((Date.now() - startedAt) / 1e3).toFixed(1)}s`;
62
76
  var resolveStrategyNameByConfigKey = (userName, key) => {
63
77
  const parts = key.split(":");
64
78
  if (parts.length !== 5) {
@@ -97,6 +111,45 @@ var loadRuntimeStrategies = async (userName) => {
97
111
  );
98
112
  return strategyConfigs.filter(Boolean);
99
113
  };
114
+ var createStrategySkipStats = (runtimeStrategies) => new Map(
115
+ runtimeStrategies.map(({ strategyName }) => [
116
+ strategyName,
117
+ {
118
+ evaluated: 0,
119
+ signals: 0,
120
+ reasons: /* @__PURE__ */ new Map()
121
+ }
122
+ ])
123
+ );
124
+ var recordStrategyReason = (strategyStats, strategyName, reason) => {
125
+ const stats = strategyStats.get(strategyName);
126
+ if (!stats) {
127
+ return;
128
+ }
129
+ stats.reasons.set(reason, (stats.reasons.get(reason) ?? 0) + 1);
130
+ };
131
+ var logStrategySkipStats = (runtimeStrategies, strategyStats) => {
132
+ import_logger.logger.info(import_chalk.default.yellow("skip stats:"));
133
+ for (const { strategyName } of runtimeStrategies) {
134
+ const stats = strategyStats.get(strategyName);
135
+ if (!stats) {
136
+ continue;
137
+ }
138
+ import_logger.logger.info(
139
+ `${strategyName}: evaluated=${stats.evaluated}, signals=${stats.signals}`
140
+ );
141
+ const sortedReasons = [...stats.reasons.entries()].sort(
142
+ (left, right) => right[1] - left[1] || left[0].localeCompare(right[0])
143
+ );
144
+ if (!sortedReasons.length) {
145
+ import_logger.logger.info(" none");
146
+ continue;
147
+ }
148
+ for (const [reason, count] of sortedReasons) {
149
+ import_logger.logger.info(` ${reason}: ${count}`);
150
+ }
151
+ }
152
+ };
100
153
  var resolveSignalsConnectorName = async (value) => {
101
154
  const connectorName = await (0, import_connectors2.resolveConnectorName)(value, projectRoot);
102
155
  if (connectorName) {
@@ -109,13 +162,9 @@ var resolveSignalsConnectorName = async (value) => {
109
162
  );
110
163
  return import_connectors2.DEFAULT_CONNECTOR_NAME;
111
164
  };
112
- var findSignals = async (symbol, connector, btcBinanceData, btcCoinbaseData, runtimeStrategies) => {
113
- const prevSignals = await (0, import_redis.getKeys)(import_redis.redisKeys.signalsBySymbol(symbol));
114
- if (prevSignals.length) {
115
- import_logger.logger.info("Exit by signal exists %s", symbol);
116
- return null;
117
- }
165
+ var findSignals = async (symbol, connector, btcBinanceData, btcCoinbaseData, runtimeStrategies, strategyStats) => {
118
166
  const currentTimestamp = (0, import_time.getTimestamp)();
167
+ const strategySignals = [];
119
168
  const [cachedData, btcCachedData] = await Promise.all([
120
169
  connector.kline({
121
170
  symbol,
@@ -135,10 +184,14 @@ var findSignals = async (symbol, connector, btcBinanceData, btcCoinbaseData, run
135
184
  const lastCandle = cachedData.pop();
136
185
  const btcLastCandle = btcCachedData.pop();
137
186
  if (!lastCandle || !btcLastCandle) {
138
- return;
187
+ return strategySignals;
139
188
  }
140
189
  for (const runtimeStrategy of runtimeStrategies) {
141
190
  const { strategyName, strategyCreator, strategyConfig } = runtimeStrategy;
191
+ const stats = strategyStats.get(strategyName);
192
+ if (stats) {
193
+ stats.evaluated += 1;
194
+ }
142
195
  const strategy = await strategyCreator({
143
196
  userName: flags.user,
144
197
  connector,
@@ -156,8 +209,15 @@ var findSignals = async (symbol, connector, btcBinanceData, btcCoinbaseData, run
156
209
  });
157
210
  const signal = await strategy(lastCandle, btcLastCandle);
158
211
  if (!signal || typeof signal === "string") {
212
+ if (typeof signal === "string") {
213
+ recordStrategyReason(strategyStats, strategyName, signal);
214
+ }
159
215
  continue;
160
216
  }
217
+ if (stats) {
218
+ stats.signals += 1;
219
+ }
220
+ strategySignals.push(signal);
161
221
  await (0, import_redis.setData)(import_redis.redisKeys.signal(symbol, signal.signalId), signal, {
162
222
  expire: import_constants.TTL_1D
163
223
  });
@@ -165,145 +225,190 @@ var findSignals = async (symbol, connector, btcBinanceData, btcCoinbaseData, run
165
225
  expire: import_constants.TTL_3M
166
226
  });
167
227
  import_logger.logger.info("Signal found %s by strategy %s", symbol, strategyName);
168
- return signal;
169
228
  }
229
+ return strategySignals;
170
230
  };
171
231
  var signals = async () => {
232
+ const startedAt = Date.now();
172
233
  const signals2 = new Array();
173
- const connectorName = await resolveSignalsConnectorName(flags.connector);
174
- const connectorFactory = await (0, import_connectors2.getConnectorCreatorByName)(
175
- connectorName,
176
- projectRoot
177
- );
178
- if (!connectorFactory) {
179
- throw new Error(`Connector "${connectorName}" is not registered`);
180
- }
181
- const marketConnector = await connectorFactory({
182
- userName: flags.user
183
- });
184
- let btcBinanceConnector = marketConnector;
185
- let btcCoinbaseConnector = marketConnector;
186
- if (connectorName.toLowerCase() === import_connectors2.DEFAULT_CONNECTOR_NAME.toLowerCase()) {
187
- const binanceFactory = await (0, import_connectors2.getConnectorCreatorByName)(
188
- import_connectors.ConnectorNames.Binance,
234
+ let status = "completed";
235
+ try {
236
+ const connectorName = await resolveSignalsConnectorName(flags.connector);
237
+ const connectorFactory = await (0, import_connectors2.getConnectorCreatorByName)(
238
+ connectorName,
189
239
  projectRoot
190
240
  );
191
- if (binanceFactory) {
192
- btcBinanceConnector = await binanceFactory({
193
- userName: flags.user
194
- });
195
- } else {
196
- import_logger.logger.warn(
197
- "Binance connector is unavailable. Reusing %s.",
198
- connectorName
241
+ if (!connectorFactory) {
242
+ throw new Error(`Connector "${connectorName}" is not registered`);
243
+ }
244
+ const marketConnector = await connectorFactory({
245
+ userName: flags.user
246
+ });
247
+ let btcBinanceConnector = marketConnector;
248
+ let btcCoinbaseConnector = marketConnector;
249
+ if (connectorName.toLowerCase() === import_connectors2.DEFAULT_CONNECTOR_NAME.toLowerCase()) {
250
+ const binanceFactory = await (0, import_connectors2.getConnectorCreatorByName)(
251
+ import_connectors.ConnectorNames.Binance,
252
+ projectRoot
253
+ );
254
+ if (binanceFactory) {
255
+ btcBinanceConnector = await binanceFactory({
256
+ userName: flags.user
257
+ });
258
+ } else {
259
+ import_logger.logger.warn(
260
+ "Binance connector is unavailable. Reusing %s.",
261
+ connectorName
262
+ );
263
+ }
264
+ const coinbaseFactory = await (0, import_connectors2.getConnectorCreatorByName)(
265
+ import_connectors.ConnectorNames.Coinbase,
266
+ projectRoot
199
267
  );
268
+ if (coinbaseFactory) {
269
+ btcCoinbaseConnector = await coinbaseFactory({
270
+ userName: flags.user
271
+ });
272
+ } else {
273
+ import_logger.logger.warn(
274
+ "Coinbase connector is unavailable. Reusing %s.",
275
+ connectorName
276
+ );
277
+ }
200
278
  }
201
- const coinbaseFactory = await (0, import_connectors2.getConnectorCreatorByName)(
202
- import_connectors.ConnectorNames.Coinbase,
203
- projectRoot
279
+ const tickers = await (0, import_cli.getTickers)(
280
+ marketConnector,
281
+ flags.tickers,
282
+ flags.exclude,
283
+ flags.tickersLimit,
284
+ flags.chunk
204
285
  );
205
- if (coinbaseFactory) {
206
- btcCoinbaseConnector = await coinbaseFactory({
207
- userName: flags.user
208
- });
209
- } else {
210
- import_logger.logger.warn(
211
- "Coinbase connector is unavailable. Reusing %s.",
212
- connectorName
286
+ if (flags.showTickersList) {
287
+ console.log(import_chalk.default.gray(JSON.stringify(tickers.sort(), null, 2)));
288
+ return;
289
+ }
290
+ if (!flags.cacheOnly) {
291
+ await (0, import_cli.update)(
292
+ marketConnector,
293
+ interval,
294
+ tickers,
295
+ import_constants.SIGNALS_CLI_PRELOAD_DAYS
213
296
  );
297
+ if (btcBinanceConnector !== marketConnector) {
298
+ await (0, import_cli.update)(
299
+ btcBinanceConnector,
300
+ interval,
301
+ ["BTCUSDT"],
302
+ import_constants.SIGNALS_CLI_PRELOAD_DAYS
303
+ );
304
+ }
305
+ if (btcCoinbaseConnector !== marketConnector) {
306
+ await (0, import_cli.update)(
307
+ btcCoinbaseConnector,
308
+ interval,
309
+ ["BTCUSDT"],
310
+ import_constants.SIGNALS_CLI_PRELOAD_DAYS
311
+ );
312
+ }
214
313
  }
215
- }
216
- const tickers = await (0, import_cli.getTickers)(
217
- marketConnector,
218
- flags.tickers,
219
- flags.exclude,
220
- flags.tickersLimit,
221
- flags.chunk
222
- );
223
- if (flags.showTickersList) {
224
- console.log(import_chalk.default.gray(JSON.stringify(tickers.sort(), null, 2)));
225
- return;
226
- }
227
- if (!flags.cacheOnly) {
228
- await (0, import_cli.update)(marketConnector, interval, tickers);
229
- if (btcBinanceConnector !== marketConnector) {
230
- await (0, import_cli.update)(btcBinanceConnector, interval, ["BTCUSDT"]);
314
+ const currentTimestamp = (0, import_time.getTimestamp)();
315
+ const [btcBinanceData, btcCoinbaseData] = await Promise.all([
316
+ btcBinanceConnector.kline({
317
+ symbol: "BTCUSDT",
318
+ start: PRELOAD_START,
319
+ end: currentTimestamp,
320
+ cacheOnly: true,
321
+ interval
322
+ }),
323
+ btcCoinbaseConnector.kline({
324
+ symbol: "BTCUSDT",
325
+ start: PRELOAD_START,
326
+ end: currentTimestamp,
327
+ cacheOnly: true,
328
+ interval
329
+ })
330
+ ]);
331
+ if (flags.updateOnly) {
332
+ return;
231
333
  }
232
- if (btcCoinbaseConnector !== marketConnector) {
233
- await (0, import_cli.update)(btcCoinbaseConnector, interval, ["BTCUSDT"]);
334
+ const runtimeStrategies = await loadRuntimeStrategies(flags.user);
335
+ if (!runtimeStrategies.length) {
336
+ import_logger.logger.warn(
337
+ "No strategy configs found by users:%s:strategies:*:config",
338
+ flags.user
339
+ );
340
+ return;
234
341
  }
235
- }
236
- const currentTimestamp = (0, import_time.getTimestamp)();
237
- const [btcBinanceData, btcCoinbaseData] = await Promise.all([
238
- btcBinanceConnector.kline({
239
- symbol: "BTCUSDT",
240
- start: PRELOAD_START,
241
- end: currentTimestamp,
242
- cacheOnly: true,
243
- interval
244
- }),
245
- btcCoinbaseConnector.kline({
246
- symbol: "BTCUSDT",
247
- start: PRELOAD_START,
248
- end: currentTimestamp,
249
- cacheOnly: true,
250
- interval
251
- })
252
- ]);
253
- if (flags.updateOnly) {
254
- return;
255
- }
256
- const runtimeStrategies = await loadRuntimeStrategies(flags.user);
257
- if (!runtimeStrategies.length) {
258
- import_logger.logger.warn(
259
- "No strategy configs found by users:%s:strategies:*:config",
260
- flags.user
342
+ import_logger.logger.info(
343
+ import_chalk.default.yellow(
344
+ `loaded strategies: ${runtimeStrategies.map((s) => s.strategyName).join(", ")}`
345
+ )
261
346
  );
262
- return;
263
- }
264
- import_logger.logger.info(
265
- import_chalk.default.yellow(
266
- `loaded strategies: ${runtimeStrategies.map((s) => s.strategyName).join(", ")}`
267
- )
268
- );
269
- const bar = new import_progress.default(
270
- ":current/:total [:bar][:percent] :found :eta(s) :symbol",
271
- {
272
- total: tickers.length,
273
- width: 30
347
+ const strategyStats = createStrategySkipStats(runtimeStrategies);
348
+ const bar = new import_progress.default(
349
+ ":current/:total [:bar][:percent] :found :eta(s) :symbol",
350
+ {
351
+ total: tickers.length,
352
+ width: 30
353
+ }
354
+ );
355
+ import_logger.logger.info(import_chalk.default.yellow(`tickers: ${tickers.length}`));
356
+ await (0, import_async.runWithConcurrency)(tickers, 5, async (symbol) => {
357
+ const strategySignals = await findSignals(
358
+ symbol,
359
+ marketConnector,
360
+ btcBinanceData,
361
+ btcCoinbaseData,
362
+ runtimeStrategies,
363
+ strategyStats
364
+ );
365
+ if (strategySignals.length > 0) {
366
+ signals2.push(...strategySignals);
367
+ }
368
+ bar.tick(1, {
369
+ found: import_chalk.default.cyan(signals2.length),
370
+ symbol: import_chalk.default.gray(symbol)
371
+ });
372
+ });
373
+ if (!flags.skipScreenshots) {
374
+ await (0, import_cli.makeScreenshots)(signals2, "15", flags.user);
274
375
  }
275
- );
276
- import_logger.logger.info(import_chalk.default.yellow(`tickers: ${tickers.length}`));
277
- await (0, import_async.runWithConcurrency)(tickers, 5, async (symbol) => {
278
- const signal = await findSignals(
279
- symbol,
280
- marketConnector,
281
- btcBinanceData,
282
- btcCoinbaseData,
283
- runtimeStrategies
376
+ if (flags.notify) {
377
+ await (0, import_cli.sendToTG)(signals2, "15", flags.user);
378
+ }
379
+ import_logger.logger.info(
380
+ JSON.stringify(
381
+ signals2.map((s) => s.symbol),
382
+ null,
383
+ 2
384
+ )
284
385
  );
285
- if (signal) {
286
- signals2.push(signal);
386
+ if (flags.showSkipStats) {
387
+ logStrategySkipStats(runtimeStrategies, strategyStats);
287
388
  }
288
- bar.tick(1, {
289
- found: import_chalk.default.cyan(signals2.length),
290
- symbol: import_chalk.default.gray(symbol)
291
- });
292
- });
293
- if (!flags.skipScreenshots) {
294
- await (0, import_cli.makeScreenshots)(signals2, "15");
295
- await (0, import_cli.makeScreenshots)(signals2, "60");
296
- }
297
- if (flags.notify) {
298
- await (0, import_cli.sendToTG)(signals2, "15");
389
+ } catch (error) {
390
+ status = "failed";
391
+ import_logger.logger.error(
392
+ "signals failed: %s",
393
+ error?.message || String(error)
394
+ );
395
+ throw error;
396
+ } finally {
397
+ import_logger.logger.info(
398
+ import_chalk.default.yellow(
399
+ `signals ${status} in ${formatElapsed(startedAt)} (found=${signals2.length})`
400
+ )
401
+ );
299
402
  }
300
- import_logger.logger.info(
301
- JSON.stringify(
302
- signals2.map((s) => s.symbol),
303
- null,
304
- 2
305
- )
306
- );
307
- process.exit();
308
403
  };
309
- signals();
404
+ if (process.env.NODE_ENV !== "test") {
405
+ signals().then(() => {
406
+ process.exit(0);
407
+ }).catch(() => {
408
+ process.exit(1);
409
+ });
410
+ }
411
+ // Annotate the CommonJS export names for ESM import in node:
412
+ 0 && (module.exports = {
413
+ signals
414
+ });
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/scripts/spawnSiblingScript.ts
31
+ var spawnSiblingScript_exports = {};
32
+ __export(spawnSiblingScript_exports, {
33
+ spawnSiblingScript: () => spawnSiblingScript
34
+ });
35
+ module.exports = __toCommonJS(spawnSiblingScript_exports);
36
+ var import_fs = __toESM(require("fs"));
37
+ var import_path = __toESM(require("path"));
38
+ var import_child_process = require("child_process");
39
+ var spawnSiblingScript = (currentDir, scriptBaseName, extraArgs) => {
40
+ const searchDirs = [currentDir, import_path.default.resolve(currentDir, "scripts")];
41
+ const candidates = searchDirs.flatMap((dir) => [
42
+ import_path.default.resolve(dir, `${scriptBaseName}.js`),
43
+ import_path.default.resolve(dir, `${scriptBaseName}.ts`)
44
+ ]);
45
+ const scriptPath = candidates.find((candidate) => import_fs.default.existsSync(candidate));
46
+ if (!scriptPath) {
47
+ throw new Error(
48
+ `Script "${scriptBaseName}" not found. Checked: ${candidates.join(", ")}`
49
+ );
50
+ }
51
+ const commandArgs = scriptPath.endsWith(".ts") ? [
52
+ "-r",
53
+ "ts-node/register",
54
+ "-r",
55
+ "tsconfig-paths/register",
56
+ scriptPath,
57
+ ...extraArgs
58
+ ] : [scriptPath, ...extraArgs];
59
+ const result = (0, import_child_process.spawnSync)(process.execPath, commandArgs, {
60
+ env: process.env,
61
+ stdio: "inherit"
62
+ });
63
+ if (result.error) {
64
+ throw result.error;
65
+ }
66
+ return result.status ?? 1;
67
+ };
68
+ // Annotate the CommonJS export names for ESM import in node:
69
+ 0 && (module.exports = {
70
+ spawnSiblingScript
71
+ });
@@ -2,6 +2,7 @@
2
2
 
3
3
  // src/workers/testerWorker.ts
4
4
  var import_backtest = require("@tradejs/node/backtest");
5
+ var import_ai = require("@tradejs/infra/ai");
5
6
  var import_logger = require("@tradejs/infra/logger");
6
7
  var import_ml = require("@tradejs/infra/ml");
7
8
  var import_redis = require("@tradejs/infra/redis");
@@ -23,10 +24,12 @@ process.on(
23
24
  if (!testResult) {
24
25
  throw new Error("No result");
25
26
  }
26
- const { stat, orderLogId } = testResult;
27
+ const { stat, orderLogId, inlineOrderLog, inlinePositionLog } = testResult;
27
28
  process.send?.({
28
29
  stat,
29
30
  orderLogId,
31
+ inlineOrderLog,
32
+ inlinePositionLog,
30
33
  test
31
34
  });
32
35
  } catch (error) {
@@ -36,6 +39,7 @@ process.on(
36
39
  process.send?.({
37
40
  error: true,
38
41
  id: test.name,
42
+ symbol: test.symbol,
39
43
  msg: {
40
44
  message: errorMessage,
41
45
  stack: errorStack
@@ -44,10 +48,17 @@ process.on(
44
48
  }
45
49
  }
46
50
  } finally {
51
+ await (0, import_ai.closeAllAiDatasetWriters)();
47
52
  await (0, import_ml.closeAllMlDatasetWriters)();
48
53
  (0, import_backtest.resetTestingKlineCache)();
49
54
  }
50
- process.send?.({ done: true });
55
+ if (process.send) {
56
+ process.send({ done: true }, () => {
57
+ process.disconnect?.();
58
+ process.exit(0);
59
+ });
60
+ return;
61
+ }
51
62
  process.disconnect?.();
52
63
  process.exit(0);
53
64
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tradejs/cli",
3
- "version": "1.0.4",
4
- "description": "Official TradeJS CLI for infra setup, backtests, signals, bots, and ML workflows.",
3
+ "version": "1.0.6",
4
+ "description": "Official CLI for the TradeJS open-source framework: infra setup, backtests, signals, bots, and ML workflows.",
5
5
  "keywords": [
6
6
  "tradejs",
7
7
  "trading",
@@ -12,6 +12,14 @@
12
12
  "automation"
13
13
  ],
14
14
  "homepage": "https://tradejs.dev",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/tradejs-dev/tradejs.git",
18
+ "directory": "packages/cli"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/tradejs-dev/tradejs/issues"
22
+ },
15
23
  "main": "dist/cli.js",
16
24
  "types": "dist/cli.d.ts",
17
25
  "files": [
@@ -21,14 +29,14 @@
21
29
  "tradejs": "dist/cli.js"
22
30
  },
23
31
  "dependencies": {
24
- "@tradejs/base": "^1.0.4",
25
- "@tradejs/connectors": "^1.0.4",
26
- "@tradejs/core": "^1.0.4",
27
- "@tradejs/indicators": "^1.0.4",
28
- "@tradejs/infra": "^1.0.4",
29
- "@tradejs/node": "^1.0.4",
30
- "@tradejs/strategies": "^1.0.4",
31
- "@tradejs/types": "^1.0.4",
32
+ "@tradejs/base": "^1.0.6",
33
+ "@tradejs/connectors": "^1.0.6",
34
+ "@tradejs/core": "^1.0.6",
35
+ "@tradejs/indicators": "^1.0.6",
36
+ "@tradejs/infra": "^1.0.6",
37
+ "@tradejs/node": "^1.0.6",
38
+ "@tradejs/strategies": "^1.0.6",
39
+ "@tradejs/types": "^1.0.6",
32
40
  "args": "^5.0.3",
33
41
  "bcryptjs": "^2.4.3",
34
42
  "chalk": "4.1.2",
@@ -55,6 +63,8 @@
55
63
  "clean-redis": "yarn run run:dist clean-redis",
56
64
  "clean-tests": "yarn run run:dist clean-tests",
57
65
  "doctor": "yarn run run:dist doctor",
66
+ "ai-export": "yarn run run:dist ai-export",
67
+ "ai-train": "yarn run run:dist ai-train",
58
68
  "backtest": "yarn run run:dist backtest",
59
69
  "bot": "yarn run run:dist bot",
60
70
  "signals": "yarn run run:dist signals",