@tradejs/node 3.1.11 → 3.1.12-beta.217
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/dist/ai.js +0 -1
- package/dist/ai.mjs +1 -1
- package/dist/backtest.js +99 -82
- package/dist/backtest.mjs +2 -2
- package/dist/{chunk-B6X2HEGL.mjs → chunk-6PIVDS4M.mjs} +6 -6
- package/dist/{chunk-C3KRBNUR.mjs → chunk-IKRSNRP6.mjs} +58 -31
- package/dist/chunk-NQO2MFP6.mjs +552 -0
- package/dist/cli.js +108 -91
- package/dist/cli.mjs +1 -1
- package/dist/registry.js +86 -65
- package/dist/registry.mjs +2 -2
- package/dist/runtimeDashboard.js +420 -216
- package/dist/runtimeDashboard.mjs +9 -9
- package/dist/runtimeStrategies.d.mts +66 -6
- package/dist/runtimeStrategies.d.ts +66 -6
- package/dist/runtimeStrategies.js +450 -218
- package/dist/runtimeStrategies.mjs +11 -3
- package/dist/strategies.js +97 -76
- package/dist/strategies.mjs +2 -2
- package/package.json +14 -8
- package/dist/chunk-7BAL7EN3.mjs +0 -345
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getStrategyCreator,
|
|
3
|
+
getStrategyEntry,
|
|
4
|
+
getStrategyPluginSource
|
|
5
|
+
} from "./chunk-IKRSNRP6.mjs";
|
|
6
|
+
import {
|
|
7
|
+
loadTradejsConfig
|
|
8
|
+
} from "./chunk-LZDXRXIU.mjs";
|
|
9
|
+
|
|
10
|
+
// src/runtimeStrategies.ts
|
|
11
|
+
import { createHash } from "crypto";
|
|
12
|
+
import { readFile } from "fs/promises";
|
|
13
|
+
import path from "path";
|
|
14
|
+
import { getRuntimeControls } from "@tradejs/infra/runtimeControls";
|
|
15
|
+
import { resolveTradingAccount } from "@tradejs/infra/tradingAccounts";
|
|
16
|
+
var RUNTIME_PACKAGE_MANIFEST_SCHEMA = "tradejs-runtime-package-manifest/v1";
|
|
17
|
+
var INTERVALS = /* @__PURE__ */ new Set([
|
|
18
|
+
"1",
|
|
19
|
+
"3",
|
|
20
|
+
"5",
|
|
21
|
+
"15",
|
|
22
|
+
"30",
|
|
23
|
+
"60",
|
|
24
|
+
"120",
|
|
25
|
+
"240",
|
|
26
|
+
"360",
|
|
27
|
+
"720",
|
|
28
|
+
"D",
|
|
29
|
+
"W",
|
|
30
|
+
"M"
|
|
31
|
+
]);
|
|
32
|
+
var RUNTIME_KEYS = /* @__PURE__ */ new Set(["deployments"]);
|
|
33
|
+
var DEPLOYMENT_KEYS = /* @__PURE__ */ new Set([
|
|
34
|
+
"label",
|
|
35
|
+
"connectorName",
|
|
36
|
+
"provider",
|
|
37
|
+
"accountId",
|
|
38
|
+
"enabled",
|
|
39
|
+
"strategies",
|
|
40
|
+
"assetClasses",
|
|
41
|
+
"tickers"
|
|
42
|
+
]);
|
|
43
|
+
var STRATEGY_KEYS = /* @__PURE__ */ new Set(["generation", "enabled", "selection", "config"]);
|
|
44
|
+
var SELECTION_KEYS = /* @__PURE__ */ new Set(["tickers"]);
|
|
45
|
+
var FORBIDDEN_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
46
|
+
"ACCOUNT_ID",
|
|
47
|
+
"DEPLOYMENT_ID",
|
|
48
|
+
"CONNECTOR_NAME",
|
|
49
|
+
"ENABLE"
|
|
50
|
+
]);
|
|
51
|
+
var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
52
|
+
var normalizeForCanonicalJson = (value) => {
|
|
53
|
+
if (Array.isArray(value)) return value.map(normalizeForCanonicalJson);
|
|
54
|
+
if (isRecord(value)) {
|
|
55
|
+
return Object.fromEntries(
|
|
56
|
+
Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, nestedValue]) => [
|
|
57
|
+
key,
|
|
58
|
+
normalizeForCanonicalJson(nestedValue)
|
|
59
|
+
])
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return value;
|
|
63
|
+
};
|
|
64
|
+
var revision = (prefix, value) => `${prefix}:${createHash("sha256").update(JSON.stringify(normalizeForCanonicalJson(value))).digest("hex").slice(0, 16)}`;
|
|
65
|
+
var computeStrategyRevision = ({
|
|
66
|
+
strategyName,
|
|
67
|
+
strategyPackage,
|
|
68
|
+
strategyPackageVersion,
|
|
69
|
+
strategyDependencyVersions,
|
|
70
|
+
runtimePackageVersion,
|
|
71
|
+
strategyConfig
|
|
72
|
+
}) => revision("sr1", {
|
|
73
|
+
schema: "tradejs-strategy-revision/v1",
|
|
74
|
+
strategyName,
|
|
75
|
+
strategyPackage,
|
|
76
|
+
strategyPackageVersion,
|
|
77
|
+
strategyDependencyVersions,
|
|
78
|
+
runtimePackageVersion,
|
|
79
|
+
strategyConfig
|
|
80
|
+
});
|
|
81
|
+
var computeDeploymentCompositionId = (value) => revision("dc1", {
|
|
82
|
+
schema: "tradejs-deployment-composition/v1",
|
|
83
|
+
...value,
|
|
84
|
+
assetClasses: value.assetClasses ? [...value.assetClasses].sort((left, right) => left.localeCompare(right)) : void 0,
|
|
85
|
+
strategies: [...value.strategies].sort(
|
|
86
|
+
(left, right) => left.strategyName.localeCompare(right.strategyName)
|
|
87
|
+
).map((strategy) => ({
|
|
88
|
+
...strategy,
|
|
89
|
+
selection: strategy.selection ? {
|
|
90
|
+
tickers: [...strategy.selection.tickers].sort(
|
|
91
|
+
(left, right) => left.localeCompare(right)
|
|
92
|
+
)
|
|
93
|
+
} : void 0
|
|
94
|
+
}))
|
|
95
|
+
});
|
|
96
|
+
var readRuntimePackageManifest = async (projectRoot) => {
|
|
97
|
+
const manifestPath = process.env.TRADEJS_RUNTIME_PACKAGE_MANIFEST?.trim() || path.join(projectRoot, "runtime-package-manifest.json");
|
|
98
|
+
let value;
|
|
99
|
+
try {
|
|
100
|
+
value = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
101
|
+
} catch (error) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`Unable to read runtime package manifest ${manifestPath}: ${String(error)}`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
if (!isRecord(value) || Object.keys(value).some(
|
|
107
|
+
(key) => !["schema", "projectSha", "packages"].includes(key)
|
|
108
|
+
) || value.schema !== RUNTIME_PACKAGE_MANIFEST_SCHEMA || typeof value.projectSha !== "string" || !/^[a-f0-9]{40}$/.test(value.projectSha) || !isRecord(value.packages) || Object.keys(value.packages).length === 0 || Object.entries(value.packages).some(
|
|
109
|
+
([packageName, version]) => !packageName.trim() || typeof version !== "string" || !version.trim()
|
|
110
|
+
)) {
|
|
111
|
+
throw new Error(`Invalid runtime package manifest: ${manifestPath}`);
|
|
112
|
+
}
|
|
113
|
+
return value;
|
|
114
|
+
};
|
|
115
|
+
var readInstalledPackageMetadata = async ({
|
|
116
|
+
projectRoot,
|
|
117
|
+
packageName,
|
|
118
|
+
projectPackage
|
|
119
|
+
}) => {
|
|
120
|
+
const packageJsonPath = projectPackage ? path.join(projectRoot, "package.json") : path.join(
|
|
121
|
+
projectRoot,
|
|
122
|
+
"node_modules",
|
|
123
|
+
...packageName.split("/"),
|
|
124
|
+
"package.json"
|
|
125
|
+
);
|
|
126
|
+
try {
|
|
127
|
+
const packageJson = JSON.parse(await readFile(packageJsonPath, "utf8"));
|
|
128
|
+
if (packageJson.name !== void 0 && packageJson.name !== packageName || typeof packageJson.version !== "string" || !packageJson.version.trim()) {
|
|
129
|
+
throw new Error("name or version is invalid");
|
|
130
|
+
}
|
|
131
|
+
const dependencyNames = [
|
|
132
|
+
...isRecord(packageJson.dependencies) ? Object.keys(packageJson.dependencies) : [],
|
|
133
|
+
...isRecord(packageJson.peerDependencies) ? Object.keys(packageJson.peerDependencies) : []
|
|
134
|
+
];
|
|
135
|
+
return {
|
|
136
|
+
version: packageJson.version,
|
|
137
|
+
runtimeDependencies: [...new Set(dependencyNames)].filter((name) => name.startsWith("@tradejs/")).sort((left, right) => left.localeCompare(right))
|
|
138
|
+
};
|
|
139
|
+
} catch (error) {
|
|
140
|
+
throw new Error(
|
|
141
|
+
`Installed package manifest not found for ${packageName}: ${String(error)}`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
var resolveVerifiedPackageVersion = async ({
|
|
146
|
+
projectRoot,
|
|
147
|
+
packageName,
|
|
148
|
+
manifest,
|
|
149
|
+
projectPackage = false
|
|
150
|
+
}) => {
|
|
151
|
+
const declaredVersion = manifest.packages[packageName];
|
|
152
|
+
if (!declaredVersion) {
|
|
153
|
+
throw new Error(`Runtime package manifest is missing ${packageName}`);
|
|
154
|
+
}
|
|
155
|
+
const installed = await readInstalledPackageMetadata({
|
|
156
|
+
projectRoot,
|
|
157
|
+
packageName,
|
|
158
|
+
projectPackage
|
|
159
|
+
});
|
|
160
|
+
if (declaredVersion !== installed.version) {
|
|
161
|
+
throw new Error(
|
|
162
|
+
`Runtime package manifest mismatch for ${packageName}: declared=${declaredVersion} installed=${installed.version}`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
return installed.version;
|
|
166
|
+
};
|
|
167
|
+
var resolveVerifiedStrategyDependencyVersions = async ({
|
|
168
|
+
projectRoot,
|
|
169
|
+
strategyPackage,
|
|
170
|
+
manifest
|
|
171
|
+
}) => {
|
|
172
|
+
const metadata = await readInstalledPackageMetadata({
|
|
173
|
+
projectRoot,
|
|
174
|
+
packageName: strategyPackage.name,
|
|
175
|
+
projectPackage: strategyPackage.projectPackage
|
|
176
|
+
});
|
|
177
|
+
return Object.fromEntries(
|
|
178
|
+
await Promise.all(
|
|
179
|
+
metadata.runtimeDependencies.map(async (packageName) => [
|
|
180
|
+
packageName,
|
|
181
|
+
await resolveVerifiedPackageVersion({
|
|
182
|
+
projectRoot,
|
|
183
|
+
packageName,
|
|
184
|
+
manifest
|
|
185
|
+
})
|
|
186
|
+
])
|
|
187
|
+
)
|
|
188
|
+
);
|
|
189
|
+
};
|
|
190
|
+
var resolveStrategyPackage = async ({
|
|
191
|
+
pluginSource,
|
|
192
|
+
projectRoot
|
|
193
|
+
}) => {
|
|
194
|
+
if (!pluginSource) return null;
|
|
195
|
+
if (!pluginSource.startsWith(".") && !path.isAbsolute(pluginSource)) {
|
|
196
|
+
return { name: pluginSource, projectPackage: false };
|
|
197
|
+
}
|
|
198
|
+
try {
|
|
199
|
+
const packageJson = JSON.parse(
|
|
200
|
+
await readFile(path.join(projectRoot, "package.json"), "utf8")
|
|
201
|
+
);
|
|
202
|
+
return typeof packageJson.name === "string" && packageJson.name.trim() ? { name: packageJson.name, projectPackage: true } : null;
|
|
203
|
+
} catch {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
var verifyStringSet = (value) => value === void 0 || Array.isArray(value) && value.length > 0 && value.every(
|
|
208
|
+
(item) => typeof item === "string" && item.length > 0 && item === item.trim()
|
|
209
|
+
) && new Set(value).size === value.length;
|
|
210
|
+
var verifyStrategySelection = (value) => value === void 0 || isRecord(value) && Object.keys(value).length === 1 && Object.keys(value).every((key) => SELECTION_KEYS.has(key)) && value.tickers !== void 0 && verifyStringSet(value.tickers);
|
|
211
|
+
var cloneSelection = (selection) => selection ? { tickers: [...selection.tickers] } : void 0;
|
|
212
|
+
var resolveStrategySelection = ({
|
|
213
|
+
deployment,
|
|
214
|
+
strategy
|
|
215
|
+
}) => cloneSelection(
|
|
216
|
+
strategy.selection ?? (deployment.tickers ? { tickers: deployment.tickers } : void 0)
|
|
217
|
+
);
|
|
218
|
+
var verifyDeploymentDeclaration = (deploymentId, value) => {
|
|
219
|
+
if (!deploymentId.trim() || !isRecord(value) || Object.keys(value).some((key) => !DEPLOYMENT_KEYS.has(key)) || typeof value.connectorName !== "string" || !value.connectorName.trim() || typeof value.accountId !== "string" || !value.accountId.trim() || value.label !== void 0 && typeof value.label !== "string" || value.provider !== void 0 && typeof value.provider !== "string" || value.enabled !== void 0 && typeof value.enabled !== "boolean" || !verifyStringSet(value.assetClasses) || !verifyStringSet(value.tickers) || !isRecord(value.strategies) || !Object.keys(value.strategies).length) {
|
|
220
|
+
throw new Error(`Invalid runtime deployment declaration: ${deploymentId}`);
|
|
221
|
+
}
|
|
222
|
+
for (const [strategyName, strategyValue] of Object.entries(
|
|
223
|
+
value.strategies
|
|
224
|
+
)) {
|
|
225
|
+
if (!strategyName.trim() || !isRecord(strategyValue) || Object.keys(strategyValue).some((key) => !STRATEGY_KEYS.has(key)) || strategyValue.generation !== void 0 && (typeof strategyValue.generation !== "string" || !strategyValue.generation.trim()) || typeof strategyValue.enabled !== "boolean" || !verifyStrategySelection(strategyValue.selection) || !isRecord(strategyValue.config) || Object.keys(strategyValue.config).some(
|
|
226
|
+
(key) => FORBIDDEN_CONFIG_KEYS.has(key)
|
|
227
|
+
) || !INTERVALS.has(String(strategyValue.config.INTERVAL)) || !["crypto", "tradfi"].includes(String(strategyValue.config.UNIVERSE))) {
|
|
228
|
+
throw new Error(
|
|
229
|
+
`Invalid runtime strategy declaration: ${deploymentId}/${strategyName}`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return value;
|
|
234
|
+
};
|
|
235
|
+
var verifyRuntimeDeclaration = (value) => {
|
|
236
|
+
if (!isRecord(value) || Object.keys(value).some((key) => !RUNTIME_KEYS.has(key)) || !isRecord(value.deployments) || !Object.keys(value.deployments).length) {
|
|
237
|
+
throw new Error("Invalid runtime declaration");
|
|
238
|
+
}
|
|
239
|
+
for (const [deploymentId, deployment] of Object.entries(value.deployments)) {
|
|
240
|
+
verifyDeploymentDeclaration(deploymentId, deployment);
|
|
241
|
+
}
|
|
242
|
+
return value;
|
|
243
|
+
};
|
|
244
|
+
var loadRuntimeDeclaration = async (projectRoot) => {
|
|
245
|
+
const projectConfig = await loadTradejsConfig(projectRoot);
|
|
246
|
+
if (!projectConfig.runtime) {
|
|
247
|
+
throw new Error("Runtime declaration is required in tradejs.config.ts");
|
|
248
|
+
}
|
|
249
|
+
return verifyRuntimeDeclaration(projectConfig.runtime);
|
|
250
|
+
};
|
|
251
|
+
var resolveStrategyComposition = async ({
|
|
252
|
+
strategyName,
|
|
253
|
+
declaration,
|
|
254
|
+
deployment,
|
|
255
|
+
projectRoot,
|
|
256
|
+
packageManifest
|
|
257
|
+
}) => {
|
|
258
|
+
const [strategyEntry, strategyCreator, pluginSource] = await Promise.all([
|
|
259
|
+
getStrategyEntry(strategyName, projectRoot),
|
|
260
|
+
getStrategyCreator(strategyName, projectRoot),
|
|
261
|
+
getStrategyPluginSource(strategyName, projectRoot)
|
|
262
|
+
]);
|
|
263
|
+
if (!strategyEntry || !strategyCreator) {
|
|
264
|
+
throw new Error(`Unknown strategy: ${strategyName}`);
|
|
265
|
+
}
|
|
266
|
+
if (typeof strategyEntry.parseConfig !== "function") {
|
|
267
|
+
throw new Error(`Strategy config parser is missing: ${strategyName}`);
|
|
268
|
+
}
|
|
269
|
+
const strategyPackage = await resolveStrategyPackage({
|
|
270
|
+
pluginSource: pluginSource ?? null,
|
|
271
|
+
projectRoot
|
|
272
|
+
});
|
|
273
|
+
if (!strategyPackage) {
|
|
274
|
+
throw new Error(`Installed strategy package not found: ${strategyName}`);
|
|
275
|
+
}
|
|
276
|
+
const [
|
|
277
|
+
strategyPackageVersion,
|
|
278
|
+
strategyDependencyVersions,
|
|
279
|
+
runtimePackageVersion
|
|
280
|
+
] = await Promise.all([
|
|
281
|
+
resolveVerifiedPackageVersion({
|
|
282
|
+
projectRoot,
|
|
283
|
+
packageName: strategyPackage.name,
|
|
284
|
+
projectPackage: strategyPackage.projectPackage,
|
|
285
|
+
manifest: packageManifest
|
|
286
|
+
}),
|
|
287
|
+
resolveVerifiedStrategyDependencyVersions({
|
|
288
|
+
projectRoot,
|
|
289
|
+
strategyPackage,
|
|
290
|
+
manifest: packageManifest
|
|
291
|
+
}),
|
|
292
|
+
resolveVerifiedPackageVersion({
|
|
293
|
+
projectRoot,
|
|
294
|
+
packageName: "@tradejs/node",
|
|
295
|
+
manifest: packageManifest
|
|
296
|
+
})
|
|
297
|
+
]);
|
|
298
|
+
const parsedConfig = strategyEntry.parseConfig(declaration.config);
|
|
299
|
+
if (!isRecord(parsedConfig)) {
|
|
300
|
+
throw new Error(
|
|
301
|
+
`Strategy config parser returned a non-object: ${strategyName}`
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
const strategyConfig = parsedConfig;
|
|
305
|
+
const selection = resolveStrategySelection({
|
|
306
|
+
deployment,
|
|
307
|
+
strategy: declaration
|
|
308
|
+
});
|
|
309
|
+
return {
|
|
310
|
+
strategyName,
|
|
311
|
+
strategyRevision: computeStrategyRevision({
|
|
312
|
+
strategyName,
|
|
313
|
+
strategyPackage: strategyPackage.name,
|
|
314
|
+
strategyPackageVersion,
|
|
315
|
+
strategyDependencyVersions,
|
|
316
|
+
runtimePackageVersion,
|
|
317
|
+
strategyConfig
|
|
318
|
+
}),
|
|
319
|
+
...declaration.generation ? { generation: declaration.generation } : {},
|
|
320
|
+
enabled: declaration.enabled,
|
|
321
|
+
interval: String(strategyConfig.INTERVAL),
|
|
322
|
+
universe: strategyConfig.UNIVERSE,
|
|
323
|
+
strategyPackage: strategyPackage.name,
|
|
324
|
+
strategyPackageVersion,
|
|
325
|
+
strategyDependencyVersions,
|
|
326
|
+
runtimePackageVersion,
|
|
327
|
+
strategyCreator,
|
|
328
|
+
sourceStrategyConfig: declaration.config,
|
|
329
|
+
strategyConfig,
|
|
330
|
+
...selection ? { selection } : {}
|
|
331
|
+
};
|
|
332
|
+
};
|
|
333
|
+
var resolveRuntimeComposition = async ({
|
|
334
|
+
projectRoot
|
|
335
|
+
}) => {
|
|
336
|
+
const [runtime, packageManifest] = await Promise.all([
|
|
337
|
+
loadRuntimeDeclaration(projectRoot),
|
|
338
|
+
readRuntimePackageManifest(projectRoot)
|
|
339
|
+
]);
|
|
340
|
+
const deployments = await Promise.all(
|
|
341
|
+
Object.entries(runtime.deployments).map(
|
|
342
|
+
async ([deploymentId, declaration]) => {
|
|
343
|
+
const strategies = await Promise.all(
|
|
344
|
+
Object.entries(declaration.strategies).map(
|
|
345
|
+
([strategyName, strategyDeclaration]) => resolveStrategyComposition({
|
|
346
|
+
strategyName,
|
|
347
|
+
declaration: strategyDeclaration,
|
|
348
|
+
deployment: declaration,
|
|
349
|
+
projectRoot,
|
|
350
|
+
packageManifest
|
|
351
|
+
})
|
|
352
|
+
)
|
|
353
|
+
);
|
|
354
|
+
const provider = (declaration.provider || declaration.connectorName).trim().toLowerCase();
|
|
355
|
+
return {
|
|
356
|
+
deploymentId,
|
|
357
|
+
deploymentCompositionId: computeDeploymentCompositionId({
|
|
358
|
+
deploymentId,
|
|
359
|
+
connectorName: declaration.connectorName.trim(),
|
|
360
|
+
provider,
|
|
361
|
+
accountId: declaration.accountId.trim(),
|
|
362
|
+
enabled: declaration.enabled ?? true,
|
|
363
|
+
...declaration.assetClasses ? { assetClasses: declaration.assetClasses } : {},
|
|
364
|
+
strategies: strategies.map((strategy) => ({
|
|
365
|
+
strategyName: strategy.strategyName,
|
|
366
|
+
strategyRevision: strategy.strategyRevision,
|
|
367
|
+
enabled: strategy.enabled,
|
|
368
|
+
...strategy.selection ? { selection: strategy.selection } : {}
|
|
369
|
+
}))
|
|
370
|
+
}),
|
|
371
|
+
declaration,
|
|
372
|
+
strategies
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
)
|
|
376
|
+
);
|
|
377
|
+
return {
|
|
378
|
+
deployments: deployments.sort(
|
|
379
|
+
(left, right) => left.deploymentId.localeCompare(right.deploymentId)
|
|
380
|
+
)
|
|
381
|
+
};
|
|
382
|
+
};
|
|
383
|
+
var toRuntimeDeployment = ({
|
|
384
|
+
composition,
|
|
385
|
+
controls
|
|
386
|
+
}) => {
|
|
387
|
+
const { declaration, deploymentId } = composition;
|
|
388
|
+
const deploymentEnabled = declaration.enabled ?? true;
|
|
389
|
+
return {
|
|
390
|
+
id: deploymentId,
|
|
391
|
+
deploymentCompositionId: composition.deploymentCompositionId,
|
|
392
|
+
label: declaration.label?.trim() || deploymentId,
|
|
393
|
+
connectorName: declaration.connectorName.trim(),
|
|
394
|
+
provider: (declaration.provider || declaration.connectorName).trim().toLowerCase(),
|
|
395
|
+
accountId: declaration.accountId.trim(),
|
|
396
|
+
enabled: deploymentEnabled,
|
|
397
|
+
strategies: composition.strategies.map((strategy) => ({
|
|
398
|
+
strategyName: strategy.strategyName,
|
|
399
|
+
strategyRevision: strategy.strategyRevision,
|
|
400
|
+
enabled: strategy.enabled,
|
|
401
|
+
controlState: deploymentEnabled && strategy.enabled && !controls.deployments[deploymentId]?.[strategy.strategyName]?.entriesPaused ? "active" : "entries_paused",
|
|
402
|
+
...strategy.selection ? { selection: strategy.selection } : {}
|
|
403
|
+
})),
|
|
404
|
+
...declaration.assetClasses ? { assetClasses: declaration.assetClasses } : {},
|
|
405
|
+
...declaration.tickers ? { tickers: declaration.tickers } : {}
|
|
406
|
+
};
|
|
407
|
+
};
|
|
408
|
+
var listRuntimeDeployments = async ({
|
|
409
|
+
userName,
|
|
410
|
+
projectRoot
|
|
411
|
+
}) => {
|
|
412
|
+
const [composition, controls] = await Promise.all([
|
|
413
|
+
resolveRuntimeComposition({ projectRoot }),
|
|
414
|
+
getRuntimeControls(userName)
|
|
415
|
+
]);
|
|
416
|
+
return composition.deployments.map(
|
|
417
|
+
(deploymentComposition) => toRuntimeDeployment({ composition: deploymentComposition, controls })
|
|
418
|
+
).sort((left, right) => left.label.localeCompare(right.label));
|
|
419
|
+
};
|
|
420
|
+
var getRuntimeDeployment = async ({
|
|
421
|
+
userName,
|
|
422
|
+
projectRoot,
|
|
423
|
+
deploymentId
|
|
424
|
+
}) => {
|
|
425
|
+
const deployments = await listRuntimeDeployments({ userName, projectRoot });
|
|
426
|
+
return deployments.find((deployment) => deployment.id === deploymentId) ?? null;
|
|
427
|
+
};
|
|
428
|
+
var resolveAccountId = async ({
|
|
429
|
+
userName,
|
|
430
|
+
deployment,
|
|
431
|
+
universe
|
|
432
|
+
}) => {
|
|
433
|
+
const account = await resolveTradingAccount({
|
|
434
|
+
userName,
|
|
435
|
+
accountId: deployment.accountId,
|
|
436
|
+
provider: deployment.provider,
|
|
437
|
+
universe
|
|
438
|
+
});
|
|
439
|
+
if (!account) {
|
|
440
|
+
throw new Error(`Trading account not found: ${deployment.accountId}`);
|
|
441
|
+
}
|
|
442
|
+
return account.id;
|
|
443
|
+
};
|
|
444
|
+
var loadResolvedRuntimeStrategies = async ({
|
|
445
|
+
userName,
|
|
446
|
+
projectRoot,
|
|
447
|
+
deploymentId,
|
|
448
|
+
universe,
|
|
449
|
+
accountId,
|
|
450
|
+
interval
|
|
451
|
+
}) => {
|
|
452
|
+
const [composition, controls] = await Promise.all([
|
|
453
|
+
resolveRuntimeComposition({ projectRoot }),
|
|
454
|
+
getRuntimeControls(userName)
|
|
455
|
+
]);
|
|
456
|
+
const deploymentComposition = composition.deployments.find(
|
|
457
|
+
(candidate) => candidate.deploymentId === deploymentId
|
|
458
|
+
);
|
|
459
|
+
if (!deploymentComposition) {
|
|
460
|
+
throw new Error(`Runtime deployment not found: ${deploymentId}`);
|
|
461
|
+
}
|
|
462
|
+
const deployment = toRuntimeDeployment({
|
|
463
|
+
composition: deploymentComposition,
|
|
464
|
+
controls
|
|
465
|
+
});
|
|
466
|
+
const strategies = await Promise.all(
|
|
467
|
+
deploymentComposition.strategies.map(async (strategy) => {
|
|
468
|
+
const strategyView = deployment.strategies.find(
|
|
469
|
+
(candidate) => candidate.strategyName === strategy.strategyName
|
|
470
|
+
);
|
|
471
|
+
const resolvedAccountId = await resolveAccountId({
|
|
472
|
+
userName,
|
|
473
|
+
deployment,
|
|
474
|
+
universe: strategy.universe
|
|
475
|
+
});
|
|
476
|
+
return {
|
|
477
|
+
...strategy,
|
|
478
|
+
deploymentCompositionId: deploymentComposition.deploymentCompositionId,
|
|
479
|
+
accountId: resolvedAccountId,
|
|
480
|
+
controlState: strategyView?.controlState ?? "entries_paused"
|
|
481
|
+
};
|
|
482
|
+
})
|
|
483
|
+
);
|
|
484
|
+
const filtered = strategies.filter(
|
|
485
|
+
(candidate) => (!universe || candidate.universe === universe) && (!interval || String(candidate.interval) === String(interval)) && (!accountId || candidate.accountId === accountId)
|
|
486
|
+
);
|
|
487
|
+
const identities = /* @__PURE__ */ new Set();
|
|
488
|
+
for (const candidate of filtered) {
|
|
489
|
+
const identity = `${candidate.strategyName}:${candidate.accountId ?? "default"}`;
|
|
490
|
+
if (identities.has(identity)) {
|
|
491
|
+
throw new Error(`Runtime strategy conflict: ${identity}`);
|
|
492
|
+
}
|
|
493
|
+
identities.add(identity);
|
|
494
|
+
}
|
|
495
|
+
return filtered;
|
|
496
|
+
};
|
|
497
|
+
var getRuntimeStrategyPackageMetadata = async ({
|
|
498
|
+
strategyName,
|
|
499
|
+
projectRoot
|
|
500
|
+
}) => {
|
|
501
|
+
const [packageManifest, pluginSource] = await Promise.all([
|
|
502
|
+
readRuntimePackageManifest(projectRoot),
|
|
503
|
+
getStrategyPluginSource(strategyName, projectRoot)
|
|
504
|
+
]);
|
|
505
|
+
const strategyPackage = await resolveStrategyPackage({
|
|
506
|
+
pluginSource: pluginSource ?? null,
|
|
507
|
+
projectRoot
|
|
508
|
+
});
|
|
509
|
+
if (!strategyPackage) {
|
|
510
|
+
throw new Error(`Installed strategy package not found: ${strategyName}`);
|
|
511
|
+
}
|
|
512
|
+
const [
|
|
513
|
+
strategyPackageVersion,
|
|
514
|
+
strategyDependencyVersions,
|
|
515
|
+
runtimePackageVersion
|
|
516
|
+
] = await Promise.all([
|
|
517
|
+
resolveVerifiedPackageVersion({
|
|
518
|
+
projectRoot,
|
|
519
|
+
packageName: strategyPackage.name,
|
|
520
|
+
projectPackage: strategyPackage.projectPackage,
|
|
521
|
+
manifest: packageManifest
|
|
522
|
+
}),
|
|
523
|
+
resolveVerifiedStrategyDependencyVersions({
|
|
524
|
+
projectRoot,
|
|
525
|
+
strategyPackage,
|
|
526
|
+
manifest: packageManifest
|
|
527
|
+
}),
|
|
528
|
+
resolveVerifiedPackageVersion({
|
|
529
|
+
projectRoot,
|
|
530
|
+
packageName: "@tradejs/node",
|
|
531
|
+
manifest: packageManifest
|
|
532
|
+
})
|
|
533
|
+
]);
|
|
534
|
+
return {
|
|
535
|
+
strategyPackage: strategyPackage.name,
|
|
536
|
+
strategyPackageVersion,
|
|
537
|
+
strategyDependencyVersions,
|
|
538
|
+
runtimePackageVersion
|
|
539
|
+
};
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
export {
|
|
543
|
+
RUNTIME_PACKAGE_MANIFEST_SCHEMA,
|
|
544
|
+
computeStrategyRevision,
|
|
545
|
+
computeDeploymentCompositionId,
|
|
546
|
+
verifyRuntimeDeclaration,
|
|
547
|
+
resolveRuntimeComposition,
|
|
548
|
+
listRuntimeDeployments,
|
|
549
|
+
getRuntimeDeployment,
|
|
550
|
+
loadResolvedRuntimeStrategies,
|
|
551
|
+
getRuntimeStrategyPackageMetadata
|
|
552
|
+
};
|