@tradejs/infra 1.0.9 → 1.0.11

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
  };
@@ -301,6 +319,25 @@ var setHashJsonField = async (key, field, data, options = {}) => {
301
319
  logger.log("error", "failed HSET %s[%s]: %s", key, field, String(e));
302
320
  }
303
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
+ };
304
341
  var getHashJsonValues = async (key) => {
305
342
  if (redisUnavailable) return [];
306
343
  const redis = await getReadyRedis();
@@ -406,13 +443,21 @@ var consumeScreenshotSessionToken = async (token) => {
406
443
  var redisKeys = {
407
444
  users: () => "users:index:",
408
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`,
409
451
  bots: (userName) => `users:${userName}:bots`,
410
452
  botsPrefix: () => "users:",
411
453
  bot: (userName, botId) => `users:${userName}:bots:${botId}`,
412
454
  backtestConfig: (userName, config) => `users:${userName}:backtests:configs:${config}`,
413
455
  strategies: (userName) => `users:${userName}:strategies`,
414
- strategyConfig: (userName, strategyName) => `users:${userName}:strategies:${strategyName}:config`,
456
+ strategyConfig: (userName, strategyName, configId = "config") => `users:${userName}:strategies:${strategyName}:${configId}`,
415
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}`,
416
461
  tests: (userName, strategyName) => strategyName ? `users:${userName}:tests:${strategyName}` : `users:${userName}:tests:`,
417
462
  testOrders: (userName, strategyName, testName) => `users:${userName}:tests:${strategyName}:${testName}:orders`,
418
463
  testConfig: (userName, strategyName, testName) => `users:${userName}:tests:${strategyName}:${testName}:config`,
@@ -421,6 +466,7 @@ var redisKeys = {
421
466
  cacheChunk: (userName, chunkId) => `users:${userName}:cache:tests:chunks:${chunkId}`,
422
467
  cacheOrders: (userName, orderLogId) => `users:${userName}:cache:tests:orders:${orderLogId}`,
423
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}`,
424
470
  signal: (symbol, signalId) => `signals:${symbol}:${signalId}`,
425
471
  signalsBySymbol: (symbol) => `signals:${symbol}:`,
426
472
  storeSignal: (symbol, signalId) => `store:signals:${symbol}:${signalId}`,
@@ -436,12 +482,20 @@ var redisKeys = {
436
482
  runtimeSignalEvaluationStatsBucket: (userName, dayKey, strategyName) => `users:${userName}:runtime:signal-evaluation-stats:days:${dayKey}:${strategyName}`,
437
483
  runtimeTrades: (userName) => `users:${userName}:runtime:trade-records:`,
438
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}`,
439
487
  runtimeActiveTrades: (userName) => `users:${userName}:runtime:active-trades:`,
440
- 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}`,
441
489
  aiChatHistory: (userName, symbolKey) => `users:${userName}:ai:chats:${symbolKey}`,
442
490
  analysis: (symbol, signalId) => `analysis:${symbol}:${signalId}`,
443
491
  screenshotSessionToken: (token) => `auth:screenshot:${token}`,
444
492
  backtestResults: (userName, config, timestamp) => `users:${userName}:backtests:results:${config}:${timestamp}`,
493
+ backtestJobs: (userName) => `users:${userName}:backtests:jobs:`,
494
+ backtestJob: (userName, jobId) => `users:${userName}:backtests:jobs:${jobId}`,
495
+ backtestRuns: (userName) => `users:${userName}:backtests:runs:`,
496
+ backtestRun: (userName, runId) => `users:${userName}:backtests:runs:${runId}`,
497
+ backtestRunResults: (userName, runId) => `users:${userName}:backtests:runs:${runId}:results`,
498
+ backtestLatestRun: (userName, config) => `users:${userName}:backtests:latest:${config}`,
445
499
  researchRuns: (userName) => `users:${userName}:research:runs:`,
446
500
  researchRun: (userName, runId) => `users:${userName}:research:runs:${runId}`,
447
501
  researchLatestRun: (userName, strategyName) => `users:${userName}:research:latest:${strategyName}`,
@@ -454,6 +508,8 @@ var redisKeys = {
454
508
  };
455
509
 
456
510
  export {
511
+ closeRedisConnection,
512
+ publishData,
457
513
  getKeys,
458
514
  getData,
459
515
  delKey,
@@ -461,6 +517,7 @@ export {
461
517
  delKeyWithOptions,
462
518
  setData,
463
519
  setHashJsonField,
520
+ getHashJsonField,
464
521
  getHashJsonValues,
465
522
  incrHashFields,
466
523
  getHashData,
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
  }
@@ -18,6 +20,7 @@ declare class RedisWriteBlockedError extends Error {
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>;
20
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>;
21
24
  declare const getHashJsonValues: <T>(key: string) => Promise<T[]>;
22
25
  declare const incrHashFields: (key: string, increments: Record<string, number>, options?: Options) => Promise<void>;
23
26
  declare const getHashData: (key: string) => Promise<Record<string, string>>;
@@ -26,13 +29,21 @@ declare const consumeScreenshotSessionToken: (token: string) => Promise<string |
26
29
  declare const redisKeys: {
27
30
  users: () => string;
28
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;
29
37
  bots: (userName: string) => string;
30
38
  botsPrefix: () => string;
31
39
  bot: (userName: string, botId: string) => string;
32
40
  backtestConfig: (userName: string, config: string) => string;
33
41
  strategies: (userName: string) => string;
34
- strategyConfig: (userName: string, strategyName: string) => string;
42
+ strategyConfig: (userName: string, strategyName: string, configId?: string) => string;
35
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;
36
47
  tests: (userName: string, strategyName?: string) => string;
37
48
  testOrders: (userName: string, strategyName: string, testName: string) => string;
38
49
  testConfig: (userName: string, strategyName: string, testName: string) => string;
@@ -41,6 +52,7 @@ declare const redisKeys: {
41
52
  cacheChunk: (userName: string, chunkId: string) => string;
42
53
  cacheOrders: (userName: string, orderLogId: string) => string;
43
54
  cachePositions: (userName: string, orderLogId: string) => string;
55
+ tickerUniverse: (userName: string, connectorName: string, universe?: string, accountId?: string) => string;
44
56
  signal: (symbol: string, signalId: string) => string;
45
57
  signalsBySymbol: (symbol: string) => string;
46
58
  storeSignal: (symbol: string, signalId: string) => string;
@@ -56,12 +68,20 @@ declare const redisKeys: {
56
68
  runtimeSignalEvaluationStatsBucket: (userName: string, dayKey: string, strategyName: string) => string;
57
69
  runtimeTrades: (userName: string) => string;
58
70
  runtimeTrade: (userName: string, orderId: string) => string;
71
+ runtimeTradeBuckets: (userName: string) => string;
72
+ runtimeTradeBucket: (userName: string, dayKey: string) => string;
59
73
  runtimeActiveTrades: (userName: string) => string;
60
- runtimeActiveTrade: (userName: string, symbol: string) => string;
74
+ runtimeActiveTrade: (userName: string, symbol: string, scopeId?: string) => string;
61
75
  aiChatHistory: (userName: string, symbolKey: string) => string;
62
76
  analysis: (symbol: string, signalId: string) => string;
63
77
  screenshotSessionToken: (token: string) => string;
64
78
  backtestResults: (userName: string, config: string, timestamp: string) => string;
79
+ backtestJobs: (userName: string) => string;
80
+ backtestJob: (userName: string, jobId: string) => string;
81
+ backtestRuns: (userName: string) => string;
82
+ backtestRun: (userName: string, runId: string) => string;
83
+ backtestRunResults: (userName: string, runId: string) => string;
84
+ backtestLatestRun: (userName: string, config: string) => string;
65
85
  researchRuns: (userName: string) => string;
66
86
  researchRun: (userName: string, runId: string) => string;
67
87
  researchLatestRun: (userName: string, strategyName: string) => string;
@@ -73,4 +93,4 @@ declare const redisKeys: {
73
93
  mlResult: (strategyName: string, signalId: string) => string;
74
94
  };
75
95
 
76
- export { RedisWriteBlockedError, consumeScreenshotSessionToken, createScreenshotSessionToken, delKey, delKeyWithOptions, getData, getHashData, getHashJsonValues, getKeys, incrHashFields, redisKeys, setData, setHashJsonField };
96
+ export { RedisWriteBlockedError, closeRedisConnection, consumeScreenshotSessionToken, createScreenshotSessionToken, delKey, delKeyWithOptions, getData, getHashData, getHashJsonField, getHashJsonValues, getKeys, incrHashFields, publishData, redisKeys, setData, setHashJsonField };
package/dist/redis.d.ts 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
  }
@@ -18,6 +20,7 @@ declare class RedisWriteBlockedError extends Error {
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>;
20
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>;
21
24
  declare const getHashJsonValues: <T>(key: string) => Promise<T[]>;
22
25
  declare const incrHashFields: (key: string, increments: Record<string, number>, options?: Options) => Promise<void>;
23
26
  declare const getHashData: (key: string) => Promise<Record<string, string>>;
@@ -26,13 +29,21 @@ declare const consumeScreenshotSessionToken: (token: string) => Promise<string |
26
29
  declare const redisKeys: {
27
30
  users: () => string;
28
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;
29
37
  bots: (userName: string) => string;
30
38
  botsPrefix: () => string;
31
39
  bot: (userName: string, botId: string) => string;
32
40
  backtestConfig: (userName: string, config: string) => string;
33
41
  strategies: (userName: string) => string;
34
- strategyConfig: (userName: string, strategyName: string) => string;
42
+ strategyConfig: (userName: string, strategyName: string, configId?: string) => string;
35
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;
36
47
  tests: (userName: string, strategyName?: string) => string;
37
48
  testOrders: (userName: string, strategyName: string, testName: string) => string;
38
49
  testConfig: (userName: string, strategyName: string, testName: string) => string;
@@ -41,6 +52,7 @@ declare const redisKeys: {
41
52
  cacheChunk: (userName: string, chunkId: string) => string;
42
53
  cacheOrders: (userName: string, orderLogId: string) => string;
43
54
  cachePositions: (userName: string, orderLogId: string) => string;
55
+ tickerUniverse: (userName: string, connectorName: string, universe?: string, accountId?: string) => string;
44
56
  signal: (symbol: string, signalId: string) => string;
45
57
  signalsBySymbol: (symbol: string) => string;
46
58
  storeSignal: (symbol: string, signalId: string) => string;
@@ -56,12 +68,20 @@ declare const redisKeys: {
56
68
  runtimeSignalEvaluationStatsBucket: (userName: string, dayKey: string, strategyName: string) => string;
57
69
  runtimeTrades: (userName: string) => string;
58
70
  runtimeTrade: (userName: string, orderId: string) => string;
71
+ runtimeTradeBuckets: (userName: string) => string;
72
+ runtimeTradeBucket: (userName: string, dayKey: string) => string;
59
73
  runtimeActiveTrades: (userName: string) => string;
60
- runtimeActiveTrade: (userName: string, symbol: string) => string;
74
+ runtimeActiveTrade: (userName: string, symbol: string, scopeId?: string) => string;
61
75
  aiChatHistory: (userName: string, symbolKey: string) => string;
62
76
  analysis: (symbol: string, signalId: string) => string;
63
77
  screenshotSessionToken: (token: string) => string;
64
78
  backtestResults: (userName: string, config: string, timestamp: string) => string;
79
+ backtestJobs: (userName: string) => string;
80
+ backtestJob: (userName: string, jobId: string) => string;
81
+ backtestRuns: (userName: string) => string;
82
+ backtestRun: (userName: string, runId: string) => string;
83
+ backtestRunResults: (userName: string, runId: string) => string;
84
+ backtestLatestRun: (userName: string, config: string) => string;
65
85
  researchRuns: (userName: string) => string;
66
86
  researchRun: (userName: string, runId: string) => string;
67
87
  researchLatestRun: (userName: string, strategyName: string) => string;
@@ -73,4 +93,4 @@ declare const redisKeys: {
73
93
  mlResult: (strategyName: string, signalId: string) => string;
74
94
  };
75
95
 
76
- export { RedisWriteBlockedError, consumeScreenshotSessionToken, createScreenshotSessionToken, delKey, delKeyWithOptions, getData, getHashData, getHashJsonValues, getKeys, incrHashFields, redisKeys, setData, setHashJsonField };
96
+ export { RedisWriteBlockedError, closeRedisConnection, consumeScreenshotSessionToken, createScreenshotSessionToken, delKey, delKeyWithOptions, getData, getHashData, getHashJsonField, getHashJsonValues, getKeys, incrHashFields, publishData, redisKeys, setData, setHashJsonField };
package/dist/redis.js CHANGED
@@ -31,15 +31,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var redis_exports = {};
32
32
  __export(redis_exports, {
33
33
  RedisWriteBlockedError: () => RedisWriteBlockedError,
34
+ closeRedisConnection: () => closeRedisConnection,
34
35
  consumeScreenshotSessionToken: () => consumeScreenshotSessionToken,
35
36
  createScreenshotSessionToken: () => createScreenshotSessionToken,
36
37
  delKey: () => delKey,
37
38
  delKeyWithOptions: () => delKeyWithOptions,
38
39
  getData: () => getData,
39
40
  getHashData: () => getHashData,
41
+ getHashJsonField: () => getHashJsonField,
40
42
  getHashJsonValues: () => getHashJsonValues,
41
43
  getKeys: () => getKeys,
42
44
  incrHashFields: () => incrHashFields,
45
+ publishData: () => publishData,
43
46
  redisKeys: () => redisKeys,
44
47
  setData: () => setData,
45
48
  setHashJsonField: () => setHashJsonField
@@ -58,9 +61,12 @@ var logger = {
58
61
  };
59
62
  var redisConnectionWarningShown = false;
60
63
  var redisUnavailable = false;
61
- var isRedisConnectivityError = (error) => /ECONNREFUSED|ENOTFOUND|EAI_AGAIN|ETIMEDOUT|MaxRetriesPerRequestError|Connection is closed|Stream isn't writeable/i.test(
62
- error.message
63
- );
64
+ var isRedisConnectivityError = (error) => {
65
+ const errorText = [error.name, error.message, String(error)].join(" ");
66
+ return /ECONNREFUSED|ECONNRESET|ECONNABORTED|EPIPE|ENOTFOUND|EAI_AGAIN|ETIMEDOUT|MaxRetriesPerRequestError|Connection is closed|Stream isn't writeable/i.test(
67
+ errorText
68
+ );
69
+ };
64
70
  var toNonNegativeInt = (value, fallback) => {
65
71
  const parsed = Number.parseInt(String(value ?? ""), 10);
66
72
  return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
@@ -116,6 +122,16 @@ var getRedis = () => {
116
122
  }
117
123
  return global.__redis__;
118
124
  };
125
+ var closeRedisConnection = async () => {
126
+ const redis = global.__redis__;
127
+ if (!redis) {
128
+ return;
129
+ }
130
+ global.__redis__ = void 0;
131
+ redisUnavailable = false;
132
+ redisConnectionWarningShown = false;
133
+ redis.disconnect();
134
+ };
119
135
  var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
120
136
  var getRedisStatus = (redis) => String(redis.status ?? "ready");
121
137
  var waitForRedisReady = async (redis) => {
@@ -156,6 +172,11 @@ var toResultString = (result) => {
156
172
  if (Buffer.isBuffer(result)) return result.toString("utf8");
157
173
  return String(result);
158
174
  };
175
+ var publishData = async (channel, value) => {
176
+ const redis = await getReadyRedis();
177
+ if (!redis) return 0;
178
+ return redis.publish(channel, toJson(value));
179
+ };
159
180
  var DEFAULT_OPTIONS = {
160
181
  expire: TTL_1D
161
182
  };
@@ -347,6 +368,25 @@ var setHashJsonField = async (key, field, data, options = {}) => {
347
368
  logger.log("error", "failed HSET %s[%s]: %s", key, field, String(e));
348
369
  }
349
370
  };
371
+ var getHashJsonField = async (key, field, fallback = null) => {
372
+ if (redisUnavailable) return fallback;
373
+ const redis = await getReadyRedis();
374
+ if (!redis) return fallback;
375
+ try {
376
+ const value = await redis.call("HGET", key, field);
377
+ if (typeof value !== "string" || !value) {
378
+ return fallback;
379
+ }
380
+ return JSON.parse(value);
381
+ } catch (e) {
382
+ if (e instanceof Error && isRedisConnectivityError(e)) {
383
+ markRedisUnavailable(e);
384
+ return fallback;
385
+ }
386
+ logger.log("error", "failed HGET %s[%s]: %s", key, field, String(e));
387
+ return fallback;
388
+ }
389
+ };
350
390
  var getHashJsonValues = async (key) => {
351
391
  if (redisUnavailable) return [];
352
392
  const redis = await getReadyRedis();
@@ -452,13 +492,21 @@ var consumeScreenshotSessionToken = async (token) => {
452
492
  var redisKeys = {
453
493
  users: () => "users:index:",
454
494
  user: (userName) => `users:index:${userName}`,
495
+ tradingAccounts: (userName) => `users:${userName}:trading-accounts:`,
496
+ tradingAccount: (userName, accountId) => `users:${userName}:trading-accounts:${accountId}`,
497
+ runtimeDeployments: (userName) => `users:${userName}:runtime:deployments:`,
498
+ runtimeDeployment: (userName, deploymentId) => `users:${userName}:runtime:deployments:${deploymentId}`,
499
+ runtimeDeploymentHeartbeat: (userName, deploymentId) => `users:${userName}:runtime:deployments:${deploymentId}:heartbeat`,
455
500
  bots: (userName) => `users:${userName}:bots`,
456
501
  botsPrefix: () => "users:",
457
502
  bot: (userName, botId) => `users:${userName}:bots:${botId}`,
458
503
  backtestConfig: (userName, config) => `users:${userName}:backtests:configs:${config}`,
459
504
  strategies: (userName) => `users:${userName}:strategies`,
460
- strategyConfig: (userName, strategyName) => `users:${userName}:strategies:${strategyName}:config`,
505
+ strategyConfig: (userName, strategyName, configId = "config") => `users:${userName}:strategies:${strategyName}:${configId}`,
461
506
  strategyResults: (userName, strategyName) => `users:${userName}:strategies:${strategyName}:results`,
507
+ strategyCharts: (userName, mode) => `users:${userName}:strategies:charts:${mode}`,
508
+ strategyChartCards: (userName, mode) => `users:${userName}:strategies:charts:${mode}:cards:`,
509
+ strategyChartCard: (userName, mode, cardId) => `users:${userName}:strategies:charts:${mode}:cards:${cardId}`,
462
510
  tests: (userName, strategyName) => strategyName ? `users:${userName}:tests:${strategyName}` : `users:${userName}:tests:`,
463
511
  testOrders: (userName, strategyName, testName) => `users:${userName}:tests:${strategyName}:${testName}:orders`,
464
512
  testConfig: (userName, strategyName, testName) => `users:${userName}:tests:${strategyName}:${testName}:config`,
@@ -467,6 +515,7 @@ var redisKeys = {
467
515
  cacheChunk: (userName, chunkId) => `users:${userName}:cache:tests:chunks:${chunkId}`,
468
516
  cacheOrders: (userName, orderLogId) => `users:${userName}:cache:tests:orders:${orderLogId}`,
469
517
  cachePositions: (userName, orderLogId) => `users:${userName}:cache:tests:positions:${orderLogId}`,
518
+ tickerUniverse: (userName, connectorName, universe, accountId) => universe || accountId ? `users:${userName}:cache:tickers:${connectorName}:${universe ?? "crypto"}:${accountId ?? "default"}` : `users:${userName}:cache:tickers:${connectorName}`,
470
519
  signal: (symbol, signalId) => `signals:${symbol}:${signalId}`,
471
520
  signalsBySymbol: (symbol) => `signals:${symbol}:`,
472
521
  storeSignal: (symbol, signalId) => `store:signals:${symbol}:${signalId}`,
@@ -482,12 +531,20 @@ var redisKeys = {
482
531
  runtimeSignalEvaluationStatsBucket: (userName, dayKey, strategyName) => `users:${userName}:runtime:signal-evaluation-stats:days:${dayKey}:${strategyName}`,
483
532
  runtimeTrades: (userName) => `users:${userName}:runtime:trade-records:`,
484
533
  runtimeTrade: (userName, orderId) => `users:${userName}:runtime:trade-records:${orderId}`,
534
+ runtimeTradeBuckets: (userName) => `users:${userName}:runtime:trade-records:days:`,
535
+ runtimeTradeBucket: (userName, dayKey) => `users:${userName}:runtime:trade-records:days:${dayKey}`,
485
536
  runtimeActiveTrades: (userName) => `users:${userName}:runtime:active-trades:`,
486
- runtimeActiveTrade: (userName, symbol) => `users:${userName}:runtime:active-trades:${symbol}`,
537
+ runtimeActiveTrade: (userName, symbol, scopeId) => scopeId ? `users:${userName}:runtime:active-trades:${scopeId}:${symbol}` : `users:${userName}:runtime:active-trades:${symbol}`,
487
538
  aiChatHistory: (userName, symbolKey) => `users:${userName}:ai:chats:${symbolKey}`,
488
539
  analysis: (symbol, signalId) => `analysis:${symbol}:${signalId}`,
489
540
  screenshotSessionToken: (token) => `auth:screenshot:${token}`,
490
541
  backtestResults: (userName, config, timestamp) => `users:${userName}:backtests:results:${config}:${timestamp}`,
542
+ backtestJobs: (userName) => `users:${userName}:backtests:jobs:`,
543
+ backtestJob: (userName, jobId) => `users:${userName}:backtests:jobs:${jobId}`,
544
+ backtestRuns: (userName) => `users:${userName}:backtests:runs:`,
545
+ backtestRun: (userName, runId) => `users:${userName}:backtests:runs:${runId}`,
546
+ backtestRunResults: (userName, runId) => `users:${userName}:backtests:runs:${runId}:results`,
547
+ backtestLatestRun: (userName, config) => `users:${userName}:backtests:latest:${config}`,
491
548
  researchRuns: (userName) => `users:${userName}:research:runs:`,
492
549
  researchRun: (userName, runId) => `users:${userName}:research:runs:${runId}`,
493
550
  researchLatestRun: (userName, strategyName) => `users:${userName}:research:latest:${strategyName}`,
@@ -501,15 +558,18 @@ var redisKeys = {
501
558
  // Annotate the CommonJS export names for ESM import in node:
502
559
  0 && (module.exports = {
503
560
  RedisWriteBlockedError,
561
+ closeRedisConnection,
504
562
  consumeScreenshotSessionToken,
505
563
  createScreenshotSessionToken,
506
564
  delKey,
507
565
  delKeyWithOptions,
508
566
  getData,
509
567
  getHashData,
568
+ getHashJsonField,
510
569
  getHashJsonValues,
511
570
  getKeys,
512
571
  incrHashFields,
572
+ publishData,
513
573
  redisKeys,
514
574
  setData,
515
575
  setHashJsonField