@tradejs/cli 2.0.19 → 2.0.20
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 +8787 -3214
- package/dist/lib/aiExport.js +14 -1
- package/dist/lib/aiPocketSearch.js +499 -67
- package/dist/lib/aiPocketSearchCli.js +113 -0
- package/dist/lib/aiTrainCharts.js +14 -1
- package/dist/lib/aiTrainMetrics.js +4 -1
- package/dist/lib/aiTrainResearch.js +88 -54
- package/dist/lib/coreResearch.js +2738 -0
- package/dist/lib/executionCalibration.js +6 -3
- package/dist/lib/runtimeLineage.js +81 -48
- package/dist/lib/runtimeScorecard.js +153 -10
- package/dist/lib/strategyGateFingerprint.js +80 -0
- package/dist/lib/strategyRelease.js +1365 -0
- package/dist/scripts/aiExport.js +16 -3
- package/dist/scripts/aiPocketSearch.js +1069 -163
- package/dist/scripts/aiTrain.js +91 -65
- package/dist/scripts/backtest.js +10 -1
- package/dist/scripts/coreResearch.js +2830 -0
- package/dist/scripts/executionCalibration.js +6 -3
- package/dist/scripts/replay.js +106 -73
- package/dist/scripts/replayRunner.js +106 -73
- package/dist/scripts/replayRuntimeEvidence.js +27 -14
- package/dist/scripts/runtimeEvidence.js +16 -8
- package/dist/scripts/runtimeScorecard.js +883 -29
- package/dist/scripts/signals.js +133 -89
- package/dist/scripts/signalsDaemon.js +133 -89
- package/dist/scripts/strategyRelease.js +1469 -0
- package/package.json +11 -9
|
@@ -2115,27 +2115,27 @@ var createCoinMarketCapFetchStats = () => ({
|
|
|
2115
2115
|
totalCredits: 0,
|
|
2116
2116
|
byPath: /* @__PURE__ */ new Map()
|
|
2117
2117
|
});
|
|
2118
|
-
var getCoinMarketCapPathLabel = (
|
|
2119
|
-
if (
|
|
2120
|
-
if (
|
|
2121
|
-
if (
|
|
2122
|
-
if (
|
|
2123
|
-
if (
|
|
2124
|
-
if (
|
|
2125
|
-
return
|
|
2126
|
-
};
|
|
2127
|
-
var recordCoinMarketCapFetchStats = (stats,
|
|
2118
|
+
var getCoinMarketCapPathLabel = (path3) => {
|
|
2119
|
+
if (path3.includes("global-metrics")) return "global";
|
|
2120
|
+
if (path3.includes("cryptocurrency/quotes")) return "reference";
|
|
2121
|
+
if (path3.includes("exchange/quotes")) return "exchangeLiquidity";
|
|
2122
|
+
if (path3.includes("fear-and-greed")) return "fearGreed";
|
|
2123
|
+
if (path3.includes("cmc100")) return "cmc100";
|
|
2124
|
+
if (path3.includes("cmc20")) return "cmc20";
|
|
2125
|
+
return path3;
|
|
2126
|
+
};
|
|
2127
|
+
var recordCoinMarketCapFetchStats = (stats, path3, creditCount) => {
|
|
2128
2128
|
if (!stats) return;
|
|
2129
|
-
const existing = stats.byPath.get(
|
|
2129
|
+
const existing = stats.byPath.get(path3) ?? { requests: 0, credits: 0 };
|
|
2130
2130
|
existing.requests += 1;
|
|
2131
2131
|
existing.credits += creditCount ?? 0;
|
|
2132
2132
|
stats.requests += 1;
|
|
2133
2133
|
stats.totalCredits += creditCount ?? 0;
|
|
2134
|
-
stats.byPath.set(
|
|
2134
|
+
stats.byPath.set(path3, existing);
|
|
2135
2135
|
};
|
|
2136
2136
|
var formatCoinMarketCapFetchStats = (stats) => {
|
|
2137
2137
|
const byPath = [...stats.byPath.entries()].map(
|
|
2138
|
-
([
|
|
2138
|
+
([path3, item]) => `${getCoinMarketCapPathLabel(path3)}=${item.requests}req/${item.credits}cr`
|
|
2139
2139
|
).join(" ");
|
|
2140
2140
|
return `apiRequests=${stats.requests} apiCredits=${stats.totalCredits}${byPath ? ` ${byPath}` : ""}`;
|
|
2141
2141
|
};
|
|
@@ -3805,6 +3805,7 @@ var loadRuntimeStrategies = async ({
|
|
|
3805
3805
|
interval: configInterval,
|
|
3806
3806
|
universe: configUniverse,
|
|
3807
3807
|
accountId: effectiveAccountId,
|
|
3808
|
+
releaseCompositionId: deploymentStrategy?.releaseCompositionId,
|
|
3808
3809
|
strategyCreator,
|
|
3809
3810
|
sourceStrategyConfig: strategyConfig,
|
|
3810
3811
|
strategyConfig: {
|
|
@@ -3995,11 +3996,56 @@ var import_redis3 = require("@tradejs/infra/redis");
|
|
|
3995
3996
|
|
|
3996
3997
|
// src/lib/runtimeLineage.ts
|
|
3997
3998
|
var import_node_child_process = require("child_process");
|
|
3999
|
+
var import_node_crypto2 = require("crypto");
|
|
4000
|
+
var import_promises2 = __toESM(require("fs/promises"));
|
|
4001
|
+
var import_node_path2 = __toESM(require("path"));
|
|
4002
|
+
var import_types = require("@tradejs/types");
|
|
4003
|
+
var import_strategies4 = require("@tradejs/node/strategies");
|
|
4004
|
+
|
|
4005
|
+
// src/lib/strategyGateFingerprint.ts
|
|
3998
4006
|
var import_node_crypto = require("crypto");
|
|
3999
4007
|
var import_promises = __toESM(require("fs/promises"));
|
|
4000
4008
|
var import_node_path = __toESM(require("path"));
|
|
4001
|
-
var
|
|
4002
|
-
|
|
4009
|
+
var gateFingerprintRelativePaths = (strategyName) => [
|
|
4010
|
+
`packages/strategies/src/${strategyName}/adapters/ai.ts`,
|
|
4011
|
+
`packages/strategies/src/${strategyName}/guardrails.ts`,
|
|
4012
|
+
`packages/strategies/src/${strategyName}/pockets.ts`,
|
|
4013
|
+
`packages/strategies/src/${strategyName}/config.ts`,
|
|
4014
|
+
"packages/strategies/src/shared/localAiGate.ts",
|
|
4015
|
+
"packages/node/src/ai.ts",
|
|
4016
|
+
"packages/core/src/utils/strategyHelpers/signalBuilders.ts"
|
|
4017
|
+
];
|
|
4018
|
+
var resolveStrategyGateFingerprint = async ({
|
|
4019
|
+
projectRoot,
|
|
4020
|
+
strategyName,
|
|
4021
|
+
gitSha
|
|
4022
|
+
}) => {
|
|
4023
|
+
const candidates = await Promise.all(
|
|
4024
|
+
gateFingerprintRelativePaths(strategyName).map(async (relativePath) => {
|
|
4025
|
+
const content = await import_promises.default.readFile(import_node_path.default.join(projectRoot, relativePath)).catch(() => null);
|
|
4026
|
+
return content == null ? null : { relativePath, content };
|
|
4027
|
+
})
|
|
4028
|
+
);
|
|
4029
|
+
const sourceEntries = candidates.filter(
|
|
4030
|
+
(entry) => entry != null
|
|
4031
|
+
);
|
|
4032
|
+
const hash = (0, import_node_crypto.createHash)("sha256");
|
|
4033
|
+
hash.update(strategyName);
|
|
4034
|
+
if (sourceEntries.length) {
|
|
4035
|
+
for (const entry of sourceEntries) {
|
|
4036
|
+
hash.update(entry.relativePath);
|
|
4037
|
+
hash.update(entry.content);
|
|
4038
|
+
}
|
|
4039
|
+
} else {
|
|
4040
|
+
hash.update(gitSha ?? "unknown-git-sha");
|
|
4041
|
+
}
|
|
4042
|
+
return {
|
|
4043
|
+
gateFingerprint: hash.digest("hex").slice(0, 16),
|
|
4044
|
+
gateFingerprintFiles: sourceEntries.map((entry) => entry.relativePath)
|
|
4045
|
+
};
|
|
4046
|
+
};
|
|
4047
|
+
|
|
4048
|
+
// src/lib/runtimeLineage.ts
|
|
4003
4049
|
var RUNTIME_CONTEXT_ENV_KEYS = [
|
|
4004
4050
|
"AI_MODE",
|
|
4005
4051
|
"MIN_AI_QUALITY",
|
|
@@ -4049,6 +4095,17 @@ var normalizeForStableJson = (value) => {
|
|
|
4049
4095
|
}
|
|
4050
4096
|
return value;
|
|
4051
4097
|
};
|
|
4098
|
+
var normalizeRuntimeLogicConfig = (value) => {
|
|
4099
|
+
if (Array.isArray(value)) {
|
|
4100
|
+
return value.map(normalizeRuntimeLogicConfig);
|
|
4101
|
+
}
|
|
4102
|
+
if (value && typeof value === "object") {
|
|
4103
|
+
return Object.fromEntries(
|
|
4104
|
+
Object.entries(value).filter(([key]) => key !== "MAX_LOSS_VALUE").sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, normalizeRuntimeLogicConfig(entry)])
|
|
4105
|
+
);
|
|
4106
|
+
}
|
|
4107
|
+
return value;
|
|
4108
|
+
};
|
|
4052
4109
|
var resolveRuntimeMaxLossValue = (config2) => {
|
|
4053
4110
|
if (!config2 || typeof config2 !== "object" || Array.isArray(config2)) {
|
|
4054
4111
|
return null;
|
|
@@ -4062,7 +4119,7 @@ var resolveRuntimeMaxLossValue = (config2) => {
|
|
|
4062
4119
|
);
|
|
4063
4120
|
return Number.isFinite(maxLossValue) ? maxLossValue : null;
|
|
4064
4121
|
};
|
|
4065
|
-
var fingerprintRuntimeValue = (value) => (0,
|
|
4122
|
+
var fingerprintRuntimeValue = (value) => (0, import_node_crypto2.createHash)("sha256").update(JSON.stringify(normalizeForStableJson(value))).digest("hex").slice(0, 16);
|
|
4066
4123
|
var gitLineageCache = /* @__PURE__ */ new Map();
|
|
4067
4124
|
var gateFingerprintCache = /* @__PURE__ */ new Map();
|
|
4068
4125
|
var binanceBreadthFingerprintCache = /* @__PURE__ */ new Map();
|
|
@@ -4113,7 +4170,7 @@ var resolveGitLineage = (projectRoot) => {
|
|
|
4113
4170
|
};
|
|
4114
4171
|
var readOptionalFile = async (filePath) => {
|
|
4115
4172
|
try {
|
|
4116
|
-
return await
|
|
4173
|
+
return await import_promises2.default.readFile(filePath);
|
|
4117
4174
|
} catch {
|
|
4118
4175
|
return null;
|
|
4119
4176
|
}
|
|
@@ -4123,7 +4180,7 @@ var resolveBinanceBreadthFingerprint = async (projectRoot) => {
|
|
|
4123
4180
|
return binanceBreadthFingerprintCache.get(projectRoot) ?? null;
|
|
4124
4181
|
}
|
|
4125
4182
|
const content = await readOptionalFile(
|
|
4126
|
-
|
|
4183
|
+
import_node_path2.default.join(
|
|
4127
4184
|
projectRoot,
|
|
4128
4185
|
"packages/node/src/config/binanceBreadthUniverses.json"
|
|
4129
4186
|
)
|
|
@@ -4145,7 +4202,7 @@ var resolveSnapshotFingerprint = async (projectRoot, relativePath) => {
|
|
|
4145
4202
|
if (snapshotFingerprintCache.has(cacheKey)) {
|
|
4146
4203
|
return snapshotFingerprintCache.get(cacheKey) ?? null;
|
|
4147
4204
|
}
|
|
4148
|
-
const content = await readOptionalFile(
|
|
4205
|
+
const content = await readOptionalFile(import_node_path2.default.join(projectRoot, relativePath));
|
|
4149
4206
|
let result = null;
|
|
4150
4207
|
if (content) {
|
|
4151
4208
|
try {
|
|
@@ -4158,55 +4215,26 @@ var resolveSnapshotFingerprint = async (projectRoot, relativePath) => {
|
|
|
4158
4215
|
snapshotFingerprintCache.set(cacheKey, result);
|
|
4159
4216
|
return result;
|
|
4160
4217
|
};
|
|
4161
|
-
var
|
|
4218
|
+
var resolveRuntimeGateFingerprint = async ({
|
|
4162
4219
|
projectRoot,
|
|
4163
4220
|
strategyName,
|
|
4164
4221
|
gitSha
|
|
4165
4222
|
}) => {
|
|
4166
4223
|
const cacheKey = `${projectRoot}:${strategyName}:${gitSha ?? "unknown"}`;
|
|
4167
4224
|
const cached = gateFingerprintCache.get(cacheKey);
|
|
4168
|
-
if (cached)
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
"packages/node/src/ai.ts",
|
|
4177
|
-
"packages/core/src/utils/strategyHelpers/signalBuilders.ts"
|
|
4178
|
-
];
|
|
4179
|
-
const optionalEntries = await Promise.all(
|
|
4180
|
-
relativeCandidates.map(async (relativePath) => {
|
|
4181
|
-
const content = await readOptionalFile(
|
|
4182
|
-
import_node_path.default.join(projectRoot, relativePath)
|
|
4183
|
-
);
|
|
4184
|
-
return content == null ? null : { relativePath, content };
|
|
4185
|
-
})
|
|
4186
|
-
);
|
|
4187
|
-
const sourceEntries = [];
|
|
4188
|
-
for (const entry of optionalEntries) {
|
|
4189
|
-
if (entry != null) {
|
|
4190
|
-
sourceEntries.push(entry);
|
|
4191
|
-
}
|
|
4192
|
-
}
|
|
4193
|
-
const hash = (0, import_node_crypto.createHash)("sha256");
|
|
4194
|
-
hash.update(strategyName);
|
|
4195
|
-
if (sourceEntries.length) {
|
|
4196
|
-
for (const entry of sourceEntries) {
|
|
4197
|
-
hash.update(entry.relativePath);
|
|
4198
|
-
hash.update(entry.content);
|
|
4199
|
-
}
|
|
4200
|
-
} else {
|
|
4201
|
-
hash.update(gitSha ?? "unknown-git-sha");
|
|
4202
|
-
}
|
|
4203
|
-
const fingerprint = hash.digest("hex").slice(0, 16);
|
|
4204
|
-
gateFingerprintCache.set(cacheKey, fingerprint);
|
|
4205
|
-
return fingerprint;
|
|
4225
|
+
if (cached) return cached;
|
|
4226
|
+
const result = await resolveStrategyGateFingerprint({
|
|
4227
|
+
projectRoot,
|
|
4228
|
+
strategyName,
|
|
4229
|
+
gitSha
|
|
4230
|
+
});
|
|
4231
|
+
gateFingerprintCache.set(cacheKey, result);
|
|
4232
|
+
return result;
|
|
4206
4233
|
};
|
|
4207
4234
|
var buildRuntimeLineage = async ({
|
|
4208
4235
|
projectRoot,
|
|
4209
4236
|
strategyName,
|
|
4237
|
+
compositionId,
|
|
4210
4238
|
config: config2,
|
|
4211
4239
|
runContext,
|
|
4212
4240
|
env = process.env
|
|
@@ -4232,19 +4260,25 @@ var buildRuntimeLineage = async ({
|
|
|
4232
4260
|
};
|
|
4233
4261
|
return {
|
|
4234
4262
|
schemaVersion: 1,
|
|
4263
|
+
compositionId: compositionId?.trim() || null,
|
|
4235
4264
|
...git,
|
|
4236
|
-
gateFingerprint: await
|
|
4265
|
+
gateFingerprint: (await resolveRuntimeGateFingerprint({
|
|
4237
4266
|
projectRoot,
|
|
4238
4267
|
strategyName,
|
|
4239
4268
|
gitSha: git.gitSha
|
|
4240
|
-
}),
|
|
4241
|
-
|
|
4269
|
+
})).gateFingerprint,
|
|
4270
|
+
// Position risk is tracked separately so changing trade size does not
|
|
4271
|
+
// masquerade as a change to the core + gate decision logic.
|
|
4272
|
+
configFingerprint: fingerprintRuntimeValue(
|
|
4273
|
+
normalizeRuntimeLogicConfig(config2)
|
|
4274
|
+
),
|
|
4242
4275
|
contextFingerprint: fingerprintRuntimeValue(context),
|
|
4243
4276
|
maxLossValue: resolveRuntimeMaxLossValue(config2)
|
|
4244
4277
|
};
|
|
4245
4278
|
};
|
|
4246
4279
|
var runtimeLineageKey = (lineage) => [
|
|
4247
4280
|
lineage.schemaVersion,
|
|
4281
|
+
lineage.compositionId ?? "unbound-composition",
|
|
4248
4282
|
lineage.gitSha ?? "unknown",
|
|
4249
4283
|
lineage.gitDirty == null ? "unknown" : lineage.gitDirty ? "dirty" : "clean",
|
|
4250
4284
|
lineage.gateFingerprint,
|
|
@@ -4381,6 +4415,39 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
|
|
|
4381
4415
|
var createRuntimeSignalEvaluationBuffer = () => {
|
|
4382
4416
|
const evaluations = [];
|
|
4383
4417
|
const signals2 = [];
|
|
4418
|
+
const flushSignals = async () => {
|
|
4419
|
+
const pendingSignals = signals2.splice(0);
|
|
4420
|
+
if (pendingSignals.length === 0) return;
|
|
4421
|
+
const runtimeSignalRetentionTtl = getRuntimeSignalRetentionTtlSeconds();
|
|
4422
|
+
const signalRefsByBucket = /* @__PURE__ */ new Map();
|
|
4423
|
+
for (const { userName, signal } of pendingSignals) {
|
|
4424
|
+
const bucket = import_redis3.redisKeys.runtimeSignalBucket(
|
|
4425
|
+
userName,
|
|
4426
|
+
(0, import_time2.getRuntimeStorageDayKey)(signal.timestamp),
|
|
4427
|
+
signal.strategy
|
|
4428
|
+
);
|
|
4429
|
+
const entries = signalRefsByBucket.get(bucket) ?? [];
|
|
4430
|
+
entries.push({
|
|
4431
|
+
field: signal.signalId,
|
|
4432
|
+
data: toRuntimeSignalBucketRef(signal)
|
|
4433
|
+
});
|
|
4434
|
+
signalRefsByBucket.set(bucket, entries);
|
|
4435
|
+
}
|
|
4436
|
+
await Promise.all([
|
|
4437
|
+
...pendingSignals.map(
|
|
4438
|
+
({ signal }) => (0, import_redis3.setData)(
|
|
4439
|
+
import_redis3.redisKeys.storeSignal(signal.symbol, signal.signalId),
|
|
4440
|
+
toStoredRuntimeSignal(signal),
|
|
4441
|
+
{ expire: runtimeSignalRetentionTtl }
|
|
4442
|
+
)
|
|
4443
|
+
),
|
|
4444
|
+
...[...signalRefsByBucket].map(
|
|
4445
|
+
([bucket, entries]) => (0, import_redis3.setHashJsonFields)(bucket, entries, {
|
|
4446
|
+
expire: runtimeSignalRetentionTtl
|
|
4447
|
+
})
|
|
4448
|
+
)
|
|
4449
|
+
]);
|
|
4450
|
+
};
|
|
4384
4451
|
return {
|
|
4385
4452
|
save: async (evaluation) => {
|
|
4386
4453
|
evaluations.push(evaluation);
|
|
@@ -4388,38 +4455,12 @@ var createRuntimeSignalEvaluationBuffer = () => {
|
|
|
4388
4455
|
saveSignal: (userName, signal) => {
|
|
4389
4456
|
signals2.push({ userName, signal });
|
|
4390
4457
|
},
|
|
4458
|
+
flushSignals,
|
|
4391
4459
|
flush: async () => {
|
|
4392
4460
|
const pending = evaluations.splice(0);
|
|
4393
|
-
const pendingSignals = signals2.splice(0);
|
|
4394
|
-
const runtimeSignalRetentionTtl = getRuntimeSignalRetentionTtlSeconds();
|
|
4395
|
-
const signalRefsByBucket = /* @__PURE__ */ new Map();
|
|
4396
|
-
for (const { userName, signal } of pendingSignals) {
|
|
4397
|
-
const bucket = import_redis3.redisKeys.runtimeSignalBucket(
|
|
4398
|
-
userName,
|
|
4399
|
-
(0, import_time2.getRuntimeStorageDayKey)(signal.timestamp),
|
|
4400
|
-
signal.strategy
|
|
4401
|
-
);
|
|
4402
|
-
const entries = signalRefsByBucket.get(bucket) ?? [];
|
|
4403
|
-
entries.push({
|
|
4404
|
-
field: signal.signalId,
|
|
4405
|
-
data: toRuntimeSignalBucketRef(signal)
|
|
4406
|
-
});
|
|
4407
|
-
signalRefsByBucket.set(bucket, entries);
|
|
4408
|
-
}
|
|
4409
4461
|
await Promise.all([
|
|
4410
4462
|
flushRuntimeSignalEvaluations(pending),
|
|
4411
|
-
|
|
4412
|
-
({ signal }) => (0, import_redis3.setData)(
|
|
4413
|
-
import_redis3.redisKeys.storeSignal(signal.symbol, signal.signalId),
|
|
4414
|
-
toStoredRuntimeSignal(signal),
|
|
4415
|
-
{ expire: runtimeSignalRetentionTtl }
|
|
4416
|
-
)
|
|
4417
|
-
),
|
|
4418
|
-
...[...signalRefsByBucket].map(
|
|
4419
|
-
([bucket, entries]) => (0, import_redis3.setHashJsonFields)(bucket, entries, {
|
|
4420
|
-
expire: runtimeSignalRetentionTtl
|
|
4421
|
-
})
|
|
4422
|
-
)
|
|
4463
|
+
flushSignals()
|
|
4423
4464
|
]);
|
|
4424
4465
|
}
|
|
4425
4466
|
};
|
|
@@ -4846,6 +4887,7 @@ var createSignalsTickerEvaluator = ({
|
|
|
4846
4887
|
strategyName,
|
|
4847
4888
|
configId,
|
|
4848
4889
|
strategyCreator,
|
|
4890
|
+
releaseCompositionId,
|
|
4849
4891
|
sourceStrategyConfig,
|
|
4850
4892
|
strategyConfig,
|
|
4851
4893
|
strategyResults
|
|
@@ -4860,6 +4902,7 @@ var createSignalsTickerEvaluator = ({
|
|
|
4860
4902
|
const runtimeLineage = await buildRuntimeLineage({
|
|
4861
4903
|
projectRoot,
|
|
4862
4904
|
strategyName,
|
|
4905
|
+
compositionId: releaseCompositionId,
|
|
4863
4906
|
config: { configId, strategyConfig, symbolResultConfig },
|
|
4864
4907
|
runContext: {
|
|
4865
4908
|
connectorName: connectorName.toLowerCase(),
|
|
@@ -5439,6 +5482,7 @@ var createSignalsRunner = (config2) => {
|
|
|
5439
5482
|
if (config2.notify) {
|
|
5440
5483
|
const telegramSignals = getTelegramDeliverableSignals(signals3);
|
|
5441
5484
|
if (!config2.skipScreenshots && telegramSignals.length > 0) {
|
|
5485
|
+
await runtimeSignalEvaluations.flushSignals();
|
|
5442
5486
|
await (0, import_cli2.makeScreenshots)(telegramSignals, interval2, config2.userName);
|
|
5443
5487
|
}
|
|
5444
5488
|
await (0, import_cli2.sendToTG)(telegramSignals, interval2, config2.userName);
|