@tradejs/cli 3.1.29-beta.281 → 3.1.29-beta.282
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/cli.js +68 -32
- package/dist/lib/mcpReplayProcess.js +14 -1
- package/dist/lib/runtimeDebugEvidence.js +45 -52
- package/dist/lib/runtimeRedis.js +45 -0
- package/dist/scripts/mcpWorker.js +22 -12
- package/dist/scripts/replayRuntimeEvidence.js +45 -52
- package/dist/scripts/runtimeEvidence.js +45 -52
- package/package.json +7 -7
package/dist/cli.js
CHANGED
|
@@ -12818,7 +12818,7 @@ var init_marketContextPrepare = __esm({
|
|
|
12818
12818
|
});
|
|
12819
12819
|
|
|
12820
12820
|
// src/lib/runtimeRedis.ts
|
|
12821
|
-
var import_redis4, import_time, import_runtimeStrategyConfigs, isRuntimeStrategyEnabled, isRuntimeTradeRecord, loadRuntimeTrades, loadRuntimeClosedTrades, loadRuntimeActiveTradeOrderIds, loadRuntimeActiveTrades, loadStrategyResultSymbols;
|
|
12821
|
+
var import_redis4, import_time, import_runtimeStrategyConfigs, isRuntimeStrategyEnabled, isRuntimeTradeRecord, loadRuntimeTrades, loadRuntimeClosedTrades, loadRuntimeEvidenceTrades, loadRuntimeActiveTradeOrderIds, loadRuntimeActiveTrades, loadStrategyResultSymbols;
|
|
12822
12822
|
var init_runtimeRedis = __esm({
|
|
12823
12823
|
"src/lib/runtimeRedis.ts"() {
|
|
12824
12824
|
"use strict";
|
|
@@ -12892,6 +12892,49 @@ var init_runtimeRedis = __esm({
|
|
|
12892
12892
|
(left, right) => left.exitTimestamp - right.exitTimestamp
|
|
12893
12893
|
);
|
|
12894
12894
|
};
|
|
12895
|
+
loadRuntimeEvidenceTrades = async (userName3, { startTime, endTime }) => {
|
|
12896
|
+
const prefix = import_redis4.redisKeys.runtimeTradeBuckets(userName3);
|
|
12897
|
+
const lastDay = (0, import_time.getRuntimeStorageDayKey)(endTime - 1);
|
|
12898
|
+
const keys = (await (0, import_redis4.getKeys)(prefix)).filter((key) => {
|
|
12899
|
+
const day = key.slice(prefix.length);
|
|
12900
|
+
return key.startsWith(prefix) && /^\d{4}-\d{2}-\d{2}$/.test(day) && day <= lastDay;
|
|
12901
|
+
});
|
|
12902
|
+
const retained = /* @__PURE__ */ new Map();
|
|
12903
|
+
for (let offset = 0; offset < keys.length; offset += 16) {
|
|
12904
|
+
const buckets = await Promise.all(
|
|
12905
|
+
keys.slice(offset, offset + 16).map((key) => (0, import_redis4.getHashJsonValues)(key))
|
|
12906
|
+
);
|
|
12907
|
+
for (const row of buckets.flat())
|
|
12908
|
+
if (isRuntimeTradeRecord(row)) retained.set(row.orderId, row);
|
|
12909
|
+
}
|
|
12910
|
+
for (const row of await loadRuntimeClosedTrades(userName3, {
|
|
12911
|
+
startTime,
|
|
12912
|
+
endTime
|
|
12913
|
+
}))
|
|
12914
|
+
retained.set(row.orderId, row);
|
|
12915
|
+
return [...retained.values()].filter(
|
|
12916
|
+
(row) => row.entryTimestamp < endTime && (row.status === "active" || Number.isFinite(row.exitTimestamp) && row.exitTimestamp >= startTime)
|
|
12917
|
+
).map((row) => {
|
|
12918
|
+
const closesAfterCutoff = row.status === "closed" && row.exitTimestamp >= endTime;
|
|
12919
|
+
if (!closesAfterCutoff && !(row.status === "active" && row.lastSyncedAt >= endTime))
|
|
12920
|
+
return row;
|
|
12921
|
+
return {
|
|
12922
|
+
...row,
|
|
12923
|
+
status: "active",
|
|
12924
|
+
currentPrice: null,
|
|
12925
|
+
currentPnl: null,
|
|
12926
|
+
closedPnl: null,
|
|
12927
|
+
exitPrice: null,
|
|
12928
|
+
actualExitPrice: null,
|
|
12929
|
+
exitTimestamp: null,
|
|
12930
|
+
exitType: null,
|
|
12931
|
+
closeFee: null,
|
|
12932
|
+
fundingFee: null,
|
|
12933
|
+
totalFee: null,
|
|
12934
|
+
lastSyncedAt: void 0
|
|
12935
|
+
};
|
|
12936
|
+
}).sort((a, b) => a.entryTimestamp - b.entryTimestamp);
|
|
12937
|
+
};
|
|
12895
12938
|
loadRuntimeActiveTradeOrderIds = async (userName3) => {
|
|
12896
12939
|
const keys = await (0, import_redis4.getKeys)(import_redis4.redisKeys.runtimeActiveTrades(userName3));
|
|
12897
12940
|
const refs = await Promise.all(keys.map((key) => (0, import_redis4.getData)(key, null)));
|
|
@@ -24601,8 +24644,21 @@ const timer = setInterval(() => {
|
|
|
24601
24644
|
try {
|
|
24602
24645
|
const result = JSON.parse(fs.readFileSync(file + '.result', 'utf8'));
|
|
24603
24646
|
console.log(result.message);
|
|
24647
|
+
if (result.exitCode !== 0) {
|
|
24648
|
+
let fd;
|
|
24649
|
+
try {
|
|
24650
|
+
fd = fs.openSync(file + '.log', fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
|
|
24651
|
+
const size = fs.fstatSync(fd).size;
|
|
24652
|
+
const tail = Buffer.alloc(Math.min(size, 65536));
|
|
24653
|
+
fs.readSync(fd, tail, 0, tail.length, Math.max(0, size - tail.length));
|
|
24654
|
+
// The worker redacts each line before persisting or returning it through MCP.
|
|
24655
|
+
for (const line of tail.toString('utf8').split(/[\\r\\n]+/).filter(Boolean).slice(-150)) console.error('Host replay: ' + line);
|
|
24656
|
+
} catch (error) {
|
|
24657
|
+
console.error('Host replay log unavailable: ' + error.code);
|
|
24658
|
+
} finally { if (fd !== undefined) fs.closeSync(fd); }
|
|
24659
|
+
}
|
|
24604
24660
|
clearInterval(timer);
|
|
24605
|
-
process.
|
|
24661
|
+
process.exitCode = result.exitCode === 0 ? 0 : 1;
|
|
24606
24662
|
} catch (error) { if (error.code !== 'ENOENT') throw error; }
|
|
24607
24663
|
}, 1000);
|
|
24608
24664
|
`;
|
|
@@ -24850,7 +24906,7 @@ var init_mcpWorker = __esm({
|
|
|
24850
24906
|
status: "failed",
|
|
24851
24907
|
finishedAt: Date.now(),
|
|
24852
24908
|
updatedAt: Date.now(),
|
|
24853
|
-
error: error instanceof Error ? error.message : "Replay preparation failed"
|
|
24909
|
+
error: error instanceof Error ? (0, import_mcp.redactMcpJobLog)(error.message) : "Replay preparation failed"
|
|
24854
24910
|
},
|
|
24855
24911
|
import_mcp.MCP_JOB_TTL
|
|
24856
24912
|
);
|
|
@@ -24871,14 +24927,7 @@ var init_mcpWorker = __esm({
|
|
|
24871
24927
|
const logs = [];
|
|
24872
24928
|
const log = (chunk) => {
|
|
24873
24929
|
const lines = chunk.toString().replace(/\u001b\[[0-9;]*m/g, "").split(/[\r\n]+/).filter(Boolean);
|
|
24874
|
-
logs.push(
|
|
24875
|
-
...lines.map(
|
|
24876
|
-
(line) => line.slice(0, 1e3).replace(
|
|
24877
|
-
/((?:secret|password|api.?key|token|authorization)\s*[:=]\s*)\S+/gi,
|
|
24878
|
-
"$1[redacted]"
|
|
24879
|
-
)
|
|
24880
|
-
)
|
|
24881
|
-
);
|
|
24930
|
+
logs.push(...lines.map(import_mcp.redactMcpJobLog));
|
|
24882
24931
|
if (logs.length > 150) logs.splice(0, logs.length - 150);
|
|
24883
24932
|
};
|
|
24884
24933
|
currentChild.stdout?.on("data", log);
|
|
@@ -24908,7 +24957,7 @@ var init_mcpWorker = __esm({
|
|
|
24908
24957
|
"error",
|
|
24909
24958
|
() => resolve({ code: null, error: "Unable to start research process" })
|
|
24910
24959
|
);
|
|
24911
|
-
currentChild.once("
|
|
24960
|
+
currentChild.once("close", (code) => resolve({ code }));
|
|
24912
24961
|
});
|
|
24913
24962
|
clearTimeout(timeout);
|
|
24914
24963
|
clearInterval(monitor);
|
|
@@ -24925,7 +24974,11 @@ var init_mcpWorker = __esm({
|
|
|
24925
24974
|
exitCode: outcome.code,
|
|
24926
24975
|
updatedAt: Date.now(),
|
|
24927
24976
|
finishedAt: Date.now(),
|
|
24928
|
-
...cancelled ? {} : timedOut ? { error: "Job exceeded one hour" } : stopping ? { error: "Worker stopped; this job was not replayed" } : outcome.code !== 0 ? {
|
|
24977
|
+
...cancelled ? {} : timedOut ? { error: "Job exceeded one hour" } : stopping ? { error: "Worker stopped; this job was not replayed" } : outcome.code !== 0 ? {
|
|
24978
|
+
error: outcome.error || (started.kind === "runtime-feedback-replay" ? logs.find(
|
|
24979
|
+
(line) => line.startsWith("Host replay: ")
|
|
24980
|
+
) || logs.at(-1) : void 0) || "Research process failed"
|
|
24981
|
+
} : {}
|
|
24929
24982
|
};
|
|
24930
24983
|
if (await import_redis15.mcpStorage.compare(jobKey, latest, next, import_mcp.MCP_JOB_TTL))
|
|
24931
24984
|
break;
|
|
@@ -31364,30 +31417,13 @@ var init_runtimeDebugEvidence = __esm({
|
|
|
31364
31417
|
}) => {
|
|
31365
31418
|
const dayKeys = (0, import_time5.getRuntimeStorageDayKeys)(startTime, endTime);
|
|
31366
31419
|
const dayKeySet = new Set(dayKeys);
|
|
31367
|
-
const [
|
|
31368
|
-
|
|
31369
|
-
closedTrades,
|
|
31370
|
-
signals2,
|
|
31371
|
-
evaluations,
|
|
31372
|
-
evaluationStatsBuckets,
|
|
31373
|
-
lineageScopes
|
|
31374
|
-
] = await Promise.all([
|
|
31375
|
-
loadRuntimeTrades(userName3, { startTime, endTime }),
|
|
31376
|
-
loadRuntimeClosedTrades(userName3, { startTime, endTime }),
|
|
31420
|
+
const [trades, signals2, evaluations, evaluationStatsBuckets, lineageScopes] = await Promise.all([
|
|
31421
|
+
loadRuntimeEvidenceTrades(userName3, { startTime, endTime }),
|
|
31377
31422
|
loadRuntimeSignals(userName3, { startTime, endTime }),
|
|
31378
31423
|
loadRuntimeSignalEvaluations(userName3, { startTime, endTime }),
|
|
31379
31424
|
loadRuntimeSignalEvaluationStatsBuckets(userName3),
|
|
31380
31425
|
loadRuntimeLineageScopes(userName3, { startTime, endTime })
|
|
31381
31426
|
]);
|
|
31382
|
-
const tradesByOrderId = new Map(
|
|
31383
|
-
entryTrades.map((trade) => [trade.orderId, trade])
|
|
31384
|
-
);
|
|
31385
|
-
for (const trade of closedTrades) {
|
|
31386
|
-
tradesByOrderId.set(trade.orderId, trade);
|
|
31387
|
-
}
|
|
31388
|
-
const trades = [...tradesByOrderId.values()].sort(
|
|
31389
|
-
(left, right) => left.entryTimestamp - right.entryTimestamp
|
|
31390
|
-
);
|
|
31391
31427
|
const filteredStats = evaluationStatsBuckets.filter(
|
|
31392
31428
|
(entry) => dayKeySet.has(entry.dayKey) && (!deploymentId || entry.deploymentId === deploymentId) && (!strategies?.length || strategies.includes(entry.strategy))
|
|
31393
31429
|
);
|
|
@@ -59,8 +59,21 @@ const timer = setInterval(() => {
|
|
|
59
59
|
try {
|
|
60
60
|
const result = JSON.parse(fs.readFileSync(file + '.result', 'utf8'));
|
|
61
61
|
console.log(result.message);
|
|
62
|
+
if (result.exitCode !== 0) {
|
|
63
|
+
let fd;
|
|
64
|
+
try {
|
|
65
|
+
fd = fs.openSync(file + '.log', fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
|
|
66
|
+
const size = fs.fstatSync(fd).size;
|
|
67
|
+
const tail = Buffer.alloc(Math.min(size, 65536));
|
|
68
|
+
fs.readSync(fd, tail, 0, tail.length, Math.max(0, size - tail.length));
|
|
69
|
+
// The worker redacts each line before persisting or returning it through MCP.
|
|
70
|
+
for (const line of tail.toString('utf8').split(/[\\r\\n]+/).filter(Boolean).slice(-150)) console.error('Host replay: ' + line);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error('Host replay log unavailable: ' + error.code);
|
|
73
|
+
} finally { if (fd !== undefined) fs.closeSync(fd); }
|
|
74
|
+
}
|
|
62
75
|
clearInterval(timer);
|
|
63
|
-
process.
|
|
76
|
+
process.exitCode = result.exitCode === 0 ? 0 : 1;
|
|
64
77
|
} catch (error) { if (error.code !== 'ENOENT') throw error; }
|
|
65
78
|
}, 1000);
|
|
66
79
|
`;
|
|
@@ -42,39 +42,6 @@ var isRuntimeTradeRecord = (value) => {
|
|
|
42
42
|
const record = value;
|
|
43
43
|
return typeof record.orderId === "string" && typeof record.strategy === "string" && typeof record.symbol === "string" && typeof record.entryTimestamp === "number" && typeof record.entryPrice === "number" && typeof record.qty === "number" && (record.direction === "LONG" || record.direction === "SHORT");
|
|
44
44
|
};
|
|
45
|
-
var loadRuntimeTrades = async (userName, {
|
|
46
|
-
startTime,
|
|
47
|
-
endTime
|
|
48
|
-
} = {}) => {
|
|
49
|
-
const hasWindow = Number.isFinite(startTime) && Number.isFinite(endTime) && startTime != null && endTime != null;
|
|
50
|
-
const filterByWindow = (trade) => !hasWindow || trade.entryTimestamp >= startTime && trade.entryTimestamp <= endTime;
|
|
51
|
-
if (hasWindow) {
|
|
52
|
-
const dayKeys = (0, import_time.getRuntimeStorageDayKeys)(startTime, endTime);
|
|
53
|
-
const bucketTrades = (await Promise.all(
|
|
54
|
-
dayKeys.map(
|
|
55
|
-
(dayKey) => (0, import_redis.getHashJsonValues)(
|
|
56
|
-
import_redis.redisKeys.runtimeTradeBucket(userName, dayKey)
|
|
57
|
-
)
|
|
58
|
-
)
|
|
59
|
-
)).flat();
|
|
60
|
-
const dedupedBucketTrades = /* @__PURE__ */ new Map();
|
|
61
|
-
for (const trade of bucketTrades) {
|
|
62
|
-
if (!isRuntimeTradeRecord(trade)) {
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
dedupedBucketTrades.set(trade.orderId, trade);
|
|
66
|
-
}
|
|
67
|
-
const windowedTrades = [...dedupedBucketTrades.values()].filter(filterByWindow).sort((left, right) => left.entryTimestamp - right.entryTimestamp);
|
|
68
|
-
return windowedTrades;
|
|
69
|
-
}
|
|
70
|
-
const tradePrefix = import_redis.redisKeys.runtimeTrades(userName);
|
|
71
|
-
const bucketPrefix = import_redis.redisKeys.runtimeTradeBuckets(userName);
|
|
72
|
-
const keys = (await (0, import_redis.getKeys)(tradePrefix)).filter(
|
|
73
|
-
(key) => !key.startsWith(bucketPrefix)
|
|
74
|
-
);
|
|
75
|
-
const trades = await Promise.all(keys.map((key) => (0, import_redis.getData)(key, null)));
|
|
76
|
-
return trades.filter(isRuntimeTradeRecord).filter(filterByWindow).sort((left, right) => left.entryTimestamp - right.entryTimestamp);
|
|
77
|
-
};
|
|
78
45
|
var loadRuntimeClosedTrades = async (userName, {
|
|
79
46
|
startTime,
|
|
80
47
|
endTime
|
|
@@ -98,6 +65,49 @@ var loadRuntimeClosedTrades = async (userName, {
|
|
|
98
65
|
(left, right) => left.exitTimestamp - right.exitTimestamp
|
|
99
66
|
);
|
|
100
67
|
};
|
|
68
|
+
var loadRuntimeEvidenceTrades = async (userName, { startTime, endTime }) => {
|
|
69
|
+
const prefix = import_redis.redisKeys.runtimeTradeBuckets(userName);
|
|
70
|
+
const lastDay = (0, import_time.getRuntimeStorageDayKey)(endTime - 1);
|
|
71
|
+
const keys = (await (0, import_redis.getKeys)(prefix)).filter((key) => {
|
|
72
|
+
const day = key.slice(prefix.length);
|
|
73
|
+
return key.startsWith(prefix) && /^\d{4}-\d{2}-\d{2}$/.test(day) && day <= lastDay;
|
|
74
|
+
});
|
|
75
|
+
const retained = /* @__PURE__ */ new Map();
|
|
76
|
+
for (let offset = 0; offset < keys.length; offset += 16) {
|
|
77
|
+
const buckets = await Promise.all(
|
|
78
|
+
keys.slice(offset, offset + 16).map((key) => (0, import_redis.getHashJsonValues)(key))
|
|
79
|
+
);
|
|
80
|
+
for (const row of buckets.flat())
|
|
81
|
+
if (isRuntimeTradeRecord(row)) retained.set(row.orderId, row);
|
|
82
|
+
}
|
|
83
|
+
for (const row of await loadRuntimeClosedTrades(userName, {
|
|
84
|
+
startTime,
|
|
85
|
+
endTime
|
|
86
|
+
}))
|
|
87
|
+
retained.set(row.orderId, row);
|
|
88
|
+
return [...retained.values()].filter(
|
|
89
|
+
(row) => row.entryTimestamp < endTime && (row.status === "active" || Number.isFinite(row.exitTimestamp) && row.exitTimestamp >= startTime)
|
|
90
|
+
).map((row) => {
|
|
91
|
+
const closesAfterCutoff = row.status === "closed" && row.exitTimestamp >= endTime;
|
|
92
|
+
if (!closesAfterCutoff && !(row.status === "active" && row.lastSyncedAt >= endTime))
|
|
93
|
+
return row;
|
|
94
|
+
return {
|
|
95
|
+
...row,
|
|
96
|
+
status: "active",
|
|
97
|
+
currentPrice: null,
|
|
98
|
+
currentPnl: null,
|
|
99
|
+
closedPnl: null,
|
|
100
|
+
exitPrice: null,
|
|
101
|
+
actualExitPrice: null,
|
|
102
|
+
exitTimestamp: null,
|
|
103
|
+
exitType: null,
|
|
104
|
+
closeFee: null,
|
|
105
|
+
fundingFee: null,
|
|
106
|
+
totalFee: null,
|
|
107
|
+
lastSyncedAt: void 0
|
|
108
|
+
};
|
|
109
|
+
}).sort((a, b) => a.entryTimestamp - b.entryTimestamp);
|
|
110
|
+
};
|
|
101
111
|
|
|
102
112
|
// src/lib/runtimeSignalsLoader.ts
|
|
103
113
|
var import_redis2 = require("@tradejs/infra/redis");
|
|
@@ -318,30 +328,13 @@ var collectRuntimeDebugEvidence = async ({
|
|
|
318
328
|
}) => {
|
|
319
329
|
const dayKeys = (0, import_time2.getRuntimeStorageDayKeys)(startTime, endTime);
|
|
320
330
|
const dayKeySet = new Set(dayKeys);
|
|
321
|
-
const [
|
|
322
|
-
|
|
323
|
-
closedTrades,
|
|
324
|
-
signals,
|
|
325
|
-
evaluations,
|
|
326
|
-
evaluationStatsBuckets,
|
|
327
|
-
lineageScopes
|
|
328
|
-
] = await Promise.all([
|
|
329
|
-
loadRuntimeTrades(userName, { startTime, endTime }),
|
|
330
|
-
loadRuntimeClosedTrades(userName, { startTime, endTime }),
|
|
331
|
+
const [trades, signals, evaluations, evaluationStatsBuckets, lineageScopes] = await Promise.all([
|
|
332
|
+
loadRuntimeEvidenceTrades(userName, { startTime, endTime }),
|
|
331
333
|
loadRuntimeSignals(userName, { startTime, endTime }),
|
|
332
334
|
loadRuntimeSignalEvaluations(userName, { startTime, endTime }),
|
|
333
335
|
loadRuntimeSignalEvaluationStatsBuckets(userName),
|
|
334
336
|
loadRuntimeLineageScopes(userName, { startTime, endTime })
|
|
335
337
|
]);
|
|
336
|
-
const tradesByOrderId = new Map(
|
|
337
|
-
entryTrades.map((trade) => [trade.orderId, trade])
|
|
338
|
-
);
|
|
339
|
-
for (const trade of closedTrades) {
|
|
340
|
-
tradesByOrderId.set(trade.orderId, trade);
|
|
341
|
-
}
|
|
342
|
-
const trades = [...tradesByOrderId.values()].sort(
|
|
343
|
-
(left, right) => left.entryTimestamp - right.entryTimestamp
|
|
344
|
-
);
|
|
345
338
|
const filteredStats = evaluationStatsBuckets.filter(
|
|
346
339
|
(entry) => dayKeySet.has(entry.dayKey) && (!deploymentId || entry.deploymentId === deploymentId) && (!strategies?.length || strategies.includes(entry.strategy))
|
|
347
340
|
);
|
package/dist/lib/runtimeRedis.js
CHANGED
|
@@ -26,6 +26,7 @@ __export(runtimeRedis_exports, {
|
|
|
26
26
|
loadRuntimeActiveTradeOrderIds: () => loadRuntimeActiveTradeOrderIds,
|
|
27
27
|
loadRuntimeActiveTrades: () => loadRuntimeActiveTrades,
|
|
28
28
|
loadRuntimeClosedTrades: () => loadRuntimeClosedTrades,
|
|
29
|
+
loadRuntimeEvidenceTrades: () => loadRuntimeEvidenceTrades,
|
|
29
30
|
loadRuntimeStrategyConfigs: () => import_runtimeStrategyConfigs.loadRuntimeStrategyConfigs,
|
|
30
31
|
loadRuntimeStrategyNames: () => import_runtimeStrategyConfigs.loadRuntimeStrategyNames,
|
|
31
32
|
loadRuntimeTrades: () => loadRuntimeTrades,
|
|
@@ -104,6 +105,49 @@ var loadRuntimeClosedTrades = async (userName, {
|
|
|
104
105
|
(left, right) => left.exitTimestamp - right.exitTimestamp
|
|
105
106
|
);
|
|
106
107
|
};
|
|
108
|
+
var loadRuntimeEvidenceTrades = async (userName, { startTime, endTime }) => {
|
|
109
|
+
const prefix = import_redis.redisKeys.runtimeTradeBuckets(userName);
|
|
110
|
+
const lastDay = (0, import_time.getRuntimeStorageDayKey)(endTime - 1);
|
|
111
|
+
const keys = (await (0, import_redis.getKeys)(prefix)).filter((key) => {
|
|
112
|
+
const day = key.slice(prefix.length);
|
|
113
|
+
return key.startsWith(prefix) && /^\d{4}-\d{2}-\d{2}$/.test(day) && day <= lastDay;
|
|
114
|
+
});
|
|
115
|
+
const retained = /* @__PURE__ */ new Map();
|
|
116
|
+
for (let offset = 0; offset < keys.length; offset += 16) {
|
|
117
|
+
const buckets = await Promise.all(
|
|
118
|
+
keys.slice(offset, offset + 16).map((key) => (0, import_redis.getHashJsonValues)(key))
|
|
119
|
+
);
|
|
120
|
+
for (const row of buckets.flat())
|
|
121
|
+
if (isRuntimeTradeRecord(row)) retained.set(row.orderId, row);
|
|
122
|
+
}
|
|
123
|
+
for (const row of await loadRuntimeClosedTrades(userName, {
|
|
124
|
+
startTime,
|
|
125
|
+
endTime
|
|
126
|
+
}))
|
|
127
|
+
retained.set(row.orderId, row);
|
|
128
|
+
return [...retained.values()].filter(
|
|
129
|
+
(row) => row.entryTimestamp < endTime && (row.status === "active" || Number.isFinite(row.exitTimestamp) && row.exitTimestamp >= startTime)
|
|
130
|
+
).map((row) => {
|
|
131
|
+
const closesAfterCutoff = row.status === "closed" && row.exitTimestamp >= endTime;
|
|
132
|
+
if (!closesAfterCutoff && !(row.status === "active" && row.lastSyncedAt >= endTime))
|
|
133
|
+
return row;
|
|
134
|
+
return {
|
|
135
|
+
...row,
|
|
136
|
+
status: "active",
|
|
137
|
+
currentPrice: null,
|
|
138
|
+
currentPnl: null,
|
|
139
|
+
closedPnl: null,
|
|
140
|
+
exitPrice: null,
|
|
141
|
+
actualExitPrice: null,
|
|
142
|
+
exitTimestamp: null,
|
|
143
|
+
exitType: null,
|
|
144
|
+
closeFee: null,
|
|
145
|
+
fundingFee: null,
|
|
146
|
+
totalFee: null,
|
|
147
|
+
lastSyncedAt: void 0
|
|
148
|
+
};
|
|
149
|
+
}).sort((a, b) => a.entryTimestamp - b.entryTimestamp);
|
|
150
|
+
};
|
|
107
151
|
var loadRuntimeActiveTradeOrderIds = async (userName) => {
|
|
108
152
|
const keys = await (0, import_redis.getKeys)(import_redis.redisKeys.runtimeActiveTrades(userName));
|
|
109
153
|
const refs = await Promise.all(keys.map((key) => (0, import_redis.getData)(key, null)));
|
|
@@ -144,6 +188,7 @@ var loadStrategyResultSymbols = async ({
|
|
|
144
188
|
loadRuntimeActiveTradeOrderIds,
|
|
145
189
|
loadRuntimeActiveTrades,
|
|
146
190
|
loadRuntimeClosedTrades,
|
|
191
|
+
loadRuntimeEvidenceTrades,
|
|
147
192
|
loadRuntimeStrategyConfigs,
|
|
148
193
|
loadRuntimeStrategyNames,
|
|
149
194
|
loadRuntimeTrades,
|
|
@@ -66,8 +66,21 @@ const timer = setInterval(() => {
|
|
|
66
66
|
try {
|
|
67
67
|
const result = JSON.parse(fs.readFileSync(file + '.result', 'utf8'));
|
|
68
68
|
console.log(result.message);
|
|
69
|
+
if (result.exitCode !== 0) {
|
|
70
|
+
let fd;
|
|
71
|
+
try {
|
|
72
|
+
fd = fs.openSync(file + '.log', fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
|
|
73
|
+
const size = fs.fstatSync(fd).size;
|
|
74
|
+
const tail = Buffer.alloc(Math.min(size, 65536));
|
|
75
|
+
fs.readSync(fd, tail, 0, tail.length, Math.max(0, size - tail.length));
|
|
76
|
+
// The worker redacts each line before persisting or returning it through MCP.
|
|
77
|
+
for (const line of tail.toString('utf8').split(/[\\r\\n]+/).filter(Boolean).slice(-150)) console.error('Host replay: ' + line);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error('Host replay log unavailable: ' + error.code);
|
|
80
|
+
} finally { if (fd !== undefined) fs.closeSync(fd); }
|
|
81
|
+
}
|
|
69
82
|
clearInterval(timer);
|
|
70
|
-
process.
|
|
83
|
+
process.exitCode = result.exitCode === 0 ? 0 : 1;
|
|
71
84
|
} catch (error) { if (error.code !== 'ENOENT') throw error; }
|
|
72
85
|
}, 1000);
|
|
73
86
|
`;
|
|
@@ -299,7 +312,7 @@ var main = async (options = {}) => {
|
|
|
299
312
|
status: "failed",
|
|
300
313
|
finishedAt: Date.now(),
|
|
301
314
|
updatedAt: Date.now(),
|
|
302
|
-
error: error instanceof Error ? error.message : "Replay preparation failed"
|
|
315
|
+
error: error instanceof Error ? (0, import_mcp.redactMcpJobLog)(error.message) : "Replay preparation failed"
|
|
303
316
|
},
|
|
304
317
|
import_mcp.MCP_JOB_TTL
|
|
305
318
|
);
|
|
@@ -320,14 +333,7 @@ var main = async (options = {}) => {
|
|
|
320
333
|
const logs = [];
|
|
321
334
|
const log = (chunk) => {
|
|
322
335
|
const lines = chunk.toString().replace(/\u001b\[[0-9;]*m/g, "").split(/[\r\n]+/).filter(Boolean);
|
|
323
|
-
logs.push(
|
|
324
|
-
...lines.map(
|
|
325
|
-
(line) => line.slice(0, 1e3).replace(
|
|
326
|
-
/((?:secret|password|api.?key|token|authorization)\s*[:=]\s*)\S+/gi,
|
|
327
|
-
"$1[redacted]"
|
|
328
|
-
)
|
|
329
|
-
)
|
|
330
|
-
);
|
|
336
|
+
logs.push(...lines.map(import_mcp.redactMcpJobLog));
|
|
331
337
|
if (logs.length > 150) logs.splice(0, logs.length - 150);
|
|
332
338
|
};
|
|
333
339
|
currentChild.stdout?.on("data", log);
|
|
@@ -357,7 +363,7 @@ var main = async (options = {}) => {
|
|
|
357
363
|
"error",
|
|
358
364
|
() => resolve({ code: null, error: "Unable to start research process" })
|
|
359
365
|
);
|
|
360
|
-
currentChild.once("
|
|
366
|
+
currentChild.once("close", (code) => resolve({ code }));
|
|
361
367
|
});
|
|
362
368
|
clearTimeout(timeout);
|
|
363
369
|
clearInterval(monitor);
|
|
@@ -374,7 +380,11 @@ var main = async (options = {}) => {
|
|
|
374
380
|
exitCode: outcome.code,
|
|
375
381
|
updatedAt: Date.now(),
|
|
376
382
|
finishedAt: Date.now(),
|
|
377
|
-
...cancelled ? {} : timedOut ? { error: "Job exceeded one hour" } : stopping ? { error: "Worker stopped; this job was not replayed" } : outcome.code !== 0 ? {
|
|
383
|
+
...cancelled ? {} : timedOut ? { error: "Job exceeded one hour" } : stopping ? { error: "Worker stopped; this job was not replayed" } : outcome.code !== 0 ? {
|
|
384
|
+
error: outcome.error || (started.kind === "runtime-feedback-replay" ? logs.find(
|
|
385
|
+
(line) => line.startsWith("Host replay: ")
|
|
386
|
+
) || logs.at(-1) : void 0) || "Research process failed"
|
|
387
|
+
} : {}
|
|
378
388
|
};
|
|
379
389
|
if (await import_redis.mcpStorage.compare(jobKey, latest, next, import_mcp.MCP_JOB_TTL))
|
|
380
390
|
break;
|
|
@@ -55,39 +55,6 @@ var isRuntimeTradeRecord = (value) => {
|
|
|
55
55
|
const record = value;
|
|
56
56
|
return typeof record.orderId === "string" && typeof record.strategy === "string" && typeof record.symbol === "string" && typeof record.entryTimestamp === "number" && typeof record.entryPrice === "number" && typeof record.qty === "number" && (record.direction === "LONG" || record.direction === "SHORT");
|
|
57
57
|
};
|
|
58
|
-
var loadRuntimeTrades = async (userName, {
|
|
59
|
-
startTime,
|
|
60
|
-
endTime
|
|
61
|
-
} = {}) => {
|
|
62
|
-
const hasWindow = Number.isFinite(startTime) && Number.isFinite(endTime) && startTime != null && endTime != null;
|
|
63
|
-
const filterByWindow = (trade) => !hasWindow || trade.entryTimestamp >= startTime && trade.entryTimestamp <= endTime;
|
|
64
|
-
if (hasWindow) {
|
|
65
|
-
const dayKeys = (0, import_time.getRuntimeStorageDayKeys)(startTime, endTime);
|
|
66
|
-
const bucketTrades = (await Promise.all(
|
|
67
|
-
dayKeys.map(
|
|
68
|
-
(dayKey) => (0, import_redis.getHashJsonValues)(
|
|
69
|
-
import_redis.redisKeys.runtimeTradeBucket(userName, dayKey)
|
|
70
|
-
)
|
|
71
|
-
)
|
|
72
|
-
)).flat();
|
|
73
|
-
const dedupedBucketTrades = /* @__PURE__ */ new Map();
|
|
74
|
-
for (const trade of bucketTrades) {
|
|
75
|
-
if (!isRuntimeTradeRecord(trade)) {
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
dedupedBucketTrades.set(trade.orderId, trade);
|
|
79
|
-
}
|
|
80
|
-
const windowedTrades = [...dedupedBucketTrades.values()].filter(filterByWindow).sort((left, right) => left.entryTimestamp - right.entryTimestamp);
|
|
81
|
-
return windowedTrades;
|
|
82
|
-
}
|
|
83
|
-
const tradePrefix = import_redis.redisKeys.runtimeTrades(userName);
|
|
84
|
-
const bucketPrefix = import_redis.redisKeys.runtimeTradeBuckets(userName);
|
|
85
|
-
const keys = (await (0, import_redis.getKeys)(tradePrefix)).filter(
|
|
86
|
-
(key) => !key.startsWith(bucketPrefix)
|
|
87
|
-
);
|
|
88
|
-
const trades = await Promise.all(keys.map((key) => (0, import_redis.getData)(key, null)));
|
|
89
|
-
return trades.filter(isRuntimeTradeRecord).filter(filterByWindow).sort((left, right) => left.entryTimestamp - right.entryTimestamp);
|
|
90
|
-
};
|
|
91
58
|
var loadRuntimeClosedTrades = async (userName, {
|
|
92
59
|
startTime,
|
|
93
60
|
endTime
|
|
@@ -111,6 +78,49 @@ var loadRuntimeClosedTrades = async (userName, {
|
|
|
111
78
|
(left, right) => left.exitTimestamp - right.exitTimestamp
|
|
112
79
|
);
|
|
113
80
|
};
|
|
81
|
+
var loadRuntimeEvidenceTrades = async (userName, { startTime, endTime }) => {
|
|
82
|
+
const prefix = import_redis.redisKeys.runtimeTradeBuckets(userName);
|
|
83
|
+
const lastDay = (0, import_time.getRuntimeStorageDayKey)(endTime - 1);
|
|
84
|
+
const keys = (await (0, import_redis.getKeys)(prefix)).filter((key) => {
|
|
85
|
+
const day = key.slice(prefix.length);
|
|
86
|
+
return key.startsWith(prefix) && /^\d{4}-\d{2}-\d{2}$/.test(day) && day <= lastDay;
|
|
87
|
+
});
|
|
88
|
+
const retained = /* @__PURE__ */ new Map();
|
|
89
|
+
for (let offset = 0; offset < keys.length; offset += 16) {
|
|
90
|
+
const buckets = await Promise.all(
|
|
91
|
+
keys.slice(offset, offset + 16).map((key) => (0, import_redis.getHashJsonValues)(key))
|
|
92
|
+
);
|
|
93
|
+
for (const row of buckets.flat())
|
|
94
|
+
if (isRuntimeTradeRecord(row)) retained.set(row.orderId, row);
|
|
95
|
+
}
|
|
96
|
+
for (const row of await loadRuntimeClosedTrades(userName, {
|
|
97
|
+
startTime,
|
|
98
|
+
endTime
|
|
99
|
+
}))
|
|
100
|
+
retained.set(row.orderId, row);
|
|
101
|
+
return [...retained.values()].filter(
|
|
102
|
+
(row) => row.entryTimestamp < endTime && (row.status === "active" || Number.isFinite(row.exitTimestamp) && row.exitTimestamp >= startTime)
|
|
103
|
+
).map((row) => {
|
|
104
|
+
const closesAfterCutoff = row.status === "closed" && row.exitTimestamp >= endTime;
|
|
105
|
+
if (!closesAfterCutoff && !(row.status === "active" && row.lastSyncedAt >= endTime))
|
|
106
|
+
return row;
|
|
107
|
+
return {
|
|
108
|
+
...row,
|
|
109
|
+
status: "active",
|
|
110
|
+
currentPrice: null,
|
|
111
|
+
currentPnl: null,
|
|
112
|
+
closedPnl: null,
|
|
113
|
+
exitPrice: null,
|
|
114
|
+
actualExitPrice: null,
|
|
115
|
+
exitTimestamp: null,
|
|
116
|
+
exitType: null,
|
|
117
|
+
closeFee: null,
|
|
118
|
+
fundingFee: null,
|
|
119
|
+
totalFee: null,
|
|
120
|
+
lastSyncedAt: void 0
|
|
121
|
+
};
|
|
122
|
+
}).sort((a, b) => a.entryTimestamp - b.entryTimestamp);
|
|
123
|
+
};
|
|
114
124
|
|
|
115
125
|
// src/lib/runtimeSignalsLoader.ts
|
|
116
126
|
var import_redis2 = require("@tradejs/infra/redis");
|
|
@@ -313,30 +323,13 @@ var collectRuntimeDebugEvidence = async ({
|
|
|
313
323
|
}) => {
|
|
314
324
|
const dayKeys = (0, import_time2.getRuntimeStorageDayKeys)(startTime, endTime);
|
|
315
325
|
const dayKeySet = new Set(dayKeys);
|
|
316
|
-
const [
|
|
317
|
-
|
|
318
|
-
closedTrades,
|
|
319
|
-
signals,
|
|
320
|
-
evaluations,
|
|
321
|
-
evaluationStatsBuckets,
|
|
322
|
-
lineageScopes
|
|
323
|
-
] = await Promise.all([
|
|
324
|
-
loadRuntimeTrades(userName, { startTime, endTime }),
|
|
325
|
-
loadRuntimeClosedTrades(userName, { startTime, endTime }),
|
|
326
|
+
const [trades, signals, evaluations, evaluationStatsBuckets, lineageScopes] = await Promise.all([
|
|
327
|
+
loadRuntimeEvidenceTrades(userName, { startTime, endTime }),
|
|
326
328
|
loadRuntimeSignals(userName, { startTime, endTime }),
|
|
327
329
|
loadRuntimeSignalEvaluations(userName, { startTime, endTime }),
|
|
328
330
|
loadRuntimeSignalEvaluationStatsBuckets(userName),
|
|
329
331
|
loadRuntimeLineageScopes(userName, { startTime, endTime })
|
|
330
332
|
]);
|
|
331
|
-
const tradesByOrderId = new Map(
|
|
332
|
-
entryTrades.map((trade) => [trade.orderId, trade])
|
|
333
|
-
);
|
|
334
|
-
for (const trade of closedTrades) {
|
|
335
|
-
tradesByOrderId.set(trade.orderId, trade);
|
|
336
|
-
}
|
|
337
|
-
const trades = [...tradesByOrderId.values()].sort(
|
|
338
|
-
(left, right) => left.entryTimestamp - right.entryTimestamp
|
|
339
|
-
);
|
|
340
333
|
const filteredStats = evaluationStatsBuckets.filter(
|
|
341
334
|
(entry) => dayKeySet.has(entry.dayKey) && (!deploymentId || entry.deploymentId === deploymentId) && (!strategies?.length || strategies.includes(entry.strategy))
|
|
342
335
|
);
|
|
@@ -54,39 +54,6 @@ var isRuntimeTradeRecord = (value) => {
|
|
|
54
54
|
const record = value;
|
|
55
55
|
return typeof record.orderId === "string" && typeof record.strategy === "string" && typeof record.symbol === "string" && typeof record.entryTimestamp === "number" && typeof record.entryPrice === "number" && typeof record.qty === "number" && (record.direction === "LONG" || record.direction === "SHORT");
|
|
56
56
|
};
|
|
57
|
-
var loadRuntimeTrades = async (userName, {
|
|
58
|
-
startTime,
|
|
59
|
-
endTime
|
|
60
|
-
} = {}) => {
|
|
61
|
-
const hasWindow = Number.isFinite(startTime) && Number.isFinite(endTime) && startTime != null && endTime != null;
|
|
62
|
-
const filterByWindow = (trade) => !hasWindow || trade.entryTimestamp >= startTime && trade.entryTimestamp <= endTime;
|
|
63
|
-
if (hasWindow) {
|
|
64
|
-
const dayKeys = (0, import_time.getRuntimeStorageDayKeys)(startTime, endTime);
|
|
65
|
-
const bucketTrades = (await Promise.all(
|
|
66
|
-
dayKeys.map(
|
|
67
|
-
(dayKey) => (0, import_redis.getHashJsonValues)(
|
|
68
|
-
import_redis.redisKeys.runtimeTradeBucket(userName, dayKey)
|
|
69
|
-
)
|
|
70
|
-
)
|
|
71
|
-
)).flat();
|
|
72
|
-
const dedupedBucketTrades = /* @__PURE__ */ new Map();
|
|
73
|
-
for (const trade of bucketTrades) {
|
|
74
|
-
if (!isRuntimeTradeRecord(trade)) {
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
dedupedBucketTrades.set(trade.orderId, trade);
|
|
78
|
-
}
|
|
79
|
-
const windowedTrades = [...dedupedBucketTrades.values()].filter(filterByWindow).sort((left, right) => left.entryTimestamp - right.entryTimestamp);
|
|
80
|
-
return windowedTrades;
|
|
81
|
-
}
|
|
82
|
-
const tradePrefix = import_redis.redisKeys.runtimeTrades(userName);
|
|
83
|
-
const bucketPrefix = import_redis.redisKeys.runtimeTradeBuckets(userName);
|
|
84
|
-
const keys = (await (0, import_redis.getKeys)(tradePrefix)).filter(
|
|
85
|
-
(key) => !key.startsWith(bucketPrefix)
|
|
86
|
-
);
|
|
87
|
-
const trades = await Promise.all(keys.map((key) => (0, import_redis.getData)(key, null)));
|
|
88
|
-
return trades.filter(isRuntimeTradeRecord).filter(filterByWindow).sort((left, right) => left.entryTimestamp - right.entryTimestamp);
|
|
89
|
-
};
|
|
90
57
|
var loadRuntimeClosedTrades = async (userName, {
|
|
91
58
|
startTime,
|
|
92
59
|
endTime
|
|
@@ -110,6 +77,49 @@ var loadRuntimeClosedTrades = async (userName, {
|
|
|
110
77
|
(left, right) => left.exitTimestamp - right.exitTimestamp
|
|
111
78
|
);
|
|
112
79
|
};
|
|
80
|
+
var loadRuntimeEvidenceTrades = async (userName, { startTime, endTime }) => {
|
|
81
|
+
const prefix = import_redis.redisKeys.runtimeTradeBuckets(userName);
|
|
82
|
+
const lastDay = (0, import_time.getRuntimeStorageDayKey)(endTime - 1);
|
|
83
|
+
const keys = (await (0, import_redis.getKeys)(prefix)).filter((key) => {
|
|
84
|
+
const day = key.slice(prefix.length);
|
|
85
|
+
return key.startsWith(prefix) && /^\d{4}-\d{2}-\d{2}$/.test(day) && day <= lastDay;
|
|
86
|
+
});
|
|
87
|
+
const retained = /* @__PURE__ */ new Map();
|
|
88
|
+
for (let offset = 0; offset < keys.length; offset += 16) {
|
|
89
|
+
const buckets = await Promise.all(
|
|
90
|
+
keys.slice(offset, offset + 16).map((key) => (0, import_redis.getHashJsonValues)(key))
|
|
91
|
+
);
|
|
92
|
+
for (const row of buckets.flat())
|
|
93
|
+
if (isRuntimeTradeRecord(row)) retained.set(row.orderId, row);
|
|
94
|
+
}
|
|
95
|
+
for (const row of await loadRuntimeClosedTrades(userName, {
|
|
96
|
+
startTime,
|
|
97
|
+
endTime
|
|
98
|
+
}))
|
|
99
|
+
retained.set(row.orderId, row);
|
|
100
|
+
return [...retained.values()].filter(
|
|
101
|
+
(row) => row.entryTimestamp < endTime && (row.status === "active" || Number.isFinite(row.exitTimestamp) && row.exitTimestamp >= startTime)
|
|
102
|
+
).map((row) => {
|
|
103
|
+
const closesAfterCutoff = row.status === "closed" && row.exitTimestamp >= endTime;
|
|
104
|
+
if (!closesAfterCutoff && !(row.status === "active" && row.lastSyncedAt >= endTime))
|
|
105
|
+
return row;
|
|
106
|
+
return {
|
|
107
|
+
...row,
|
|
108
|
+
status: "active",
|
|
109
|
+
currentPrice: null,
|
|
110
|
+
currentPnl: null,
|
|
111
|
+
closedPnl: null,
|
|
112
|
+
exitPrice: null,
|
|
113
|
+
actualExitPrice: null,
|
|
114
|
+
exitTimestamp: null,
|
|
115
|
+
exitType: null,
|
|
116
|
+
closeFee: null,
|
|
117
|
+
fundingFee: null,
|
|
118
|
+
totalFee: null,
|
|
119
|
+
lastSyncedAt: void 0
|
|
120
|
+
};
|
|
121
|
+
}).sort((a, b) => a.entryTimestamp - b.entryTimestamp);
|
|
122
|
+
};
|
|
113
123
|
|
|
114
124
|
// src/lib/runtimeSignalsLoader.ts
|
|
115
125
|
var import_redis2 = require("@tradejs/infra/redis");
|
|
@@ -312,30 +322,13 @@ var collectRuntimeDebugEvidence = async ({
|
|
|
312
322
|
}) => {
|
|
313
323
|
const dayKeys = (0, import_time2.getRuntimeStorageDayKeys)(startTime, endTime);
|
|
314
324
|
const dayKeySet = new Set(dayKeys);
|
|
315
|
-
const [
|
|
316
|
-
|
|
317
|
-
closedTrades,
|
|
318
|
-
signals,
|
|
319
|
-
evaluations,
|
|
320
|
-
evaluationStatsBuckets,
|
|
321
|
-
lineageScopes
|
|
322
|
-
] = await Promise.all([
|
|
323
|
-
loadRuntimeTrades(userName, { startTime, endTime }),
|
|
324
|
-
loadRuntimeClosedTrades(userName, { startTime, endTime }),
|
|
325
|
+
const [trades, signals, evaluations, evaluationStatsBuckets, lineageScopes] = await Promise.all([
|
|
326
|
+
loadRuntimeEvidenceTrades(userName, { startTime, endTime }),
|
|
325
327
|
loadRuntimeSignals(userName, { startTime, endTime }),
|
|
326
328
|
loadRuntimeSignalEvaluations(userName, { startTime, endTime }),
|
|
327
329
|
loadRuntimeSignalEvaluationStatsBuckets(userName),
|
|
328
330
|
loadRuntimeLineageScopes(userName, { startTime, endTime })
|
|
329
331
|
]);
|
|
330
|
-
const tradesByOrderId = new Map(
|
|
331
|
-
entryTrades.map((trade) => [trade.orderId, trade])
|
|
332
|
-
);
|
|
333
|
-
for (const trade of closedTrades) {
|
|
334
|
-
tradesByOrderId.set(trade.orderId, trade);
|
|
335
|
-
}
|
|
336
|
-
const trades = [...tradesByOrderId.values()].sort(
|
|
337
|
-
(left, right) => left.entryTimestamp - right.entryTimestamp
|
|
338
|
-
);
|
|
339
332
|
const filteredStats = evaluationStatsBuckets.filter(
|
|
340
333
|
(entry) => dayKeySet.has(entry.dayKey) && (!deploymentId || entry.deploymentId === deploymentId) && (!strategies?.length || strategies.includes(entry.strategy))
|
|
341
334
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tradejs/cli",
|
|
3
|
-
"version": "3.1.29-beta.
|
|
3
|
+
"version": "3.1.29-beta.282",
|
|
4
4
|
"description": "Official CLI for the TradeJS TypeScript framework: infra setup, backtests, signals, bots, and ML workflows.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tradejs",
|
|
@@ -37,13 +37,13 @@
|
|
|
37
37
|
"tradejs": "dist/cli.js"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@tradejs/connectors": "^3.1.29-beta.
|
|
41
|
-
"@tradejs/core": "^3.1.29-beta.
|
|
42
|
-
"@tradejs/indicators": "^3.1.29-beta.
|
|
43
|
-
"@tradejs/infra": "^3.1.29-beta.
|
|
44
|
-
"@tradejs/node": "^3.1.29-beta.
|
|
40
|
+
"@tradejs/connectors": "^3.1.29-beta.282",
|
|
41
|
+
"@tradejs/core": "^3.1.29-beta.282",
|
|
42
|
+
"@tradejs/indicators": "^3.1.29-beta.282",
|
|
43
|
+
"@tradejs/infra": "^3.1.29-beta.282",
|
|
44
|
+
"@tradejs/node": "^3.1.29-beta.282",
|
|
45
45
|
"@tradejs/strategy-kit": "3.0.6",
|
|
46
|
-
"@tradejs/types": "^3.1.29-beta.
|
|
46
|
+
"@tradejs/types": "^3.1.29-beta.282",
|
|
47
47
|
"args": "^5.0.3",
|
|
48
48
|
"bcryptjs": "^2.4.3",
|
|
49
49
|
"chalk": "4.1.2",
|