@tradejs/cli 1.0.5 → 1.0.8

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.
Files changed (34) hide show
  1. package/README.md +4 -1
  2. package/dist/cli.js +13791 -7652
  3. package/dist/lib/aiTrainMetrics.js +173 -0
  4. package/dist/lib/candlesProviderMigration.js +266 -0
  5. package/dist/lib/cliArgs.js +40 -0
  6. package/dist/lib/derivativesContextBackfill.js +487 -0
  7. package/dist/lib/researchAgent.js +195 -0
  8. package/dist/lib/runtimeParity.js +234 -0
  9. package/dist/lib/telegramReports.js +91 -0
  10. package/dist/lib/timeWindow.js +101 -0
  11. package/dist/scripts/agentRun.js +895 -0
  12. package/dist/scripts/aiExport.js +192 -0
  13. package/dist/scripts/aiExportSelect.js +135 -0
  14. package/dist/scripts/aiTrain.js +2321 -0
  15. package/dist/scripts/backtest.js +998 -111
  16. package/dist/scripts/candlesMigrateProvider.js +5302 -0
  17. package/dist/scripts/continuity.js +1 -1
  18. package/dist/scripts/derivativesIngest.js +5 -0
  19. package/dist/scripts/derivativesIngestCoinalyzeAll.js +21 -12
  20. package/dist/scripts/migration.js +1 -1
  21. package/dist/scripts/mlExport.js +112 -13
  22. package/dist/scripts/mlExportSelect.js +43 -9
  23. package/dist/scripts/mlTrainLatestSelect.js +4 -3
  24. package/dist/scripts/researchAuto.js +845 -0
  25. package/dist/scripts/resolveExportStrategy.js +133 -0
  26. package/dist/scripts/results.js +46 -22
  27. package/dist/scripts/runtimeParity.js +1651 -0
  28. package/dist/scripts/selectStrategy.js +4 -3
  29. package/dist/scripts/signals.js +799 -62
  30. package/dist/scripts/signalsSummary.js +655 -0
  31. package/dist/scripts/spawnSiblingScript.js +71 -0
  32. package/dist/scripts/user-add.js +0 -9
  33. package/dist/workers/testerWorker.js +13 -2
  34. package/package.json +18 -12
@@ -160,7 +160,7 @@ var continuity = async () => {
160
160
  (0, import_time.formatUnix)(gap.ts),
161
161
  gap.diffSeconds
162
162
  );
