@tradejs/node 3.1.8-beta.206 → 3.1.8-beta.210
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.mjs +3 -3
- package/dist/backtest.js +8 -6
- package/dist/backtest.mjs +7 -7
- package/dist/{chunk-SEQJ6V6B.mjs → chunk-733VVTWU.mjs} +1 -1
- package/dist/chunk-F7ODY5W4.mjs +322 -0
- package/dist/{chunk-WS5DYEVZ.mjs → chunk-LZDXRXIU.mjs} +3 -1
- package/dist/{chunk-XN7BC7XK.mjs → chunk-MEZIXBZ3.mjs} +1 -1
- package/dist/{chunk-3TWULKHV.mjs → chunk-QVKUROR2.mjs} +1 -1
- package/dist/{chunk-MCDICN3I.mjs → chunk-SVAUSZUE.mjs} +8 -8
- package/dist/cli.d.mts +2 -1
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +3 -1
- package/dist/cli.mjs +3 -3
- package/dist/connectors.js +3 -1
- package/dist/connectors.mjs +2 -2
- package/dist/registry.js +8 -6
- package/dist/registry.mjs +4 -4
- package/dist/runtimeDashboard.js +548 -275
- package/dist/runtimeDashboard.mjs +46 -267
- package/dist/runtimeStrategies.d.mts +16 -5
- package/dist/runtimeStrategies.d.ts +16 -5
- package/dist/runtimeStrategies.js +204 -103
- package/dist/runtimeStrategies.mjs +12 -219
- package/dist/strategies.js +8 -6
- package/dist/strategies.mjs +4 -4
- package/package.json +4 -4
package/dist/runtimeDashboard.js
CHANGED
|
@@ -34,10 +34,8 @@ __export(runtimeDashboard_exports, {
|
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(runtimeDashboard_exports);
|
|
36
36
|
var import_time3 = require("@tradejs/core/time");
|
|
37
|
-
var
|
|
38
|
-
var
|
|
39
|
-
var import_runtimeDeployments = require("@tradejs/infra/runtimeDeployments");
|
|
40
|
-
var import_runtimeStrategyReleases = require("@tradejs/infra/runtimeStrategyReleases");
|
|
37
|
+
var import_logger6 = require("@tradejs/infra/logger");
|
|
38
|
+
var import_tradingAccounts3 = require("@tradejs/infra/tradingAccounts");
|
|
41
39
|
var import_redis2 = require("@tradejs/infra/redis");
|
|
42
40
|
|
|
43
41
|
// src/connectorsRegistry.ts
|
|
@@ -78,17 +76,19 @@ var normalizeConfig = (rawConfig) => {
|
|
|
78
76
|
return {};
|
|
79
77
|
}
|
|
80
78
|
const config = rawConfig;
|
|
81
|
-
const
|
|
79
|
+
const strategies2 = Array.isArray(config.strategies) ? config.strategies.map((value) => String(value || "").trim()).filter(Boolean) : [];
|
|
82
80
|
const indicators = Array.isArray(config.indicators) ? config.indicators.map((value) => String(value || "").trim()).filter(Boolean) : [];
|
|
83
81
|
const connectors = Array.isArray(config.connectors) ? config.connectors.map((value) => String(value || "").trim()).filter(Boolean) : [];
|
|
84
82
|
const hooks = (0, import_config.normalizeTradejsConfigHooks)(
|
|
85
83
|
config.hooks
|
|
86
84
|
);
|
|
85
|
+
const runtime = config.runtime && typeof config.runtime === "object" && !Array.isArray(config.runtime) ? config.runtime : void 0;
|
|
87
86
|
return {
|
|
88
|
-
strategies,
|
|
87
|
+
strategies: strategies2,
|
|
89
88
|
indicators,
|
|
90
89
|
connectors,
|
|
91
|
-
...hooks ? { hooks } : {}
|
|
90
|
+
...hooks ? { hooks } : {},
|
|
91
|
+
...runtime ? { runtime } : {}
|
|
92
92
|
};
|
|
93
93
|
};
|
|
94
94
|
var getNodeCreateRequire = () => {
|
|
@@ -835,210 +835,6 @@ var DEFAULT_CONNECTOR_NAME = BUILTIN_CONNECTOR_NAMES.ByBit;
|
|
|
835
835
|
// src/runtimeDashboard.ts
|
|
836
836
|
var import_runtimeTrades3 = require("@tradejs/core/runtimeTrades");
|
|
837
837
|
|
|
838
|
-
// src/strategyEvidenceTimeline.ts
|
|
839
|
-
var import_promises = __toESM(require("fs/promises"));
|
|
840
|
-
var import_node_path = __toESM(require("path"));
|
|
841
|
-
var import_strategyReleaseEvidence = require("@tradejs/infra/strategyReleaseEvidence");
|
|
842
|
-
var DEFAULT_MARKER_DIRECTORY = "data/strategy-release/markers";
|
|
843
|
-
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
844
|
-
var isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
|
|
845
|
-
var strategyEvidenceTimelineSelectorKey = (selector) => [
|
|
846
|
-
selector.strategy,
|
|
847
|
-
selector.releaseVersion ?? "",
|
|
848
|
-
selector.compositionId ?? "",
|
|
849
|
-
selector.gitSha ?? "",
|
|
850
|
-
selector.gateFingerprint ?? "",
|
|
851
|
-
selector.configFingerprint ?? "",
|
|
852
|
-
selector.contextFingerprint ?? "",
|
|
853
|
-
selector.requireCompleteLineage ? "exact" : "partial"
|
|
854
|
-
].join(":");
|
|
855
|
-
var discoverJsonFiles = async (rootDir) => {
|
|
856
|
-
const files = [];
|
|
857
|
-
const visit = async (directory, depth) => {
|
|
858
|
-
if (depth > 12) return;
|
|
859
|
-
let entries;
|
|
860
|
-
try {
|
|
861
|
-
entries = await import_promises.default.readdir(directory, { withFileTypes: true });
|
|
862
|
-
} catch (error) {
|
|
863
|
-
if (error.code === "ENOENT") return;
|
|
864
|
-
throw error;
|
|
865
|
-
}
|
|
866
|
-
await Promise.all(
|
|
867
|
-
entries.map(async (entry) => {
|
|
868
|
-
if (entry.name.startsWith(".") || entry.name.includes(".tmp-")) return;
|
|
869
|
-
const entryPath = import_node_path.default.join(directory, entry.name);
|
|
870
|
-
if (entry.isDirectory()) {
|
|
871
|
-
await visit(entryPath, depth + 1);
|
|
872
|
-
} else if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
873
|
-
files.push(entryPath);
|
|
874
|
-
}
|
|
875
|
-
})
|
|
876
|
-
);
|
|
877
|
-
};
|
|
878
|
-
await visit(rootDir, 0);
|
|
879
|
-
return files.sort();
|
|
880
|
-
};
|
|
881
|
-
var inferMatchingStrategies = ({
|
|
882
|
-
filePath,
|
|
883
|
-
rootDir,
|
|
884
|
-
parsed,
|
|
885
|
-
strategies
|
|
886
|
-
}) => {
|
|
887
|
-
const payload = asRecord(asRecord(parsed)?.payload);
|
|
888
|
-
const declaredStrategy = isNonEmptyString(payload?.strategy) ? payload.strategy : null;
|
|
889
|
-
const directoryStrategy = import_node_path.default.relative(rootDir, filePath).split(import_node_path.default.sep)[0];
|
|
890
|
-
return strategies.filter(
|
|
891
|
-
(strategy) => strategy === declaredStrategy || directoryStrategy === (0, import_strategyReleaseEvidence.safeStrategyEvidenceSegment)(strategy)
|
|
892
|
-
);
|
|
893
|
-
};
|
|
894
|
-
var missingTimeline = () => ({
|
|
895
|
-
status: "missing",
|
|
896
|
-
observedFrom: null,
|
|
897
|
-
markers: []
|
|
898
|
-
});
|
|
899
|
-
var notAttachedTimeline = () => ({
|
|
900
|
-
status: "not_attached",
|
|
901
|
-
observedFrom: null,
|
|
902
|
-
markers: []
|
|
903
|
-
});
|
|
904
|
-
var invalidTimeline = () => ({
|
|
905
|
-
status: "invalid",
|
|
906
|
-
observedFrom: null,
|
|
907
|
-
markers: []
|
|
908
|
-
});
|
|
909
|
-
var loadStrategyEvidenceTimelines = async ({
|
|
910
|
-
projectRoot,
|
|
911
|
-
markerDir,
|
|
912
|
-
selectors: requestedSelectors,
|
|
913
|
-
startTime,
|
|
914
|
-
endTime
|
|
915
|
-
}) => {
|
|
916
|
-
const selectors = [...requestedSelectors].filter((selector) => selector.strategy.trim().length > 0).sort(
|
|
917
|
-
(left, right) => strategyEvidenceTimelineSelectorKey(left).localeCompare(
|
|
918
|
-
strategyEvidenceTimelineSelectorKey(right)
|
|
919
|
-
)
|
|
920
|
-
);
|
|
921
|
-
const strategies = [...new Set(selectors.map(({ strategy }) => strategy))];
|
|
922
|
-
const timelines = new Map(
|
|
923
|
-
selectors.map((selector) => [
|
|
924
|
-
strategyEvidenceTimelineSelectorKey(selector),
|
|
925
|
-
selector.releaseVersion ? notAttachedTimeline() : missingTimeline()
|
|
926
|
-
])
|
|
927
|
-
);
|
|
928
|
-
if (!selectors.length) return timelines;
|
|
929
|
-
const configuredDir = markerDir?.trim() || DEFAULT_MARKER_DIRECTORY;
|
|
930
|
-
const rootDir = import_node_path.default.isAbsolute(configuredDir) ? configuredDir : import_node_path.default.resolve(projectRoot, configuredDir);
|
|
931
|
-
let files;
|
|
932
|
-
try {
|
|
933
|
-
files = await discoverJsonFiles(rootDir);
|
|
934
|
-
} catch {
|
|
935
|
-
for (const selector of selectors) {
|
|
936
|
-
timelines.set(
|
|
937
|
-
strategyEvidenceTimelineSelectorKey(selector),
|
|
938
|
-
invalidTimeline()
|
|
939
|
-
);
|
|
940
|
-
}
|
|
941
|
-
return timelines;
|
|
942
|
-
}
|
|
943
|
-
const envelopesByStrategy = /* @__PURE__ */ new Map();
|
|
944
|
-
const invalidStrategies = /* @__PURE__ */ new Set();
|
|
945
|
-
for (const filePath of files) {
|
|
946
|
-
let parsed = null;
|
|
947
|
-
try {
|
|
948
|
-
parsed = JSON.parse(await import_promises.default.readFile(filePath, "utf8"));
|
|
949
|
-
} catch {
|
|
950
|
-
for (const strategy of inferMatchingStrategies({
|
|
951
|
-
filePath,
|
|
952
|
-
rootDir,
|
|
953
|
-
parsed,
|
|
954
|
-
strategies
|
|
955
|
-
})) {
|
|
956
|
-
invalidStrategies.add(strategy);
|
|
957
|
-
}
|
|
958
|
-
continue;
|
|
959
|
-
}
|
|
960
|
-
const matchingStrategies = inferMatchingStrategies({
|
|
961
|
-
filePath,
|
|
962
|
-
rootDir,
|
|
963
|
-
parsed,
|
|
964
|
-
strategies
|
|
965
|
-
});
|
|
966
|
-
if (!matchingStrategies.length) continue;
|
|
967
|
-
try {
|
|
968
|
-
const envelope = (0, import_strategyReleaseEvidence.verifyStrategyEvidenceMarkerEnvelope)(parsed);
|
|
969
|
-
for (const strategy of matchingStrategies) {
|
|
970
|
-
if (strategy !== envelope.payload.strategy) {
|
|
971
|
-
invalidStrategies.add(strategy);
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
if (!strategies.includes(envelope.payload.strategy)) {
|
|
975
|
-
continue;
|
|
976
|
-
}
|
|
977
|
-
const envelopes = envelopesByStrategy.get(envelope.payload.strategy) ?? [];
|
|
978
|
-
envelopes.push(envelope);
|
|
979
|
-
envelopesByStrategy.set(envelope.payload.strategy, envelopes);
|
|
980
|
-
} catch {
|
|
981
|
-
for (const strategy of matchingStrategies) {
|
|
982
|
-
invalidStrategies.add(strategy);
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
for (const selector of selectors) {
|
|
987
|
-
const strategy = selector.strategy;
|
|
988
|
-
const selectorKey = strategyEvidenceTimelineSelectorKey(selector);
|
|
989
|
-
if (invalidStrategies.has(strategy)) {
|
|
990
|
-
timelines.set(selectorKey, invalidTimeline());
|
|
991
|
-
continue;
|
|
992
|
-
}
|
|
993
|
-
const envelopes = envelopesByStrategy.get(strategy) ?? [];
|
|
994
|
-
if (!envelopes.length) continue;
|
|
995
|
-
const hasCompleteSelector = selector.releaseVersion != null || Boolean(selector.compositionId) && Boolean(selector.gitSha) && Boolean(selector.gateFingerprint) && Boolean(selector.configFingerprint) && Boolean(selector.contextFingerprint);
|
|
996
|
-
if (selector.requireCompleteLineage && !hasCompleteSelector) continue;
|
|
997
|
-
const markersById = /* @__PURE__ */ new Map();
|
|
998
|
-
let hasConflict = false;
|
|
999
|
-
for (const envelope of envelopes) {
|
|
1000
|
-
for (const marker of envelope.payload.markers) {
|
|
1001
|
-
const existing = markersById.get(marker.id);
|
|
1002
|
-
if (existing && (0, import_strategyReleaseEvidence.canonicalStrategyEvidenceJson)(existing) !== (0, import_strategyReleaseEvidence.canonicalStrategyEvidenceJson)(marker)) {
|
|
1003
|
-
hasConflict = true;
|
|
1004
|
-
break;
|
|
1005
|
-
}
|
|
1006
|
-
markersById.set(marker.id, marker);
|
|
1007
|
-
}
|
|
1008
|
-
if (hasConflict) break;
|
|
1009
|
-
}
|
|
1010
|
-
if (hasConflict) {
|
|
1011
|
-
timelines.set(selectorKey, invalidTimeline());
|
|
1012
|
-
continue;
|
|
1013
|
-
}
|
|
1014
|
-
const matchingMarkers = [...markersById.values()].filter(
|
|
1015
|
-
(marker) => (!selector.compositionId || marker.compositionId === selector.compositionId) && (!selector.releaseVersion || marker.releaseVersion === selector.releaseVersion) && (!selector.gitSha || marker.gitSha === selector.gitSha) && (!selector.gateFingerprint || marker.gateFingerprint === selector.gateFingerprint) && (!selector.configFingerprint || marker.configFingerprint === selector.configFingerprint) && (!selector.contextFingerprint || marker.contextFingerprint === selector.contextFingerprint) && marker.timestamp >= startTime && marker.timestamp < endTime
|
|
1016
|
-
).sort(
|
|
1017
|
-
(left, right) => left.timestamp - right.timestamp || left.type.localeCompare(right.type) || left.id.localeCompare(right.id)
|
|
1018
|
-
);
|
|
1019
|
-
let lastLossValue;
|
|
1020
|
-
let hasLastLossValue = false;
|
|
1021
|
-
const markers = matchingMarkers.filter((marker) => {
|
|
1022
|
-
if (marker.type !== "L") return true;
|
|
1023
|
-
if (hasLastLossValue && marker.maxLossValue === lastLossValue) {
|
|
1024
|
-
return false;
|
|
1025
|
-
}
|
|
1026
|
-
hasLastLossValue = true;
|
|
1027
|
-
lastLossValue = marker.maxLossValue;
|
|
1028
|
-
return true;
|
|
1029
|
-
});
|
|
1030
|
-
if (!markers.length && (selector.compositionId || selector.releaseVersion || selector.gitSha || selector.gateFingerprint || selector.configFingerprint || selector.contextFingerprint)) {
|
|
1031
|
-
continue;
|
|
1032
|
-
}
|
|
1033
|
-
timelines.set(selectorKey, {
|
|
1034
|
-
status: "verified",
|
|
1035
|
-
observedFrom: markers.length ? Math.min(...markers.map((marker) => marker.timestamp)) : Math.min(...envelopes.map((envelope) => envelope.payload.createdAt)),
|
|
1036
|
-
markers
|
|
1037
|
-
});
|
|
1038
|
-
}
|
|
1039
|
-
return timelines;
|
|
1040
|
-
};
|
|
1041
|
-
|
|
1042
838
|
// src/runtimeTradeSync.ts
|
|
1043
839
|
var import_constants2 = require("@tradejs/core/constants");
|
|
1044
840
|
var import_time2 = require("@tradejs/core/time");
|
|
@@ -1532,6 +1328,499 @@ var syncRuntimeTrades = async ({
|
|
|
1532
1328
|
return syncedTrades;
|
|
1533
1329
|
};
|
|
1534
1330
|
|
|
1331
|
+
// src/runtimeStrategies.ts
|
|
1332
|
+
var import_promises = require("fs/promises");
|
|
1333
|
+
var import_node_path = __toESM(require("path"));
|
|
1334
|
+
var import_runtimeControls = require("@tradejs/infra/runtimeControls");
|
|
1335
|
+
var import_tradingAccounts2 = require("@tradejs/infra/tradingAccounts");
|
|
1336
|
+
|
|
1337
|
+
// src/strategy/manifests.ts
|
|
1338
|
+
var import_indicators = require("@tradejs/core/indicators");
|
|
1339
|
+
var import_logger5 = require("@tradejs/infra/logger");
|
|
1340
|
+
var SHARED_STRATEGY_REGISTRY_KEY = "__tradejsNodeSharedStrategyRegistryV1__";
|
|
1341
|
+
var sharedRegistryScope = globalThis;
|
|
1342
|
+
var sharedStrategyRegistry = sharedRegistryScope[SHARED_STRATEGY_REGISTRY_KEY] ?? (sharedRegistryScope[SHARED_STRATEGY_REGISTRY_KEY] = {
|
|
1343
|
+
registryStateByProjectRoot: /* @__PURE__ */ new Map()
|
|
1344
|
+
});
|
|
1345
|
+
var createStrategyRegistryState = () => ({
|
|
1346
|
+
strategyCreators: /* @__PURE__ */ new Map(),
|
|
1347
|
+
strategyManifestsMap: /* @__PURE__ */ new Map(),
|
|
1348
|
+
strategyEntriesMap: /* @__PURE__ */ new Map(),
|
|
1349
|
+
strategySourcesMap: /* @__PURE__ */ new Map(),
|
|
1350
|
+
pluginsLoadPromise: null
|
|
1351
|
+
});
|
|
1352
|
+
var registryStateByProjectRoot2 = sharedStrategyRegistry.registryStateByProjectRoot;
|
|
1353
|
+
var getStrategyRegistryState = (cwd = getTradejsProjectCwd()) => {
|
|
1354
|
+
const projectRoot = getTradejsProjectCwd(cwd);
|
|
1355
|
+
let state = registryStateByProjectRoot2.get(projectRoot);
|
|
1356
|
+
if (!state) {
|
|
1357
|
+
state = createStrategyRegistryState();
|
|
1358
|
+
registryStateByProjectRoot2.set(projectRoot, state);
|
|
1359
|
+
}
|
|
1360
|
+
return {
|
|
1361
|
+
projectRoot,
|
|
1362
|
+
state
|
|
1363
|
+
};
|
|
1364
|
+
};
|
|
1365
|
+
var toUniqueModules2 = (modules = []) => [
|
|
1366
|
+
...new Set(modules.map((moduleName) => moduleName.trim()).filter(Boolean))
|
|
1367
|
+
];
|
|
1368
|
+
var getConfiguredPluginModuleNames = async (cwd = getTradejsProjectCwd()) => {
|
|
1369
|
+
const config = await loadTradejsConfig(cwd);
|
|
1370
|
+
return {
|
|
1371
|
+
strategyModules: toUniqueModules2(config.strategies),
|
|
1372
|
+
indicatorModules: toUniqueModules2(config.indicators)
|
|
1373
|
+
};
|
|
1374
|
+
};
|
|
1375
|
+
var extractModuleEntries = (moduleExport, key) => {
|
|
1376
|
+
if (!moduleExport || typeof moduleExport !== "object") {
|
|
1377
|
+
return null;
|
|
1378
|
+
}
|
|
1379
|
+
const candidate = moduleExport;
|
|
1380
|
+
if (Array.isArray(candidate[key])) {
|
|
1381
|
+
return candidate[key];
|
|
1382
|
+
}
|
|
1383
|
+
const defaultExport = candidate.default;
|
|
1384
|
+
if (defaultExport && Array.isArray(defaultExport[key])) {
|
|
1385
|
+
return defaultExport[key];
|
|
1386
|
+
}
|
|
1387
|
+
return null;
|
|
1388
|
+
};
|
|
1389
|
+
var extractStrategyPluginDefinition = (moduleExport) => {
|
|
1390
|
+
const strategyEntries = extractModuleEntries(
|
|
1391
|
+
moduleExport,
|
|
1392
|
+
"strategyEntries"
|
|
1393
|
+
);
|
|
1394
|
+
return strategyEntries ? { strategyEntries } : null;
|
|
1395
|
+
};
|
|
1396
|
+
var extractIndicatorPluginDefinition = (moduleExport) => {
|
|
1397
|
+
const indicatorEntries = extractModuleEntries(
|
|
1398
|
+
moduleExport,
|
|
1399
|
+
"indicatorEntries"
|
|
1400
|
+
);
|
|
1401
|
+
return indicatorEntries ? { indicatorEntries } : null;
|
|
1402
|
+
};
|
|
1403
|
+
var registerEntries2 = (entries, source, state) => {
|
|
1404
|
+
for (const entry of entries) {
|
|
1405
|
+
const strategyName = entry.manifest?.name;
|
|
1406
|
+
if (!strategyName) {
|
|
1407
|
+
import_logger5.logger.warn("Skip strategy entry without name from %s", source);
|
|
1408
|
+
continue;
|
|
1409
|
+
}
|
|
1410
|
+
if (state.strategyCreators.has(strategyName)) {
|
|
1411
|
+
import_logger5.logger.warn(
|
|
1412
|
+
'Skip duplicate strategy "%s" from %s: already registered',
|
|
1413
|
+
strategyName,
|
|
1414
|
+
source
|
|
1415
|
+
);
|
|
1416
|
+
continue;
|
|
1417
|
+
}
|
|
1418
|
+
state.strategyManifestsMap.set(strategyName, entry.manifest);
|
|
1419
|
+
state.strategyEntriesMap.set(strategyName, entry);
|
|
1420
|
+
state.strategySourcesMap.set(strategyName, source);
|
|
1421
|
+
materializeStrategyCreator(strategyName, state);
|
|
1422
|
+
}
|
|
1423
|
+
};
|
|
1424
|
+
var materializeStrategyCreator = (strategyName, state) => {
|
|
1425
|
+
if (state.strategyCreators.has(strategyName) || !sharedStrategyRegistry.strategyRuntimeFactory) {
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1428
|
+
const entry = state.strategyEntriesMap.get(strategyName);
|
|
1429
|
+
if (!entry) return;
|
|
1430
|
+
state.strategyCreators.set(
|
|
1431
|
+
strategyName,
|
|
1432
|
+
sharedStrategyRegistry.strategyRuntimeFactory({
|
|
1433
|
+
strategyName,
|
|
1434
|
+
defaults: entry.defaults,
|
|
1435
|
+
createCore: entry.createCore,
|
|
1436
|
+
manifest: entry.manifest,
|
|
1437
|
+
detectorKey: entry.detectorKey,
|
|
1438
|
+
detectorNoSignalSkipReason: entry.detectorNoSignalSkipReason,
|
|
1439
|
+
resolveRegisteredManifest: (name) => state.strategyManifestsMap.get(name)
|
|
1440
|
+
})
|
|
1441
|
+
);
|
|
1442
|
+
};
|
|
1443
|
+
var importStrategyPluginModule = async (moduleName, cwd = getTradejsProjectCwd()) => {
|
|
1444
|
+
if (typeof importTradejsModule === "function") {
|
|
1445
|
+
return importTradejsModule(moduleName, cwd);
|
|
1446
|
+
}
|
|
1447
|
+
return import(
|
|
1448
|
+
/* webpackIgnore: true */
|
|
1449
|
+
moduleName
|
|
1450
|
+
);
|
|
1451
|
+
};
|
|
1452
|
+
var ensureStrategyPluginsLoaded = async (cwd = getTradejsProjectCwd()) => {
|
|
1453
|
+
const { projectRoot, state } = getStrategyRegistryState(cwd);
|
|
1454
|
+
if (!state.pluginsLoadPromise) {
|
|
1455
|
+
(0, import_indicators.resetIndicatorRegistryCache)(projectRoot);
|
|
1456
|
+
state.pluginsLoadPromise = (async () => {
|
|
1457
|
+
const { strategyModules, indicatorModules } = await getConfiguredPluginModuleNames(projectRoot);
|
|
1458
|
+
const strategySet = new Set(strategyModules);
|
|
1459
|
+
const indicatorSet = new Set(indicatorModules);
|
|
1460
|
+
const pluginModuleNames = [
|
|
1461
|
+
.../* @__PURE__ */ new Set([...strategyModules, ...indicatorModules])
|
|
1462
|
+
];
|
|
1463
|
+
if (!pluginModuleNames.length) {
|
|
1464
|
+
return;
|
|
1465
|
+
}
|
|
1466
|
+
for (const moduleName of pluginModuleNames) {
|
|
1467
|
+
try {
|
|
1468
|
+
const resolvedModuleName = resolvePluginModuleSpecifier(
|
|
1469
|
+
moduleName,
|
|
1470
|
+
projectRoot
|
|
1471
|
+
);
|
|
1472
|
+
const moduleExport = await importStrategyPluginModule(
|
|
1473
|
+
resolvedModuleName,
|
|
1474
|
+
projectRoot
|
|
1475
|
+
);
|
|
1476
|
+
if (strategySet.has(moduleName)) {
|
|
1477
|
+
const pluginDefinition = extractStrategyPluginDefinition(moduleExport);
|
|
1478
|
+
if (!pluginDefinition) {
|
|
1479
|
+
import_logger5.logger.warn(
|
|
1480
|
+
'Skip strategy plugin "%s": export { strategyEntries } is missing',
|
|
1481
|
+
moduleName
|
|
1482
|
+
);
|
|
1483
|
+
} else {
|
|
1484
|
+
registerEntries2(
|
|
1485
|
+
pluginDefinition.strategyEntries,
|
|
1486
|
+
moduleName,
|
|
1487
|
+
state
|
|
1488
|
+
);
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
if (indicatorSet.has(moduleName)) {
|
|
1492
|
+
const indicatorPluginDefinition = extractIndicatorPluginDefinition(moduleExport);
|
|
1493
|
+
if (!indicatorPluginDefinition) {
|
|
1494
|
+
import_logger5.logger.warn(
|
|
1495
|
+
'Skip indicator plugin "%s": export { indicatorEntries } is missing',
|
|
1496
|
+
moduleName
|
|
1497
|
+
);
|
|
1498
|
+
} else {
|
|
1499
|
+
(0, import_indicators.registerIndicatorEntries)(
|
|
1500
|
+
indicatorPluginDefinition.indicatorEntries,
|
|
1501
|
+
moduleName,
|
|
1502
|
+
projectRoot
|
|
1503
|
+
);
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
if (!strategySet.has(moduleName) && !indicatorSet.has(moduleName)) {
|
|
1507
|
+
import_logger5.logger.warn(
|
|
1508
|
+
'Skip plugin "%s": no strategy/indicator sections requested in config',
|
|
1509
|
+
moduleName
|
|
1510
|
+
);
|
|
1511
|
+
}
|
|
1512
|
+
} catch (error) {
|
|
1513
|
+
import_logger5.logger.warn(
|
|
1514
|
+
'Failed to load plugin "%s": %s',
|
|
1515
|
+
moduleName,
|
|
1516
|
+
String(error)
|
|
1517
|
+
);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
})();
|
|
1521
|
+
}
|
|
1522
|
+
await state.pluginsLoadPromise;
|
|
1523
|
+
};
|
|
1524
|
+
var getStrategyCreator = async (name, cwd = getTradejsProjectCwd()) => {
|
|
1525
|
+
await ensureStrategyPluginsLoaded(cwd);
|
|
1526
|
+
const { state } = getStrategyRegistryState(cwd);
|
|
1527
|
+
return state.strategyCreators.get(name);
|
|
1528
|
+
};
|
|
1529
|
+
var getStrategyPluginSource = async (name, cwd = getTradejsProjectCwd()) => {
|
|
1530
|
+
await ensureStrategyPluginsLoaded(cwd);
|
|
1531
|
+
const { state } = getStrategyRegistryState(cwd);
|
|
1532
|
+
return state.strategySourcesMap.get(name);
|
|
1533
|
+
};
|
|
1534
|
+
var strategies = new Proxy(
|
|
1535
|
+
{},
|
|
1536
|
+
{
|
|
1537
|
+
get: (_target, property) => {
|
|
1538
|
+
if (typeof property !== "string") {
|
|
1539
|
+
return void 0;
|
|
1540
|
+
}
|
|
1541
|
+
return getStrategyRegistryState().state.strategyCreators.get(property);
|
|
1542
|
+
},
|
|
1543
|
+
ownKeys: () => {
|
|
1544
|
+
return [...getStrategyRegistryState().state.strategyCreators.keys()];
|
|
1545
|
+
},
|
|
1546
|
+
getOwnPropertyDescriptor: () => ({
|
|
1547
|
+
enumerable: true,
|
|
1548
|
+
configurable: true
|
|
1549
|
+
})
|
|
1550
|
+
}
|
|
1551
|
+
);
|
|
1552
|
+
|
|
1553
|
+
// src/runtimeStrategies.ts
|
|
1554
|
+
var INTERVALS = /* @__PURE__ */ new Set([
|
|
1555
|
+
"1",
|
|
1556
|
+
"3",
|
|
1557
|
+
"5",
|
|
1558
|
+
"15",
|
|
1559
|
+
"30",
|
|
1560
|
+
"60",
|
|
1561
|
+
"120",
|
|
1562
|
+
"240",
|
|
1563
|
+
"360",
|
|
1564
|
+
"720",
|
|
1565
|
+
"D",
|
|
1566
|
+
"W",
|
|
1567
|
+
"M"
|
|
1568
|
+
]);
|
|
1569
|
+
var RUNTIME_KEYS = /* @__PURE__ */ new Set(["deployments"]);
|
|
1570
|
+
var DEPLOYMENT_KEYS = /* @__PURE__ */ new Set([
|
|
1571
|
+
"label",
|
|
1572
|
+
"connectorName",
|
|
1573
|
+
"provider",
|
|
1574
|
+
"accountId",
|
|
1575
|
+
"enabled",
|
|
1576
|
+
"strategies",
|
|
1577
|
+
"assetClasses",
|
|
1578
|
+
"tickers"
|
|
1579
|
+
]);
|
|
1580
|
+
var STRATEGY_KEYS = /* @__PURE__ */ new Set(["version", "enabled", "config"]);
|
|
1581
|
+
var FORBIDDEN_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
1582
|
+
"ACCOUNT_ID",
|
|
1583
|
+
"DEPLOYMENT_ID",
|
|
1584
|
+
"CONNECTOR_NAME",
|
|
1585
|
+
"ENABLE"
|
|
1586
|
+
]);
|
|
1587
|
+
var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1588
|
+
var readPackageManifest = async (projectRoot) => {
|
|
1589
|
+
const candidates = [
|
|
1590
|
+
process.env.TRADEJS_RUNTIME_PACKAGE_MANIFEST,
|
|
1591
|
+
import_node_path.default.join(projectRoot, "runtime-package-manifest.json"),
|
|
1592
|
+
"/app/runtime-package-manifest.json"
|
|
1593
|
+
].filter((candidate) => Boolean(candidate));
|
|
1594
|
+
for (const candidate of candidates) {
|
|
1595
|
+
try {
|
|
1596
|
+
return JSON.parse(
|
|
1597
|
+
await (0, import_promises.readFile)(candidate, "utf8")
|
|
1598
|
+
);
|
|
1599
|
+
} catch {
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
return { packages: {} };
|
|
1603
|
+
};
|
|
1604
|
+
var resolveInstalledPackageVersion = async (projectRoot, packageName, manifest) => {
|
|
1605
|
+
if (!packageName || packageName === "runtime") return null;
|
|
1606
|
+
const manifestVersion = manifest.packages?.[packageName];
|
|
1607
|
+
if (manifestVersion) return manifestVersion;
|
|
1608
|
+
try {
|
|
1609
|
+
const packageJsonPath = import_node_path.default.join(
|
|
1610
|
+
projectRoot,
|
|
1611
|
+
"node_modules",
|
|
1612
|
+
...packageName.split("/"),
|
|
1613
|
+
"package.json"
|
|
1614
|
+
);
|
|
1615
|
+
const packageJson = JSON.parse(await (0, import_promises.readFile)(packageJsonPath, "utf8"));
|
|
1616
|
+
return packageJson.version ?? null;
|
|
1617
|
+
} catch {
|
|
1618
|
+
return null;
|
|
1619
|
+
}
|
|
1620
|
+
};
|
|
1621
|
+
var resolveStrategyPackageName = async ({
|
|
1622
|
+
pluginSource,
|
|
1623
|
+
projectRoot
|
|
1624
|
+
}) => {
|
|
1625
|
+
if (!pluginSource) return null;
|
|
1626
|
+
if (!pluginSource.startsWith(".") && !import_node_path.default.isAbsolute(pluginSource)) {
|
|
1627
|
+
return pluginSource;
|
|
1628
|
+
}
|
|
1629
|
+
try {
|
|
1630
|
+
const packageJson = JSON.parse(
|
|
1631
|
+
await (0, import_promises.readFile)(import_node_path.default.join(projectRoot, "package.json"), "utf8")
|
|
1632
|
+
);
|
|
1633
|
+
return typeof packageJson.name === "string" && packageJson.name.trim() ? packageJson.name : null;
|
|
1634
|
+
} catch {
|
|
1635
|
+
return null;
|
|
1636
|
+
}
|
|
1637
|
+
};
|
|
1638
|
+
var verifyStringArray = (value) => value === void 0 || Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
1639
|
+
var verifyDeploymentDeclaration = (deploymentId, value) => {
|
|
1640
|
+
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" || !verifyStringArray(value.assetClasses) || !verifyStringArray(value.tickers) || !isRecord(value.strategies) || !Object.keys(value.strategies).length) {
|
|
1641
|
+
throw new Error(`Invalid runtime deployment declaration: ${deploymentId}`);
|
|
1642
|
+
}
|
|
1643
|
+
for (const [strategyName, strategyValue] of Object.entries(
|
|
1644
|
+
value.strategies
|
|
1645
|
+
)) {
|
|
1646
|
+
if (!strategyName.trim() || !isRecord(strategyValue) || Object.keys(strategyValue).some((key) => !STRATEGY_KEYS.has(key)) || !Number.isSafeInteger(strategyValue.version) || Number(strategyValue.version) <= 0 || typeof strategyValue.enabled !== "boolean" || !isRecord(strategyValue.config) || Object.keys(strategyValue.config).some(
|
|
1647
|
+
(key) => FORBIDDEN_CONFIG_KEYS.has(key)
|
|
1648
|
+
) || !INTERVALS.has(String(strategyValue.config.INTERVAL)) || !["crypto", "tradfi"].includes(String(strategyValue.config.UNIVERSE))) {
|
|
1649
|
+
throw new Error(
|
|
1650
|
+
`Invalid runtime strategy declaration: ${deploymentId}/${strategyName}`
|
|
1651
|
+
);
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
return value;
|
|
1655
|
+
};
|
|
1656
|
+
var verifyRuntimeDeclaration = (value) => {
|
|
1657
|
+
if (!isRecord(value) || Object.keys(value).some((key) => !RUNTIME_KEYS.has(key)) || !isRecord(value.deployments) || !Object.keys(value.deployments).length) {
|
|
1658
|
+
throw new Error("Invalid runtime declaration");
|
|
1659
|
+
}
|
|
1660
|
+
for (const [deploymentId, deployment] of Object.entries(value.deployments)) {
|
|
1661
|
+
verifyDeploymentDeclaration(deploymentId, deployment);
|
|
1662
|
+
}
|
|
1663
|
+
return value;
|
|
1664
|
+
};
|
|
1665
|
+
var toRuntimeDeployment = ({
|
|
1666
|
+
id,
|
|
1667
|
+
declaration,
|
|
1668
|
+
controls
|
|
1669
|
+
}) => {
|
|
1670
|
+
const deploymentEnabled = declaration.enabled ?? true;
|
|
1671
|
+
return {
|
|
1672
|
+
id,
|
|
1673
|
+
label: declaration.label?.trim() || id,
|
|
1674
|
+
connectorName: declaration.connectorName.trim(),
|
|
1675
|
+
provider: (declaration.provider || declaration.connectorName).trim().toLowerCase(),
|
|
1676
|
+
accountId: declaration.accountId.trim(),
|
|
1677
|
+
enabled: deploymentEnabled,
|
|
1678
|
+
strategies: Object.entries(declaration.strategies).map(
|
|
1679
|
+
([strategyName, strategy]) => ({
|
|
1680
|
+
strategyName,
|
|
1681
|
+
version: strategy.version,
|
|
1682
|
+
enabled: strategy.enabled,
|
|
1683
|
+
controlState: deploymentEnabled && strategy.enabled && !controls.deployments[id]?.[strategyName]?.entriesPaused ? "active" : "entries_paused"
|
|
1684
|
+
})
|
|
1685
|
+
),
|
|
1686
|
+
...declaration.assetClasses ? { assetClasses: declaration.assetClasses } : {},
|
|
1687
|
+
...declaration.tickers ? { tickers: declaration.tickers } : {}
|
|
1688
|
+
};
|
|
1689
|
+
};
|
|
1690
|
+
var loadRuntimeDeclaration = async (projectRoot) => {
|
|
1691
|
+
const projectConfig = await loadTradejsConfig(projectRoot);
|
|
1692
|
+
if (!projectConfig.runtime) {
|
|
1693
|
+
throw new Error("Runtime declaration is required in tradejs.config.ts");
|
|
1694
|
+
}
|
|
1695
|
+
return verifyRuntimeDeclaration(projectConfig.runtime);
|
|
1696
|
+
};
|
|
1697
|
+
var listRuntimeDeployments = async ({
|
|
1698
|
+
userName,
|
|
1699
|
+
projectRoot
|
|
1700
|
+
}) => {
|
|
1701
|
+
const [runtime, controls] = await Promise.all([
|
|
1702
|
+
loadRuntimeDeclaration(projectRoot),
|
|
1703
|
+
(0, import_runtimeControls.getRuntimeControls)(userName)
|
|
1704
|
+
]);
|
|
1705
|
+
return Object.entries(runtime.deployments).map(
|
|
1706
|
+
([id, declaration]) => toRuntimeDeployment({ id, declaration, controls })
|
|
1707
|
+
).sort((left, right) => left.label.localeCompare(right.label));
|
|
1708
|
+
};
|
|
1709
|
+
var resolveAccountId = async ({
|
|
1710
|
+
userName,
|
|
1711
|
+
deployment,
|
|
1712
|
+
universe
|
|
1713
|
+
}) => {
|
|
1714
|
+
const account = await (0, import_tradingAccounts2.resolveTradingAccount)({
|
|
1715
|
+
userName,
|
|
1716
|
+
accountId: deployment.accountId,
|
|
1717
|
+
provider: deployment.provider,
|
|
1718
|
+
universe
|
|
1719
|
+
});
|
|
1720
|
+
if (!account) {
|
|
1721
|
+
throw new Error(`Trading account not found: ${deployment.accountId}`);
|
|
1722
|
+
}
|
|
1723
|
+
return account.id;
|
|
1724
|
+
};
|
|
1725
|
+
var loadResolvedRuntimeStrategies = async ({
|
|
1726
|
+
userName,
|
|
1727
|
+
projectRoot,
|
|
1728
|
+
deploymentId,
|
|
1729
|
+
universe,
|
|
1730
|
+
accountId,
|
|
1731
|
+
interval
|
|
1732
|
+
}) => {
|
|
1733
|
+
const [runtime, controls, packageManifest] = await Promise.all([
|
|
1734
|
+
loadRuntimeDeclaration(projectRoot),
|
|
1735
|
+
(0, import_runtimeControls.getRuntimeControls)(userName),
|
|
1736
|
+
readPackageManifest(projectRoot)
|
|
1737
|
+
]);
|
|
1738
|
+
const declaration = runtime.deployments[deploymentId];
|
|
1739
|
+
if (!declaration) {
|
|
1740
|
+
throw new Error(`Runtime deployment not found: ${deploymentId}`);
|
|
1741
|
+
}
|
|
1742
|
+
const deployment = toRuntimeDeployment({
|
|
1743
|
+
id: deploymentId,
|
|
1744
|
+
declaration,
|
|
1745
|
+
controls
|
|
1746
|
+
});
|
|
1747
|
+
const strategies2 = await Promise.all(
|
|
1748
|
+
Object.entries(declaration.strategies).map(
|
|
1749
|
+
async ([strategyName, strategyDeclaration]) => {
|
|
1750
|
+
const strategyCreator = await getStrategyCreator(
|
|
1751
|
+
strategyName,
|
|
1752
|
+
projectRoot
|
|
1753
|
+
);
|
|
1754
|
+
if (!strategyCreator) {
|
|
1755
|
+
throw new Error(`Unknown strategy: ${strategyName}`);
|
|
1756
|
+
}
|
|
1757
|
+
const pluginSource = await getStrategyPluginSource(strategyName, projectRoot) ?? null;
|
|
1758
|
+
const strategyPackage = await resolveStrategyPackageName({
|
|
1759
|
+
pluginSource,
|
|
1760
|
+
projectRoot
|
|
1761
|
+
});
|
|
1762
|
+
const [strategyPackageVersion, runtimePackageVersion] = await Promise.all([
|
|
1763
|
+
resolveInstalledPackageVersion(
|
|
1764
|
+
projectRoot,
|
|
1765
|
+
strategyPackage,
|
|
1766
|
+
packageManifest
|
|
1767
|
+
),
|
|
1768
|
+
resolveInstalledPackageVersion(
|
|
1769
|
+
projectRoot,
|
|
1770
|
+
"@tradejs/node",
|
|
1771
|
+
packageManifest
|
|
1772
|
+
)
|
|
1773
|
+
]);
|
|
1774
|
+
if (!strategyPackage || !strategyPackageVersion) {
|
|
1775
|
+
throw new Error(
|
|
1776
|
+
`Installed strategy package not found: ${strategyName}`
|
|
1777
|
+
);
|
|
1778
|
+
}
|
|
1779
|
+
if (!runtimePackageVersion) {
|
|
1780
|
+
throw new Error("Installed @tradejs/node package version not found");
|
|
1781
|
+
}
|
|
1782
|
+
const strategyView = deployment.strategies.find(
|
|
1783
|
+
(candidate) => candidate.strategyName === strategyName
|
|
1784
|
+
);
|
|
1785
|
+
const strategyConfig = strategyDeclaration.config;
|
|
1786
|
+
const strategyUniverse = strategyConfig.UNIVERSE;
|
|
1787
|
+
const resolvedAccountId = await resolveAccountId({
|
|
1788
|
+
userName,
|
|
1789
|
+
deployment,
|
|
1790
|
+
universe: strategyUniverse
|
|
1791
|
+
});
|
|
1792
|
+
return {
|
|
1793
|
+
strategyName,
|
|
1794
|
+
version: strategyDeclaration.version,
|
|
1795
|
+
enabled: strategyDeclaration.enabled,
|
|
1796
|
+
controlState: strategyView?.controlState ?? "entries_paused",
|
|
1797
|
+
interval: String(strategyConfig.INTERVAL),
|
|
1798
|
+
universe: strategyUniverse,
|
|
1799
|
+
accountId: resolvedAccountId,
|
|
1800
|
+
strategyPackage,
|
|
1801
|
+
strategyPackageVersion,
|
|
1802
|
+
runtimePackageVersion,
|
|
1803
|
+
strategyCreator,
|
|
1804
|
+
sourceStrategyConfig: strategyConfig,
|
|
1805
|
+
strategyConfig
|
|
1806
|
+
};
|
|
1807
|
+
}
|
|
1808
|
+
)
|
|
1809
|
+
);
|
|
1810
|
+
const filtered = strategies2.filter(
|
|
1811
|
+
(candidate) => (!universe || candidate.universe === universe) && (!interval || String(candidate.interval) === String(interval)) && (!accountId || candidate.accountId === accountId)
|
|
1812
|
+
);
|
|
1813
|
+
const identities = /* @__PURE__ */ new Set();
|
|
1814
|
+
for (const candidate of filtered) {
|
|
1815
|
+
const identity = `${candidate.strategyName}:${candidate.accountId ?? "default"}`;
|
|
1816
|
+
if (identities.has(identity)) {
|
|
1817
|
+
throw new Error(`Runtime strategy conflict: ${identity}`);
|
|
1818
|
+
}
|
|
1819
|
+
identities.add(identity);
|
|
1820
|
+
}
|
|
1821
|
+
return filtered;
|
|
1822
|
+
};
|
|
1823
|
+
|
|
1535
1824
|
// src/runtimeDashboard.ts
|
|
1536
1825
|
var DEFAULT_PROVIDER = "bybit";
|
|
1537
1826
|
var DEFAULT_HOURS = 168;
|
|
@@ -1550,7 +1839,7 @@ var resolveConnectorCreatorByProvider = async (provider, projectRoot) => await g
|
|
|
1550
1839
|
var resolveConnectorAccountId = async ({
|
|
1551
1840
|
userName,
|
|
1552
1841
|
provider
|
|
1553
|
-
}) => (await (0,
|
|
1842
|
+
}) => (await (0, import_tradingAccounts3.resolveTradingAccount)({
|
|
1554
1843
|
userName,
|
|
1555
1844
|
provider,
|
|
1556
1845
|
universe: "crypto"
|
|
@@ -1616,7 +1905,7 @@ var loadExchangeRange = async ({
|
|
|
1616
1905
|
} catch (error) {
|
|
1617
1906
|
const message = error?.message || String(error);
|
|
1618
1907
|
errors?.push(`${label}: ${message}`);
|
|
1619
|
-
|
|
1908
|
+
import_logger6.logger.warn("strategies runtime: %s failed: %s", label, message);
|
|
1620
1909
|
return [];
|
|
1621
1910
|
}
|
|
1622
1911
|
};
|
|
@@ -1656,7 +1945,7 @@ var loadClosedPnlRows = async ({
|
|
|
1656
1945
|
} catch (error) {
|
|
1657
1946
|
const message = error?.message || String(error);
|
|
1658
1947
|
errors?.push(`getClosedPnl: ${message}`);
|
|
1659
|
-
|
|
1948
|
+
import_logger6.logger.warn("strategies runtime: getClosedPnl failed: %s", message);
|
|
1660
1949
|
return [];
|
|
1661
1950
|
}
|
|
1662
1951
|
};
|
|
@@ -1689,7 +1978,7 @@ var loadExchangeEntryRows = async ({
|
|
|
1689
1978
|
} catch (error) {
|
|
1690
1979
|
const message = error?.message || String(error);
|
|
1691
1980
|
errors?.push(`getEntryExecutions: ${message}`);
|
|
1692
|
-
|
|
1981
|
+
import_logger6.logger.warn("strategies runtime: getEntryExecutions failed: %s", message);
|
|
1693
1982
|
return [];
|
|
1694
1983
|
}
|
|
1695
1984
|
};
|
|
@@ -1705,7 +1994,7 @@ var loadOpenPositions = async (connector, errors) => {
|
|
|
1705
1994
|
} catch (error) {
|
|
1706
1995
|
const message = error?.message || String(error);
|
|
1707
1996
|
errors?.push(`getOpenPositionPnl: ${message}`);
|
|
1708
|
-
|
|
1997
|
+
import_logger6.logger.warn("strategies runtime: getOpenPositionPnl failed: %s", message);
|
|
1709
1998
|
return { positions: [], reliable: false };
|
|
1710
1999
|
}
|
|
1711
2000
|
};
|
|
@@ -1762,8 +2051,8 @@ var loadRuntimeDashboard = async ({
|
|
|
1762
2051
|
errors: exchangeErrors
|
|
1763
2052
|
}),
|
|
1764
2053
|
loadOpenPositions(connector, exchangeErrors),
|
|
1765
|
-
(
|
|
1766
|
-
(0,
|
|
2054
|
+
listRuntimeDeployments({ userName, projectRoot }),
|
|
2055
|
+
(0, import_tradingAccounts3.listTradingAccounts)(userName)
|
|
1767
2056
|
]);
|
|
1768
2057
|
const relevantTrades = (0, import_runtimeTrades3.selectTradesForWindow)(
|
|
1769
2058
|
runtimeTrades,
|
|
@@ -1807,9 +2096,23 @@ var loadRuntimeDashboard = async ({
|
|
|
1807
2096
|
const accountsById = new Map(
|
|
1808
2097
|
tradingAccounts.map((account) => [account.id, account])
|
|
1809
2098
|
);
|
|
2099
|
+
const resolvedStrategiesByDeployment = new Map(
|
|
2100
|
+
await Promise.all(
|
|
2101
|
+
runtimeDeployments.map(
|
|
2102
|
+
async (deployment) => [
|
|
2103
|
+
deployment.id,
|
|
2104
|
+
await loadResolvedRuntimeStrategies({
|
|
2105
|
+
userName,
|
|
2106
|
+
projectRoot,
|
|
2107
|
+
deploymentId: deployment.id
|
|
2108
|
+
})
|
|
2109
|
+
]
|
|
2110
|
+
)
|
|
2111
|
+
)
|
|
2112
|
+
);
|
|
1810
2113
|
const runtimeIdentityKey = (trade) => (0, import_runtimeTrades3.buildRuntimeStrategyIdentityKey)({
|
|
1811
2114
|
strategyName: trade.strategy,
|
|
1812
|
-
configId: trade.
|
|
2115
|
+
configId: trade.runtimeVersion ? `v${trade.runtimeVersion}` : void 0,
|
|
1813
2116
|
universe: trade.universe,
|
|
1814
2117
|
accountId: trade.accountId,
|
|
1815
2118
|
deploymentId: trade.deploymentId,
|
|
@@ -1817,42 +2120,34 @@ var loadRuntimeDashboard = async ({
|
|
|
1817
2120
|
});
|
|
1818
2121
|
const identityByKey = /* @__PURE__ */ new Map();
|
|
1819
2122
|
for (const deployment of runtimeDeployments) {
|
|
1820
|
-
for (const
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
throw new Error(
|
|
1828
|
-
`Runtime release not found: ${deploymentStrategy.strategyName} v${deploymentStrategy.releaseVersion}`
|
|
1829
|
-
);
|
|
1830
|
-
}
|
|
1831
|
-
const releaseUniverse = release.config.UNIVERSE === "tradfi" ? "tradfi" : "crypto";
|
|
1832
|
-
const releaseInterval = String(release.config.INTERVAL);
|
|
1833
|
-
const releaseConfigId = `v${deploymentStrategy.releaseVersion}`;
|
|
1834
|
-
const releasePolicyProfileId = typeof release.config.POLICY_PROFILE_ID === "string" ? release.config.POLICY_PROFILE_ID : void 0;
|
|
2123
|
+
for (const resolvedStrategy of resolvedStrategiesByDeployment.get(
|
|
2124
|
+
deployment.id
|
|
2125
|
+
) ?? []) {
|
|
2126
|
+
const strategyUniverse = resolvedStrategy.universe;
|
|
2127
|
+
const strategyInterval = resolvedStrategy.interval;
|
|
2128
|
+
const strategyConfigId = `v${resolvedStrategy.version}`;
|
|
2129
|
+
const strategyPolicyProfileId = typeof resolvedStrategy.strategyConfig.POLICY_PROFILE_ID === "string" ? resolvedStrategy.strategyConfig.POLICY_PROFILE_ID : void 0;
|
|
1835
2130
|
const runtimeKey = (0, import_runtimeTrades3.buildRuntimeStrategyIdentityKey)({
|
|
1836
|
-
strategyName:
|
|
1837
|
-
configId:
|
|
1838
|
-
universe:
|
|
2131
|
+
strategyName: resolvedStrategy.strategyName,
|
|
2132
|
+
configId: strategyConfigId,
|
|
2133
|
+
universe: strategyUniverse,
|
|
1839
2134
|
accountId: deployment.accountId,
|
|
1840
2135
|
deploymentId: deployment.id,
|
|
1841
|
-
policyProfileId:
|
|
2136
|
+
policyProfileId: strategyPolicyProfileId
|
|
1842
2137
|
});
|
|
1843
2138
|
identityByKey.set(runtimeKey, {
|
|
1844
|
-
strategyName:
|
|
1845
|
-
configId:
|
|
1846
|
-
|
|
1847
|
-
controlState:
|
|
1848
|
-
interval:
|
|
1849
|
-
universe:
|
|
2139
|
+
strategyName: resolvedStrategy.strategyName,
|
|
2140
|
+
configId: strategyConfigId,
|
|
2141
|
+
version: resolvedStrategy.version,
|
|
2142
|
+
controlState: resolvedStrategy.controlState,
|
|
2143
|
+
interval: strategyInterval,
|
|
2144
|
+
universe: strategyUniverse,
|
|
1850
2145
|
accountId: deployment.accountId,
|
|
1851
2146
|
accountLabel: accountsById.get(deployment.accountId)?.label,
|
|
1852
2147
|
deploymentId: deployment.id,
|
|
1853
|
-
policyProfileId:
|
|
1854
|
-
enabled:
|
|
1855
|
-
config:
|
|
2148
|
+
policyProfileId: strategyPolicyProfileId,
|
|
2149
|
+
enabled: resolvedStrategy.controlState !== "entries_paused",
|
|
2150
|
+
config: resolvedStrategy.strategyConfig,
|
|
1856
2151
|
connected: deployment.enabled
|
|
1857
2152
|
});
|
|
1858
2153
|
}
|
|
@@ -1862,8 +2157,8 @@ var loadRuntimeDashboard = async ({
|
|
|
1862
2157
|
const key = runtimeIdentityKey(trade);
|
|
1863
2158
|
const configuredIdentity = identityByKey.get(key);
|
|
1864
2159
|
if (!configuredIdentity) continue;
|
|
1865
|
-
const
|
|
1866
|
-
if (
|
|
2160
|
+
const version = trade.runtimeVersion ?? (trade.runtimeLineage?.schemaVersion === 2 ? trade.runtimeLineage.version : void 0);
|
|
2161
|
+
if (version !== configuredIdentity.version) continue;
|
|
1867
2162
|
identityByKey.set(key, {
|
|
1868
2163
|
...configuredIdentity,
|
|
1869
2164
|
interval: String(
|
|
@@ -1871,18 +2166,7 @@ var loadRuntimeDashboard = async ({
|
|
|
1871
2166
|
)
|
|
1872
2167
|
});
|
|
1873
2168
|
}
|
|
1874
|
-
const
|
|
1875
|
-
projectRoot,
|
|
1876
|
-
markerDir: process.env.STRATEGY_RELEASE_MARKER_DIR,
|
|
1877
|
-
selectors: [...identityByKey.values()].map((identity) => ({
|
|
1878
|
-
strategy: identity.strategyName,
|
|
1879
|
-
releaseVersion: identity.releaseVersion,
|
|
1880
|
-
requireCompleteLineage: true
|
|
1881
|
-
})),
|
|
1882
|
-
startTime,
|
|
1883
|
-
endTime
|
|
1884
|
-
});
|
|
1885
|
-
const strategies = await Promise.all(
|
|
2169
|
+
const strategies2 = await Promise.all(
|
|
1886
2170
|
[...identityByKey.entries()].map(async ([runtimeKey, identity]) => {
|
|
1887
2171
|
const { strategyName } = identity;
|
|
1888
2172
|
const strategyTrades = accountScopedTrades.filter((trade) => runtimeIdentityKey(trade) === runtimeKey).sort((left, right) => right.entryTimestamp - left.entryTimestamp);
|
|
@@ -1901,7 +2185,7 @@ var loadRuntimeDashboard = async ({
|
|
|
1901
2185
|
runtimeKey,
|
|
1902
2186
|
strategyName,
|
|
1903
2187
|
configId: identity.configId,
|
|
1904
|
-
|
|
2188
|
+
version: identity.version,
|
|
1905
2189
|
controlState: identity.controlState,
|
|
1906
2190
|
interval: identity.interval,
|
|
1907
2191
|
universe: identity.universe,
|
|
@@ -1916,23 +2200,12 @@ var loadRuntimeDashboard = async ({
|
|
|
1916
2200
|
stat: analytics.stat,
|
|
1917
2201
|
summary: analytics.summary,
|
|
1918
2202
|
orderLog: analytics.orderLog,
|
|
1919
|
-
evidenceTimeline: evidenceTimelines.get(
|
|
1920
|
-
strategyEvidenceTimelineSelectorKey({
|
|
1921
|
-
strategy: strategyName,
|
|
1922
|
-
releaseVersion: identity.releaseVersion,
|
|
1923
|
-
requireCompleteLineage: true
|
|
1924
|
-
})
|
|
1925
|
-
) ?? {
|
|
1926
|
-
status: "not_attached",
|
|
1927
|
-
observedFrom: null,
|
|
1928
|
-
markers: []
|
|
1929
|
-
},
|
|
1930
2203
|
recentTrades: strategyTrades.slice(0, 8).map((trade) => (0, import_runtimeTrades3.toRuntimeTradeView)(trade, endTime)),
|
|
1931
2204
|
orders
|
|
1932
2205
|
};
|
|
1933
2206
|
})
|
|
1934
2207
|
);
|
|
1935
|
-
|
|
2208
|
+
strategies2.sort((left, right) => {
|
|
1936
2209
|
if (left.stat.netProfit !== right.stat.netProfit) {
|
|
1937
2210
|
return right.stat.netProfit - left.stat.netProfit;
|
|
1938
2211
|
}
|
|
@@ -1953,7 +2226,7 @@ var loadRuntimeDashboard = async ({
|
|
|
1953
2226
|
exchangeFallbackTrades: fallbackTrades.length,
|
|
1954
2227
|
exchangeErrors: [...new Set(exchangeErrors)].sort()
|
|
1955
2228
|
},
|
|
1956
|
-
strategies
|
|
2229
|
+
strategies: strategies2
|
|
1957
2230
|
};
|
|
1958
2231
|
return response;
|
|
1959
2232
|
};
|