@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.
- package/README.md +2 -0
- package/dist/ai.js +1 -0
- package/dist/ai.mjs +2 -1
- package/dist/backtest.js +19 -0
- package/dist/backtest.mjs +5 -3
- package/dist/{chunk-KOQSLFT2.mjs → chunk-SEQJ6V6B.mjs} +2 -283
- package/dist/{chunk-MKLTJQLY.mjs → chunk-W26Y6IRP.mjs} +22 -3
- package/dist/chunk-XN7BC7XK.mjs +295 -0
- package/dist/cli.js +2 -0
- package/dist/cli.mjs +4 -2
- package/dist/registry-DHTLjQcr.d.mts +17 -0
- package/dist/registry-DHTLjQcr.d.ts +17 -0
- package/dist/registry.d.mts +2 -16
- package/dist/registry.d.ts +2 -16
- package/dist/registry.js +19 -0
- package/dist/registry.mjs +3 -2
- package/dist/runtimeDashboard.js +93 -24
- package/dist/runtimeDashboard.mjs +71 -26
- package/dist/runtimeStrategies.d.mts +38 -0
- package/dist/runtimeStrategies.d.ts +38 -0
- package/dist/runtimeStrategies.js +798 -0
- package/dist/runtimeStrategies.mjs +264 -0
- package/dist/strategies.d.mts +1 -1
- package/dist/strategies.d.ts +1 -1
- package/dist/strategies.js +26 -0
- package/dist/strategies.mjs +14 -10
- package/package.json +13 -5
|
@@ -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
|
+
});
|