@tradejs/cli 1.0.8 → 1.0.10
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 +29748 -13641
- package/dist/lib/aiPocketSearch.js +1316 -0
- package/dist/lib/aiTrainCharts.js +711 -0
- package/dist/lib/aiTrainDataset.js +40 -0
- package/dist/lib/aiTrainEvaluationDump.js +76 -0
- package/dist/lib/aiTrainMetrics.js +148 -8
- package/dist/lib/aiTrainOptions.js +130 -0
- package/dist/lib/aiTrainQuarantine.js +199 -0
- package/dist/lib/aiTrainResearch.js +467 -0
- package/dist/lib/binanceMarketContextBackfill.js +841 -0
- package/dist/lib/binanceMarketData.js +392 -0
- package/dist/lib/coinMarketCapContextBackfill.js +1088 -0
- package/dist/lib/continuity.js +60 -0
- package/dist/lib/derivativesContextBackfill.js +490 -77
- package/dist/lib/executionCalibration.js +705 -0
- package/dist/lib/marketContextPrepare.js +4403 -0
- package/dist/lib/paritySummary.js +101 -0
- package/dist/lib/runBot.js +1 -0
- package/dist/lib/runEnvironment.js +2254 -0
- package/dist/lib/runFormatting.js +1657 -0
- package/dist/lib/runtimeDebugEvidence.js +526 -0
- package/dist/lib/runtimeModeConfig.js +53 -0
- package/dist/lib/runtimeParity.js +88 -11
- package/dist/lib/runtimeParityDetails.js +751 -0
- package/dist/lib/runtimeRedis.js +157 -0
- package/dist/lib/runtimeSignalsLoader.js +187 -0
- package/dist/lib/runtimeSignalsStorage.js +185 -0
- package/dist/lib/runtimeStrategyBacktest.js +134 -0
- package/dist/lib/runtimeTradeSync.js +263 -0
- package/dist/lib/telegramReports.js +9 -0
- package/dist/lib/tickerUniverseCache.js +116 -0
- package/dist/lib/timeWindow.js +3 -3
- package/dist/scripts/agentRun.js +28 -8
- package/dist/scripts/aiExport.js +1721 -7
- package/dist/scripts/aiExportSelect.js +16 -4
- package/dist/scripts/aiPocketSearch.js +4190 -0
- package/dist/scripts/aiTrain.js +1866 -87
- package/dist/scripts/backtest.js +4509 -818
- package/dist/scripts/binanceMarketIngest.js +565 -0
- package/dist/scripts/bot.js +16 -2
- package/dist/scripts/candlesMigrateProvider.js +32 -22
- package/dist/scripts/cleanDir.js +15 -2
- package/dist/scripts/cleanRedis.js +15 -2
- package/dist/scripts/cleanTests.js +38 -2
- package/dist/scripts/cleanupMarketContext.js +69 -0
- package/dist/scripts/continuity.js +148 -67
- package/dist/scripts/derivativesIngest.js +14 -4
- package/dist/scripts/derivativesIngestCoinalyzeAll.js +14 -4
- package/dist/scripts/doctor.js +14 -4
- package/dist/scripts/executionCalibration.js +792 -0
- package/dist/scripts/infraCommon.js +10 -0
- package/dist/scripts/infraDown.js +14 -4
- package/dist/scripts/infraInit.js +24 -4
- package/dist/scripts/infraUp.js +14 -4
- package/dist/scripts/marketWs.js +222 -0
- package/dist/scripts/migration.js +15 -2
- package/dist/scripts/mlExport.js +1698 -10
- package/dist/scripts/mlExportSelect.js +16 -4
- package/dist/scripts/mlInspect.js +13 -3
- package/dist/scripts/mlTrainLatestSelect.js +14 -4
- package/dist/scripts/replay.js +8824 -0
- package/dist/scripts/replayRunner.js +8827 -0
- package/dist/scripts/replayRuntimeEvidence.js +868 -0
- package/dist/scripts/researchAuto.js +289 -60
- package/dist/scripts/results.js +3 -3
- package/dist/scripts/runtimeEvidence.js +605 -0
- package/dist/scripts/runtimeParity.js +6360 -1004
- package/dist/scripts/serverHealth.js +588 -0
- package/dist/scripts/signals.js +5623 -486
- package/dist/scripts/signalsDaemon.js +6167 -0
- package/dist/scripts/signalsSummary.js +954 -312
- package/dist/scripts/test-ml.js +25 -3
- package/dist/scripts/test.js +26 -1
- package/dist/scripts/user-add.js +15 -2
- package/dist/workers/testerWorker.js +130 -21
- package/package.json +18 -12
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/lib/runtimeRedis.ts
|
|
21
|
+
var runtimeRedis_exports = {};
|
|
22
|
+
__export(runtimeRedis_exports, {
|
|
23
|
+
getRuntimeStrategyConfigKeys: () => getRuntimeStrategyConfigKeys,
|
|
24
|
+
isRuntimeStrategyEnabled: () => isRuntimeStrategyEnabled,
|
|
25
|
+
isRuntimeTradeRecord: () => isRuntimeTradeRecord,
|
|
26
|
+
loadRuntimeStrategyConfigs: () => loadRuntimeStrategyConfigs,
|
|
27
|
+
loadRuntimeStrategyNames: () => loadRuntimeStrategyNames,
|
|
28
|
+
loadRuntimeTrades: () => loadRuntimeTrades,
|
|
29
|
+
loadStrategyResultSymbols: () => loadStrategyResultSymbols,
|
|
30
|
+
resolveStrategyConfigIdentityByKey: () => resolveStrategyConfigIdentityByKey,
|
|
31
|
+
resolveStrategyNameByConfigKey: () => resolveStrategyNameByConfigKey
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(runtimeRedis_exports);
|
|
34
|
+
var import_redis = require("@tradejs/infra/redis");
|
|
35
|
+
var import_time = require("@tradejs/core/time");
|
|
36
|
+
var RESERVED_STRATEGY_NAMES = /* @__PURE__ */ new Set(["charts"]);
|
|
37
|
+
var RESERVED_CONFIG_IDS = /* @__PURE__ */ new Set(["results"]);
|
|
38
|
+
var resolveStrategyConfigIdentityByKey = (userName, key) => {
|
|
39
|
+
const parts = key.split(":");
|
|
40
|
+
if (parts.length !== 5) return null;
|
|
41
|
+
const [users, keyUserName, strategiesKey, strategyName, configId] = parts;
|
|
42
|
+
if (users !== "users" || keyUserName !== userName || strategiesKey !== "strategies" || !strategyName || !configId || RESERVED_STRATEGY_NAMES.has(strategyName) || RESERVED_CONFIG_IDS.has(configId) || !/^[a-zA-Z0-9_-]+$/.test(configId)) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return { strategyName, configId };
|
|
46
|
+
};
|
|
47
|
+
var isRuntimeStrategyEnabled = (strategyConfig) => {
|
|
48
|
+
const enabled = strategyConfig.ENABLE;
|
|
49
|
+
return enabled !== false;
|
|
50
|
+
};
|
|
51
|
+
var isRuntimeTradeRecord = (value) => {
|
|
52
|
+
if (!value || typeof value !== "object") {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
const record = value;
|
|
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
|
+
};
|
|
58
|
+
var resolveStrategyNameByConfigKey = (userName, key) => {
|
|
59
|
+
return resolveStrategyConfigIdentityByKey(userName, key)?.strategyName ?? null;
|
|
60
|
+
};
|
|
61
|
+
var getRuntimeStrategyConfigKeys = async (userName) => {
|
|
62
|
+
const keys = await (0, import_redis.getKeys)(`${import_redis.redisKeys.strategies(userName)}:`);
|
|
63
|
+
return keys.filter((key) => resolveStrategyConfigIdentityByKey(userName, key)).sort((left, right) => left.localeCompare(right));
|
|
64
|
+
};
|
|
65
|
+
var loadRuntimeStrategyNames = async (userName) => [
|
|
66
|
+
...new Set(
|
|
67
|
+
(await getRuntimeStrategyConfigKeys(userName)).map((key) => resolveStrategyNameByConfigKey(userName, key)).filter((value) => Boolean(value))
|
|
68
|
+
)
|
|
69
|
+
].sort((left, right) => left.localeCompare(right));
|
|
70
|
+
var loadRuntimeStrategyConfigs = async (userName, {
|
|
71
|
+
onInvalidConfig
|
|
72
|
+
} = {}) => {
|
|
73
|
+
const configKeys = await getRuntimeStrategyConfigKeys(userName);
|
|
74
|
+
const strategyConfigs = await Promise.all(
|
|
75
|
+
configKeys.map(async (key) => {
|
|
76
|
+
const strategyName = resolveStrategyNameByConfigKey(userName, key);
|
|
77
|
+
const identity = resolveStrategyConfigIdentityByKey(userName, key);
|
|
78
|
+
if (!strategyName || !identity) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const strategyConfig = await (0, import_redis.getData)(
|
|
82
|
+
key,
|
|
83
|
+
null
|
|
84
|
+
);
|
|
85
|
+
if (!strategyConfig || typeof strategyConfig !== "object" || Array.isArray(strategyConfig)) {
|
|
86
|
+
onInvalidConfig?.(key);
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
key,
|
|
91
|
+
strategyName,
|
|
92
|
+
configId: identity.configId,
|
|
93
|
+
strategyConfig
|
|
94
|
+
};
|
|
95
|
+
})
|
|
96
|
+
);
|
|
97
|
+
return strategyConfigs.filter(Boolean);
|
|
98
|
+
};
|
|
99
|
+
var loadRuntimeTrades = async (userName, {
|
|
100
|
+
startTime,
|
|
101
|
+
endTime
|
|
102
|
+
} = {}) => {
|
|
103
|
+
const hasWindow = Number.isFinite(startTime) && Number.isFinite(endTime) && startTime != null && endTime != null;
|
|
104
|
+
const filterByWindow = (trade) => !hasWindow || trade.entryTimestamp >= startTime && trade.entryTimestamp <= endTime;
|
|
105
|
+
if (hasWindow) {
|
|
106
|
+
const dayKeys = (0, import_time.getRuntimeStorageDayKeys)(startTime, endTime);
|
|
107
|
+
const bucketTrades = (await Promise.all(
|
|
108
|
+
dayKeys.map(
|
|
109
|
+
(dayKey) => (0, import_redis.getHashJsonValues)(
|
|
110
|
+
import_redis.redisKeys.runtimeTradeBucket(userName, dayKey)
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
)).flat();
|
|
114
|
+
const dedupedBucketTrades = /* @__PURE__ */ new Map();
|
|
115
|
+
for (const trade of bucketTrades) {
|
|
116
|
+
if (!isRuntimeTradeRecord(trade)) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
dedupedBucketTrades.set(trade.orderId, trade);
|
|
120
|
+
}
|
|
121
|
+
const windowedTrades = [...dedupedBucketTrades.values()].filter(filterByWindow).sort((left, right) => left.entryTimestamp - right.entryTimestamp);
|
|
122
|
+
if (windowedTrades.length > 0 || dayKeys.length === 0) {
|
|
123
|
+
return windowedTrades;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const tradePrefix = import_redis.redisKeys.runtimeTrades(userName);
|
|
127
|
+
const bucketPrefix = import_redis.redisKeys.runtimeTradeBuckets(userName);
|
|
128
|
+
const keys = (await (0, import_redis.getKeys)(tradePrefix)).filter(
|
|
129
|
+
(key) => !key.startsWith(bucketPrefix)
|
|
130
|
+
);
|
|
131
|
+
const trades = await Promise.all(keys.map((key) => (0, import_redis.getData)(key, null)));
|
|
132
|
+
return trades.filter(isRuntimeTradeRecord).filter(filterByWindow).sort((left, right) => left.entryTimestamp - right.entryTimestamp);
|
|
133
|
+
};
|
|
134
|
+
var loadStrategyResultSymbols = async ({
|
|
135
|
+
userName,
|
|
136
|
+
strategy
|
|
137
|
+
}) => {
|
|
138
|
+
const results = await (0, import_redis.getData)(
|
|
139
|
+
import_redis.redisKeys.strategyResults(userName, strategy),
|
|
140
|
+
{}
|
|
141
|
+
);
|
|
142
|
+
return Object.keys(results ?? {}).sort(
|
|
143
|
+
(left, right) => left.localeCompare(right)
|
|
144
|
+
);
|
|
145
|
+
};
|
|
146
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
147
|
+
0 && (module.exports = {
|
|
148
|
+
getRuntimeStrategyConfigKeys,
|
|
149
|
+
isRuntimeStrategyEnabled,
|
|
150
|
+
isRuntimeTradeRecord,
|
|
151
|
+
loadRuntimeStrategyConfigs,
|
|
152
|
+
loadRuntimeStrategyNames,
|
|
153
|
+
loadRuntimeTrades,
|
|
154
|
+
loadStrategyResultSymbols,
|
|
155
|
+
resolveStrategyConfigIdentityByKey,
|
|
156
|
+
resolveStrategyNameByConfigKey
|
|
157
|
+
});
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/lib/runtimeSignalsLoader.ts
|
|
21
|
+
var runtimeSignalsLoader_exports = {};
|
|
22
|
+
__export(runtimeSignalsLoader_exports, {
|
|
23
|
+
isRuntimeSignalEvaluationRecord: () => isRuntimeSignalEvaluationRecord,
|
|
24
|
+
isSignalRecord: () => isSignalRecord,
|
|
25
|
+
loadRuntimeSignalEvaluationStatsBuckets: () => loadRuntimeSignalEvaluationStatsBuckets,
|
|
26
|
+
loadRuntimeSignalEvaluations: () => loadRuntimeSignalEvaluations,
|
|
27
|
+
loadRuntimeSignals: () => loadRuntimeSignals
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(runtimeSignalsLoader_exports);
|
|
30
|
+
var import_redis = require("@tradejs/infra/redis");
|
|
31
|
+
|
|
32
|
+
// src/lib/runtimeSignalsStorage.ts
|
|
33
|
+
var import_constants = require("@tradejs/core/constants");
|
|
34
|
+
var import_time = require("@tradejs/core/time");
|
|
35
|
+
var isRuntimeSignalBucketRef = (value) => {
|
|
36
|
+
if (!value || typeof value !== "object") {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
const record = value;
|
|
40
|
+
return typeof record.signalId === "string" && typeof record.symbol === "string" && typeof record.strategy === "string" && typeof record.timestamp === "number";
|
|
41
|
+
};
|
|
42
|
+
var toCount = (value) => {
|
|
43
|
+
const parsed = Number.parseInt(String(value ?? "0"), 10);
|
|
44
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
45
|
+
};
|
|
46
|
+
var parseRuntimeSignalStatsBucket = (value) => {
|
|
47
|
+
const reasonGroups = /* @__PURE__ */ new Map();
|
|
48
|
+
for (const [field, rawCount] of Object.entries(value)) {
|
|
49
|
+
if (!field.startsWith("reason:")) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const [, source, ...reasonParts] = field.split(":");
|
|
53
|
+
const reason = reasonParts.join(":");
|
|
54
|
+
if (!source || !reason) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const reasons = reasonGroups.get(source) ?? /* @__PURE__ */ new Map();
|
|
58
|
+
reasons.set(reason, toCount(rawCount));
|
|
59
|
+
reasonGroups.set(source, reasons);
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
evaluated: toCount(value.evaluated),
|
|
63
|
+
signals: toCount(value.signals),
|
|
64
|
+
reasonGroups
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// src/lib/runtimeSignalsLoader.ts
|
|
69
|
+
var isSignalRecord = (value) => {
|
|
70
|
+
if (!value || typeof value !== "object") {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
const record = value;
|
|
74
|
+
return typeof record.signalId === "string" && typeof record.strategy === "string" && typeof record.symbol === "string" && typeof record.timestamp === "number";
|
|
75
|
+
};
|
|
76
|
+
var isRuntimeSignalEvaluationRecord = (value) => {
|
|
77
|
+
if (!value || typeof value !== "object") {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
const record = value;
|
|
81
|
+
return typeof record.evaluationId === "string" && typeof record.strategy === "string" && typeof record.symbol === "string" && typeof record.timestamp === "number" && (record.status === "signal" || record.status === "skip" || record.status === "error");
|
|
82
|
+
};
|
|
83
|
+
var sortByTimestamp = (records) => [...records].sort((left, right) => left.timestamp - right.timestamp);
|
|
84
|
+
var filterBucketKeysByWindow = (bucketKeys, prefix, startTime, endTime) => {
|
|
85
|
+
if (!Number.isFinite(startTime) || !Number.isFinite(endTime) || startTime == null || endTime == null) {
|
|
86
|
+
return bucketKeys;
|
|
87
|
+
}
|
|
88
|
+
const allowedDayKeys = new Set((0, import_time.getRuntimeStorageDayKeys)(startTime, endTime));
|
|
89
|
+
if (!allowedDayKeys.size) {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
return bucketKeys.filter((key) => {
|
|
93
|
+
const suffix = key.slice(prefix.length);
|
|
94
|
+
const firstColon = suffix.indexOf(":");
|
|
95
|
+
if (firstColon <= 0) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
return allowedDayKeys.has(suffix.slice(0, firstColon));
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
var loadRuntimeSignals = async (userName, {
|
|
102
|
+
startTime,
|
|
103
|
+
endTime
|
|
104
|
+
} = {}) => {
|
|
105
|
+
const bucketPrefix = import_redis.redisKeys.runtimeSignalBuckets(userName);
|
|
106
|
+
const bucketKeys = filterBucketKeysByWindow(
|
|
107
|
+
await (0, import_redis.getKeys)(bucketPrefix),
|
|
108
|
+
bucketPrefix,
|
|
109
|
+
startTime,
|
|
110
|
+
endTime
|
|
111
|
+
);
|
|
112
|
+
const refs = (await Promise.all(bucketKeys.map((key) => (0, import_redis.getHashJsonValues)(key)))).flat().filter(isRuntimeSignalBucketRef);
|
|
113
|
+
const seen = /* @__PURE__ */ new Set();
|
|
114
|
+
const payloads = await Promise.all(
|
|
115
|
+
refs.map(async (ref) => {
|
|
116
|
+
const dedupeKey = `${ref.symbol}:${ref.signalId}`;
|
|
117
|
+
if (seen.has(dedupeKey)) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
seen.add(dedupeKey);
|
|
121
|
+
const signal = await (0, import_redis.getData)(
|
|
122
|
+
import_redis.redisKeys.storeSignal(ref.symbol, ref.signalId),
|
|
123
|
+
null
|
|
124
|
+
);
|
|
125
|
+
return isSignalRecord(signal) ? signal : null;
|
|
126
|
+
})
|
|
127
|
+
);
|
|
128
|
+
return sortByTimestamp(payloads.filter(isSignalRecord));
|
|
129
|
+
};
|
|
130
|
+
var loadRuntimeSignalEvaluations = async (userName, {
|
|
131
|
+
startTime,
|
|
132
|
+
endTime
|
|
133
|
+
} = {}) => {
|
|
134
|
+
const bucketPrefix = import_redis.redisKeys.runtimeSignalEvaluationBuckets(userName);
|
|
135
|
+
const bucketKeys = filterBucketKeysByWindow(
|
|
136
|
+
await (0, import_redis.getKeys)(bucketPrefix),
|
|
137
|
+
bucketPrefix,
|
|
138
|
+
startTime,
|
|
139
|
+
endTime
|
|
140
|
+
);
|
|
141
|
+
const evaluations = (await Promise.all(bucketKeys.map((key) => (0, import_redis.getHashJsonValues)(key)))).flat().filter(isRuntimeSignalEvaluationRecord);
|
|
142
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
143
|
+
for (const evaluation of evaluations) {
|
|
144
|
+
deduped.set(evaluation.evaluationId, evaluation);
|
|
145
|
+
}
|
|
146
|
+
return sortByTimestamp([...deduped.values()]);
|
|
147
|
+
};
|
|
148
|
+
var parseBucketKey = (prefix, key) => {
|
|
149
|
+
if (!key.startsWith(prefix)) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
const suffix = key.slice(prefix.length);
|
|
153
|
+
const firstColon = suffix.indexOf(":");
|
|
154
|
+
if (firstColon <= 0 || firstColon >= suffix.length - 1) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
dayKey: suffix.slice(0, firstColon),
|
|
159
|
+
strategy: suffix.slice(firstColon + 1)
|
|
160
|
+
};
|
|
161
|
+
};
|
|
162
|
+
var loadRuntimeSignalEvaluationStatsBuckets = async (userName) => {
|
|
163
|
+
const prefix = import_redis.redisKeys.runtimeSignalEvaluationStatsBuckets(userName);
|
|
164
|
+
const keys = await (0, import_redis.getKeys)(prefix);
|
|
165
|
+
const entries = await Promise.all(
|
|
166
|
+
keys.map(async (key) => {
|
|
167
|
+
const parsedKey = parseBucketKey(prefix, key);
|
|
168
|
+
if (!parsedKey) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
key,
|
|
173
|
+
...parsedKey,
|
|
174
|
+
stats: parseRuntimeSignalStatsBucket(await (0, import_redis.getHashData)(key))
|
|
175
|
+
};
|
|
176
|
+
})
|
|
177
|
+
);
|
|
178
|
+
return entries.filter(Boolean);
|
|
179
|
+
};
|
|
180
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
181
|
+
0 && (module.exports = {
|
|
182
|
+
isRuntimeSignalEvaluationRecord,
|
|
183
|
+
isSignalRecord,
|
|
184
|
+
loadRuntimeSignalEvaluationStatsBuckets,
|
|
185
|
+
loadRuntimeSignalEvaluations,
|
|
186
|
+
loadRuntimeSignals
|
|
187
|
+
});
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/lib/runtimeSignalsStorage.ts
|
|
21
|
+
var runtimeSignalsStorage_exports = {};
|
|
22
|
+
__export(runtimeSignalsStorage_exports, {
|
|
23
|
+
DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS: () => DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS,
|
|
24
|
+
RUNTIME_SIGNAL_RETENTION_DAYS_ENV: () => RUNTIME_SIGNAL_RETENTION_DAYS_ENV,
|
|
25
|
+
buildRuntimeSignalStatsIncrements: () => buildRuntimeSignalStatsIncrements,
|
|
26
|
+
getRuntimeSignalRetentionTtlSeconds: () => getRuntimeSignalRetentionTtlSeconds,
|
|
27
|
+
getRuntimeStorageDayKey: () => import_time.getRuntimeStorageDayKey,
|
|
28
|
+
getRuntimeStorageDayKeys: () => import_time.getRuntimeStorageDayKeys,
|
|
29
|
+
isRuntimeSignalBucketRef: () => isRuntimeSignalBucketRef,
|
|
30
|
+
normalizeRuntimeSignalSkipReason: () => normalizeRuntimeSignalSkipReason,
|
|
31
|
+
parseRuntimeSignalStatsBucket: () => parseRuntimeSignalStatsBucket,
|
|
32
|
+
shouldStoreDetailedRuntimeSignalEvaluation: () => shouldStoreDetailedRuntimeSignalEvaluation,
|
|
33
|
+
shouldStoreRuntimeSignalDiagnostics: () => shouldStoreRuntimeSignalDiagnostics,
|
|
34
|
+
toRuntimeSignalBucketRef: () => toRuntimeSignalBucketRef,
|
|
35
|
+
toStoredRuntimeSignal: () => toStoredRuntimeSignal
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(runtimeSignalsStorage_exports);
|
|
38
|
+
var import_constants = require("@tradejs/core/constants");
|
|
39
|
+
var import_time = require("@tradejs/core/time");
|
|
40
|
+
var SECONDS_PER_DAY = 86400;
|
|
41
|
+
var RUNTIME_SIGNAL_RETENTION_DAYS_ENV = "RUNTIME_SIGNAL_RETENTION_DAYS";
|
|
42
|
+
var DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS = import_constants.TTL_3D;
|
|
43
|
+
var parsePositiveNumber = (value) => {
|
|
44
|
+
if (value == null || value === "") {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const parsed = Number(value);
|
|
48
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
49
|
+
};
|
|
50
|
+
var getRuntimeSignalRetentionTtlSeconds = () => {
|
|
51
|
+
const explicitDays = parsePositiveNumber(
|
|
52
|
+
process.env[RUNTIME_SIGNAL_RETENTION_DAYS_ENV]
|
|
53
|
+
);
|
|
54
|
+
if (explicitDays != null) {
|
|
55
|
+
return Math.ceil(explicitDays * SECONDS_PER_DAY);
|
|
56
|
+
}
|
|
57
|
+
return DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS;
|
|
58
|
+
};
|
|
59
|
+
var toRuntimeSignalBucketRef = (signal) => ({
|
|
60
|
+
signalId: signal.signalId,
|
|
61
|
+
symbol: signal.symbol,
|
|
62
|
+
strategy: signal.strategy,
|
|
63
|
+
timestamp: signal.timestamp,
|
|
64
|
+
...signal.runtimeConfigId && signal.runtimeConfigId !== "config" ? { runtimeConfigId: signal.runtimeConfigId } : {}
|
|
65
|
+
});
|
|
66
|
+
var shouldStoreRuntimeSignalDiagnostics = (signal) => signal.orderStatus === "completed" || signal.orderStatus === "failed";
|
|
67
|
+
var toStoredRuntimeSignal = (signal) => {
|
|
68
|
+
if (shouldStoreRuntimeSignalDiagnostics(signal)) {
|
|
69
|
+
return signal;
|
|
70
|
+
}
|
|
71
|
+
const stored = { ...signal };
|
|
72
|
+
delete stored.figures;
|
|
73
|
+
delete stored.indicators;
|
|
74
|
+
delete stored.additionalIndicators;
|
|
75
|
+
return stored;
|
|
76
|
+
};
|
|
77
|
+
var isRuntimeSignalBucketRef = (value) => {
|
|
78
|
+
if (!value || typeof value !== "object") {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
const record = value;
|
|
82
|
+
return typeof record.signalId === "string" && typeof record.symbol === "string" && typeof record.strategy === "string" && typeof record.timestamp === "number";
|
|
83
|
+
};
|
|
84
|
+
var normalizeRuntimeSignalSkipReason = (reason, fallbackSource) => {
|
|
85
|
+
let source = fallbackSource;
|
|
86
|
+
let normalizedReason = reason;
|
|
87
|
+
if (reason.startsWith("AI_QUALITY_BELOW_MIN")) {
|
|
88
|
+
source = "AI";
|
|
89
|
+
normalizedReason = "MIN_AI_QUALITY";
|
|
90
|
+
} else if (reason === "AI_QUALITY_UNAVAILABLE") {
|
|
91
|
+
source = "AI";
|
|
92
|
+
normalizedReason = "QUALITY_UNAVAILABLE";
|
|
93
|
+
} else if (reason === "ML_RESULT_UNAVAILABLE") {
|
|
94
|
+
source = "ML";
|
|
95
|
+
normalizedReason = "RESULT_UNAVAILABLE";
|
|
96
|
+
} else if (reason.startsWith("ML_THRESHOLD_NOT_MET")) {
|
|
97
|
+
source = "ML";
|
|
98
|
+
normalizedReason = "ML_THRESHOLD";
|
|
99
|
+
} else if (reason.startsWith("HOOK_BEFORE_ENTRY_GATE:")) {
|
|
100
|
+
source = "hook";
|
|
101
|
+
normalizedReason = `BEFORE_ENTRY_GATE:${reason.slice(
|
|
102
|
+
"HOOK_BEFORE_ENTRY_GATE:".length
|
|
103
|
+
)}`;
|
|
104
|
+
} else if (reason === "HOOK_BEFORE_ENTRY_GATE") {
|
|
105
|
+
source = "hook";
|
|
106
|
+
normalizedReason = "BEFORE_ENTRY_GATE";
|
|
107
|
+
} else if (reason === "MAKE_ORDERS_DISABLED" || reason === "ENTRY_POLICY_BLOCKED") {
|
|
108
|
+
source = "policy";
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
source: `skip from ${source}`,
|
|
112
|
+
reason: normalizedReason
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
var buildStatsReasonField = (source, reason) => `reason:${source}:${reason}`;
|
|
116
|
+
var buildRuntimeSignalStatsIncrements = (evaluation) => {
|
|
117
|
+
const increments = {
|
|
118
|
+
evaluated: 1
|
|
119
|
+
};
|
|
120
|
+
if (evaluation.status === "signal") {
|
|
121
|
+
increments.signals = 1;
|
|
122
|
+
}
|
|
123
|
+
let skipReason = null;
|
|
124
|
+
let fallbackSource = "core";
|
|
125
|
+
if (evaluation.status === "skip") {
|
|
126
|
+
skipReason = evaluation.reason || "NO_SIGNAL";
|
|
127
|
+
fallbackSource = "core";
|
|
128
|
+
} else if (evaluation.status === "signal" && (evaluation.orderStatus === "skipped" || evaluation.orderStatus === "canceled")) {
|
|
129
|
+
skipReason = evaluation.orderSkipReason || evaluation.reason || `orderStatus=${evaluation.orderStatus}`;
|
|
130
|
+
fallbackSource = "runtime";
|
|
131
|
+
} else if (evaluation.status === "error") {
|
|
132
|
+
skipReason = evaluation.reason || "EVALUATION_ERROR";
|
|
133
|
+
fallbackSource = "runtime";
|
|
134
|
+
}
|
|
135
|
+
if (skipReason) {
|
|
136
|
+
const normalized = normalizeRuntimeSignalSkipReason(
|
|
137
|
+
skipReason,
|
|
138
|
+
fallbackSource
|
|
139
|
+
);
|
|
140
|
+
increments[buildStatsReasonField(normalized.source, normalized.reason)] = 1;
|
|
141
|
+
}
|
|
142
|
+
return increments;
|
|
143
|
+
};
|
|
144
|
+
var shouldStoreDetailedRuntimeSignalEvaluation = (evaluation) => evaluation.status !== "skip";
|
|
145
|
+
var toCount = (value) => {
|
|
146
|
+
const parsed = Number.parseInt(String(value ?? "0"), 10);
|
|
147
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
148
|
+
};
|
|
149
|
+
var parseRuntimeSignalStatsBucket = (value) => {
|
|
150
|
+
const reasonGroups = /* @__PURE__ */ new Map();
|
|
151
|
+
for (const [field, rawCount] of Object.entries(value)) {
|
|
152
|
+
if (!field.startsWith("reason:")) {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const [, source, ...reasonParts] = field.split(":");
|
|
156
|
+
const reason = reasonParts.join(":");
|
|
157
|
+
if (!source || !reason) {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const reasons = reasonGroups.get(source) ?? /* @__PURE__ */ new Map();
|
|
161
|
+
reasons.set(reason, toCount(rawCount));
|
|
162
|
+
reasonGroups.set(source, reasons);
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
evaluated: toCount(value.evaluated),
|
|
166
|
+
signals: toCount(value.signals),
|
|
167
|
+
reasonGroups
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
171
|
+
0 && (module.exports = {
|
|
172
|
+
DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS,
|
|
173
|
+
RUNTIME_SIGNAL_RETENTION_DAYS_ENV,
|
|
174
|
+
buildRuntimeSignalStatsIncrements,
|
|
175
|
+
getRuntimeSignalRetentionTtlSeconds,
|
|
176
|
+
getRuntimeStorageDayKey,
|
|
177
|
+
getRuntimeStorageDayKeys,
|
|
178
|
+
isRuntimeSignalBucketRef,
|
|
179
|
+
normalizeRuntimeSignalSkipReason,
|
|
180
|
+
parseRuntimeSignalStatsBucket,
|
|
181
|
+
shouldStoreDetailedRuntimeSignalEvaluation,
|
|
182
|
+
shouldStoreRuntimeSignalDiagnostics,
|
|
183
|
+
toRuntimeSignalBucketRef,
|
|
184
|
+
toStoredRuntimeSignal
|
|
185
|
+
});
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
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
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/lib/runtimeStrategyBacktest.ts
|
|
31
|
+
var runtimeStrategyBacktest_exports = {};
|
|
32
|
+
__export(runtimeStrategyBacktest_exports, {
|
|
33
|
+
BACKTEST_CLI_RUNTIME_CONFIG_KEYS: () => BACKTEST_CLI_RUNTIME_CONFIG_KEYS,
|
|
34
|
+
loadRuntimeStrategyBacktestConfigs: () => loadRuntimeStrategyBacktestConfigs,
|
|
35
|
+
toStrategyConfigGrid: () => toStrategyConfigGrid
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(runtimeStrategyBacktest_exports);
|
|
38
|
+
var import_chalk = __toESM(require("chalk"));
|
|
39
|
+
|
|
40
|
+
// src/lib/runtimeRedis.ts
|
|
41
|
+
var import_redis = require("@tradejs/infra/redis");
|
|
42
|
+
var import_time = require("@tradejs/core/time");
|
|
43
|
+
var RESERVED_STRATEGY_NAMES = /* @__PURE__ */ new Set(["charts"]);
|
|
44
|
+
var RESERVED_CONFIG_IDS = /* @__PURE__ */ new Set(["results"]);
|
|
45
|
+
var resolveStrategyConfigIdentityByKey = (userName, key) => {
|
|
46
|
+
const parts = key.split(":");
|
|
47
|
+
if (parts.length !== 5) return null;
|
|
48
|
+
const [users, keyUserName, strategiesKey, strategyName, configId] = parts;
|
|
49
|
+
if (users !== "users" || keyUserName !== userName || strategiesKey !== "strategies" || !strategyName || !configId || RESERVED_STRATEGY_NAMES.has(strategyName) || RESERVED_CONFIG_IDS.has(configId) || !/^[a-zA-Z0-9_-]+$/.test(configId)) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return { strategyName, configId };
|
|
53
|
+
};
|
|
54
|
+
var isRuntimeStrategyEnabled = (strategyConfig) => {
|
|
55
|
+
const enabled = strategyConfig.ENABLE;
|
|
56
|
+
return enabled !== false;
|
|
57
|
+
};
|
|
58
|
+
var resolveStrategyNameByConfigKey = (userName, key) => {
|
|
59
|
+
return resolveStrategyConfigIdentityByKey(userName, key)?.strategyName ?? null;
|
|
60
|
+
};
|
|
61
|
+
var getRuntimeStrategyConfigKeys = async (userName) => {
|
|
62
|
+
const keys = await (0, import_redis.getKeys)(`${import_redis.redisKeys.strategies(userName)}:`);
|
|
63
|
+
return keys.filter((key) => resolveStrategyConfigIdentityByKey(userName, key)).sort((left, right) => left.localeCompare(right));
|
|
64
|
+
};
|
|
65
|
+
var loadRuntimeStrategyConfigs = async (userName, {
|
|
66
|
+
onInvalidConfig
|
|
67
|
+
} = {}) => {
|
|
68
|
+
const configKeys = await getRuntimeStrategyConfigKeys(userName);
|
|
69
|
+
const strategyConfigs = await Promise.all(
|
|
70
|
+
configKeys.map(async (key) => {
|
|
71
|
+
const strategyName = resolveStrategyNameByConfigKey(userName, key);
|
|
72
|
+
const identity = resolveStrategyConfigIdentityByKey(userName, key);
|
|
73
|
+
if (!strategyName || !identity) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const strategyConfig = await (0, import_redis.getData)(
|
|
77
|
+
key,
|
|
78
|
+
null
|
|
79
|
+
);
|
|
80
|
+
if (!strategyConfig || typeof strategyConfig !== "object" || Array.isArray(strategyConfig)) {
|
|
81
|
+
onInvalidConfig?.(key);
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
key,
|
|
86
|
+
strategyName,
|
|
87
|
+
configId: identity.configId,
|
|
88
|
+
strategyConfig
|
|
89
|
+
};
|
|
90
|
+
})
|
|
91
|
+
);
|
|
92
|
+
return strategyConfigs.filter(Boolean);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// src/lib/runtimeStrategyBacktest.ts
|
|
96
|
+
var BACKTEST_CLI_RUNTIME_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
97
|
+
"ENV",
|
|
98
|
+
"INTERVAL",
|
|
99
|
+
"MAKE_ORDERS",
|
|
100
|
+
"CLOSE_OPPOSITE_POSITIONS",
|
|
101
|
+
"BACKTEST_PRICE_MODE",
|
|
102
|
+
"BACKTEST_ENTRY_DELAY_BARS"
|
|
103
|
+
]);
|
|
104
|
+
var toStrategyConfigGrid = (strategyConfig) => Object.fromEntries(
|
|
105
|
+
Object.entries(strategyConfig).filter(([key]) => !BACKTEST_CLI_RUNTIME_CONFIG_KEYS.has(key)).map(([key, value]) => [key, [value]])
|
|
106
|
+
);
|
|
107
|
+
var loadRuntimeStrategyBacktestConfigs = async (userName) => {
|
|
108
|
+
const configs = await loadRuntimeStrategyConfigs(userName, {
|
|
109
|
+
onInvalidConfig: (key) => {
|
|
110
|
+
console.log(import_chalk.default.yellow(`Skip invalid runtime strategy config: ${key}`));
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
return configs.filter(({ configId }) => configId === "config").filter(({ strategyName, strategyConfig }) => {
|
|
114
|
+
const enabled = isRuntimeStrategyEnabled(strategyConfig);
|
|
115
|
+
if (!enabled) {
|
|
116
|
+
console.log(
|
|
117
|
+
import_chalk.default.yellow(
|
|
118
|
+
`Skip inactive runtime strategy config by ENABLE=false: ${strategyName}`
|
|
119
|
+
)
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
return enabled;
|
|
123
|
+
}).map(({ strategyName, strategyConfig }) => ({
|
|
124
|
+
strategyName,
|
|
125
|
+
strategyConfig,
|
|
126
|
+
backtestConfig: toStrategyConfigGrid(strategyConfig)
|
|
127
|
+
}));
|
|
128
|
+
};
|
|
129
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
130
|
+
0 && (module.exports = {
|
|
131
|
+
BACKTEST_CLI_RUNTIME_CONFIG_KEYS,
|
|
132
|
+
loadRuntimeStrategyBacktestConfigs,
|
|
133
|
+
toStrategyConfigGrid
|
|
134
|
+
});
|