163
- await (0, import_timescale.deleteCandles)(symbol, interval);
163
+ await (0, import_timescale.deleteCandles)(provider.id, symbol, interval);
164
164
  const reloaded = await connector.kline({
165
165
  symbol,
166
166
  interval: intervalKey,
@@ -28,6 +28,7 @@ var import_args = __toESM(require("args"));
28
28
  var import_chalk = __toESM(require("chalk"));
29
29
  var import_indicators = require("@tradejs/core/indicators");
30
30
  var import_timescale = require("@tradejs/infra/timescale");
31
+ var import_userSettings = require("@tradejs/infra/userSettings");
31
32
  var import_connectors = require("@tradejs/connectors");
32
33
  import_args.default.example(
33
34
  "yarn ts-node ./src/scripts/derivativesIngest --provider coinalyze --symbols BTCUSDT,ETHUSDT --intervals 15m,1h --days 120",
@@ -41,6 +42,7 @@ import_args.default.option(
41
42
  "Provider name: coinalyze | binance_coinbase_spread",
42
43
  "coinalyze"
43
44
  );
45
+ import_args.default.option(["U", "user"], "User settings profile name from Redis", "root");
44
46
  import_args.default.option(["b", "batchDays"], "Request chunk size in days", 7);
45
47
  var flags = import_args.default.parse(process.argv);
46
48
  var asInt = (value, fallback) => {
@@ -59,6 +61,8 @@ var run = async () => {
59
61
  );
60
62
  const days = asInt(flags.days, 120);
61
63
  const batchDays = asInt(flags.batchDays, 7);
64
+ const userName = String(flags.user || "root").trim() || "root";
65
+ const coinalyzeApiKey = providerName === "coinalyze" ? (await (0, import_userSettings.getUserSettings)(userName)).COINALYZE_API_KEY : void 0;
62
66
  if (!symbols.length) throw new Error("No symbols provided");
63
67
  if (!intervals.length) throw new Error("No intervals provided");
64
68
  await (0, import_timescale.waitForDbReady)();
@@ -76,6 +80,7 @@ var run = async () => {
76
80
  );
77
81
  const window = await provider.fetchWindow({
78
82
  symbol,
83
+ apiKey: coinalyzeApiKey,
79
84
  interval,
80
85
  fromMs: cursor,
81
86
  toMs
@@ -31,6 +31,7 @@ var import_lodash = __toESM(require("lodash"));
31
31
  var import_connectors = require("@tradejs/connectors");
32
32
  var import_indicators = require("@tradejs/core/indicators");
33
33
  var import_async = require("@tradejs/core/async");
34
+ var import_userSettings = require("@tradejs/infra/userSettings");
34
35
  var import_cli = require("@tradejs/node/cli");
35
36
  var import_timescale = require("@tradejs/infra/timescale");
36
37
  var coinalyzeIntervalMap = {
@@ -41,7 +42,7 @@ import_args.default.example(
41
42
  "yarn ts-node ./src/scripts/derivativesIngestCoinalyzeAll --days 120 --intervals 15m,1h",
42
43
  "Fetch derivatives for all getTickers symbols matched to Coinalyze markets"
43
44
  );
44
- import_args.default.option(["U", "user"], "Bybit user config name from Redis", "root");
45
+ import_args.default.option(["U", "user"], "User settings profile name from Redis", "root");
45
46
  import_args.default.option(["t", "tickers"], "Comma-separated include symbols");
46
47
  import_args.default.option(["e", "exclude"], "Comma-separated exclude symbols");
47
48
  import_args.default.option(["l", "tickersLimit"], "Tickers limit");
@@ -76,14 +77,10 @@ var asInt = (value, fallback) => {
76
77
  return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
77
78
  };
78
79
  var parseList = (value) => String(value ?? "").split(",").map((item) => item.trim().toUpperCase()).filter(Boolean);
79
- var apiKey = process.env.COINALYZE_API_KEY?.trim();
80
- if (!apiKey) {
81
- throw new Error("Missing COINALYZE_API_KEY");
82
- }
83
80
  var coinalyzeBaseUrl = process.env.COINALYZE_BASE_URL?.trim() || "https://api.coinalyze.net/v1";
84
81
  var coinalyzeMaxRetries = asInt(process.env.COINALYZE_MAX_RETRIES, 4);
85
82
  var lastRequestTs = 0;
86
- var fetchJsonWithRateLimit = async (url) => {
83
+ var fetchJsonWithRateLimit = async (url, apiKey) => {
87
84
  const requestDelayMs = Math.max(100, asInt(flags.requestDelayMs, 200));
88
85
  const requestTimeoutMs = Math.max(
89
86
  5e3,
@@ -135,9 +132,10 @@ var fetchJsonWithRateLimit = async (url) => {
135
132
  }
136
133
  throw new Error("Coinalyze request failed after retries");
137
134
  };
138
- var fetchCoinalyzeMarkets = async () => {
135
+ var fetchCoinalyzeMarkets = async (apiKey) => {
139
136
  const raw = await fetchJsonWithRateLimit(
140
- `${coinalyzeBaseUrl}/future-markets`
137
+ `${coinalyzeBaseUrl}/future-markets`,
138
+ apiKey
141
139
  );
142
140
  return Array.isArray(raw) ? raw : [];
143
141
  };
@@ -220,16 +218,17 @@ var toSeriesMap = (raw, metric) => {
220
218
  return out;
221
219
  };
222
220
  var fetchMetricBatch = async (params) => {
223
- const { endpoint, metric, marketSymbols, interval, fromMs, toMs } = params;
221
+ const { endpoint, metric, marketSymbols, apiKey, interval, fromMs, toMs } = params;
224
222
  const url = new URL(`${coinalyzeBaseUrl}${endpoint}`);
225
223
  url.searchParams.set("symbols", marketSymbols.join(","));
226
224
  url.searchParams.set("interval", coinalyzeIntervalMap[interval] || interval);
227
225
  url.searchParams.set("from", String(Math.floor(fromMs / 1e3)));
228
226
  url.searchParams.set("to", String(Math.floor(toMs / 1e3)));
229
- const raw = await fetchJsonWithRateLimit(url.toString());
227
+ const raw = await fetchJsonWithRateLimit(url.toString(), apiKey);
230
228
  return toSeriesMap(raw, metric);
231
229
  };
232
230
  var run = async () => {
231
+ const userName = String(flags.user || "root").trim() || "root";
233
232
  const intervals = (0, import_indicators.normalizeDerivativesIntervals)(
234
233
  flags.intervals
235
234
  );
@@ -239,9 +238,16 @@ var run = async () => {
239
238
  const tickersLimit = flags.tickersLimit !== void 0 ? asInt(flags.tickersLimit, 0) || void 0 : void 0;
240
239
  const exchangePriority = parseList(flags.exchangePriority || "A,6,0");
241
240
  if (!intervals.length) throw new Error("No intervals provided");
241
+ const settings = await (0, import_userSettings.getUserSettings)(userName);
242
+ const coinalyzeApiKey = settings.COINALYZE_API_KEY.trim();
243
+ if (!coinalyzeApiKey) {
244
+ throw new Error(
245
+ `Missing COINALYZE_API_KEY in user settings for user ${userName}`
246
+ );
247
+ }
242
248
  const connectorFactory = import_connectors.connectors[import_connectors.ConnectorNames.ByBit];
243
249
  const bybit = await connectorFactory({
244
- userName: String(flags.user || "root")
250
+ userName
245
251
  });
246
252
  const tickers = await (0, import_cli.getTickers)(
247
253
  bybit,
@@ -253,7 +259,7 @@ var run = async () => {
253
259
  if (!tickers.length) {
254
260
  throw new Error("No tickers loaded via getTickers");
255
261
  }
256
- const markets = await fetchCoinalyzeMarkets();
262
+ const markets = await fetchCoinalyzeMarkets(coinalyzeApiKey);
257
263
  if (!markets.length) {
258
264
  throw new Error("No markets returned by Coinalyze /future-markets");
259
265
  }
@@ -321,6 +327,7 @@ var run = async () => {
321
327
  endpoint: oiPath,
322
328
  metric: "oi",
323
329
  marketSymbols,
330
+ apiKey: coinalyzeApiKey,
324
331
  interval,
325
332
  fromMs: cursor,
326
333
  toMs
@@ -329,6 +336,7 @@ var run = async () => {
329
336
  endpoint: fundingPath,
330
337
  metric: "funding",
331
338
  marketSymbols,
339
+ apiKey: coinalyzeApiKey,
332
340
  interval,
333
341
  fromMs: cursor,
334
342
  toMs
@@ -337,6 +345,7 @@ var run = async () => {
337
345
  endpoint: liqPath,
338
346
  metric: "liq",
339
347
  marketSymbols,
348
+ apiKey: coinalyzeApiKey,
340
349
  interval,
341
350
  fromMs: cursor,
342
351
  toMs
@@ -43,7 +43,7 @@ var migrateFile = async (file) => {
43
43
  [],
44
44
  projectRoot
45
45
  );
46
- const rows = (0, import_timescale.toRows)(symbol, interval, data);
46
+ const rows = (0, import_timescale.toRows)("bybit", symbol, interval, data);
47
47
  for (let i = 0; i < rows.length; i += BATCH) {
48
48
  await (0, import_timescale.upsertCandles)(rows.slice(i, i + BATCH));
49
49
  }
@@ -24,10 +24,106 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
 
25
25
  // src/scripts/mlExport.ts
26
26
  var import_args = __toESM(require("args"));
27
- var import_chalk = __toESM(require("chalk"));
27
+ var import_chalk2 = __toESM(require("chalk"));
28
28
  var import_promises = __toESM(require("fs/promises"));
29
29
  var import_path = __toESM(require("path"));
30
30
  var import_ml = require("@tradejs/infra/ml");
31
+
32
+ // src/scripts/selectStrategy.ts
33
+ var import_readline = __toESM(require("readline"));
34
+ var import_chalk = __toESM(require("chalk"));
35
+ var import_strategies = require("@tradejs/node/strategies");
36
+ var defaultStrategy = "TrendLine";
37
+ var projectRoot = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
38
+ var getStrategyChoices = async () => {
39
+ try {
40
+ const loaded = await (0, import_strategies.getAvailableStrategyNames)(projectRoot);
41
+ if (loaded.length) {
42
+ return loaded;
43
+ }
44
+ } catch (error) {
45
+ console.warn(`Failed to load strategy list: ${String(error)}`);
46
+ }
47
+ return [
48
+ "Breakout",
49
+ "MaStrategy",
50
+ "AdaptiveMomentumRibbon",
51
+ "TrendLine",
52
+ "VolumeDivergence"
53
+ ];
54
+ };
55
+ var selectStrategy = async (promptLabel = "Select strategy", options) => {
56
+ const strategies = options?.strategies && options.strategies.length ? options.strategies : await getStrategyChoices();
57
+ const preferredDefault = options?.defaultStrategy || defaultStrategy;
58
+ const fallbackStrategy = strategies.includes(preferredDefault) ? preferredDefault : strategies[0];
59
+ if (!process.stdin.isTTY) {
60
+ return fallbackStrategy;
61
+ }
62
+ console.log(import_chalk.default.cyan("Available strategies:"));
63
+ strategies.forEach((name, index) => {
64
+ const isDefault = name === defaultStrategy;
65
+ const label = isDefault ? import_chalk.default.green(name) : name;
66
+ const suffix = isDefault ? import_chalk.default.gray(" (default)") : "";
67
+ console.log(` ${import_chalk.default.yellow(String(index + 1))}) ${label}${suffix}`);
68
+ });
69
+ const rl = import_readline.default.createInterface({
70
+ input: process.stdin,
71
+ output: process.stdout
72
+ });
73
+ const question = (text) => new Promise((resolve) => rl.question(text, resolve));
74
+ const answer = await question(
75
+ `${promptLabel} [${import_chalk.default.green(fallbackStrategy)}]: `
76
+ );
77
+ rl.close();
78
+ const trimmed = answer.trim();
79
+ if (!trimmed) {
80
+ return fallbackStrategy;
81
+ }
82
+ const asNumber = Number(trimmed);
83
+ if (Number.isFinite(asNumber) && asNumber >= 1 && asNumber <= strategies.length) {
84
+ return strategies[asNumber - 1];
85
+ }
86
+ const byName = strategies.find(
87
+ (name) => name.toLowerCase() === trimmed.toLowerCase()
88
+ );
89
+ if (byName) {
90
+ return byName;
91
+ }
92
+ console.warn(`Unknown strategy "${trimmed}", using ${fallbackStrategy}.`);
93
+ return fallbackStrategy;
94
+ };
95
+
96
+ // src/scripts/resolveExportStrategy.ts
97
+ var resolveExportStrategy = async ({
98
+ explicitStrategy,
99
+ outDir,
100
+ datasetLabel,
101
+ promptLabel,
102
+ listStrategies
103
+ }) => {
104
+ const raw = String(explicitStrategy || "").trim();
105
+ if (raw) {
106
+ return raw;
107
+ }
108
+ const availableStrategies = await listStrategies({ outDir });
109
+ if (!availableStrategies.length) {
110
+ return null;
111
+ }
112
+ if (availableStrategies.length === 1) {
113
+ return availableStrategies[0];
114
+ }
115
+ if (!process.stdin.isTTY) {
116
+ throw new Error(
117
+ `Multiple ${datasetLabel} chunk strategies found in ${outDir}: ${availableStrategies.join(", ")}. Pass --strategy.`
118
+ );
119
+ }
120
+ return selectStrategy(promptLabel, {
121
+ strategies: availableStrategies,
122
+ defaultStrategy: availableStrategies[0]
123
+ });
124
+ };
125
+
126
+ // src/scripts/mlExport.ts
31
127
  import_args.default.example(
32
128
  "yarn ts-node ./src/scripts/mlExport --strategy trendline",
33
129
  "Merge per-worker ML chunks into one dataset JSONL"
@@ -45,21 +141,24 @@ var listChunkFiles = async (outDir, strategyName) => {
45
141
  const entries = await import_promises.default.readdir(outDir, { withFileTypes: true });
46
142
  return entries.filter((entry) => entry.isFile()).map((entry) => entry.name).filter((name) => name.startsWith(prefix) && name.endsWith(".jsonl")).map((name) => import_path.default.join(outDir, name)).sort();
47
143
  };
48
- var resolveStrategyName = async () => {
49
- const raw = String(flags.strategy || "").trim();
50
- if (raw) {
51
- return raw;
52
- }
53
- throw new Error("Missing --strategy. Use mlExportSelect or pass --strategy.");
54
- };
55
144
  var main = async () => {
56
145
  const outDir = String(flags.outDir || "data/ml/export");
57
- const strategyName = await resolveStrategyName();
58
146
  await import_promises.default.mkdir(outDir, { recursive: true });
147
+ const strategyName = await resolveExportStrategy({
148
+ explicitStrategy: String(flags.strategy || ""),
149
+ outDir,
150
+ datasetLabel: "ML",
151
+ promptLabel: "Select ML export strategy",
152
+ listStrategies: import_ml.listMlChunkStrategies
153
+ });
154
+ if (!strategyName) {
155
+ console.log(import_chalk2.default.yellow(`No ML chunk files found in ${outDir}`));
156
+ process.exit(0);
157
+ }
59
158
  const chunkFiles = await listChunkFiles(outDir, strategyName);
60
159
  if (!chunkFiles.length) {
61
160
  console.log(
62
- import_chalk.default.yellow(
161
+ import_chalk2.default.yellow(
63
162
  `No chunk files found for strategy "${strategyName}" in ${outDir}`
64
163
  )
65
164
  );
@@ -79,9 +178,9 @@ var main = async () => {
79
178
  await import_promises.default.rm(filePath, { force: true });
80
179
  }
81
180
  }
82
- console.log(import_chalk.default.green(`Merged dataset saved: ${mergedPath}`));
181
+ console.log(import_chalk2.default.green(`Merged dataset saved: ${mergedPath}`));
83
182
  console.log(
84
- import_chalk.default.gray(
183
+ import_chalk2.default.gray(
85
184
  `strategy=${strategyName}, source_chunks=${chunkFiles.length}, deleteChunks=${Boolean(
86
185
  shouldDeleteChunks
87
186
  )}`
@@ -90,6 +189,6 @@ var main = async () => {
90
189
  process.exit(0);
91
190
  };
92
191
  main().catch((error) => {
93
- console.error(import_chalk.default.red(error?.message || String(error)));
192
+ console.error(import_chalk2.default.red(error?.message || String(error)));
94
193
  process.exit(1);
95
194
  });
@@ -22,9 +22,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
22
22
  mod
23
23
  ));
24
24
 
25
- // src/scripts/mlExportSelect.ts
26
- var import_child_process = require("child_process");
27
-
28
25
  // src/scripts/selectStrategy.ts
29
26
  var import_readline = __toESM(require("readline"));
30
27
  var import_chalk = __toESM(require("chalk"));
@@ -48,9 +45,10 @@ var getStrategyChoices = async () => {
48
45
  "VolumeDivergence"
49
46
  ];
50
47
  };
51
- var selectStrategy = async (promptLabel = "Select strategy") => {
52
- const strategies = await getStrategyChoices();
53
- const fallbackStrategy = strategies.includes(defaultStrategy) ? defaultStrategy : strategies[0];
48
+ var selectStrategy = async (promptLabel = "Select strategy", options) => {
49
+ const strategies = options?.strategies && options.strategies.length ? options.strategies : await getStrategyChoices();
50
+ const preferredDefault = options?.defaultStrategy || defaultStrategy;
51
+ const fallbackStrategy = strategies.includes(preferredDefault) ? preferredDefault : strategies[0];
54
52
  if (!process.stdin.isTTY) {
55
53
  return fallbackStrategy;
56
54
  }
@@ -88,12 +86,48 @@ var selectStrategy = async (promptLabel = "Select strategy") => {
88
86
  return fallbackStrategy;
89
87
  };
90
88
 
89
+ // src/scripts/spawnSiblingScript.ts
90
+ var import_fs = __toESM(require("fs"));
91
+ var import_path = __toESM(require("path"));
92
+ var import_child_process = require("child_process");
93
+ var spawnSiblingScript = (currentDir, scriptBaseName, extraArgs) => {
94
+ const searchDirs = [currentDir, import_path.default.resolve(currentDir, "scripts")];
95
+ const candidates = searchDirs.flatMap((dir) => [
96
+ import_path.default.resolve(dir, `${scriptBaseName}.js`),
97
+ import_path.default.resolve(dir, `${scriptBaseName}.ts`)
98
+ ]);
99
+ const scriptPath = candidates.find((candidate) => import_fs.default.existsSync(candidate));
100
+ if (!scriptPath) {
101
+ throw new Error(
102
+ `Script "${scriptBaseName}" not found. Checked: ${candidates.join(", ")}`
103
+ );
104
+ }
105
+ const commandArgs = scriptPath.endsWith(".ts") ? [
106
+ "-r",
107
+ "ts-node/register",
108
+ "-r",
109
+ "tsconfig-paths/register",
110
+ scriptPath,
111
+ ...extraArgs
112
+ ] : [scriptPath, ...extraArgs];
113
+ const result = (0, import_child_process.spawnSync)(process.execPath, commandArgs, {
114
+ env: process.env,
115
+ stdio: "inherit"
116
+ });
117
+ if (result.error) {
118
+ throw result.error;
119
+ }
120
+ return result.status ?? 1;
121
+ };
122
+
91
123
  // src/scripts/mlExportSelect.ts
92
124
  var run = async () => {
93
125
  const selected = await selectStrategy();
94
- const args = ["ts-node", "./src/scripts/mlExport", "--strategy", selected];
95
- const result = (0, import_child_process.spawnSync)("yarn", args, { stdio: "inherit" });
96
- process.exit(result.status ?? 1);
126
+ const status = spawnSiblingScript(__dirname, "mlExport", [
127
+ "--strategy",
128
+ selected
129
+ ]);
130
+ process.exit(status);
97
131
  };
98
132
  run().catch((err) => {
99
133
  console.error("Failed to select strategy:", err);
@@ -56,9 +56,10 @@ var getStrategyChoices = async () => {
56
56
  "VolumeDivergence"
57
57
  ];
58
58
  };
59
- var selectStrategy = async (promptLabel = "Select strategy") => {
60
- const strategies = await getStrategyChoices();
61
- const fallbackStrategy = strategies.includes(defaultStrategy) ? defaultStrategy : strategies[0];
59
+ var selectStrategy = async (promptLabel = "Select strategy", options) => {
60
+ const strategies = options?.strategies && options.strategies.length ? options.strategies : await getStrategyChoices();
61
+ const preferredDefault = options?.defaultStrategy || defaultStrategy;
62
+ const fallbackStrategy = strategies.includes(preferredDefault) ? preferredDefault : strategies[0];
62
63
  if (!process.stdin.isTTY) {
63
64
  return fallbackStrategy;
64
65
  }