@tradejs/cli 3.1.29-beta.291 → 3.1.29-beta.296
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 +7150 -6352
- package/dist/lib/binanceMarketContextBackfill.js +2 -2
- package/dist/lib/coinMarketCapContextBackfill.js +1 -1
- package/dist/lib/coreResearch.js +261 -115
- package/dist/lib/marketContextPrepare.js +3 -3
- package/dist/lib/researchVisuals.js +27 -6
- package/dist/lib/strategyResearch.js +234 -245
- package/dist/scripts/aiPocketSearch.js +3343 -3029
- package/dist/scripts/backtest.js +192 -69
- package/dist/scripts/binanceMarketIngest.js +3 -3
- package/dist/scripts/coreResearch.js +263 -115
- package/dist/scripts/replay.js +483 -114
- package/dist/scripts/replayRunner.js +483 -114
- package/dist/scripts/researchVisuals.js +27 -6
- package/dist/scripts/runtimeParity.js +3 -3
- package/dist/scripts/signals.js +3 -3
- package/dist/scripts/signalsDaemon.js +3 -3
- package/dist/scripts/strategyRelease.js +6 -37
- package/dist/scripts/strategyResearch.js +239 -249
- package/package.json +7 -7
- package/dist/lib/researchMetricSnapshot.js +0 -74
|
@@ -170,7 +170,7 @@ function normalizeTrade(raw, fallbackStrategy, candlesBySymbol = {}) {
|
|
|
170
170
|
pockets,
|
|
171
171
|
features,
|
|
172
172
|
groups,
|
|
173
|
-
candles:
|
|
173
|
+
candles: r.candles === void 0 ? candlesBySymbol[symbol] ?? [] : normalizeCandles(r.candles),
|
|
174
174
|
figures: normalizeFigures(r.figures ?? payload.figures)
|
|
175
175
|
};
|
|
176
176
|
}
|
|
@@ -192,11 +192,16 @@ function normalizeDataset(input, options = {}) {
|
|
|
192
192
|
);
|
|
193
193
|
if (runs.size > 1)
|
|
194
194
|
throw new Error("Mixed backtest runs; provide one immutable lineage");
|
|
195
|
+
const candlesBySymbol = Object.fromEntries(
|
|
196
|
+
Object.entries(options.candlesBySymbol ?? object(root.candlesBySymbol)).map(
|
|
197
|
+
([symbol, candles]) => [symbol, normalizeCandles(candles)]
|
|
198
|
+
)
|
|
199
|
+
);
|
|
195
200
|
const trades = rows.map(
|
|
196
201
|
(r) => normalizeTrade(
|
|
197
202
|
r,
|
|
198
203
|
options.strategy ?? String(root.strategy ?? ""),
|
|
199
|
-
|
|
204
|
+
candlesBySymbol
|
|
200
205
|
)
|
|
201
206
|
);
|
|
202
207
|
const ids = /* @__PURE__ */ new Set();
|
|
@@ -213,6 +218,21 @@ function normalizeDataset(input, options = {}) {
|
|
|
213
218
|
trades
|
|
214
219
|
};
|
|
215
220
|
}
|
|
221
|
+
function tradeCandleWindow(trade, intervalMs) {
|
|
222
|
+
const lowerBound = (timestamp) => {
|
|
223
|
+
let lo = 0, hi = trade.candles.length;
|
|
224
|
+
while (lo < hi) {
|
|
225
|
+
const mid = Math.floor((lo + hi) / 2);
|
|
226
|
+
if (trade.candles[mid].timestamp < timestamp) lo = mid + 1;
|
|
227
|
+
else hi = mid;
|
|
228
|
+
}
|
|
229
|
+
return lo;
|
|
230
|
+
};
|
|
231
|
+
return trade.candles.slice(
|
|
232
|
+
lowerBound(trade.signalTime - 140 * intervalMs),
|
|
233
|
+
lowerBound(trade.exitTime + 20 * intervalMs + 1)
|
|
234
|
+
);
|
|
235
|
+
}
|
|
216
236
|
|
|
217
237
|
// src/lib/researchVisuals/analysis.ts
|
|
218
238
|
var import_node_crypto = require("crypto");
|
|
@@ -534,7 +554,10 @@ function pricePanel(t, c, step, x, y, w, h) {
|
|
|
534
554
|
);
|
|
535
555
|
}
|
|
536
556
|
function renderTradeSvg(t, intervalMs) {
|
|
537
|
-
const candles = chartCandles(
|
|
557
|
+
const candles = chartCandles({
|
|
558
|
+
...t,
|
|
559
|
+
candles: tradeCandleWindow(t, intervalMs)
|
|
560
|
+
});
|
|
538
561
|
const start = Math.min(
|
|
539
562
|
t.signalTime - 90 * intervalMs,
|
|
540
563
|
t.entryTime - 90 * intervalMs
|
|
@@ -809,9 +832,7 @@ async function generateResearchVisuals(options) {
|
|
|
809
832
|
population: "selected",
|
|
810
833
|
trades: selected.map((trade) => ({
|
|
811
834
|
...trade,
|
|
812
|
-
candles: trade.
|
|
813
|
-
(bar) => bar.timestamp >= trade.signalTime - 140 * dataset.intervalMs && bar.timestamp <= trade.exitTime + 20 * dataset.intervalMs
|
|
814
|
-
)
|
|
835
|
+
candles: tradeCandleWindow(trade, dataset.intervalMs)
|
|
815
836
|
}))
|
|
816
837
|
};
|
|
817
838
|
await write("chart-inputs.json.gz", (0, import_node_zlib.gzipSync)(JSON.stringify(retained)));
|
|
@@ -1028,13 +1028,12 @@ var getReferenceSymbols = () => {
|
|
|
1028
1028
|
};
|
|
1029
1029
|
var intervalToMarketFeatureInterval = (interval2) => {
|
|
1030
1030
|
const normalized = String(interval2).trim().toLowerCase();
|
|
1031
|
-
if (normalized === "1" || normalized === "1m") return
|
|
1031
|
+
if (normalized === "1" || normalized === "1m") return null;
|
|
1032
1032
|
if (normalized === "5" || normalized === "5m") return "5m";
|
|
1033
1033
|
if (normalized === "60" || normalized === "1h") return "1h";
|
|
1034
1034
|
return "15m";
|
|
1035
1035
|
};
|
|
1036
1036
|
var marketIntervalToConnectorInterval = (interval2) => {
|
|
1037
|
-
if (interval2 === "1m") return "1";
|
|
1038
1037
|
if (interval2 === "5m") return "5";
|
|
1039
1038
|
if (interval2 === "1h") return "60";
|
|
1040
1039
|
return "15";
|
|
@@ -1199,6 +1198,7 @@ var backfillBinanceMarketContext = async (params, enabled) => {
|
|
|
1199
1198
|
return skippedBackfillResult();
|
|
1200
1199
|
}
|
|
1201
1200
|
const interval2 = intervalToMarketFeatureInterval(params.interval);
|
|
1201
|
+
if (interval2 === null) return skippedBackfillResult();
|
|
1202
1202
|
const intervalMs = MARKET_FEATURE_INTERVAL_MS[interval2];
|
|
1203
1203
|
const { breadthStartMs, tradeFlowStartMs, endMs } = resolveBinanceMarketContextBackfillWindow(params);
|
|
1204
1204
|
if (endMs <= Math.min(breadthStartMs, tradeFlowStartMs)) {
|
|
@@ -2779,7 +2779,7 @@ var coinMarketCapFetch = async (params) => {
|
|
|
2779
2779
|
};
|
|
2780
2780
|
var fetchCoinMarketCapIndexRows = async (params) => {
|
|
2781
2781
|
const rows = [];
|
|
2782
|
-
const maxChunkDays =
|
|
2782
|
+
const maxChunkDays = 4;
|
|
2783
2783
|
const totalChunks = Math.max(
|
|
2784
2784
|
1,
|
|
2785
2785
|
Math.ceil(
|
package/dist/scripts/signals.js
CHANGED
|
@@ -488,13 +488,12 @@ var getReferenceSymbols = () => {
|
|
|
488
488
|
};
|
|
489
489
|
var intervalToMarketFeatureInterval = (interval) => {
|
|
490
490
|
const normalized = String(interval).trim().toLowerCase();
|
|
491
|
-
if (normalized === "1" || normalized === "1m") return
|
|
491
|
+
if (normalized === "1" || normalized === "1m") return null;
|
|
492
492
|
if (normalized === "5" || normalized === "5m") return "5m";
|
|
493
493
|
if (normalized === "60" || normalized === "1h") return "1h";
|
|
494
494
|
return "15m";
|
|
495
495
|
};
|
|
496
496
|
var marketIntervalToConnectorInterval = (interval) => {
|
|
497
|
-
if (interval === "1m") return "1";
|
|
498
497
|
if (interval === "5m") return "5";
|
|
499
498
|
if (interval === "1h") return "60";
|
|
500
499
|
return "15";
|
|
@@ -659,6 +658,7 @@ var backfillBinanceMarketContext = async (params, enabled) => {
|
|
|
659
658
|
return skippedBackfillResult();
|
|
660
659
|
}
|
|
661
660
|
const interval = intervalToMarketFeatureInterval(params.interval);
|
|
661
|
+
if (interval === null) return skippedBackfillResult();
|
|
662
662
|
const intervalMs = MARKET_FEATURE_INTERVAL_MS[interval];
|
|
663
663
|
const { breadthStartMs, tradeFlowStartMs, endMs } = resolveBinanceMarketContextBackfillWindow(params);
|
|
664
664
|
if (endMs <= Math.min(breadthStartMs, tradeFlowStartMs)) {
|
|
@@ -2239,7 +2239,7 @@ var coinMarketCapFetch = async (params) => {
|
|
|
2239
2239
|
};
|
|
2240
2240
|
var fetchCoinMarketCapIndexRows = async (params) => {
|
|
2241
2241
|
const rows = [];
|
|
2242
|
-
const maxChunkDays =
|
|
2242
|
+
const maxChunkDays = 4;
|
|
2243
2243
|
const totalChunks = Math.max(
|
|
2244
2244
|
1,
|
|
2245
2245
|
Math.ceil(
|
|
@@ -486,13 +486,12 @@ var getReferenceSymbols = () => {
|
|
|
486
486
|
};
|
|
487
487
|
var intervalToMarketFeatureInterval = (interval) => {
|
|
488
488
|
const normalized = String(interval).trim().toLowerCase();
|
|
489
|
-
if (normalized === "1" || normalized === "1m") return
|
|
489
|
+
if (normalized === "1" || normalized === "1m") return null;
|
|
490
490
|
if (normalized === "5" || normalized === "5m") return "5m";
|
|
491
491
|
if (normalized === "60" || normalized === "1h") return "1h";
|
|
492
492
|
return "15m";
|
|
493
493
|
};
|
|
494
494
|
var marketIntervalToConnectorInterval = (interval) => {
|
|
495
|
-
if (interval === "1m") return "1";
|
|
496
495
|
if (interval === "5m") return "5";
|
|
497
496
|
if (interval === "1h") return "60";
|
|
498
497
|
return "15";
|
|
@@ -657,6 +656,7 @@ var backfillBinanceMarketContext = async (params, enabled) => {
|
|
|
657
656
|
return skippedBackfillResult();
|
|
658
657
|
}
|
|
659
658
|
const interval = intervalToMarketFeatureInterval(params.interval);
|
|
659
|
+
if (interval === null) return skippedBackfillResult();
|
|
660
660
|
const intervalMs = MARKET_FEATURE_INTERVAL_MS[interval];
|
|
661
661
|
const { breadthStartMs, tradeFlowStartMs, endMs } = resolveBinanceMarketContextBackfillWindow(params);
|
|
662
662
|
if (endMs <= Math.min(breadthStartMs, tradeFlowStartMs)) {
|
|
@@ -2237,7 +2237,7 @@ var coinMarketCapFetch = async (params) => {
|
|
|
2237
2237
|
};
|
|
2238
2238
|
var fetchCoinMarketCapIndexRows = async (params) => {
|
|
2239
2239
|
const rows = [];
|
|
2240
|
-
const maxChunkDays =
|
|
2240
|
+
const maxChunkDays = 4;
|
|
2241
2241
|
const totalChunks = Math.max(
|
|
2242
2242
|
1,
|
|
2243
2243
|
Math.ceil(
|
|
@@ -42,40 +42,9 @@ var import_chalk = __toESM(require("chalk"));
|
|
|
42
42
|
|
|
43
43
|
// src/lib/strategyRelease/researchRetention.ts
|
|
44
44
|
var import_promises = __toESM(require("fs/promises"));
|
|
45
|
-
|
|
46
|
-
// src/lib/researchMetricSnapshot.ts
|
|
47
|
-
var import_node_crypto = require("crypto");
|
|
48
|
-
var import_node_zlib = require("zlib");
|
|
49
|
-
var MAX_BYTES = 256 * 1024 * 1024;
|
|
50
|
-
var hash = (bytes) => (0, import_node_crypto.createHash)("sha256").update(bytes).digest("hex");
|
|
51
|
-
var validatePermanentResearchMetrics = (value) => {
|
|
52
|
-
if (!value || typeof value !== "object") return;
|
|
53
|
-
for (const [key, child] of Object.entries(value)) {
|
|
54
|
-
if (["matchedPairs", "tradeRows", "allProjectedLeaves"].includes(key) && Array.isArray(child) && child.length)
|
|
55
|
-
throw new Error(`Temporary payload ${key} is not a permanent metric`);
|
|
56
|
-
if (key === "trades" && Array.isArray(child) && child.some(
|
|
57
|
-
(row) => row && typeof row === "object" && ("signalId" in row || "entryTimestamp" in row)
|
|
58
|
-
))
|
|
59
|
-
throw new Error("Temporary trade records are not permanent metrics");
|
|
60
|
-
validatePermanentResearchMetrics(child);
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
var decodeResearchMetricSnapshot = (snapshot) => {
|
|
64
|
-
if (!snapshot || typeof snapshot !== "object" || !("schema" in snapshot) || snapshot.schema !== "tradejs-metric-snapshot/v1" || !("encoding" in snapshot) || snapshot.encoding !== "gzip+base64" || !("decodedBytes" in snapshot) || typeof snapshot.decodedBytes !== "number" || !Number.isSafeInteger(snapshot.decodedBytes) || snapshot.decodedBytes <= 0 || snapshot.decodedBytes > MAX_BYTES || !("sha256" in snapshot) || typeof snapshot.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(snapshot.sha256) || !("payload" in snapshot) || typeof snapshot.payload !== "string")
|
|
65
|
-
throw new Error("Invalid metric snapshot envelope");
|
|
66
|
-
const bytes = (0, import_node_zlib.gunzipSync)(Buffer.from(snapshot.payload, "base64"), {
|
|
67
|
-
maxOutputLength: snapshot.decodedBytes
|
|
68
|
-
});
|
|
69
|
-
if (bytes.length !== snapshot.decodedBytes || hash(bytes) !== snapshot.sha256)
|
|
70
|
-
throw new Error("Metric snapshot checksum or size mismatch");
|
|
71
|
-
const metrics = JSON.parse(bytes.toString("utf8"));
|
|
72
|
-
validatePermanentResearchMetrics(metrics);
|
|
73
|
-
return metrics;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
// src/lib/strategyRelease/researchRetention.ts
|
|
45
|
+
var import_researchMetricSnapshot = require("@tradejs/infra/researchMetricSnapshot");
|
|
77
46
|
var import_node_path2 = __toESM(require("path"));
|
|
78
|
-
var
|
|
47
|
+
var import_node_crypto = require("crypto");
|
|
79
48
|
var import_strategyReleaseEvidence = require("@tradejs/infra/strategyReleaseEvidence");
|
|
80
49
|
|
|
81
50
|
// src/lib/strategyRelease/researchFileConsumers.ts
|
|
@@ -267,7 +236,7 @@ var mapFiles = async (entries, visit) => {
|
|
|
267
236
|
if (worker.status === "rejected") throw worker.reason;
|
|
268
237
|
return results;
|
|
269
238
|
};
|
|
270
|
-
var fileSha256 = async (file, bytes) => bytes <= 1024 * 1024 ? (0,
|
|
239
|
+
var fileSha256 = async (file, bytes) => bytes <= 1024 * 1024 ? (0, import_node_crypto.createHash)("sha256").update(await import_promises.default.readFile(file)).digest("hex") : (0, import_strategyReleaseEvidence.strategyEvidenceFileSha256)(file);
|
|
271
240
|
var inside = (root, file) => {
|
|
272
241
|
const relative = import_node_path2.default.relative(root, file);
|
|
273
242
|
return relative !== "" && !relative.startsWith(`..${import_node_path2.default.sep}`) && relative !== ".." && !import_node_path2.default.isAbsolute(relative);
|
|
@@ -330,7 +299,7 @@ var applyResearchRetention = async (options) => {
|
|
|
330
299
|
throw new Error(
|
|
331
300
|
"Complete v2 note needs one verified metric snapshot"
|
|
332
301
|
);
|
|
333
|
-
decodeResearchMetricSnapshot(JSON.parse(blocks[0][1]));
|
|
302
|
+
(0, import_researchMetricSnapshot.decodeResearchMetricSnapshot)(JSON.parse(blocks[0][1]));
|
|
334
303
|
}
|
|
335
304
|
} finally {
|
|
336
305
|
await handle.close();
|
|
@@ -415,7 +384,7 @@ var applyResearchRetention = async (options) => {
|
|
|
415
384
|
};
|
|
416
385
|
|
|
417
386
|
// src/lib/strategyRelease.ts
|
|
418
|
-
var
|
|
387
|
+
var import_node_crypto2 = require("crypto");
|
|
419
388
|
var import_promises2 = __toESM(require("fs/promises"));
|
|
420
389
|
var import_node_path3 = __toESM(require("path"));
|
|
421
390
|
var import_types2 = require("@tradejs/types");
|
|
@@ -1585,7 +1554,7 @@ var verifyCompletedCoreResearchBundle = async ({
|
|
|
1585
1554
|
const artifactHashes = manifest.artifactHashes != null && typeof manifest.artifactHashes === "object" && !Array.isArray(manifest.artifactHashes) ? manifest.artifactHashes : null;
|
|
1586
1555
|
const parsedSpec = JSON.parse(specBytes.toString("utf8"));
|
|
1587
1556
|
assert2(
|
|
1588
|
-
manifest.schema === "tradejs-core-research-manifest/v1" && manifest.status === "completed" && manifest.researchId === researchId && manifest.specSha256 === specSha256 && artifactHashes?.["result.json"] === resultSha256 && (0,
|
|
1557
|
+
manifest.schema === "tradejs-core-research-manifest/v1" && manifest.status === "completed" && manifest.researchId === researchId && manifest.specSha256 === specSha256 && artifactHashes?.["result.json"] === resultSha256 && (0, import_node_crypto2.createHash)("sha256").update(canonicalStrategyReleaseJson(parsedSpec)).digest("hex") === specSha256,
|
|
1589
1558
|
"core result is not bound by its completed core research manifest"
|
|
1590
1559
|
);
|
|
1591
1560
|
for (const [relativePath, expectedSha256] of Object.entries(
|