@tradejs/cli 2.0.16 → 2.0.18
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 +271 -68
- package/dist/lib/hyperliquidWhaleBackfill.js +37 -1
- package/dist/lib/marketContextPrepare.js +27 -1
- package/dist/lib/runtimeEvidenceArtifacts.js +13 -1
- package/dist/scripts/backtest.js +53 -3
- package/dist/scripts/hyperliquidWhaleBackfill.js +27 -1
- package/dist/scripts/hyperliquidWhalesUpdate.js +1 -0
- package/dist/scripts/replay.js +189 -30
- package/dist/scripts/replayRunner.js +189 -30
- package/dist/scripts/runtimeEvidence.js +13 -1
- package/dist/scripts/runtimeParity.js +27 -1
- package/dist/scripts/signals.js +29 -3
- package/dist/scripts/signalsDaemon.js +29 -3
- package/package.json +9 -9
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/lib/hyperliquidWhaleBackfill.ts
|
|
@@ -24,6 +34,7 @@ __export(hyperliquidWhaleBackfill_exports, {
|
|
|
24
34
|
fetchHyperliquidUserFillsByTime: () => fetchHyperliquidUserFillsByTime
|
|
25
35
|
});
|
|
26
36
|
module.exports = __toCommonJS(hyperliquidWhaleBackfill_exports);
|
|
37
|
+
var import_progress = __toESM(require("progress"));
|
|
27
38
|
var import_timescale = require("@tradejs/infra/timescale");
|
|
28
39
|
var import_strategies = require("@tradejs/node/strategies");
|
|
29
40
|
|
|
@@ -398,11 +409,36 @@ var backfillHyperliquidWhaleContext = async (params) => {
|
|
|
398
409
|
toMs,
|
|
399
410
|
...identity
|
|
400
411
|
});
|
|
412
|
+
const totalCoverageBuckets = Math.ceil(
|
|
413
|
+
(toMs - fromMs) / HYPERLIQUID_WHALE_BUCKET_MS
|
|
414
|
+
);
|
|
415
|
+
const coverageBar = new import_progress.default(
|
|
416
|
+
"Hyperliquid coverage :current/:total [:bar][:percent] :eta(s) rows=:rows chunk=:chunk",
|
|
417
|
+
{
|
|
418
|
+
total: Math.max(1, totalCoverageBuckets),
|
|
419
|
+
width: 24
|
|
420
|
+
}
|
|
421
|
+
);
|
|
422
|
+
let displayedCoverageBuckets = 0;
|
|
401
423
|
const coverageBuckets = await (0, import_timescale.rebuildHyperliquidWhaleCoverageRows)({
|
|
402
424
|
fromMs,
|
|
403
425
|
toMs,
|
|
404
426
|
expectedWhales: whales.addresses.length,
|
|
405
|
-
...identity
|
|
427
|
+
...identity,
|
|
428
|
+
onProgress: (progress) => {
|
|
429
|
+
const next = Math.max(
|
|
430
|
+
displayedCoverageBuckets,
|
|
431
|
+
Math.min(progress.completedBuckets, coverageBar.total)
|
|
432
|
+
);
|
|
433
|
+
const delta = next - displayedCoverageBuckets;
|
|
434
|
+
if (delta > 0) {
|
|
435
|
+
coverageBar.tick(delta, {
|
|
436
|
+
rows: progress.rows,
|
|
437
|
+
chunk: `${progress.chunkIndex}/${progress.totalChunks}`
|
|
438
|
+
});
|
|
439
|
+
displayedCoverageBuckets = next;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
406
442
|
});
|
|
407
443
|
const result = {
|
|
408
444
|
cached: false,
|
|
@@ -4501,6 +4501,7 @@ var timeOperation = async (label, operation, log = console.log) => {
|
|
|
4501
4501
|
};
|
|
4502
4502
|
|
|
4503
4503
|
// src/lib/hyperliquidWhaleBackfill.ts
|
|
4504
|
+
var import_progress3 = __toESM(require("progress"));
|
|
4504
4505
|
var import_timescale4 = require("@tradejs/infra/timescale");
|
|
4505
4506
|
var import_strategies2 = require("@tradejs/node/strategies");
|
|
4506
4507
|
|
|
@@ -4875,11 +4876,36 @@ var backfillHyperliquidWhaleContext = async (params) => {
|
|
|
4875
4876
|
toMs,
|
|
4876
4877
|
...identity
|
|
4877
4878
|
});
|
|
4879
|
+
const totalCoverageBuckets = Math.ceil(
|
|
4880
|
+
(toMs - fromMs) / HYPERLIQUID_WHALE_BUCKET_MS
|
|
4881
|
+
);
|
|
4882
|
+
const coverageBar = new import_progress3.default(
|
|
4883
|
+
"Hyperliquid coverage :current/:total [:bar][:percent] :eta(s) rows=:rows chunk=:chunk",
|
|
4884
|
+
{
|
|
4885
|
+
total: Math.max(1, totalCoverageBuckets),
|
|
4886
|
+
width: 24
|
|
4887
|
+
}
|
|
4888
|
+
);
|
|
4889
|
+
let displayedCoverageBuckets = 0;
|
|
4878
4890
|
const coverageBuckets = await (0, import_timescale4.rebuildHyperliquidWhaleCoverageRows)({
|
|
4879
4891
|
fromMs,
|
|
4880
4892
|
toMs,
|
|
4881
4893
|
expectedWhales: whales.addresses.length,
|
|
4882
|
-
...identity
|
|
4894
|
+
...identity,
|
|
4895
|
+
onProgress: (progress) => {
|
|
4896
|
+
const next = Math.max(
|
|
4897
|
+
displayedCoverageBuckets,
|
|
4898
|
+
Math.min(progress.completedBuckets, coverageBar.total)
|
|
4899
|
+
);
|
|
4900
|
+
const delta = next - displayedCoverageBuckets;
|
|
4901
|
+
if (delta > 0) {
|
|
4902
|
+
coverageBar.tick(delta, {
|
|
4903
|
+
rows: progress.rows,
|
|
4904
|
+
chunk: `${progress.chunkIndex}/${progress.totalChunks}`
|
|
4905
|
+
});
|
|
4906
|
+
displayedCoverageBuckets = next;
|
|
4907
|
+
}
|
|
4908
|
+
}
|
|
4883
4909
|
});
|
|
4884
4910
|
const result = {
|
|
4885
4911
|
cached: false,
|
|
@@ -46,6 +46,17 @@ var RUNTIME_EVIDENCE_PAYLOAD_FILE = "runtime-evidence.json";
|
|
|
46
46
|
var RUNTIME_EVIDENCE_COMPLETE_FILE = ".complete";
|
|
47
47
|
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
48
48
|
var sha256 = (value) => (0, import_node_crypto.createHash)("sha256").update(value).digest("hex");
|
|
49
|
+
var runtimeEvidenceIdentitySha256 = (artifact) => {
|
|
50
|
+
const identityArtifact = { ...artifact };
|
|
51
|
+
delete identityArtifact.generatedAt;
|
|
52
|
+
const runtime = asRecord(identityArtifact.runtime);
|
|
53
|
+
if (runtime) {
|
|
54
|
+
const identityRuntime = { ...runtime };
|
|
55
|
+
delete identityRuntime.generatedAt;
|
|
56
|
+
identityArtifact.runtime = identityRuntime;
|
|
57
|
+
}
|
|
58
|
+
return sha256(JSON.stringify(identityArtifact));
|
|
59
|
+
};
|
|
49
60
|
var isExistingDestinationError = (error) => {
|
|
50
61
|
const code = error.code;
|
|
51
62
|
return code === "EEXIST" || code === "ENOTEMPTY";
|
|
@@ -116,11 +127,12 @@ var publishRuntimeEvidenceBundle = async ({
|
|
|
116
127
|
const payload = `${JSON.stringify(artifact, null, 2)}
|
|
117
128
|
`;
|
|
118
129
|
const payloadSha256 = sha256(payload);
|
|
130
|
+
const identitySha256 = runtimeEvidenceIdentitySha256(artifact);
|
|
119
131
|
const safeDeploymentId = safeSegment(deploymentId, "default");
|
|
120
132
|
const artifactId = [
|
|
121
133
|
compactTimestamp(startTime),
|
|
122
134
|
compactTimestamp(endTime),
|
|
123
|
-
|
|
135
|
+
identitySha256.slice(0, 16)
|
|
124
136
|
].join("_");
|
|
125
137
|
const createdAt = Date.now();
|
|
126
138
|
const manifest = {
|
package/dist/scripts/backtest.js
CHANGED
|
@@ -4542,6 +4542,7 @@ var backfillCoinMarketCapContextForReplay = backfillCoinMarketCapContext;
|
|
|
4542
4542
|
var backfillCoinMarketCapContextForSignals = (params) => backfillCoinMarketCapContext({ ...params, refreshStaleFearGreed: true });
|
|
4543
4543
|
|
|
4544
4544
|
// src/lib/hyperliquidWhaleBackfill.ts
|
|
4545
|
+
var import_progress3 = __toESM(require("progress"));
|
|
4545
4546
|
var import_timescale4 = require("@tradejs/infra/timescale");
|
|
4546
4547
|
var import_strategies2 = require("@tradejs/node/strategies");
|
|
4547
4548
|
|
|
@@ -4916,11 +4917,36 @@ var backfillHyperliquidWhaleContext = async (params) => {
|
|
|
4916
4917
|
toMs,
|
|
4917
4918
|
...identity
|
|
4918
4919
|
});
|
|
4920
|
+
const totalCoverageBuckets = Math.ceil(
|
|
4921
|
+
(toMs - fromMs) / HYPERLIQUID_WHALE_BUCKET_MS
|
|
4922
|
+
);
|
|
4923
|
+
const coverageBar = new import_progress3.default(
|
|
4924
|
+
"Hyperliquid coverage :current/:total [:bar][:percent] :eta(s) rows=:rows chunk=:chunk",
|
|
4925
|
+
{
|
|
4926
|
+
total: Math.max(1, totalCoverageBuckets),
|
|
4927
|
+
width: 24
|
|
4928
|
+
}
|
|
4929
|
+
);
|
|
4930
|
+
let displayedCoverageBuckets = 0;
|
|
4919
4931
|
const coverageBuckets = await (0, import_timescale4.rebuildHyperliquidWhaleCoverageRows)({
|
|
4920
4932
|
fromMs,
|
|
4921
4933
|
toMs,
|
|
4922
4934
|
expectedWhales: whales.addresses.length,
|
|
4923
|
-
...identity
|
|
4935
|
+
...identity,
|
|
4936
|
+
onProgress: (progress) => {
|
|
4937
|
+
const next = Math.max(
|
|
4938
|
+
displayedCoverageBuckets,
|
|
4939
|
+
Math.min(progress.completedBuckets, coverageBar.total)
|
|
4940
|
+
);
|
|
4941
|
+
const delta = next - displayedCoverageBuckets;
|
|
4942
|
+
if (delta > 0) {
|
|
4943
|
+
coverageBar.tick(delta, {
|
|
4944
|
+
rows: progress.rows,
|
|
4945
|
+
chunk: `${progress.chunkIndex}/${progress.totalChunks}`
|
|
4946
|
+
});
|
|
4947
|
+
displayedCoverageBuckets = next;
|
|
4948
|
+
}
|
|
4949
|
+
}
|
|
4924
4950
|
});
|
|
4925
4951
|
const result = {
|
|
4926
4952
|
cached: false,
|
|
@@ -6273,10 +6299,27 @@ var resetRunState = () => {
|
|
|
6273
6299
|
};
|
|
6274
6300
|
|
|
6275
6301
|
// src/lib/backtest/workerPool.ts
|
|
6276
|
-
var
|
|
6302
|
+
var import_progress4 = __toESM(require("progress"));
|
|
6277
6303
|
var import_child_process = require("child_process");
|
|
6278
6304
|
var import_chalk8 = __toESM(require("chalk"));
|
|
6279
6305
|
var import_node_crypto2 = require("crypto");
|
|
6306
|
+
var DEFAULT_PG_POOL_MAX = 10;
|
|
6307
|
+
var FAST_AI_PG_CONNECTION_BUDGET = 18;
|
|
6308
|
+
var MIN_FAST_AI_WORKER_PG_POOL_MAX = 3;
|
|
6309
|
+
var resolveFastAiWorkerPgPoolMax = (testSuite, workerCount, configuredPoolMax) => {
|
|
6310
|
+
if (configuredPoolMax?.trim() || workerCount <= 1 || testSuite.length === 0 || testSuite.some((test) => !test.fast || !test.ai)) {
|
|
6311
|
+
return void 0;
|
|
6312
|
+
}
|
|
6313
|
+
return String(
|
|
6314
|
+
Math.min(
|
|
6315
|
+
DEFAULT_PG_POOL_MAX,
|
|
6316
|
+
Math.max(
|
|
6317
|
+
MIN_FAST_AI_WORKER_PG_POOL_MAX,
|
|
6318
|
+
Math.floor(FAST_AI_PG_CONNECTION_BUDGET / workerCount)
|
|
6319
|
+
)
|
|
6320
|
+
)
|
|
6321
|
+
);
|
|
6322
|
+
};
|
|
6280
6323
|
var executeBacktestWorkerPool = async ({
|
|
6281
6324
|
testSuite,
|
|
6282
6325
|
userName: userName2,
|
|
@@ -6295,6 +6338,12 @@ var executeBacktestWorkerPool = async ({
|
|
|
6295
6338
|
totalTests
|
|
6296
6339
|
}) => {
|
|
6297
6340
|
const chunks = chunkTestSuite(testSuite);
|
|
6341
|
+
const fastAiWorkerPgPoolMax = resolveFastAiWorkerPgPoolMax(
|
|
6342
|
+
testSuite,
|
|
6343
|
+
chunks.length,
|
|
6344
|
+
process.env.PG_POOL_MAX
|
|
6345
|
+
);
|
|
6346
|
+
const workerEnv = fastAiWorkerPgPoolMax ? { ...process.env, PG_POOL_MAX: fastAiWorkerPgPoolMax } : void 0;
|
|
6298
6347
|
const totalExpectedTests = Math.max(
|
|
6299
6348
|
testSuite.length,
|
|
6300
6349
|
totalTests ?? testSuite.length,
|
|
@@ -6352,7 +6401,7 @@ var executeBacktestWorkerPool = async ({
|
|
|
6352
6401
|
console.log(line);
|
|
6353
6402
|
}
|
|
6354
6403
|
console.log("");
|
|
6355
|
-
const bar = new
|
|
6404
|
+
const bar = new import_progress4.default(
|
|
6356
6405
|
":current/:total [:bar][:percent] avg :amount win :winRate trades :trades :eta(s)",
|
|
6357
6406
|
{
|
|
6358
6407
|
total: totalExpectedTests,
|
|
@@ -6380,6 +6429,7 @@ var executeBacktestWorkerPool = async ({
|
|
|
6380
6429
|
const chunkId = runId ? `${runId}-${attemptId}` : attemptId;
|
|
6381
6430
|
const chunkWithId = chunk.map((test) => ({ ...test, chunkId }));
|
|
6382
6431
|
const tester = (0, import_child_process.fork)(testerWorkerPath, [], {
|
|
6432
|
+
...workerEnv ? { env: workerEnv } : {},
|
|
6383
6433
|
execArgv: testerNeedsTsRuntime ? [
|
|
6384
6434
|
`--max-old-space-size=${workerHeapMb2}`,
|
|
6385
6435
|
"-r",
|
|
@@ -36,6 +36,7 @@ module.exports = __toCommonJS(hyperliquidWhaleBackfill_exports);
|
|
|
36
36
|
var import_args = __toESM(require("args"));
|
|
37
37
|
|
|
38
38
|
// src/lib/hyperliquidWhaleBackfill.ts
|
|
39
|
+
var import_progress = __toESM(require("progress"));
|
|
39
40
|
var import_timescale = require("@tradejs/infra/timescale");
|
|
40
41
|
var import_strategies = require("@tradejs/node/strategies");
|
|
41
42
|
|
|
@@ -410,11 +411,36 @@ var backfillHyperliquidWhaleContext = async (params) => {
|
|
|
410
411
|
toMs,
|
|
411
412
|
...identity
|
|
412
413
|
});
|
|
414
|
+
const totalCoverageBuckets = Math.ceil(
|
|
415
|
+
(toMs - fromMs) / HYPERLIQUID_WHALE_BUCKET_MS
|
|
416
|
+
);
|
|
417
|
+
const coverageBar = new import_progress.default(
|
|
418
|
+
"Hyperliquid coverage :current/:total [:bar][:percent] :eta(s) rows=:rows chunk=:chunk",
|
|
419
|
+
{
|
|
420
|
+
total: Math.max(1, totalCoverageBuckets),
|
|
421
|
+
width: 24
|
|
422
|
+
}
|
|
423
|
+
);
|
|
424
|
+
let displayedCoverageBuckets = 0;
|
|
413
425
|
const coverageBuckets = await (0, import_timescale.rebuildHyperliquidWhaleCoverageRows)({
|
|
414
426
|
fromMs,
|
|
415
427
|
toMs,
|
|
416
428
|
expectedWhales: whales.addresses.length,
|
|
417
|
-
...identity
|
|
429
|
+
...identity,
|
|
430
|
+
onProgress: (progress) => {
|
|
431
|
+
const next = Math.max(
|
|
432
|
+
displayedCoverageBuckets,
|
|
433
|
+
Math.min(progress.completedBuckets, coverageBar.total)
|
|
434
|
+
);
|
|
435
|
+
const delta = next - displayedCoverageBuckets;
|
|
436
|
+
if (delta > 0) {
|
|
437
|
+
coverageBar.tick(delta, {
|
|
438
|
+
rows: progress.rows,
|
|
439
|
+
chunk: `${progress.chunkIndex}/${progress.totalChunks}`
|
|
440
|
+
});
|
|
441
|
+
displayedCoverageBuckets = next;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
418
444
|
});
|
|
419
445
|
const result = {
|
|
420
446
|
cached: false,
|
|
@@ -39,6 +39,7 @@ var import_node_crypto = require("crypto");
|
|
|
39
39
|
var import_strategies2 = require("@tradejs/node/strategies");
|
|
40
40
|
|
|
41
41
|
// src/lib/hyperliquidWhaleBackfill.ts
|
|
42
|
+
var import_progress = __toESM(require("progress"));
|
|
42
43
|
var import_timescale = require("@tradejs/infra/timescale");
|
|
43
44
|
var import_strategies = require("@tradejs/node/strategies");
|
|
44
45
|
|
package/dist/scripts/replay.js
CHANGED
|
@@ -4349,27 +4349,27 @@ var createCoinMarketCapFetchStats = () => ({
|
|
|
4349
4349
|
totalCredits: 0,
|
|
4350
4350
|
byPath: /* @__PURE__ */ new Map()
|
|
4351
4351
|
});
|
|
4352
|
-
var getCoinMarketCapPathLabel = (
|
|
4353
|
-
if (
|
|
4354
|
-
if (
|
|
4355
|
-
if (
|
|
4356
|
-
if (
|
|
4357
|
-
if (
|
|
4358
|
-
if (
|
|
4359
|
-
return
|
|
4360
|
-
};
|
|
4361
|
-
var recordCoinMarketCapFetchStats = (stats,
|
|
4352
|
+
var getCoinMarketCapPathLabel = (path4) => {
|
|
4353
|
+
if (path4.includes("global-metrics")) return "global";
|
|
4354
|
+
if (path4.includes("cryptocurrency/quotes")) return "reference";
|
|
4355
|
+
if (path4.includes("exchange/quotes")) return "exchangeLiquidity";
|
|
4356
|
+
if (path4.includes("fear-and-greed")) return "fearGreed";
|
|
4357
|
+
if (path4.includes("cmc100")) return "cmc100";
|
|
4358
|
+
if (path4.includes("cmc20")) return "cmc20";
|
|
4359
|
+
return path4;
|
|
4360
|
+
};
|
|
4361
|
+
var recordCoinMarketCapFetchStats = (stats, path4, creditCount) => {
|
|
4362
4362
|
if (!stats) return;
|
|
4363
|
-
const existing = stats.byPath.get(
|
|
4363
|
+
const existing = stats.byPath.get(path4) ?? { requests: 0, credits: 0 };
|
|
4364
4364
|
existing.requests += 1;
|
|
4365
4365
|
existing.credits += creditCount ?? 0;
|
|
4366
4366
|
stats.requests += 1;
|
|
4367
4367
|
stats.totalCredits += creditCount ?? 0;
|
|
4368
|
-
stats.byPath.set(
|
|
4368
|
+
stats.byPath.set(path4, existing);
|
|
4369
4369
|
};
|
|
4370
4370
|
var formatCoinMarketCapFetchStats = (stats) => {
|
|
4371
4371
|
const byPath = [...stats.byPath.entries()].map(
|
|
4372
|
-
([
|
|
4372
|
+
([path4, item]) => `${getCoinMarketCapPathLabel(path4)}=${item.requests}req/${item.credits}cr`
|
|
4373
4373
|
).join(" ");
|
|
4374
4374
|
return `apiRequests=${stats.requests} apiCredits=${stats.totalCredits}${byPath ? ` ${byPath}` : ""}`;
|
|
4375
4375
|
};
|
|
@@ -5183,6 +5183,7 @@ var backfillCoinMarketCapContextForReplay = backfillCoinMarketCapContext;
|
|
|
5183
5183
|
var backfillCoinMarketCapContextForSignals = (params) => backfillCoinMarketCapContext({ ...params, refreshStaleFearGreed: true });
|
|
5184
5184
|
|
|
5185
5185
|
// src/lib/hyperliquidWhaleBackfill.ts
|
|
5186
|
+
var import_progress3 = __toESM(require("progress"));
|
|
5186
5187
|
var import_timescale4 = require("@tradejs/infra/timescale");
|
|
5187
5188
|
var import_strategies2 = require("@tradejs/node/strategies");
|
|
5188
5189
|
|
|
@@ -5557,11 +5558,36 @@ var backfillHyperliquidWhaleContext = async (params) => {
|
|
|
5557
5558
|
toMs,
|
|
5558
5559
|
...identity
|
|
5559
5560
|
});
|
|
5561
|
+
const totalCoverageBuckets = Math.ceil(
|
|
5562
|
+
(toMs - fromMs) / HYPERLIQUID_WHALE_BUCKET_MS
|
|
5563
|
+
);
|
|
5564
|
+
const coverageBar = new import_progress3.default(
|
|
5565
|
+
"Hyperliquid coverage :current/:total [:bar][:percent] :eta(s) rows=:rows chunk=:chunk",
|
|
5566
|
+
{
|
|
5567
|
+
total: Math.max(1, totalCoverageBuckets),
|
|
5568
|
+
width: 24
|
|
5569
|
+
}
|
|
5570
|
+
);
|
|
5571
|
+
let displayedCoverageBuckets = 0;
|
|
5560
5572
|
const coverageBuckets = await (0, import_timescale4.rebuildHyperliquidWhaleCoverageRows)({
|
|
5561
5573
|
fromMs,
|
|
5562
5574
|
toMs,
|
|
5563
5575
|
expectedWhales: whales.addresses.length,
|
|
5564
|
-
...identity
|
|
5576
|
+
...identity,
|
|
5577
|
+
onProgress: (progress) => {
|
|
5578
|
+
const next = Math.max(
|
|
5579
|
+
displayedCoverageBuckets,
|
|
5580
|
+
Math.min(progress.completedBuckets, coverageBar.total)
|
|
5581
|
+
);
|
|
5582
|
+
const delta = next - displayedCoverageBuckets;
|
|
5583
|
+
if (delta > 0) {
|
|
5584
|
+
coverageBar.tick(delta, {
|
|
5585
|
+
rows: progress.rows,
|
|
5586
|
+
chunk: `${progress.chunkIndex}/${progress.totalChunks}`
|
|
5587
|
+
});
|
|
5588
|
+
displayedCoverageBuckets = next;
|
|
5589
|
+
}
|
|
5590
|
+
}
|
|
5565
5591
|
});
|
|
5566
5592
|
const result = {
|
|
5567
5593
|
cached: false,
|
|
@@ -5923,6 +5949,10 @@ import_args.default.option(["f", "timeframe"], "Timeframe", 15);
|
|
|
5923
5949
|
import_args.default.option(["d", "days"], "Run replay only for the last N days");
|
|
5924
5950
|
import_args.default.option("startTime", "Explicit replay start timestamp (ms or seconds)");
|
|
5925
5951
|
import_args.default.option("endTime", "Explicit replay end timestamp (ms or seconds)");
|
|
5952
|
+
import_args.default.option(
|
|
5953
|
+
"runtimeEvidence",
|
|
5954
|
+
"Immutable runtime evidence JSON used for offline runtime comparison"
|
|
5955
|
+
);
|
|
5926
5956
|
import_args.default.option(["u", "updateOnly"], "Only update tickers history", false);
|
|
5927
5957
|
import_args.default.option(["C", "cacheOnly"], "Do not update tickers history", false);
|
|
5928
5958
|
import_args.default.option(
|
|
@@ -5948,6 +5978,7 @@ var replayFlags = import_args.default.parse(replayNormalizedArgv);
|
|
|
5948
5978
|
var replayInterval = String(replayFlags.timeframe ?? 15);
|
|
5949
5979
|
var replayUserName = String(replayFlags.user ?? "root");
|
|
5950
5980
|
var replayProjectRoot = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
|
|
5981
|
+
var replayRuntimeEvidencePath = typeof replayFlags.runtimeEvidence === "string" && replayFlags.runtimeEvidence.trim() ? replayFlags.runtimeEvidence.trim() : null;
|
|
5951
5982
|
var isReplayUpdateOnlyRun = Boolean(replayFlags.updateOnly);
|
|
5952
5983
|
|
|
5953
5984
|
// src/lib/replay/support.ts
|
|
@@ -6059,7 +6090,7 @@ var compactReplaySignal = (signal) => {
|
|
|
6059
6090
|
|
|
6060
6091
|
// src/lib/replay/historicalSignalsReplay.ts
|
|
6061
6092
|
var import_chalk9 = __toESM(require("chalk"));
|
|
6062
|
-
var
|
|
6093
|
+
var import_progress4 = __toESM(require("progress"));
|
|
6063
6094
|
var import_backtest = require("@tradejs/core/backtest");
|
|
6064
6095
|
var import_strategies4 = require("@tradejs/core/strategies");
|
|
6065
6096
|
var import_time3 = require("@tradejs/core/time");
|
|
@@ -6943,7 +6974,7 @@ var runHistoricalSignalsReplay = async ({
|
|
|
6943
6974
|
const sharedReplayKeyPrefixes = [];
|
|
6944
6975
|
let preparedSymbols = 0;
|
|
6945
6976
|
let skippedSymbols = 0;
|
|
6946
|
-
const prepareBar = new
|
|
6977
|
+
const prepareBar = new import_progress4.default(
|
|
6947
6978
|
"prepare :current/:total [:bar][:percent] skipped=:skipped :etas(s) :symbol",
|
|
6948
6979
|
{
|
|
6949
6980
|
total: preparedRun.tickers.length,
|
|
@@ -7065,7 +7096,7 @@ var runHistoricalSignalsReplay = async ({
|
|
|
7065
7096
|
interval
|
|
7066
7097
|
});
|
|
7067
7098
|
let abortedCycles = 0;
|
|
7068
|
-
const cycleBar = new
|
|
7099
|
+
const cycleBar = new import_progress4.default(
|
|
7069
7100
|
"cycles :current/:total [:bar][:percent] sig=:signals abort=:aborted :etas(s) :ts",
|
|
7070
7101
|
{
|
|
7071
7102
|
total: orderedTimestamps.length,
|
|
@@ -8780,6 +8811,114 @@ var buildReplayExchangeComparisonDetails = ({
|
|
|
8780
8811
|
};
|
|
8781
8812
|
};
|
|
8782
8813
|
|
|
8814
|
+
// src/lib/replay/runtimeEvidenceSource.ts
|
|
8815
|
+
var import_promises2 = __toESM(require("fs/promises"));
|
|
8816
|
+
var import_node_path2 = __toESM(require("path"));
|
|
8817
|
+
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
8818
|
+
var asFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
8819
|
+
var unwrapRows = (value, field) => (Array.isArray(value) ? value : []).map((item) => {
|
|
8820
|
+
const row = asRecord(item);
|
|
8821
|
+
return asRecord(row?.[field]) ?? row;
|
|
8822
|
+
}).filter((item) => item != null);
|
|
8823
|
+
var buildRuntimeEvidenceLineageScopes = ({
|
|
8824
|
+
signals,
|
|
8825
|
+
evaluations
|
|
8826
|
+
}) => {
|
|
8827
|
+
const scopes = /* @__PURE__ */ new Map();
|
|
8828
|
+
const add = ({
|
|
8829
|
+
strategy,
|
|
8830
|
+
symbol,
|
|
8831
|
+
runtimeConfigId,
|
|
8832
|
+
timestamp,
|
|
8833
|
+
lineage
|
|
8834
|
+
}) => {
|
|
8835
|
+
if (!lineage || !Number.isFinite(timestamp)) {
|
|
8836
|
+
return;
|
|
8837
|
+
}
|
|
8838
|
+
const key = `${strategy}::${symbol}::${runtimeLineageKey(lineage)}`;
|
|
8839
|
+
const existing = scopes.get(key);
|
|
8840
|
+
scopes.set(key, {
|
|
8841
|
+
strategy,
|
|
8842
|
+
symbol,
|
|
8843
|
+
...runtimeConfigId ? { runtimeConfigId } : {},
|
|
8844
|
+
lineage,
|
|
8845
|
+
firstTimestamp: existing ? Math.min(existing.firstTimestamp, timestamp) : timestamp,
|
|
8846
|
+
lastTimestamp: existing ? Math.max(existing.lastTimestamp, timestamp) : timestamp
|
|
8847
|
+
});
|
|
8848
|
+
};
|
|
8849
|
+
for (const signal of signals) {
|
|
8850
|
+
add({
|
|
8851
|
+
strategy: signal.strategy,
|
|
8852
|
+
symbol: signal.symbol,
|
|
8853
|
+
runtimeConfigId: signal.runtimeConfigId,
|
|
8854
|
+
timestamp: signal.timestamp,
|
|
8855
|
+
lineage: signal.runtimeLineage
|
|
8856
|
+
});
|
|
8857
|
+
}
|
|
8858
|
+
for (const evaluation of evaluations) {
|
|
8859
|
+
add({
|
|
8860
|
+
strategy: evaluation.strategy,
|
|
8861
|
+
symbol: evaluation.symbol,
|
|
8862
|
+
runtimeConfigId: evaluation.runtimeConfigId,
|
|
8863
|
+
timestamp: evaluation.timestamp,
|
|
8864
|
+
lineage: evaluation.runtimeLineage
|
|
8865
|
+
});
|
|
8866
|
+
}
|
|
8867
|
+
return [...scopes.values()].sort(
|
|
8868
|
+
(left, right) => left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol) || left.firstTimestamp - right.firstTimestamp
|
|
8869
|
+
);
|
|
8870
|
+
};
|
|
8871
|
+
var loadReplayRuntimeEvidenceSource = async ({
|
|
8872
|
+
filePath,
|
|
8873
|
+
projectRoot,
|
|
8874
|
+
expectedUserName,
|
|
8875
|
+
expectedWindow
|
|
8876
|
+
}) => {
|
|
8877
|
+
const resolvedPath = import_node_path2.default.resolve(projectRoot, filePath);
|
|
8878
|
+
const root = asRecord(
|
|
8879
|
+
JSON.parse(await import_promises2.default.readFile(resolvedPath, "utf8"))
|
|
8880
|
+
);
|
|
8881
|
+
const runtime = asRecord(root?.runtime) ?? root;
|
|
8882
|
+
if (!root || !runtime) {
|
|
8883
|
+
throw new Error(`Invalid runtime evidence JSON: ${resolvedPath}`);
|
|
8884
|
+
}
|
|
8885
|
+
const rootWindow = asRecord(root.window);
|
|
8886
|
+
const runtimeWindow = asRecord(runtime.window);
|
|
8887
|
+
const startTime = asFiniteNumber(rootWindow?.startTime) ?? asFiniteNumber(runtimeWindow?.startTime);
|
|
8888
|
+
const endTime = asFiniteNumber(rootWindow?.endTime) ?? asFiniteNumber(runtimeWindow?.endTime);
|
|
8889
|
+
const userName = String(root.userName ?? runtime.userName ?? "").trim();
|
|
8890
|
+
if (!userName || userName !== expectedUserName) {
|
|
8891
|
+
throw new Error(
|
|
8892
|
+
`Runtime evidence user mismatch: expected=${expectedUserName}, actual=${userName || "missing"}`
|
|
8893
|
+
);
|
|
8894
|
+
}
|
|
8895
|
+
const endTimeMatches = endTime === expectedWindow.end || endTime === expectedWindow.end + 1;
|
|
8896
|
+
if (startTime !== expectedWindow.start || !endTimeMatches) {
|
|
8897
|
+
throw new Error(
|
|
8898
|
+
`Runtime evidence window mismatch: expected=${expectedWindow.start}..${expectedWindow.end}, actual=${startTime ?? "missing"}..${endTime ?? "missing"}`
|
|
8899
|
+
);
|
|
8900
|
+
}
|
|
8901
|
+
const trades = unwrapRows(runtime.trades, "trade");
|
|
8902
|
+
const signals = unwrapRows(runtime.signals, "signal");
|
|
8903
|
+
const evaluations = unwrapRows(
|
|
8904
|
+
runtime.evaluations,
|
|
8905
|
+
"evaluation"
|
|
8906
|
+
);
|
|
8907
|
+
return {
|
|
8908
|
+
path: resolvedPath,
|
|
8909
|
+
userName,
|
|
8910
|
+
startTime,
|
|
8911
|
+
endTime,
|
|
8912
|
+
trades,
|
|
8913
|
+
signals,
|
|
8914
|
+
evaluations,
|
|
8915
|
+
lineageScopes: buildRuntimeEvidenceLineageScopes({
|
|
8916
|
+
signals,
|
|
8917
|
+
evaluations
|
|
8918
|
+
})
|
|
8919
|
+
};
|
|
8920
|
+
};
|
|
8921
|
+
|
|
8783
8922
|
// src/lib/replay/runtimeComparison.ts
|
|
8784
8923
|
var getReplayEntryTimestampCompareOffsetMs = () => {
|
|
8785
8924
|
const intervalMinutes = Number(replayInterval);
|
|
@@ -9549,7 +9688,8 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
9549
9688
|
liveStrategySummaries,
|
|
9550
9689
|
backtestEntries,
|
|
9551
9690
|
replaySignals,
|
|
9552
|
-
replayLineages
|
|
9691
|
+
replayLineages,
|
|
9692
|
+
runtimeEvidencePath
|
|
9553
9693
|
}) => {
|
|
9554
9694
|
const { connector, connectorName, window: window2 } = getRuntimeCompareContext();
|
|
9555
9695
|
if (!connector || !window2) {
|
|
@@ -9559,12 +9699,23 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
9559
9699
|
liveStrategySummaries.map((summary) => summary.strategyName)
|
|
9560
9700
|
);
|
|
9561
9701
|
const replaySignalEvaluations = buildReplaySignalEvaluations(replaySignals);
|
|
9702
|
+
const runtimeEvidence = runtimeEvidencePath ? await loadReplayRuntimeEvidenceSource({
|
|
9703
|
+
filePath: runtimeEvidencePath,
|
|
9704
|
+
projectRoot: replayProjectRoot,
|
|
9705
|
+
expectedUserName: replayUserName,
|
|
9706
|
+
expectedWindow: window2
|
|
9707
|
+
}) : null;
|
|
9562
9708
|
const [
|
|
9563
9709
|
rawRuntimeTrades,
|
|
9564
9710
|
runtimeSignals,
|
|
9565
9711
|
runtimeSignalEvaluations,
|
|
9566
9712
|
runtimeLineageScopes
|
|
9567
|
-
] =
|
|
9713
|
+
] = runtimeEvidence ? [
|
|
9714
|
+
runtimeEvidence.trades,
|
|
9715
|
+
runtimeEvidence.signals,
|
|
9716
|
+
runtimeEvidence.evaluations,
|
|
9717
|
+
runtimeEvidence.lineageScopes
|
|
9718
|
+
] : await Promise.all([
|
|
9568
9719
|
loadRuntimeTrades(replayUserName, {
|
|
9569
9720
|
startTime: window2.start,
|
|
9570
9721
|
endTime: window2.end
|
|
@@ -9582,7 +9733,14 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
9582
9733
|
endTime: window2.end
|
|
9583
9734
|
})
|
|
9584
9735
|
]);
|
|
9585
|
-
|
|
9736
|
+
if (runtimeEvidence) {
|
|
9737
|
+
console.log(
|
|
9738
|
+
import_chalk11.default.gray(
|
|
9739
|
+
`runtime compare: using immutable evidence ${runtimeEvidence.path}`
|
|
9740
|
+
)
|
|
9741
|
+
);
|
|
9742
|
+
}
|
|
9743
|
+
const syncedRuntimeTrades = runtimeEvidence ? rawRuntimeTrades : await syncRuntimeTrades({
|
|
9586
9744
|
userName: replayUserName,
|
|
9587
9745
|
connector,
|
|
9588
9746
|
trades: rawRuntimeTrades,
|
|
@@ -9633,7 +9791,7 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
9633
9791
|
const windowRuntimeTrades = comparable.runtimeTrades.filter(
|
|
9634
9792
|
(trade) => trade.entryTimestamp >= window2.start && trade.entryTimestamp < window2.end && relevantStrategies.has(trade.strategy)
|
|
9635
9793
|
);
|
|
9636
|
-
if (!windowRuntimeTrades.length) {
|
|
9794
|
+
if (!windowRuntimeTrades.length && !runtimeEvidence) {
|
|
9637
9795
|
console.log("");
|
|
9638
9796
|
console.log(
|
|
9639
9797
|
import_chalk11.default.yellow(
|
|
@@ -9765,8 +9923,8 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
9765
9923
|
};
|
|
9766
9924
|
|
|
9767
9925
|
// src/lib/replay/outputReport.ts
|
|
9768
|
-
var
|
|
9769
|
-
var
|
|
9926
|
+
var import_promises3 = __toESM(require("fs/promises"));
|
|
9927
|
+
var import_node_path3 = __toESM(require("path"));
|
|
9770
9928
|
var import_data = require("@tradejs/core/data");
|
|
9771
9929
|
var formatMsk = (timestamp) => typeof timestamp === "number" && Number.isFinite(timestamp) ? new Intl.DateTimeFormat("ru-RU", {
|
|
9772
9930
|
timeZone: "Europe/Moscow",
|
|
@@ -10070,8 +10228,8 @@ var writeReplayOutputReport = async ({
|
|
|
10070
10228
|
strategySnapshot,
|
|
10071
10229
|
runtimeComparison
|
|
10072
10230
|
}) => {
|
|
10073
|
-
const outputDir =
|
|
10074
|
-
await
|
|
10231
|
+
const outputDir = import_node_path3.default.resolve(projectRoot, "data/replay/output");
|
|
10232
|
+
await import_promises3.default.mkdir(outputDir, { recursive: true });
|
|
10075
10233
|
const perTradeAnalysis = buildPerTradeAnalysis(runtimeComparison);
|
|
10076
10234
|
const report = {
|
|
10077
10235
|
reportType: "replay-output",
|
|
@@ -10106,8 +10264,8 @@ var writeReplayOutputReport = async ({
|
|
|
10106
10264
|
runtimeComparison,
|
|
10107
10265
|
perTradeAnalysis
|
|
10108
10266
|
};
|
|
10109
|
-
const markdownPath =
|
|
10110
|
-
const jsonPath =
|
|
10267
|
+
const markdownPath = import_node_path3.default.join(outputDir, `${timestamp}-replay-report.md`);
|
|
10268
|
+
const jsonPath = import_node_path3.default.join(outputDir, `${timestamp}-replay-report.json`);
|
|
10111
10269
|
const lines = [
|
|
10112
10270
|
"# Replay Report",
|
|
10113
10271
|
"",
|
|
@@ -10173,8 +10331,8 @@ var writeReplayOutputReport = async ({
|
|
|
10173
10331
|
""
|
|
10174
10332
|
];
|
|
10175
10333
|
await Promise.all([
|
|
10176
|
-
|
|
10177
|
-
|
|
10334
|
+
import_promises3.default.writeFile(markdownPath, lines.join("\n"), "utf8"),
|
|
10335
|
+
import_promises3.default.writeFile(jsonPath, `${(0, import_data.toJson)(report, true)}
|
|
10178
10336
|
`, "utf8")
|
|
10179
10337
|
]);
|
|
10180
10338
|
return {
|
|
@@ -10215,7 +10373,8 @@ var finishReplay = async ({
|
|
|
10215
10373
|
liveStrategySummaries: replayStrategySnapshot.summaries,
|
|
10216
10374
|
backtestEntries: replayStrategySnapshot.backtestEntries,
|
|
10217
10375
|
replaySignals: replayResult.signals,
|
|
10218
|
-
replayLineages: replayResult.runtimeLineages
|
|
10376
|
+
replayLineages: replayResult.runtimeLineages,
|
|
10377
|
+
runtimeEvidencePath: replayRuntimeEvidencePath
|
|
10219
10378
|
});
|
|
10220
10379
|
const finishedAt = /* @__PURE__ */ new Date();
|
|
10221
10380
|
const durationSeconds = Number(
|