@tradejs/infra 1.0.9 → 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.
@@ -0,0 +1,255 @@
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/backtestArtifacts.ts
31
+ var backtestArtifacts_exports = {};
32
+ __export(backtestArtifacts_exports, {
33
+ deleteCachedBacktestArtifacts: () => deleteCachedBacktestArtifacts,
34
+ deletePersistedBacktestOrderLog: () => deletePersistedBacktestOrderLog,
35
+ getBacktestArtifactsRootDir: () => getBacktestArtifactsRootDir,
36
+ getBacktestCacheArtifactsDirForUser: () => getBacktestCacheArtifactsDirForUser,
37
+ getPersistedBacktestArtifactsDirForUser: () => getPersistedBacktestArtifactsDirForUser,
38
+ parseBacktestArtifactRef: () => parseBacktestArtifactRef,
39
+ readCachedBacktestArtifacts: () => readCachedBacktestArtifacts,
40
+ readPersistedBacktestOrderLog: () => readPersistedBacktestOrderLog,
41
+ writeCachedBacktestArtifacts: () => writeCachedBacktestArtifacts,
42
+ writePersistedBacktestOrderLog: () => writePersistedBacktestOrderLog
43
+ });
44
+ module.exports = __toCommonJS(backtestArtifacts_exports);
45
+ var import_promises = __toESM(require("fs/promises"));
46
+ var import_path = __toESM(require("path"));
47
+ var BACKTEST_ARTIFACTS_DIR = import_path.default.join("data", "backtests");
48
+ var resolveProjectRoot = (projectRoot) => {
49
+ const explicit = String(projectRoot || "").trim();
50
+ if (explicit) {
51
+ return import_path.default.resolve(explicit);
52
+ }
53
+ const fromEnv = String(process.env.PROJECT_CWD || "").trim();
54
+ if (fromEnv) {
55
+ return import_path.default.resolve(fromEnv);
56
+ }
57
+ return process.cwd();
58
+ };
59
+ var encodeSegment = (value) => encodeURIComponent(String(value));
60
+ var toProjectRelativePath = (projectRoot, absolutePath) => import_path.default.relative(projectRoot, absolutePath);
61
+ var resolveArtifactPath = (relativePath, projectRoot) => {
62
+ const root = resolveProjectRoot(projectRoot);
63
+ return import_path.default.isAbsolute(relativePath) ? relativePath : import_path.default.resolve(root, relativePath);
64
+ };
65
+ var isEnoentError = (error) => error?.code === "ENOENT";
66
+ var createRef = (absolutePath, projectRoot) => ({
67
+ kind: "file",
68
+ version: 1,
69
+ path: toProjectRelativePath(resolveProjectRoot(projectRoot), absolutePath)
70
+ });
71
+ var isBacktestArtifactRef = (value) => Boolean(
72
+ value && typeof value === "object" && value.kind === "file" && value.version === 1 && typeof value.path === "string"
73
+ );
74
+ var getCachedArtifactPath = (userName, orderLogId, artifactName, projectRoot) => import_path.default.join(
75
+ resolveProjectRoot(projectRoot),
76
+ BACKTEST_ARTIFACTS_DIR,
77
+ "cache",
78
+ encodeSegment(userName),
79
+ artifactName,
80
+ `${encodeSegment(orderLogId)}.json`
81
+ );
82
+ var getPersistedOrderLogPath = (userName, strategyName, testName, projectRoot) => import_path.default.join(
83
+ resolveProjectRoot(projectRoot),
84
+ BACKTEST_ARTIFACTS_DIR,
85
+ "tests",
86
+ encodeSegment(userName),
87
+ encodeSegment(strategyName),
88
+ `${encodeSegment(testName)}.json`
89
+ );
90
+ var readJsonFile = async (absolutePath, fallback) => {
91
+ try {
92
+ const raw = await import_promises.default.readFile(absolutePath, "utf8");
93
+ return JSON.parse(raw);
94
+ } catch (error) {
95
+ if (isEnoentError(error)) {
96
+ return fallback;
97
+ }
98
+ throw error;
99
+ }
100
+ };
101
+ var writeJsonFile = async (absolutePath, value) => {
102
+ await import_promises.default.mkdir(import_path.default.dirname(absolutePath), { recursive: true });
103
+ await import_promises.default.writeFile(absolutePath, JSON.stringify(value), "utf8");
104
+ };
105
+ var removeFileIfExists = async (absolutePath) => {
106
+ try {
107
+ await import_promises.default.rm(absolutePath, { force: true });
108
+ return true;
109
+ } catch (error) {
110
+ if (isEnoentError(error)) {
111
+ return false;
112
+ }
113
+ throw error;
114
+ }
115
+ };
116
+ var writeCachedBacktestArtifacts = async ({
117
+ orderLog,
118
+ orderLogId,
119
+ positionLog,
120
+ projectRoot,
121
+ userName
122
+ }) => {
123
+ const orderPath = getCachedArtifactPath(
124
+ userName,
125
+ orderLogId,
126
+ "orders",
127
+ projectRoot
128
+ );
129
+ const positionPath = getCachedArtifactPath(
130
+ userName,
131
+ orderLogId,
132
+ "positions",
133
+ projectRoot
134
+ );
135
+ await Promise.all([
136
+ writeJsonFile(orderPath, orderLog),
137
+ writeJsonFile(positionPath, positionLog)
138
+ ]);
139
+ return {
140
+ orderLog: createRef(orderPath, projectRoot),
141
+ positionLog: createRef(positionPath, projectRoot)
142
+ };
143
+ };
144
+ var readCachedBacktestArtifacts = async ({
145
+ orderLogId,
146
+ projectRoot,
147
+ userName
148
+ }) => {
149
+ const orderPath = getCachedArtifactPath(
150
+ userName,
151
+ orderLogId,
152
+ "orders",
153
+ projectRoot
154
+ );
155
+ const positionPath = getCachedArtifactPath(
156
+ userName,
157
+ orderLogId,
158
+ "positions",
159
+ projectRoot
160
+ );
161
+ const [orderLog, positionLog] = await Promise.all([
162
+ readJsonFile(orderPath, null),
163
+ readJsonFile(positionPath, null)
164
+ ]);
165
+ return { orderLog, positionLog };
166
+ };
167
+ var deleteCachedBacktestArtifacts = async ({
168
+ orderLogId,
169
+ projectRoot,
170
+ userName
171
+ }) => {
172
+ const orderPath = getCachedArtifactPath(
173
+ userName,
174
+ orderLogId,
175
+ "orders",
176
+ projectRoot
177
+ );
178
+ const positionPath = getCachedArtifactPath(
179
+ userName,
180
+ orderLogId,
181
+ "positions",
182
+ projectRoot
183
+ );
184
+ const [removedOrderLog, removedPositionLog] = await Promise.all([
185
+ removeFileIfExists(orderPath),
186
+ removeFileIfExists(positionPath)
187
+ ]);
188
+ return removedOrderLog || removedPositionLog;
189
+ };
190
+ var writePersistedBacktestOrderLog = async ({
191
+ orderLog,
192
+ projectRoot,
193
+ strategyName,
194
+ testName,
195
+ userName
196
+ }) => {
197
+ const absolutePath = getPersistedOrderLogPath(
198
+ userName,
199
+ strategyName,
200
+ testName,
201
+ projectRoot
202
+ );
203
+ await writeJsonFile(absolutePath, orderLog);
204
+ return createRef(absolutePath, projectRoot);
205
+ };
206
+ var readPersistedBacktestOrderLog = async ({
207
+ projectRoot,
208
+ ref,
209
+ strategyName,
210
+ testName,
211
+ userName
212
+ }) => {
213
+ const absolutePath = ref ? resolveArtifactPath(ref.path, projectRoot) : getPersistedOrderLogPath(userName, strategyName, testName, projectRoot);
214
+ return readJsonFile(absolutePath, null);
215
+ };
216
+ var deletePersistedBacktestOrderLog = async ({
217
+ projectRoot,
218
+ ref,
219
+ strategyName,
220
+ testName,
221
+ userName
222
+ }) => {
223
+ const absolutePath = ref ? resolveArtifactPath(ref.path, projectRoot) : getPersistedOrderLogPath(userName, strategyName, testName, projectRoot);
224
+ return removeFileIfExists(absolutePath);
225
+ };
226
+ var parseBacktestArtifactRef = (value) => {
227
+ if (isBacktestArtifactRef(value)) {
228
+ return value;
229
+ }
230
+ return null;
231
+ };
232
+ var getBacktestArtifactsRootDir = (projectRoot) => import_path.default.join(resolveProjectRoot(projectRoot), BACKTEST_ARTIFACTS_DIR);
233
+ var getBacktestCacheArtifactsDirForUser = (userName, projectRoot) => import_path.default.join(
234
+ getBacktestArtifactsRootDir(projectRoot),
235
+ "cache",
236
+ encodeSegment(userName)
237
+ );
238
+ var getPersistedBacktestArtifactsDirForUser = (userName, projectRoot) => import_path.default.join(
239
+ getBacktestArtifactsRootDir(projectRoot),
240
+ "tests",
241
+ encodeSegment(userName)
242
+ );
243
+ // Annotate the CommonJS export names for ESM import in node:
244
+ 0 && (module.exports = {
245
+ deleteCachedBacktestArtifacts,
246
+ deletePersistedBacktestOrderLog,
247
+ getBacktestArtifactsRootDir,
248
+ getBacktestCacheArtifactsDirForUser,
249
+ getPersistedBacktestArtifactsDirForUser,
250
+ parseBacktestArtifactRef,
251
+ readCachedBacktestArtifacts,
252
+ readPersistedBacktestOrderLog,
253
+ writeCachedBacktestArtifacts,
254
+ writePersistedBacktestOrderLog
255
+ });
@@ -0,0 +1,211 @@
1
+ // src/backtestArtifacts.ts
2
+ import fs from "fs/promises";
3
+ import path from "path";
4
+ var BACKTEST_ARTIFACTS_DIR = path.join("data", "backtests");
5
+ var resolveProjectRoot = (projectRoot) => {
6
+ const explicit = String(projectRoot || "").trim();
7
+ if (explicit) {
8
+ return path.resolve(explicit);
9
+ }
10
+ const fromEnv = String(process.env.PROJECT_CWD || "").trim();
11
+ if (fromEnv) {
12
+ return path.resolve(fromEnv);
13
+ }
14
+ return process.cwd();
15
+ };
16
+ var encodeSegment = (value) => encodeURIComponent(String(value));
17
+ var toProjectRelativePath = (projectRoot, absolutePath) => path.relative(projectRoot, absolutePath);
18
+ var resolveArtifactPath = (relativePath, projectRoot) => {
19
+ const root = resolveProjectRoot(projectRoot);
20
+ return path.isAbsolute(relativePath) ? relativePath : path.resolve(root, relativePath);
21
+ };
22
+ var isEnoentError = (error) => error?.code === "ENOENT";
23
+ var createRef = (absolutePath, projectRoot) => ({
24
+ kind: "file",
25
+ version: 1,
26
+ path: toProjectRelativePath(resolveProjectRoot(projectRoot), absolutePath)
27
+ });
28
+ var isBacktestArtifactRef = (value) => Boolean(
29
+ value && typeof value === "object" && value.kind === "file" && value.version === 1 && typeof value.path === "string"
30
+ );
31
+ var getCachedArtifactPath = (userName, orderLogId, artifactName, projectRoot) => path.join(
32
+ resolveProjectRoot(projectRoot),
33
+ BACKTEST_ARTIFACTS_DIR,
34
+ "cache",
35
+ encodeSegment(userName),
36
+ artifactName,
37
+ `${encodeSegment(orderLogId)}.json`
38
+ );
39
+ var getPersistedOrderLogPath = (userName, strategyName, testName, projectRoot) => path.join(
40
+ resolveProjectRoot(projectRoot),
41
+ BACKTEST_ARTIFACTS_DIR,
42
+ "tests",
43
+ encodeSegment(userName),
44
+ encodeSegment(strategyName),
45
+ `${encodeSegment(testName)}.json`
46
+ );
47
+ var readJsonFile = async (absolutePath, fallback) => {
48
+ try {
49
+ const raw = await fs.readFile(absolutePath, "utf8");
50
+ return JSON.parse(raw);
51
+ } catch (error) {
52
+ if (isEnoentError(error)) {
53
+ return fallback;
54
+ }
55
+ throw error;
56
+ }
57
+ };
58
+ var writeJsonFile = async (absolutePath, value) => {
59
+ await fs.mkdir(path.dirname(absolutePath), { recursive: true });
60
+ await fs.writeFile(absolutePath, JSON.stringify(value), "utf8");
61
+ };
62
+ var removeFileIfExists = async (absolutePath) => {
63
+ try {
64
+ await fs.rm(absolutePath, { force: true });
65
+ return true;
66
+ } catch (error) {
67
+ if (isEnoentError(error)) {
68
+ return false;
69
+ }
70
+ throw error;
71
+ }
72
+ };
73
+ var writeCachedBacktestArtifacts = async ({
74
+ orderLog,
75
+ orderLogId,
76
+ positionLog,
77
+ projectRoot,
78
+ userName
79
+ }) => {
80
+ const orderPath = getCachedArtifactPath(
81
+ userName,
82
+ orderLogId,
83
+ "orders",
84
+ projectRoot
85
+ );
86
+ const positionPath = getCachedArtifactPath(
87
+ userName,
88
+ orderLogId,
89
+ "positions",
90
+ projectRoot
91
+ );
92
+ await Promise.all([
93
+ writeJsonFile(orderPath, orderLog),
94
+ writeJsonFile(positionPath, positionLog)
95
+ ]);
96
+ return {
97
+ orderLog: createRef(orderPath, projectRoot),
98
+ positionLog: createRef(positionPath, projectRoot)
99
+ };
100
+ };
101
+ var readCachedBacktestArtifacts = async ({
102
+ orderLogId,
103
+ projectRoot,
104
+ userName
105
+ }) => {
106
+ const orderPath = getCachedArtifactPath(
107
+ userName,
108
+ orderLogId,
109
+ "orders",
110
+ projectRoot
111
+ );
112
+ const positionPath = getCachedArtifactPath(
113
+ userName,
114
+ orderLogId,
115
+ "positions",
116
+ projectRoot
117
+ );
118
+ const [orderLog, positionLog] = await Promise.all([
119
+ readJsonFile(orderPath, null),
120
+ readJsonFile(positionPath, null)
121
+ ]);
122
+ return { orderLog, positionLog };
123
+ };
124
+ var deleteCachedBacktestArtifacts = async ({
125
+ orderLogId,
126
+ projectRoot,
127
+ userName
128
+ }) => {
129
+ const orderPath = getCachedArtifactPath(
130
+ userName,
131
+ orderLogId,
132
+ "orders",
133
+ projectRoot
134
+ );
135
+ const positionPath = getCachedArtifactPath(
136
+ userName,
137
+ orderLogId,
138
+ "positions",
139
+ projectRoot
140
+ );
141
+ const [removedOrderLog, removedPositionLog] = await Promise.all([
142
+ removeFileIfExists(orderPath),
143
+ removeFileIfExists(positionPath)
144
+ ]);
145
+ return removedOrderLog || removedPositionLog;
146
+ };
147
+ var writePersistedBacktestOrderLog = async ({
148
+ orderLog,
149
+ projectRoot,
150
+ strategyName,
151
+ testName,
152
+ userName
153
+ }) => {
154
+ const absolutePath = getPersistedOrderLogPath(
155
+ userName,
156
+ strategyName,
157
+ testName,
158
+ projectRoot
159
+ );
160
+ await writeJsonFile(absolutePath, orderLog);
161
+ return createRef(absolutePath, projectRoot);
162
+ };
163
+ var readPersistedBacktestOrderLog = async ({
164
+ projectRoot,
165
+ ref,
166
+ strategyName,
167
+ testName,
168
+ userName
169
+ }) => {
170
+ const absolutePath = ref ? resolveArtifactPath(ref.path, projectRoot) : getPersistedOrderLogPath(userName, strategyName, testName, projectRoot);
171
+ return readJsonFile(absolutePath, null);
172
+ };
173
+ var deletePersistedBacktestOrderLog = async ({
174
+ projectRoot,
175
+ ref,
176
+ strategyName,
177
+ testName,
178
+ userName
179
+ }) => {
180
+ const absolutePath = ref ? resolveArtifactPath(ref.path, projectRoot) : getPersistedOrderLogPath(userName, strategyName, testName, projectRoot);
181
+ return removeFileIfExists(absolutePath);
182
+ };
183
+ var parseBacktestArtifactRef = (value) => {
184
+ if (isBacktestArtifactRef(value)) {
185
+ return value;
186
+ }
187
+ return null;
188
+ };
189
+ var getBacktestArtifactsRootDir = (projectRoot) => path.join(resolveProjectRoot(projectRoot), BACKTEST_ARTIFACTS_DIR);
190
+ var getBacktestCacheArtifactsDirForUser = (userName, projectRoot) => path.join(
191
+ getBacktestArtifactsRootDir(projectRoot),
192
+ "cache",
193
+ encodeSegment(userName)
194
+ );
195
+ var getPersistedBacktestArtifactsDirForUser = (userName, projectRoot) => path.join(
196
+ getBacktestArtifactsRootDir(projectRoot),
197
+ "tests",
198
+ encodeSegment(userName)
199
+ );
200
+ export {
201
+ deleteCachedBacktestArtifacts,
202
+ deletePersistedBacktestOrderLog,
203
+ getBacktestArtifactsRootDir,
204
+ getBacktestCacheArtifactsDirForUser,
205
+ getPersistedBacktestArtifactsDirForUser,
206
+ parseBacktestArtifactRef,
207
+ readCachedBacktestArtifacts,
208
+ readPersistedBacktestOrderLog,
209
+ writeCachedBacktestArtifacts,
210
+ writePersistedBacktestOrderLog
211
+ };
@@ -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,18 @@ 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
+ 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}`,
445
497
  researchRuns: (userName) => `users:${userName}:research:runs:`,
446
498
  researchRun: (userName, runId) => `users:${userName}:research:runs:${runId}`,
447
499
  researchLatestRun: (userName, strategyName) => `users:${userName}:research:latest:${strategyName}`,
@@ -454,6 +506,8 @@ var redisKeys = {
454
506
  };
455
507
 
456
508
  export {
509
+ closeRedisConnection,
510
+ publishData,
457
511
  getKeys,
458
512
  getData,
459
513
  delKey,
@@ -461,6 +515,7 @@ export {
461
515
  delKeyWithOptions,
462
516
  setData,
463
517
  setHashJsonField,
518
+ getHashJsonField,
464
519
  getHashJsonValues,
465
520
  incrHashFields,
466
521
  getHashData,