@tradejs/node 3.1.0 → 3.1.2

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,798 @@
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/runtimeStrategies.ts
31
+ var runtimeStrategies_exports = {};
32
+ __export(runtimeStrategies_exports, {
33
+ getRuntimeStrategyPackageMetadata: () => getRuntimeStrategyPackageMetadata,
34
+ loadResolvedRuntimeStrategies: () => loadResolvedRuntimeStrategies
35
+ });
36
+ module.exports = __toCommonJS(runtimeStrategies_exports);
37
+ var import_promises = require("fs/promises");
38
+ var import_node_path = __toESM(require("path"));
39
+ var import_redis = require("@tradejs/infra/redis");
40
+ var import_runtimeStrategyReleases = require("@tradejs/infra/runtimeStrategyReleases");
41
+ var import_runtimeStrategyConfigs = require("@tradejs/infra/runtimeStrategyConfigs");
42
+ var import_tradingAccounts = require("@tradejs/infra/tradingAccounts");
43
+
44
+ // src/strategy/manifests.ts
45
+ var import_indicators = require("@tradejs/core/indicators");
46
+ var import_logger2 = require("@tradejs/infra/logger");
47
+
48
+ // src/tradejsConfig.ts
49
+ var import_fs = __toESM(require("fs"));
50
+ var import_path = __toESM(require("path"));
51
+ var import_url = require("url");
52
+ var import_config = require("@tradejs/core/config");
53
+ var import_logger = require("@tradejs/infra/logger");
54
+ var CONFIG_FILE_NAMES = [
55
+ "tradejs.config.ts",
56
+ "tradejs.config.mts",
57
+ "tradejs.config.js",
58
+ "tradejs.config.mjs",
59
+ "tradejs.config.cjs"
60
+ ];
61
+ var TS_MODULE_RE = /\.(cts|mts|ts)$/i;
62
+ var cachedByCwd = /* @__PURE__ */ new Map();
63
+ var announcedConfigFile = /* @__PURE__ */ new Set();
64
+ var tsNodeRegistered = false;
65
+ var tsconfigPathsRegisteredByCwd = /* @__PURE__ */ new Set();
66
+ var tsconfigPathMatchersByCwd = /* @__PURE__ */ new Map();
67
+ var getTradejsProjectCwd = (cwd) => {
68
+ const explicit = String(cwd ?? "").trim();
69
+ if (explicit) {
70
+ return import_path.default.resolve(explicit);
71
+ }
72
+ const fromEnv = String(process.env.PROJECT_CWD || "").trim();
73
+ if (fromEnv) {
74
+ return import_path.default.resolve(fromEnv);
75
+ }
76
+ return process.cwd();
77
+ };
78
+ var normalizeConfig = (rawConfig) => {
79
+ if (!rawConfig || typeof rawConfig !== "object") {
80
+ return {};
81
+ }
82
+ const config = rawConfig;
83
+ const strategies2 = Array.isArray(config.strategies) ? config.strategies.map((value) => String(value || "").trim()).filter(Boolean) : [];
84
+ const indicators = Array.isArray(config.indicators) ? config.indicators.map((value) => String(value || "").trim()).filter(Boolean) : [];
85
+ const connectors = Array.isArray(config.connectors) ? config.connectors.map((value) => String(value || "").trim()).filter(Boolean) : [];
86
+ const hooks = (0, import_config.normalizeTradejsConfigHooks)(
87
+ config.hooks
88
+ );
89
+ return {
90
+ strategies: strategies2,
91
+ indicators,
92
+ connectors,
93
+ ...hooks ? { hooks } : {}
94
+ };
95
+ };
96
+ var getNodeCreateRequire = () => {
97
+ const builtinModule = process.getBuiltinModule?.("module");
98
+ if (typeof builtinModule?.createRequire === "function") {
99
+ return builtinModule.createRequire;
100
+ }
101
+ throw new TypeError("module.createRequire is not available");
102
+ };
103
+ var getRequireFn = (cwd = getTradejsProjectCwd()) => getNodeCreateRequire()(import_path.default.join(import_path.default.resolve(cwd), "__tradejs_loader__.js"));
104
+ var ensureTsNodeRegistered = async () => {
105
+ if (tsNodeRegistered) {
106
+ return;
107
+ }
108
+ const tsNodeModule = await import("ts-node");
109
+ const tsNode = tsNodeModule.default ?? tsNodeModule;
110
+ tsNode.register?.({
111
+ transpileOnly: true,
112
+ compilerOptions: {
113
+ module: "Node16",
114
+ moduleResolution: "node16"
115
+ }
116
+ });
117
+ tsNodeRegistered = true;
118
+ };
119
+ var ensureTsconfigPathsRegistered = async (cwd = getTradejsProjectCwd()) => {
120
+ const projectRoot = getTradejsProjectCwd(cwd);
121
+ if (tsconfigPathsRegisteredByCwd.has(projectRoot)) {
122
+ return;
123
+ }
124
+ const tsconfigPathsModule = await import("tsconfig-paths");
125
+ const loadConfig = tsconfigPathsModule.loadConfig;
126
+ const register = tsconfigPathsModule.register;
127
+ if (typeof loadConfig !== "function" || typeof register !== "function") {
128
+ return;
129
+ }
130
+ const loadedConfig = loadConfig(projectRoot);
131
+ if (loadedConfig.resultType !== "success") {
132
+ return;
133
+ }
134
+ register({
135
+ baseUrl: loadedConfig.absoluteBaseUrl,
136
+ paths: loadedConfig.paths,
137
+ addMatchAll: false
138
+ });
139
+ tsconfigPathsRegisteredByCwd.add(projectRoot);
140
+ };
141
+ var resolveTsconfigPathModule = async (moduleName, cwd = getTradejsProjectCwd()) => {
142
+ const projectRoot = getTradejsProjectCwd(cwd);
143
+ const cachedMatcher = tsconfigPathMatchersByCwd.get(projectRoot);
144
+ if (cachedMatcher) {
145
+ const resolved2 = cachedMatcher(moduleName);
146
+ return resolved2 || null;
147
+ }
148
+ const tsconfigPathsModule = await import("tsconfig-paths");
149
+ const loadConfig = tsconfigPathsModule.loadConfig;
150
+ const createMatchPath = tsconfigPathsModule.createMatchPath;
151
+ if (typeof loadConfig !== "function" || typeof createMatchPath !== "function") {
152
+ return null;
153
+ }
154
+ const loadedConfig = loadConfig(projectRoot);
155
+ if (loadedConfig.resultType !== "success") {
156
+ return null;
157
+ }
158
+ const matchPath = createMatchPath(
159
+ loadedConfig.absoluteBaseUrl,
160
+ loadedConfig.paths
161
+ );
162
+ const matcher = (requestedModule) => matchPath(requestedModule, void 0, import_fs.default.existsSync, [
163
+ ".ts",
164
+ ".tsx",
165
+ ".mts",
166
+ ".cts",
167
+ ".js",
168
+ ".jsx",
169
+ ".mjs",
170
+ ".cjs",
171
+ ".json"
172
+ ]) || "";
173
+ tsconfigPathMatchersByCwd.set(projectRoot, matcher);
174
+ const resolved = matcher(moduleName);
175
+ return resolved || null;
176
+ };
177
+ var toImportSpecifier = (moduleName) => {
178
+ if (moduleName.startsWith("file://")) {
179
+ return moduleName;
180
+ }
181
+ if (import_path.default.isAbsolute(moduleName)) {
182
+ return (0, import_url.pathToFileURL)(moduleName).href;
183
+ }
184
+ return moduleName;
185
+ };
186
+ var isTsModulePath = (moduleName) => TS_MODULE_RE.test(moduleName.split("?")[0]);
187
+ var isRelativeModulePath = (moduleName) => moduleName.startsWith("./") || moduleName.startsWith("../");
188
+ var isBareModuleSpecifier = (moduleName) => {
189
+ const normalized = String(moduleName ?? "").trim();
190
+ if (!normalized) {
191
+ return false;
192
+ }
193
+ if (normalized.startsWith("file://") || import_path.default.isAbsolute(normalized) || isRelativeModulePath(normalized)) {
194
+ return false;
195
+ }
196
+ return true;
197
+ };
198
+ var importConfigFile = async (configFilePath) => {
199
+ const ext = import_path.default.extname(configFilePath).toLowerCase();
200
+ const configFileUrl = `${toImportSpecifier(configFilePath)}?t=${Date.now()}`;
201
+ if (ext === ".ts" || ext === ".mts") {
202
+ const requireFn = getRequireFn(import_path.default.dirname(configFilePath));
203
+ await ensureTsNodeRegistered();
204
+ await ensureTsconfigPathsRegistered(import_path.default.dirname(configFilePath));
205
+ return requireFn(configFilePath);
206
+ }
207
+ return import(
208
+ /* webpackIgnore: true */
209
+ configFileUrl
210
+ );
211
+ };
212
+ var importTradejsModule = async (moduleName, cwd = getTradejsProjectCwd()) => {
213
+ const normalized = String(moduleName ?? "").trim();
214
+ if (!normalized) {
215
+ return {};
216
+ }
217
+ let modulePath = normalized;
218
+ if (normalized.startsWith("file://")) {
219
+ try {
220
+ modulePath = (0, import_url.fileURLToPath)(normalized);
221
+ } catch {
222
+ modulePath = normalized;
223
+ }
224
+ }
225
+ const requireFn = getRequireFn(
226
+ import_path.default.isAbsolute(modulePath) ? import_path.default.dirname(modulePath) : cwd
227
+ );
228
+ if (isTsModulePath(modulePath)) {
229
+ await ensureTsNodeRegistered();
230
+ await ensureTsconfigPathsRegistered(cwd);
231
+ return requireFn(modulePath);
232
+ }
233
+ if (isBareModuleSpecifier(normalized)) {
234
+ await ensureTsconfigPathsRegistered(cwd);
235
+ try {
236
+ return requireFn(normalized);
237
+ } catch (error) {
238
+ const resolvedByTsconfig = await resolveTsconfigPathModule(
239
+ normalized,
240
+ cwd
241
+ );
242
+ if (resolvedByTsconfig && resolvedByTsconfig !== normalized) {
243
+ return requireFn(resolvedByTsconfig);
244
+ }
245
+ throw error;
246
+ }
247
+ }
248
+ try {
249
+ return await import(
250
+ /* webpackIgnore: true */
251
+ toImportSpecifier(normalized)
252
+ );
253
+ } catch (error) {
254
+ if (isTsModulePath(modulePath)) {
255
+ await ensureTsNodeRegistered();
256
+ await ensureTsconfigPathsRegistered(cwd);
257
+ return requireFn(modulePath);
258
+ }
259
+ throw error;
260
+ }
261
+ };
262
+ var resolveExportedConfig = (moduleExports) => {
263
+ const candidate = moduleExports && typeof moduleExports === "object" && "default" in moduleExports ? moduleExports.default : moduleExports;
264
+ return normalizeConfig(candidate);
265
+ };
266
+ var findConfigFilePath = (cwd) => {
267
+ let currentDir = import_path.default.resolve(cwd);
268
+ while (true) {
269
+ for (const fileName of CONFIG_FILE_NAMES) {
270
+ const fullPath = import_path.default.join(currentDir, fileName);
271
+ if (import_fs.default.existsSync(fullPath) && import_fs.default.statSync(fullPath).isFile()) {
272
+ return fullPath;
273
+ }
274
+ }
275
+ const parentDir = import_path.default.dirname(currentDir);
276
+ if (parentDir === currentDir) {
277
+ return null;
278
+ }
279
+ currentDir = parentDir;
280
+ }
281
+ };
282
+ var resolvePluginModuleSpecifier = (moduleName, cwd = getTradejsProjectCwd()) => {
283
+ const normalized = String(moduleName ?? "").trim();
284
+ if (!normalized) {
285
+ return "";
286
+ }
287
+ if (normalized.startsWith("file://")) {
288
+ try {
289
+ return (0, import_url.fileURLToPath)(normalized);
290
+ } catch {
291
+ return normalized;
292
+ }
293
+ }
294
+ if (import_path.default.isAbsolute(normalized)) {
295
+ return normalized;
296
+ }
297
+ if (isRelativeModulePath(normalized)) {
298
+ return import_path.default.resolve(cwd, normalized);
299
+ }
300
+ return normalized;
301
+ };
302
+ var loadTradejsConfig = async (cwd = getTradejsProjectCwd()) => {
303
+ const cached = cachedByCwd.get(cwd);
304
+ if (cached) {
305
+ return cached;
306
+ }
307
+ const configFilePath = findConfigFilePath(cwd);
308
+ if (!configFilePath) {
309
+ cachedByCwd.set(cwd, {});
310
+ return {};
311
+ }
312
+ try {
313
+ const moduleExports = await importConfigFile(configFilePath);
314
+ const config = resolveExportedConfig(moduleExports);
315
+ cachedByCwd.set(cwd, config);
316
+ if (!announcedConfigFile.has(configFilePath)) {
317
+ announcedConfigFile.add(configFilePath);
318
+ import_logger.logger.log("debug", "Loaded TradeJS config: %s", configFilePath);
319
+ }
320
+ return config;
321
+ } catch (error) {
322
+ import_logger.logger.log(
323
+ "warn",
324
+ "Failed to load TradeJS config from %s: %s",
325
+ configFilePath,
326
+ String(error)
327
+ );
328
+ cachedByCwd.set(cwd, {});
329
+ return {};
330
+ }
331
+ };
332
+
333
+ // src/strategy/manifests.ts
334
+ var SHARED_STRATEGY_REGISTRY_KEY = "__tradejsNodeSharedStrategyRegistryV1__";
335
+ var sharedRegistryScope = globalThis;
336
+ var sharedStrategyRegistry = sharedRegistryScope[SHARED_STRATEGY_REGISTRY_KEY] ?? (sharedRegistryScope[SHARED_STRATEGY_REGISTRY_KEY] = {
337
+ registryStateByProjectRoot: /* @__PURE__ */ new Map()
338
+ });
339
+ var createStrategyRegistryState = () => ({
340
+ strategyCreators: /* @__PURE__ */ new Map(),
341
+ strategyManifestsMap: /* @__PURE__ */ new Map(),
342
+ strategyEntriesMap: /* @__PURE__ */ new Map(),
343
+ strategySourcesMap: /* @__PURE__ */ new Map(),
344
+ pluginsLoadPromise: null
345
+ });
346
+ var registryStateByProjectRoot = sharedStrategyRegistry.registryStateByProjectRoot;
347
+ var getStrategyRegistryState = (cwd = getTradejsProjectCwd()) => {
348
+ const projectRoot = getTradejsProjectCwd(cwd);
349
+ let state = registryStateByProjectRoot.get(projectRoot);
350
+ if (!state) {
351
+ state = createStrategyRegistryState();
352
+ registryStateByProjectRoot.set(projectRoot, state);
353
+ }
354
+ return {
355
+ projectRoot,
356
+ state
357
+ };
358
+ };
359
+ var toUniqueModules = (modules = []) => [
360
+ ...new Set(modules.map((moduleName) => moduleName.trim()).filter(Boolean))
361
+ ];
362
+ var getConfiguredPluginModuleNames = async (cwd = getTradejsProjectCwd()) => {
363
+ const config = await loadTradejsConfig(cwd);
364
+ return {
365
+ strategyModules: toUniqueModules(config.strategies),
366
+ indicatorModules: toUniqueModules(config.indicators)
367
+ };
368
+ };
369
+ var extractModuleEntries = (moduleExport, key) => {
370
+ if (!moduleExport || typeof moduleExport !== "object") {
371
+ return null;
372
+ }
373
+ const candidate = moduleExport;
374
+ if (Array.isArray(candidate[key])) {
375
+ return candidate[key];
376
+ }
377
+ const defaultExport = candidate.default;
378
+ if (defaultExport && Array.isArray(defaultExport[key])) {
379
+ return defaultExport[key];
380
+ }
381
+ return null;
382
+ };
383
+ var extractStrategyPluginDefinition = (moduleExport) => {
384
+ const strategyEntries = extractModuleEntries(
385
+ moduleExport,
386
+ "strategyEntries"
387
+ );
388
+ return strategyEntries ? { strategyEntries } : null;
389
+ };
390
+ var extractIndicatorPluginDefinition = (moduleExport) => {
391
+ const indicatorEntries = extractModuleEntries(
392
+ moduleExport,
393
+ "indicatorEntries"
394
+ );
395
+ return indicatorEntries ? { indicatorEntries } : null;
396
+ };
397
+ var registerEntries = (entries, source, state) => {
398
+ for (const entry of entries) {
399
+ const strategyName = entry.manifest?.name;
400
+ if (!strategyName) {
401
+ import_logger2.logger.warn("Skip strategy entry without name from %s", source);
402
+ continue;
403
+ }
404
+ if (state.strategyCreators.has(strategyName)) {
405
+ import_logger2.logger.warn(
406
+ 'Skip duplicate strategy "%s" from %s: already registered',
407
+ strategyName,
408
+ source
409
+ );
410
+ continue;
411
+ }
412
+ state.strategyManifestsMap.set(strategyName, entry.manifest);
413
+ state.strategyEntriesMap.set(strategyName, entry);
414
+ state.strategySourcesMap.set(strategyName, source);
415
+ materializeStrategyCreator(strategyName, state);
416
+ }
417
+ };
418
+ var materializeStrategyCreator = (strategyName, state) => {
419
+ if (state.strategyCreators.has(strategyName) || !sharedStrategyRegistry.strategyRuntimeFactory) {
420
+ return;
421
+ }
422
+ const entry = state.strategyEntriesMap.get(strategyName);
423
+ if (!entry) return;
424
+ state.strategyCreators.set(
425
+ strategyName,
426
+ sharedStrategyRegistry.strategyRuntimeFactory({
427
+ strategyName,
428
+ defaults: entry.defaults,
429
+ createCore: entry.createCore,
430
+ manifest: entry.manifest,
431
+ detectorKey: entry.detectorKey,
432
+ detectorNoSignalSkipReason: entry.detectorNoSignalSkipReason,
433
+ resolveRegisteredManifest: (name) => state.strategyManifestsMap.get(name)
434
+ })
435
+ );
436
+ };
437
+ var importStrategyPluginModule = async (moduleName, cwd = getTradejsProjectCwd()) => {
438
+ if (typeof importTradejsModule === "function") {
439
+ return importTradejsModule(moduleName, cwd);
440
+ }
441
+ return import(
442
+ /* webpackIgnore: true */
443
+ moduleName
444
+ );
445
+ };
446
+ var ensureStrategyPluginsLoaded = async (cwd = getTradejsProjectCwd()) => {
447
+ const { projectRoot, state } = getStrategyRegistryState(cwd);
448
+ if (!state.pluginsLoadPromise) {
449
+ (0, import_indicators.resetIndicatorRegistryCache)(projectRoot);
450
+ state.pluginsLoadPromise = (async () => {
451
+ const { strategyModules, indicatorModules } = await getConfiguredPluginModuleNames(projectRoot);
452
+ const strategySet = new Set(strategyModules);
453
+ const indicatorSet = new Set(indicatorModules);
454
+ const pluginModuleNames = [
455
+ .../* @__PURE__ */ new Set([...strategyModules, ...indicatorModules])
456
+ ];
457
+ if (!pluginModuleNames.length) {
458
+ return;
459
+ }
460
+ for (const moduleName of pluginModuleNames) {
461
+ try {
462
+ const resolvedModuleName = resolvePluginModuleSpecifier(
463
+ moduleName,
464
+ projectRoot
465
+ );
466
+ const moduleExport = await importStrategyPluginModule(
467
+ resolvedModuleName,
468
+ projectRoot
469
+ );
470
+ if (strategySet.has(moduleName)) {
471
+ const pluginDefinition = extractStrategyPluginDefinition(moduleExport);
472
+ if (!pluginDefinition) {
473
+ import_logger2.logger.warn(
474
+ 'Skip strategy plugin "%s": export { strategyEntries } is missing',
475
+ moduleName
476
+ );
477
+ } else {
478
+ registerEntries(
479
+ pluginDefinition.strategyEntries,
480
+ moduleName,
481
+ state
482
+ );
483
+ }
484
+ }
485
+ if (indicatorSet.has(moduleName)) {
486
+ const indicatorPluginDefinition = extractIndicatorPluginDefinition(moduleExport);
487
+ if (!indicatorPluginDefinition) {
488
+ import_logger2.logger.warn(
489
+ 'Skip indicator plugin "%s": export { indicatorEntries } is missing',
490
+ moduleName
491
+ );
492
+ } else {
493
+ (0, import_indicators.registerIndicatorEntries)(
494
+ indicatorPluginDefinition.indicatorEntries,
495
+ moduleName,
496
+ projectRoot
497
+ );
498
+ }
499
+ }
500
+ if (!strategySet.has(moduleName) && !indicatorSet.has(moduleName)) {
501
+ import_logger2.logger.warn(
502
+ 'Skip plugin "%s": no strategy/indicator sections requested in config',
503
+ moduleName
504
+ );
505
+ }
506
+ } catch (error) {
507
+ import_logger2.logger.warn(
508
+ 'Failed to load plugin "%s": %s',
509
+ moduleName,
510
+ String(error)
511
+ );
512
+ }
513
+ }
514
+ })();
515
+ }
516
+ await state.pluginsLoadPromise;
517
+ };
518
+ var getStrategyCreator = async (name, cwd = getTradejsProjectCwd()) => {
519
+ await ensureStrategyPluginsLoaded(cwd);
520
+ const { state } = getStrategyRegistryState(cwd);
521
+ return state.strategyCreators.get(name);
522
+ };
523
+ var getStrategyPluginSource = async (name, cwd = getTradejsProjectCwd()) => {
524
+ await ensureStrategyPluginsLoaded(cwd);
525
+ const { state } = getStrategyRegistryState(cwd);
526
+ return state.strategySourcesMap.get(name);
527
+ };
528
+ var strategies = new Proxy(
529
+ {},
530
+ {
531
+ get: (_target, property) => {
532
+ if (typeof property !== "string") {
533
+ return void 0;
534
+ }
535
+ return getStrategyRegistryState().state.strategyCreators.get(property);
536
+ },
537
+ ownKeys: () => {
538
+ return [...getStrategyRegistryState().state.strategyCreators.keys()];
539
+ },
540
+ getOwnPropertyDescriptor: () => ({
541
+ enumerable: true,
542
+ configurable: true
543
+ })
544
+ }
545
+ );
546
+
547
+ // src/runtimeStrategies.ts
548
+ var readPackageManifest = async (projectRoot) => {
549
+ const candidates = [
550
+ process.env.TRADEJS_RUNTIME_PACKAGE_MANIFEST,
551
+ import_node_path.default.join(projectRoot, "runtime-package-manifest.json"),
552
+ "/app/runtime-package-manifest.json"
553
+ ].filter((candidate) => Boolean(candidate));
554
+ for (const candidate of candidates) {
555
+ try {
556
+ return JSON.parse(
557
+ await (0, import_promises.readFile)(candidate, "utf8")
558
+ );
559
+ } catch {
560
+ }
561
+ }
562
+ return { packages: {} };
563
+ };
564
+ var resolveInstalledPackageVersion = async (projectRoot, packageName, manifest) => {
565
+ if (!packageName || packageName === "runtime") return null;
566
+ const manifestVersion = manifest.packages?.[packageName];
567
+ if (manifestVersion) return manifestVersion;
568
+ try {
569
+ const packageJsonPath = import_node_path.default.join(
570
+ projectRoot,
571
+ "node_modules",
572
+ ...packageName.split("/"),
573
+ "package.json"
574
+ );
575
+ const packageJson = JSON.parse(await (0, import_promises.readFile)(packageJsonPath, "utf8"));
576
+ return packageJson.version ?? null;
577
+ } catch {
578
+ return null;
579
+ }
580
+ };
581
+ var validateReleaseRuntimeCompatibility = async ({
582
+ release,
583
+ projectRoot,
584
+ packageManifest
585
+ }) => {
586
+ const installedStrategyVersion = await resolveInstalledPackageVersion(
587
+ projectRoot,
588
+ release.strategyPackage,
589
+ packageManifest
590
+ );
591
+ if (release.strategyPackageVersion && installedStrategyVersion && release.strategyPackageVersion !== installedStrategyVersion) {
592
+ throw new Error(
593
+ `${release.strategyName} v${release.releaseVersion} requires ${release.strategyPackage}@${release.strategyPackageVersion}, image has ${installedStrategyVersion}`
594
+ );
595
+ }
596
+ const installedRuntimeVersion = packageManifest.packages?.["@tradejs/node"];
597
+ if (release.runtimePackageVersion && installedRuntimeVersion && release.runtimePackageVersion !== installedRuntimeVersion) {
598
+ throw new Error(
599
+ `${release.strategyName} v${release.releaseVersion} requires @tradejs/node@${release.runtimePackageVersion}, image has ${installedRuntimeVersion}`
600
+ );
601
+ }
602
+ };
603
+ var resolveAccountId = async ({
604
+ userName,
605
+ deployment,
606
+ connectorName,
607
+ universe,
608
+ legacyAccountId
609
+ }) => {
610
+ const requestedAccountId = deployment?.accountId ?? legacyAccountId;
611
+ const account = await (0, import_tradingAccounts.resolveTradingAccount)({
612
+ userName,
613
+ accountId: requestedAccountId,
614
+ provider: deployment?.provider ?? connectorName,
615
+ universe
616
+ });
617
+ return account?.id ?? requestedAccountId;
618
+ };
619
+ var loadVersionedRuntimeStrategies = async ({
620
+ userName,
621
+ projectRoot,
622
+ deployment,
623
+ connectorName
624
+ }) => {
625
+ const packageManifest = await readPackageManifest(projectRoot);
626
+ return Promise.all(
627
+ deployment.strategies.map(async (reference) => {
628
+ if (!Number.isSafeInteger(reference.releaseVersion) || !reference.releaseVersion) {
629
+ throw new Error(
630
+ `Deployment ${deployment.id} strategy ${reference.strategyName} has no releaseVersion`
631
+ );
632
+ }
633
+ if (reference.config && Object.keys(reference.config).length) {
634
+ throw new Error(
635
+ `Deployment ${deployment.id} must not embed config for ${reference.strategyName}`
636
+ );
637
+ }
638
+ const release = await (0, import_runtimeStrategyReleases.getRuntimeStrategyRelease)(
639
+ userName,
640
+ reference.strategyName,
641
+ reference.releaseVersion
642
+ );
643
+ if (!release) {
644
+ throw new Error(
645
+ `Runtime release not found: ${reference.strategyName} v${reference.releaseVersion}`
646
+ );
647
+ }
648
+ await validateReleaseRuntimeCompatibility({
649
+ release,
650
+ projectRoot,
651
+ packageManifest
652
+ });
653
+ const strategyCreator = await getStrategyCreator(
654
+ reference.strategyName,
655
+ projectRoot
656
+ );
657
+ if (!strategyCreator) {
658
+ throw new Error(`Unknown strategy: ${reference.strategyName}`);
659
+ }
660
+ const interval = String(release.config.INTERVAL);
661
+ const universe = release.config.UNIVERSE;
662
+ const accountId = await resolveAccountId({
663
+ userName,
664
+ deployment,
665
+ connectorName,
666
+ universe
667
+ });
668
+ return {
669
+ strategyName: reference.strategyName,
670
+ releaseVersion: release.releaseVersion,
671
+ controlState: reference.controlState ?? "active",
672
+ interval,
673
+ universe,
674
+ accountId,
675
+ strategyPackage: release.strategyPackage,
676
+ strategyPackageVersion: release.strategyPackageVersion,
677
+ runtimePackageVersion: release.runtimePackageVersion,
678
+ strategyCreator,
679
+ sourceStrategyConfig: release.config,
680
+ strategyConfig: release.config,
681
+ // Symbol result configs are mutable legacy overlays and are not read by v2.
682
+ strategyResults: {}
683
+ };
684
+ })
685
+ );
686
+ };
687
+ var loadLegacyRuntimeStrategies = async ({
688
+ userName,
689
+ projectRoot,
690
+ deployment,
691
+ connectorName
692
+ }) => {
693
+ const deploymentStrategies = new Map(
694
+ (deployment?.strategies ?? []).map((strategy) => [
695
+ strategy.strategyName,
696
+ strategy
697
+ ])
698
+ );
699
+ const candidates = await Promise.all(
700
+ (await (0, import_runtimeStrategyConfigs.loadRuntimeStrategyConfigs)(userName)).map(async (record) => {
701
+ const binding = deploymentStrategies.get(record.strategyName);
702
+ if (binding?.enabled === false || record.strategyConfig.ENABLE === false) {
703
+ return null;
704
+ }
705
+ const universe = record.strategyConfig.UNIVERSE === "tradfi" ? "tradfi" : "crypto";
706
+ const interval = String(
707
+ record.strategyConfig.INTERVAL ?? "15"
708
+ );
709
+ const accountId = await resolveAccountId({
710
+ userName,
711
+ deployment,
712
+ connectorName,
713
+ universe,
714
+ legacyAccountId: typeof record.strategyConfig.ACCOUNT_ID === "string" ? record.strategyConfig.ACCOUNT_ID : void 0
715
+ });
716
+ const [strategyCreator, strategyResults] = await Promise.all([
717
+ getStrategyCreator(record.strategyName, projectRoot),
718
+ (0, import_redis.getData)(import_redis.redisKeys.strategyResults(userName, record.strategyName), {})
719
+ ]);
720
+ if (!strategyCreator) return null;
721
+ return {
722
+ strategyName: record.strategyName,
723
+ configId: record.configId,
724
+ controlState: "active",
725
+ interval,
726
+ universe,
727
+ accountId,
728
+ strategyCreator,
729
+ sourceStrategyConfig: record.strategyConfig,
730
+ strategyConfig: record.strategyConfig,
731
+ strategyResults: strategyResults ?? {}
732
+ };
733
+ })
734
+ );
735
+ return candidates.filter(Boolean);
736
+ };
737
+ var loadResolvedRuntimeStrategies = async ({
738
+ userName,
739
+ projectRoot,
740
+ deployment,
741
+ connectorName = "bybit",
742
+ universe,
743
+ accountId,
744
+ interval
745
+ }) => {
746
+ const hasVersionedReferences = Boolean(
747
+ deployment?.strategies.some((strategy) => strategy.releaseVersion != null)
748
+ );
749
+ if (hasVersionedReferences && deployment?.strategies.some((strategy) => strategy.releaseVersion == null)) {
750
+ throw new Error(
751
+ `Deployment ${deployment.id} mixes legacy configs and versioned releases`
752
+ );
753
+ }
754
+ const strategies2 = hasVersionedReferences ? await loadVersionedRuntimeStrategies({
755
+ userName,
756
+ projectRoot,
757
+ deployment,
758
+ connectorName
759
+ }) : await loadLegacyRuntimeStrategies({
760
+ userName,
761
+ projectRoot,
762
+ deployment,
763
+ connectorName
764
+ });
765
+ const filtered = strategies2.filter(
766
+ (candidate) => (!universe || candidate.universe === universe) && (!interval || String(candidate.interval) === String(interval)) && (!accountId || candidate.accountId === accountId)
767
+ );
768
+ const identities = /* @__PURE__ */ new Set();
769
+ for (const candidate of filtered) {
770
+ const identity = `${candidate.strategyName}:${candidate.accountId ?? "default"}`;
771
+ if (identities.has(identity)) {
772
+ throw new Error(`Runtime strategy conflict: ${identity}`);
773
+ }
774
+ identities.add(identity);
775
+ }
776
+ return filtered;
777
+ };
778
+ var getRuntimeStrategyPackageMetadata = async ({
779
+ strategyName,
780
+ projectRoot
781
+ }) => {
782
+ const packageManifest = await readPackageManifest(projectRoot);
783
+ const strategyPackage = await getStrategyPluginSource(strategyName, projectRoot) ?? null;
784
+ return {
785
+ strategyPackage,
786
+ strategyPackageVersion: await resolveInstalledPackageVersion(
787
+ projectRoot,
788
+ strategyPackage,
789
+ packageManifest
790
+ ),
791
+ runtimePackageVersion: packageManifest.packages?.["@tradejs/node"] ?? null
792
+ };
793
+ };
794
+ // Annotate the CommonJS export names for ESM import in node:
795
+ 0 && (module.exports = {
796
+ getRuntimeStrategyPackageMetadata,
797
+ loadResolvedRuntimeStrategies
798
+ });