@tradejs/infra 1.0.8 → 1.0.10

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.
@@ -12,9 +12,12 @@ var logger = {
12
12
  };
13
13
  var redisConnectionWarningShown = false;
14
14
  var redisUnavailable = false;
15
- var isRedisConnectivityError = (error) => /ECONNREFUSED|ENOTFOUND|EAI_AGAIN|ETIMEDOUT|MaxRetriesPerRequestError|Connection is closed|Stream isn't writeable/i.test(
16
- error.message
17
- );
15
+ var isRedisConnectivityError = (error) => {
16
+ const errorText = [error.name, error.message, String(error)].join(" ");
17
+ return /ECONNREFUSED|ECONNRESET|ECONNABORTED|EPIPE|ENOTFOUND|EAI_AGAIN|ETIMEDOUT|MaxRetriesPerRequestError|Connection is closed|Stream isn't writeable/i.test(
18
+ errorText
19
+ );
20
+ };
18
21
  var toNonNegativeInt = (value, fallback) => {
19
22
  const parsed = Number.parseInt(String(value ?? ""), 10);
20
23
  return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
@@ -70,6 +73,16 @@ var getRedis = () => {
70
73
  }
71
74
  return global.__redis__;
72
75
  };
76
+ var closeRedisConnection = async () => {
77
+ const redis = global.__redis__;
78
+ if (!redis) {
79
+ return;
80
+ }
81
+ global.__redis__ = void 0;
82
+ redisUnavailable = false;
83
+ redisConnectionWarningShown = false;
84
+ redis.disconnect();
85
+ };
73
86
  var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
74
87
  var getRedisStatus = (redis) => String(redis.status ?? "ready");
75
88
  var waitForRedisReady = async (redis) => {
@@ -110,6 +123,11 @@ var toResultString = (result) => {
110
123
  if (Buffer.isBuffer(result)) return result.toString("utf8");
111
124
  return String(result);
112
125
  };
126
+ var publishData = async (channel, value) => {
127
+ const redis = await getReadyRedis();
128
+ if (!redis) return 0;
129
+ return redis.publish(channel, toJson(value));
130
+ };
113
131
  var DEFAULT_OPTIONS = {
114
132
  expire: TTL_1D
115
133
  };
@@ -122,6 +140,31 @@ var parseJsonOrDeleteKey = async (redis, key, raw, fallback) => {
122
140
  return fallback;
123
141
  }
124
142
  };
143
+ var parseHashMap = (value) => {
144
+ if (!value) {
145
+ return {};
146
+ }
147
+ if (Array.isArray(value)) {
148
+ const result = {};
149
+ for (let index = 0; index < value.length; index += 2) {
150
+ const field = value[index];
151
+ const fieldValue = value[index + 1];
152
+ if (field == null || fieldValue == null) {
153
+ continue;
154
+ }
155
+ result[String(field)] = toResultString(fieldValue) ?? "";
156
+ }
157
+ return result;
158
+ }
159
+ if (typeof value === "object") {
160
+ return Object.fromEntries(
161
+ Object.entries(value).map(
162
+ ([field, fieldValue]) => [field, toResultString(fieldValue) ?? ""]
163
+ )
164
+ );
165
+ }
166
+ return {};
167
+ };
125
168
  var getKeys = async (prefix) => {
126
169
  if (redisUnavailable) return [];
127
170
  const redis = await getReadyRedis();
@@ -258,6 +301,115 @@ var setData = async (key, data, options = {}) => {
258
301
  }
259
302
  }
260
303
  };
