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