304
+ var setHashJsonField = async (key, field, data, options = {}) => {
305
+ if (redisUnavailable) return;
306
+ const { expire } = { ...DEFAULT_OPTIONS, ...options };
307
+ const redis = await getReadyRedis();
308
+ if (!redis) return;
309
+ try {
310
+ await redis.call("HSET", key, field, toJson(data));
311
+ if (expire) {
312
+ await redis.expire(key, expire);
313
+ }
314
+ } catch (e) {
315
+ if (e instanceof Error && isRedisConnectivityError(e)) {
316
+ markRedisUnavailable(e);
317
+ return;
318
+ }
319
+ logger.log("error", "failed HSET %s[%s]: %s", key, field, String(e));
320
+ }
321
+ };
322
+ var getHashJsonField = async (key, field, fallback = null) => {
323
+ if (redisUnavailable) return fallback;
324
+ const redis = await getReadyRedis();
325
+ if (!redis) return fallback;
326
+ try {
327
+ const value = await redis.call("HGET", key, field);
328
+ if (typeof value !== "string" || !value) {
329
+ return fallback;
330
+ }
331
+ return JSON.parse(value);
332
+ } catch (e) {
333
+ if (e instanceof Error && isRedisConnectivityError(e)) {
334
+ markRedisUnavailable(e);
335
+ return fallback;
336
+ }
337
+ logger.log("error", "failed HGET %s[%s]: %s", key, field, String(e));
338
+ return fallback;
339
+ }
340
+ };
341
+ var getHashJsonValues = async (key) => {
342
+ if (redisUnavailable) return [];
343
+ const redis = await getReadyRedis();
344
+ if (!redis) return [];
345
+ try {
346
+ const raw = await redis.call("HGETALL", key);
347
+ const values = Object.values(parseHashMap(raw));
348
+ const parsed = [];
349
+ for (const value of values) {
350
+ if (!value) {
351
+ continue;
352
+ }
353
+ try {
354
+ parsed.push(JSON.parse(value));
355
+ } catch (e) {
356
+ logger.log(
357
+ "error",
358
+ "failed JSON.parse(HGETALL %s value): %s",
359
+ key,
360
+ String(e)
361
+ );
362
+ }
363
+ }
364
+ return parsed;
365
+ } catch (e) {
366
+ if (e instanceof Error && isRedisConnectivityError(e)) {
367
+ markRedisUnavailable(e);
368
+ return [];
369
+ }
370
+ logger.log("error", "failed HGETALL %s: %s", key, String(e));
371
+ return [];
372
+ }
373
+ };
374
+ var incrHashFields = async (key, increments, options = {}) => {
375
+ if (redisUnavailable) return;
376
+ const { expire } = { ...DEFAULT_OPTIONS, ...options };
377
+ const redis = await getReadyRedis();
378
+ if (!redis) return;
379
+ try {
380
+ for (const [field, increment] of Object.entries(increments)) {
381
+ if (!Number.isFinite(increment) || increment === 0) {
382
+ continue;
383
+ }
384
+ await redis.call("HINCRBY", key, field, Math.trunc(increment));
385
+ }
386
+ if (expire) {
387
+ await redis.expire(key, expire);
388
+ }
389
+ } catch (e) {
390
+ if (e instanceof Error && isRedisConnectivityError(e)) {
391
+ markRedisUnavailable(e);
392
+ return;
393
+ }
394
+ logger.log("error", "failed HINCRBY %s: %s", key, String(e));
395
+ }
396
+ };
397
+ var getHashData = async (key) => {
398
+ if (redisUnavailable) return {};
399
+ const redis = await getReadyRedis();
400
+ if (!redis) return {};
401
+ try {
402
+ const raw = await redis.call("HGETALL", key);
403
+ return parseHashMap(raw);
404
+ } catch (e) {
405
+ if (e instanceof Error && isRedisConnectivityError(e)) {
406
+ markRedisUnavailable(e);
407
+ return {};
408
+ }
409
+ logger.log("error", "failed HGETALL %s: %s", key, String(e));
410
+ return {};
411
+ }
412
+ };
261
413
  var createScreenshotSessionToken = async (userName) => {
262
414
  const token = randomBytes(24).toString("hex");
263
415
  await setData(
@@ -291,13 +443,21 @@ var consumeScreenshotSessionToken = async (token) => {
291
443
  var redisKeys = {
292
444
  users: () => "users:index:",
293
445
  user: (userName) => `users:index:${userName}`,
446
+ tradingAccounts: (userName) => `users:${userName}:trading-accounts:`,
447
+ tradingAccount: (userName, accountId) => `users:${userName}:trading-accounts:${accountId}`,
448
+ runtimeDeployments: (userName) => `users:${userName}:runtime:deployments:`,
449
+ runtimeDeployment: (userName, deploymentId) => `users:${userName}:runtime:deployments:${deploymentId}`,
450
+ runtimeDeploymentHeartbeat: (userName, deploymentId) => `users:${userName}:runtime:deployments:${deploymentId}:heartbeat`,
294
451
  bots: (userName) => `users:${userName}:bots`,
295
452
  botsPrefix: () => "users:",
296
453
  bot: (userName, botId) => `users:${userName}:bots:${botId}`,
297
454
  backtestConfig: (userName, config) => `users:${userName}:backtests:configs:${config}`,
298
455
  strategies: (userName) => `users:${userName}:strategies`,
299
- strategyConfig: (userName, strategyName) => `users:${userName}:strategies:${strategyName}:config`,
456
+ strategyConfig: (userName, strategyName, configId = "config") => `users:${userName}:strategies:${strategyName}:${configId}`,
300
457
  strategyResults: (userName, strategyName) => `users:${userName}:strategies:${strategyName}:results`,
458
+ strategyCharts: (userName, mode) => `users:${userName}:strategies:charts:${mode}`,
459
+ strategyChartCards: (userName, mode) => `users:${userName}:strategies:charts:${mode}:cards:`,
460
+ strategyChartCard: (userName, mode, cardId) => `users:${userName}:strategies:charts:${mode}:cards:${cardId}`,
301
461
  tests: (userName, strategyName) => strategyName ? `users:${userName}:tests:${strategyName}` : `users:${userName}:tests:`,
302
462
  testOrders: (userName, strategyName, testName) => `users:${userName}:tests:${strategyName}:${testName}:orders`,
303
463
  testConfig: (userName, strategyName, testName) => `users:${userName}:tests:${strategyName}:${testName}:config`,
@@ -306,21 +466,34 @@ var redisKeys = {
306
466
  cacheChunk: (userName, chunkId) => `users:${userName}:cache:tests:chunks:${chunkId}`,
307
467
  cacheOrders: (userName, orderLogId) => `users:${userName}:cache:tests:orders:${orderLogId}`,
308
468
  cachePositions: (userName, orderLogId) => `users:${userName}:cache:tests:positions:${orderLogId}`,
469
+ tickerUniverse: (userName, connectorName, universe, accountId) => universe || accountId ? `users:${userName}:cache:tickers:${connectorName}:${universe ?? "crypto"}:${accountId ?? "default"}` : `users:${userName}:cache:tickers:${connectorName}`,
309
470
  signal: (symbol, signalId) => `signals:${symbol}:${signalId}`,
310
471
  signalsBySymbol: (symbol) => `signals:${symbol}:`,
311
472
  storeSignal: (symbol, signalId) => `store:signals:${symbol}:${signalId}`,
312
473
  runtimeSignals: (userName) => `users:${userName}:runtime:signals:`,
313
474
  runtimeSignal: (userName, signalId) => `users:${userName}:runtime:signals:${signalId}`,
475
+ runtimeSignalBuckets: (userName) => `users:${userName}:runtime:signals:days:`,
476
+ runtimeSignalBucket: (userName, dayKey, strategyName) => `users:${userName}:runtime:signals:days:${dayKey}:${strategyName}`,
314
477
  runtimeSignalEvaluations: (userName) => `users:${userName}:runtime:signal-evaluations:`,
315
478
  runtimeSignalEvaluation: (userName, evaluationId) => `users:${userName}:runtime:signal-evaluations:${evaluationId}`,
479
+ runtimeSignalEvaluationBuckets: (userName) => `users:${userName}:runtime:signal-evaluations:days:`,
480
+ runtimeSignalEvaluationBucket: (userName, dayKey, strategyName) => `users:${userName}:runtime:signal-evaluations:days:${dayKey}:${strategyName}`,
481
+ runtimeSignalEvaluationStatsBuckets: (userName) => `users:${userName}:runtime:signal-evaluation-stats:days:`,
482
+ runtimeSignalEvaluationStatsBucket: (userName, dayKey, strategyName) => `users:${userName}:runtime:signal-evaluation-stats:days:${dayKey}:${strategyName}`,
316
483
  runtimeTrades: (userName) => `users:${userName}:runtime:trade-records:`,
317
484
  runtimeTrade: (userName, orderId) => `users:${userName}:runtime:trade-records:${orderId}`,
485
+ runtimeTradeBuckets: (userName) => `users:${userName}:runtime:trade-records:days:`,
486
+ runtimeTradeBucket: (userName, dayKey) => `users:${userName}:runtime:trade-records:days:${dayKey}`,
318
487
  runtimeActiveTrades: (userName) => `users:${userName}:runtime:active-trades:`,
319
- runtimeActiveTrade: (userName, symbol) => `users:${userName}:runtime:active-trades:${symbol}`,
488
+ runtimeActiveTrade: (userName, symbol, scopeId) => scopeId ? `users:${userName}:runtime:active-trades:${scopeId}:${symbol}` : `users:${userName}:runtime:active-trades:${symbol}`,
320
489
  aiChatHistory: (userName, symbolKey) => `users:${userName}:ai:chats:${symbolKey}`,
321
490
  analysis: (symbol, signalId) => `analysis:${symbol}:${signalId}`,
322
491
  screenshotSessionToken: (token) => `auth:screenshot:${token}`,
323
492
  backtestResults: (userName, config, timestamp) => `users:${userName}:backtests:results:${config}:${timestamp}`,
493
+ backtestRuns: (userName) => `users:${userName}:backtests:runs:`,
494
+ backtestRun: (userName, runId) => `users:${userName}:backtests:runs:${runId}`,
495
+ backtestRunResults: (userName, runId) => `users:${userName}:backtests:runs:${runId}:results`,
496
+ backtestLatestRun: (userName, config) => `users:${userName}:backtests:latest:${config}`,
324
497
  researchRuns: (userName) => `users:${userName}:research:runs:`,
325
498
  researchRun: (userName, runId) => `users:${userName}:research:runs:${runId}`,
326
499
  researchLatestRun: (userName, strategyName) => `users:${userName}:research:latest:${strategyName}`,
@@ -333,12 +506,19 @@ var redisKeys = {
333
506
  };
334
507
 
335
508
  export {
509
+ closeRedisConnection,
510
+ publishData,
336
511
  getKeys,
337
512
  getData,
338
513
  delKey,
339
514
  RedisWriteBlockedError,
340
515
  delKeyWithOptions,
341
516
  setData,
517
+ setHashJsonField,
518
+ getHashJsonField,
519
+ getHashJsonValues,
520
+ incrHashFields,
521
+ getHashData,
342
522
  createScreenshotSessionToken,
343
523
  consumeScreenshotSessionToken,
344
524
  redisKeys
@@ -3,15 +3,26 @@ import { once } from "events";
3
3
  import { createReadStream, createWriteStream } from "fs";
4
4
  import fs from "fs/promises";
5
5
  import path from "path";
6
+ import readline from "readline";
6
7
  var DEFAULT_DIR = "data/ml/export";
7
8
  var ML_DATASET_WRITE_BATCH_SIZE = 200;
8
9
  var ML_CHUNK_FILE_RE = /^ml-dataset-(.+)-chunk-[^.]+\.jsonl$/;
10
+ var BACKTEST_RUN_CHUNK_ID_RE = /^(\d{12}-[a-f0-9]{8})-/;
9
11
  var writerByPath = /* @__PURE__ */ new Map();
10
12
  var toFileToken = (value) => value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "_").replace(/^_+|_+$/g, "") || "any";
11
13
  var getMlChunkFilePath = (strategyName, chunkId, outDir = DEFAULT_DIR) => path.join(
12
14
  outDir,
13
15
  `ml-dataset-${toFileToken(strategyName)}-chunk-${toFileToken(chunkId)}.jsonl`
14
16
  );
17
+ var getMlChunkFilePrefix = (strategyName, runId) => `ml-dataset-${toFileToken(strategyName)}-chunk-${runId ? `${toFileToken(runId)}-` : ""}`;
18
+ var getRunIdFromMlChunkFileName = (strategyName, fileName) => {
19
+ const prefix = getMlChunkFilePrefix(strategyName);
20
+ if (!fileName.startsWith(prefix) || !fileName.endsWith(".jsonl")) {
21
+ return "";
22
+ }
23
+ const chunkToken = fileName.slice(prefix.length, -".jsonl".length);
24
+ return chunkToken.match(BACKTEST_RUN_CHUNK_ID_RE)?.[1] ?? "";
25
+ };
15
26
  var appendMlDatasetRow = async (params) => {
16
27
  const { strategyName, chunkId, row, outDir = DEFAULT_DIR } = params;
17
28
  const filePath = getMlChunkFilePath(strategyName, chunkId, outDir);
@@ -85,8 +96,8 @@ var closeAllMlDatasetWriters = async () => {
85
96
  }
86
97
  };
87
98
  var listMlChunkFiles = async (params) => {
88
- const { strategyName, outDir = DEFAULT_DIR } = params;
89
- const prefix = `ml-dataset-${toFileToken(strategyName)}-chunk-`;
99
+ const { strategyName, outDir = DEFAULT_DIR, runId } = params;
100
+ const prefix = getMlChunkFilePrefix(strategyName, runId);
90
101
  let entries = [];
91
102
  try {
92
103
  entries = await fs.readdir(outDir);
@@ -95,6 +106,20 @@ var listMlChunkFiles = async (params) => {
95
106
  }
96
107
  return entries.filter((name) => name.startsWith(prefix) && name.endsWith(".jsonl")).map((name) => path.join(outDir, name)).sort();
97
108
  };
109
+ var listMlChunkRunIds = async (params) => {
110
+ const { strategyName, outDir = DEFAULT_DIR } = params;
111
+ let entries = [];
112
+ try {
113
+ entries = await fs.readdir(outDir);
114
+ } catch {
115
+ return [];
116
+ }
117
+ return [
118
+ ...new Set(
119
+ entries.map((name) => getRunIdFromMlChunkFileName(strategyName, name)).filter(Boolean)
120
+ )
121
+ ].sort();
122
+ };
98
123
  var listMlChunkStrategies = async (params) => {
99
124
  const outDir = params?.outDir ?? DEFAULT_DIR;
100
125
  let entries = [];
@@ -110,12 +135,37 @@ var listMlChunkStrategies = async (params) => {
110
135
  ].sort();
111
136
  };
112
137
  var mergeJsonlFiles = async (params) => {
113
- const { filePaths, outPath } = params;
138
+ const { filePaths, outPath, shouldIncludeRow } = params;
114
139
  await fs.mkdir(path.dirname(outPath), { recursive: true });
115
140
  const stream = createWriteStream(outPath, { encoding: "utf8" });
116
141
  const done = Promise.all([once(stream, "finish"), once(stream, "close")]);
117
142
  try {
118
143
  for (const filePath of filePaths) {
144
+ if (shouldIncludeRow) {
145
+ const reader2 = readline.createInterface({
146
+ input: createReadStream(filePath, { encoding: "utf8" }),
147
+ crlfDelay: Infinity
148
+ });
149
+ try {
150
+ for await (const line of reader2) {
151
+ const trimmed = line.trim();
152
+ if (!trimmed) {
153
+ continue;
154
+ }
155
+ const row = JSON.parse(trimmed);
156
+ if (!shouldIncludeRow(row)) {
157
+ continue;
158
+ }
159
+ if (!stream.write(`${trimmed}
160
+ `)) {
161
+ await once(stream, "drain");
162
+ }
163
+ }
164
+ } finally {
165
+ reader2.close();
166
+ }
167
+ continue;
168
+ }
119
169
  const reader = createReadStream(filePath, { encoding: "utf8" });
120
170
  for await (const chunk of reader) {
121
171
  if (!stream.write(chunk)) {
@@ -137,6 +187,7 @@ export {
137
187
  closeMlDatasetWriter,
138
188
  closeAllMlDatasetWriters,
139
189
  listMlChunkFiles,
190
+ listMlChunkRunIds,
140
191
  listMlChunkStrategies,
141
192
  mergeJsonlFiles
142
193
  };
package/dist/ml.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- export { a as appendMlDatasetRow, c as closeAllMlDatasetWriters, b as closeMlDatasetWriter, f as flushMlDatasetWriter, g as getMlChunkFilePath, l as listMlChunkFiles, d as listMlChunkStrategies, m as mergeJsonlFiles, t as toFileToken } from './mlDatasetFile-sRGWgR_o.mjs';
1
+ export { a as appendMlDatasetRow, c as closeAllMlDatasetWriters, b as closeMlDatasetWriter, f as flushMlDatasetWriter, g as getMlChunkFilePath, l as listMlChunkFiles, d as listMlChunkRunIds, e as listMlChunkStrategies, m as mergeJsonlFiles, t as toFileToken } from './mlDatasetFile-Czx__g9M.mjs';
2
2
 
3
3
  type MlPredictResponse = {
4
4
  probability: number;
package/dist/ml.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { a as appendMlDatasetRow, c as closeAllMlDatasetWriters, b as closeMlDatasetWriter, f as flushMlDatasetWriter, g as getMlChunkFilePath, l as listMlChunkFiles, d as listMlChunkStrategies, m as mergeJsonlFiles, t as toFileToken } from './mlDatasetFile-sRGWgR_o.js';
1
+ export { a as appendMlDatasetRow, c as closeAllMlDatasetWriters, b as closeMlDatasetWriter, f as flushMlDatasetWriter, g as getMlChunkFilePath, l as listMlChunkFiles, d as listMlChunkRunIds, e as listMlChunkStrategies, m as mergeJsonlFiles, t as toFileToken } from './mlDatasetFile-Czx__g9M.js';
2
2
 
3
3
  type MlPredictResponse = {
4
4
  probability: number;
package/dist/ml.js CHANGED
@@ -45,6 +45,7 @@ __export(ml_exports, {
45
45
  isDerivedDatasetFileName: () => isDerivedDatasetFileName,
46
46
  isTimestampFeatureKey: () => isTimestampFeatureKey,
47
47
  listMlChunkFiles: () => listMlChunkFiles,
48
+ listMlChunkRunIds: () => listMlChunkRunIds,
48
49
  listMlChunkStrategies: () => listMlChunkStrategies,
49
50
  mergeJsonlFiles: () => mergeJsonlFiles,
50
51
  toFileToken: () => toFileToken,
@@ -58,15 +59,26 @@ var import_events = require("events");
58
59
  var import_fs = require("fs");
59
60
  var import_promises = __toESM(require("fs/promises"));
60
61
  var import_path = __toESM(require("path"));
62
+ var import_node_readline = __toESM(require("readline"));
61
63
  var DEFAULT_DIR = "data/ml/export";
62
64
  var ML_DATASET_WRITE_BATCH_SIZE = 200;
63
65
  var ML_CHUNK_FILE_RE = /^ml-dataset-(.+)-chunk-[^.]+\.jsonl$/;
66
+ var BACKTEST_RUN_CHUNK_ID_RE = /^(\d{12}-[a-f0-9]{8})-/;
64
67
  var writerByPath = /* @__PURE__ */ new Map();
65
68
  var toFileToken = (value) => value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "_").replace(/^_+|_+$/g, "") || "any";
66
69
  var getMlChunkFilePath = (strategyName, chunkId, outDir = DEFAULT_DIR) => import_path.default.join(
67
70
  outDir,
68
71
  `ml-dataset-${toFileToken(strategyName)}-chunk-${toFileToken(chunkId)}.jsonl`
69
72
  );
73
+ var getMlChunkFilePrefix = (strategyName, runId) => `ml-dataset-${toFileToken(strategyName)}-chunk-${runId ? `${toFileToken(runId)}-` : ""}`;
74
+ var getRunIdFromMlChunkFileName = (strategyName, fileName) => {
75
+ const prefix = getMlChunkFilePrefix(strategyName);
76
+ if (!fileName.startsWith(prefix) || !fileName.endsWith(".jsonl")) {
77
+ return "";
78
+ }
79
+ const chunkToken = fileName.slice(prefix.length, -".jsonl".length);
80
+ return chunkToken.match(BACKTEST_RUN_CHUNK_ID_RE)?.[1] ?? "";
81
+ };
70
82
  var appendMlDatasetRow = async (params) => {
71
83
  const { strategyName, chunkId, row, outDir = DEFAULT_DIR } = params;
72
84
  const filePath = getMlChunkFilePath(strategyName, chunkId, outDir);
@@ -140,8 +152,8 @@ var closeAllMlDatasetWriters = async () => {
140
152
  }
141
153
  };
142
154
  var listMlChunkFiles = async (params) => {
143
- const { strategyName, outDir = DEFAULT_DIR } = params;
144
- const prefix = `ml-dataset-${toFileToken(strategyName)}-chunk-`;
155
+ const { strategyName, outDir = DEFAULT_DIR, runId } = params;
156
+ const prefix = getMlChunkFilePrefix(strategyName, runId);
145
157
  let entries = [];
146
158
  try {
147
159
  entries = await import_promises.default.readdir(outDir);
@@ -150,6 +162,20 @@ var listMlChunkFiles = async (params) => {
150
162
  }
151
163
  return entries.filter((name) => name.startsWith(prefix) && name.endsWith(".jsonl")).map((name) => import_path.default.join(outDir, name)).sort();
152
164
  };
165
+ var listMlChunkRunIds = async (params) => {
166
+ const { strategyName, outDir = DEFAULT_DIR } = params;
167
+ let entries = [];
168
+ try {
169
+ entries = await import_promises.default.readdir(outDir);
170
+ } catch {
171
+ return [];
172
+ }
173
+ return [
174
+ ...new Set(
175
+ entries.map((name) => getRunIdFromMlChunkFileName(strategyName, name)).filter(Boolean)
176
+ )
177
+ ].sort();
178
+ };
153
179
  var listMlChunkStrategies = async (params) => {
154
180
  const outDir = params?.outDir ?? DEFAULT_DIR;
155
181
  let entries = [];
@@ -165,12 +191,37 @@ var listMlChunkStrategies = async (params) => {
165
191
  ].sort();
166
192
  };
167
193
  var mergeJsonlFiles = async (params) => {
168
- const { filePaths, outPath } = params;
194
+ const { filePaths, outPath, shouldIncludeRow } = params;
169
195
  await import_promises.default.mkdir(import_path.default.dirname(outPath), { recursive: true });
170
196
  const stream = (0, import_fs.createWriteStream)(outPath, { encoding: "utf8" });
171
197
  const done = Promise.all([(0, import_events.once)(stream, "finish"), (0, import_events.once)(stream, "close")]);
172
198
  try {
173
199
  for (const filePath of filePaths) {
200
+ if (shouldIncludeRow) {
201
+ const reader2 = import_node_readline.default.createInterface({
202
+ input: (0, import_fs.createReadStream)(filePath, { encoding: "utf8" }),
203
+ crlfDelay: Infinity
204
+ });
205
+ try {
206
+ for await (const line of reader2) {
207
+ const trimmed = line.trim();
208
+ if (!trimmed) {
209
+ continue;
210
+ }
211
+ const row = JSON.parse(trimmed);
212
+ if (!shouldIncludeRow(row)) {
213
+ continue;
214
+ }
215
+ if (!stream.write(`${trimmed}
216
+ `)) {
217
+ await (0, import_events.once)(stream, "drain");
218
+ }
219
+ }
220
+ } finally {
221
+ reader2.close();
222
+ }
223
+ continue;
224
+ }
174
225
  const reader = (0, import_fs.createReadStream)(filePath, { encoding: "utf8" });
175
226
  for await (const chunk of reader) {
176
227
  if (!stream.write(chunk)) {
@@ -1740,6 +1791,7 @@ var findLookaheadViolations = (row) => {
1740
1791
  isDerivedDatasetFileName,
1741
1792
  isTimestampFeatureKey,
1742
1793
  listMlChunkFiles,
1794
+ listMlChunkRunIds,
1743
1795
  listMlChunkStrategies,
1744
1796
  mergeJsonlFiles,
1745
1797
  toFileToken,
package/dist/ml.mjs CHANGED
@@ -5,10 +5,11 @@ import {
5
5
  flushMlDatasetWriter,
6
6
  getMlChunkFilePath,
7
7
  listMlChunkFiles,
8
+ listMlChunkRunIds,
8
9
  listMlChunkStrategies,
9
10
  mergeJsonlFiles,
10
11
  toFileToken
11
- } from "./chunk-KVEZORMS.mjs";
12
+ } from "./chunk-RQP5VSTH.mjs";
12
13
  import {
13
14
  logger
14
15
  } from "./chunk-LNFUOXDW.mjs";
@@ -1527,6 +1528,7 @@ export {
1527
1528
  isDerivedDatasetFileName,
1528
1529
  isTimestampFeatureKey,
1529
1530
  listMlChunkFiles,
1531
+ listMlChunkRunIds,
1530
1532
  listMlChunkStrategies,
1531
1533
  mergeJsonlFiles,
1532
1534
  toFileToken,
@@ -12,6 +12,11 @@ declare const closeAllMlDatasetWriters: () => Promise<void>;
12
12
  declare const listMlChunkFiles: (params: {
13
13
  strategyName: string;
14
14
  outDir?: string;
15
+ runId?: string;
16
+ }) => Promise<string[]>;
17
+ declare const listMlChunkRunIds: (params: {
18
+ strategyName: string;
19
+ outDir?: string;
15
20
  }) => Promise<string[]>;
16
21
  declare const listMlChunkStrategies: (params?: {
17
22
  outDir?: string;
@@ -19,6 +24,7 @@ declare const listMlChunkStrategies: (params?: {
19
24
  declare const mergeJsonlFiles: (params: {
20
25
  filePaths: string[];
21
26
  outPath: string;
27
+ shouldIncludeRow?: (row: Record<string, unknown>) => boolean;
22
28
  }) => Promise<void>;
23
29
 
24
- export { appendMlDatasetRow as a, closeMlDatasetWriter as b, closeAllMlDatasetWriters as c, listMlChunkStrategies as d, flushMlDatasetWriter as f, getMlChunkFilePath as g, listMlChunkFiles as l, mergeJsonlFiles as m, toFileToken as t };
30
+ export { appendMlDatasetRow as a, closeMlDatasetWriter as b, closeAllMlDatasetWriters as c, listMlChunkRunIds as d, listMlChunkStrategies as e, flushMlDatasetWriter as f, getMlChunkFilePath as g, listMlChunkFiles as l, mergeJsonlFiles as m, toFileToken as t };
@@ -12,6 +12,11 @@ declare const closeAllMlDatasetWriters: () => Promise<void>;
12
12
  declare const listMlChunkFiles: (params: {
13
13
  strategyName: string;
14
14
  outDir?: string;
15
+ runId?: string;
16
+ }) => Promise<string[]>;
17
+ declare const listMlChunkRunIds: (params: {
18
+ strategyName: string;
19
+ outDir?: string;
15
20
  }) => Promise<string[]>;
16
21
  declare const listMlChunkStrategies: (params?: {
17
22
  outDir?: string;
@@ -19,6 +24,7 @@ declare const listMlChunkStrategies: (params?: {
19
24
  declare const mergeJsonlFiles: (params: {
20
25
  filePaths: string[];
21
26
  outPath: string;
27
+ shouldIncludeRow?: (row: Record<string, unknown>) => boolean;
22
28
  }) => Promise<void>;
23
29
 
24
- export { appendMlDatasetRow as a, closeMlDatasetWriter as b, closeAllMlDatasetWriters as c, listMlChunkStrategies as d, flushMlDatasetWriter as f, getMlChunkFilePath as g, listMlChunkFiles as l, mergeJsonlFiles as m, toFileToken as t };
30
+ export { appendMlDatasetRow as a, closeMlDatasetWriter as b, closeAllMlDatasetWriters as c, listMlChunkRunIds as d, listMlChunkStrategies as e, flushMlDatasetWriter as f, getMlChunkFilePath as g, listMlChunkFiles as l, mergeJsonlFiles as m, toFileToken as t };
package/dist/redis.d.mts CHANGED
@@ -3,9 +3,11 @@ import Redis from 'ioredis';
3
3
  declare global {
4
4
  var __redis__: Redis | undefined;
5
5
  }
6
+ declare const closeRedisConnection: () => Promise<void>;
6
7
  interface Options {
7
8
  expire?: number;
8
9
  }
10
+ declare const publishData: (channel: string, value: unknown) => Promise<number>;
9
11
  interface DelKeyOptions {
10
12
  raiseOnMisconf?: boolean;
11
13
  }
@@ -17,18 +19,31 @@ declare class RedisWriteBlockedError extends Error {
17
19
  }
18
20
  declare const delKeyWithOptions: (key: string, options?: DelKeyOptions) => Promise<boolean>;
19
21
  declare const setData: <T>(key: string, data: T, options?: Options) => Promise<void>;
22
+ declare const setHashJsonField: <T>(key: string, field: string, data: T, options?: Options) => Promise<void>;
23
+ declare const getHashJsonField: <T>(key: string, field: string, fallback?: T | null) => Promise<T | null>;
24
+ declare const getHashJsonValues: <T>(key: string) => Promise<T[]>;
25
+ declare const incrHashFields: (key: string, increments: Record<string, number>, options?: Options) => Promise<void>;
26
+ declare const getHashData: (key: string) => Promise<Record<string, string>>;
20
27
  declare const createScreenshotSessionToken: (userName: string) => Promise<string | null>;
21
28
  declare const consumeScreenshotSessionToken: (token: string) => Promise<string | null>;
22
29
  declare const redisKeys: {
23
30
  users: () => string;
24
31
  user: (userName: string) => string;
32
+ tradingAccounts: (userName: string) => string;
33
+ tradingAccount: (userName: string, accountId: string) => string;
34
+ runtimeDeployments: (userName: string) => string;
35
+ runtimeDeployment: (userName: string, deploymentId: string) => string;
36
+ runtimeDeploymentHeartbeat: (userName: string, deploymentId: string) => string;
25
37
  bots: (userName: string) => string;
26
38
  botsPrefix: () => string;
27
39
  bot: (userName: string, botId: string) => string;
28
40
  backtestConfig: (userName: string, config: string) => string;
29
41
  strategies: (userName: string) => string;
30
- strategyConfig: (userName: string, strategyName: string) => string;
42
+ strategyConfig: (userName: string, strategyName: string, configId?: string) => string;
31
43
  strategyResults: (userName: string, strategyName: string) => string;
44
+ strategyCharts: (userName: string, mode: string) => string;
45
+ strategyChartCards: (userName: string, mode: string) => string;
46
+ strategyChartCard: (userName: string, mode: string, cardId: string) => string;
32
47
  tests: (userName: string, strategyName?: string) => string;
33
48
  testOrders: (userName: string, strategyName: string, testName: string) => string;
34
49
  testConfig: (userName: string, strategyName: string, testName: string) => string;
@@ -37,21 +52,34 @@ declare const redisKeys: {
37
52
  cacheChunk: (userName: string, chunkId: string) => string;
38
53
  cacheOrders: (userName: string, orderLogId: string) => string;
39
54
  cachePositions: (userName: string, orderLogId: string) => string;
55
+ tickerUniverse: (userName: string, connectorName: string, universe?: string, accountId?: string) => string;
40
56
  signal: (symbol: string, signalId: string) => string;
41
57
  signalsBySymbol: (symbol: string) => string;
42
58
  storeSignal: (symbol: string, signalId: string) => string;
43
59
  runtimeSignals: (userName: string) => string;
44
60
  runtimeSignal: (userName: string, signalId: string) => string;
61
+ runtimeSignalBuckets: (userName: string) => string;
62
+ runtimeSignalBucket: (userName: string, dayKey: string, strategyName: string) => string;
45
63
  runtimeSignalEvaluations: (userName: string) => string;
46
64
  runtimeSignalEvaluation: (userName: string, evaluationId: string) => string;
65
+ runtimeSignalEvaluationBuckets: (userName: string) => string;
66
+ runtimeSignalEvaluationBucket: (userName: string, dayKey: string, strategyName: string) => string;
67
+ runtimeSignalEvaluationStatsBuckets: (userName: string) => string;
68
+ runtimeSignalEvaluationStatsBucket: (userName: string, dayKey: string, strategyName: string) => string;
47
69
  runtimeTrades: (userName: string) => string;
48
70
  runtimeTrade: (userName: string, orderId: string) => string;
71
+ runtimeTradeBuckets: (userName: string) => string;
72
+ runtimeTradeBucket: (userName: string, dayKey: string) => string;
49
73
  runtimeActiveTrades: (userName: string) => string;
50
- runtimeActiveTrade: (userName: string, symbol: string) => string;
74
+ runtimeActiveTrade: (userName: string, symbol: string, scopeId?: string) => string;
51
75
  aiChatHistory: (userName: string, symbolKey: string) => string;
52
76
  analysis: (symbol: string, signalId: string) => string;
53
77
  screenshotSessionToken: (token: string) => string;
54
78
  backtestResults: (userName: string, config: string, timestamp: string) => string;
79
+ backtestRuns: (userName: string) => string;
80
+ backtestRun: (userName: string, runId: string) => string;
81
+ backtestRunResults: (userName: string, runId: string) => string;
82
+ backtestLatestRun: (userName: string, config: string) => string;
55
83
  researchRuns: (userName: string) => string;
56
84
  researchRun: (userName: string, runId: string) => string;
57
85
  researchLatestRun: (userName: string, strategyName: string) => string;
@@ -63,4 +91,4 @@ declare const redisKeys: {
63
91
  mlResult: (strategyName: string, signalId: string) => string;
64
92
  };
65
93
 
66
- export { RedisWriteBlockedError, consumeScreenshotSessionToken, createScreenshotSessionToken, delKey, delKeyWithOptions, getData, getKeys, redisKeys, setData };
94
+ export { RedisWriteBlockedError, closeRedisConnection, consumeScreenshotSessionToken, createScreenshotSessionToken, delKey, delKeyWithOptions, getData, getHashData, getHashJsonField, getHashJsonValues, getKeys, incrHashFields, publishData, redisKeys, setData, setHashJsonField };