@tradejs/cli 2.0.15 → 2.0.16
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 +2916 -978
- package/dist/lib/aiPocketSearch.js +493 -24
- package/dist/lib/aiTrainResearch.js +19 -2
- package/dist/lib/marketContextPrepare.js +85 -8
- package/dist/lib/researchAgent.js +3 -30
- package/dist/lib/runtimeDebugEvidence.js +76 -1
- package/dist/lib/runtimeEvidenceArtifacts.js +255 -0
- package/dist/lib/runtimeEvidenceSync.js +308 -0
- package/dist/lib/runtimeRedis.js +25 -0
- package/dist/lib/runtimeScorecard.js +442 -0
- package/dist/lib/runtimeTradeSync.js +9 -0
- package/dist/scripts/agentRun.js +16 -31
- package/dist/scripts/aiPocketSearch.js +717 -29
- package/dist/scripts/aiTrain.js +32 -2
- package/dist/scripts/backtest.js +83 -8
- package/dist/scripts/replay.js +92 -8
- package/dist/scripts/replayRunner.js +92 -8
- package/dist/scripts/replayRuntimeEvidence.js +68 -161
- package/dist/scripts/runtimeEvidence.js +316 -161
- package/dist/scripts/runtimeEvidenceSync.js +335 -0
- package/dist/scripts/runtimeParity.js +83 -8
- package/dist/scripts/runtimeScorecard.js +731 -0
- package/dist/scripts/signals.js +234 -142
- package/dist/scripts/signalsDaemon.js +234 -142
- package/dist/scripts/signalsSummary.js +9 -0
- package/dist/workers/testerWorker.js +2 -0
- package/package.json +11 -9
package/dist/scripts/signals.js
CHANGED
|
@@ -1655,112 +1655,8 @@ var import_constants3 = require("@tradejs/core/constants");
|
|
|
1655
1655
|
var import_strategies6 = require("@tradejs/node/strategies");
|
|
1656
1656
|
var import_time3 = require("@tradejs/core/time");
|
|
1657
1657
|
var import_logger4 = require("@tradejs/infra/logger");
|
|
1658
|
-
var import_redis5 = require("@tradejs/infra/redis");
|
|
1659
1658
|
var import_tradingAccounts2 = require("@tradejs/infra/tradingAccounts");
|
|
1660
1659
|
|
|
1661
|
-
// src/lib/runtimeSignalsStorage.ts
|
|
1662
|
-
var import_constants = require("@tradejs/core/constants");
|
|
1663
|
-
var import_time = require("@tradejs/core/time");
|
|
1664
|
-
var SECONDS_PER_DAY = 86400;
|
|
1665
|
-
var RUNTIME_SIGNAL_RETENTION_DAYS_ENV = "RUNTIME_SIGNAL_RETENTION_DAYS";
|
|
1666
|
-
var DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS = import_constants.TTL_3D;
|
|
1667
|
-
var RUNTIME_LINEAGE_SCOPE_RETENTION_TTL_SECONDS = import_constants.TTL_1M;
|
|
1668
|
-
var parsePositiveNumber = (value) => {
|
|
1669
|
-
if (value == null || value === "") {
|
|
1670
|
-
return null;
|
|
1671
|
-
}
|
|
1672
|
-
const parsed = Number(value);
|
|
1673
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
1674
|
-
};
|
|
1675
|
-
var getRuntimeSignalRetentionTtlSeconds = () => {
|
|
1676
|
-
const explicitDays = parsePositiveNumber(
|
|
1677
|
-
process.env[RUNTIME_SIGNAL_RETENTION_DAYS_ENV]
|
|
1678
|
-
);
|
|
1679
|
-
if (explicitDays != null) {
|
|
1680
|
-
return Math.ceil(explicitDays * SECONDS_PER_DAY);
|
|
1681
|
-
}
|
|
1682
|
-
return DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS;
|
|
1683
|
-
};
|
|
1684
|
-
var toRuntimeSignalBucketRef = (signal) => ({
|
|
1685
|
-
signalId: signal.signalId,
|
|
1686
|
-
symbol: signal.symbol,
|
|
1687
|
-
strategy: signal.strategy,
|
|
1688
|
-
timestamp: signal.timestamp,
|
|
1689
|
-
...signal.runtimeConfigId && signal.runtimeConfigId !== "config" ? { runtimeConfigId: signal.runtimeConfigId } : {}
|
|
1690
|
-
});
|
|
1691
|
-
var shouldStoreRuntimeSignalDiagnostics = (signal) => signal.orderStatus === "completed" || signal.orderStatus === "failed";
|
|
1692
|
-
var toStoredRuntimeSignal = (signal) => {
|
|
1693
|
-
if (shouldStoreRuntimeSignalDiagnostics(signal)) {
|
|
1694
|
-
return signal;
|
|
1695
|
-
}
|
|
1696
|
-
const stored = { ...signal };
|
|
1697
|
-
delete stored.figures;
|
|
1698
|
-
delete stored.indicators;
|
|
1699
|
-
delete stored.additionalIndicators;
|
|
1700
|
-
return stored;
|
|
1701
|
-
};
|
|
1702
|
-
var normalizeRuntimeSignalSkipReason = (reason, fallbackSource) => {
|
|
1703
|
-
let source = fallbackSource;
|
|
1704
|
-
let normalizedReason = reason;
|
|
1705
|
-
if (reason.startsWith("AI_QUALITY_BELOW_MIN")) {
|
|
1706
|
-
source = "AI";
|
|
1707
|
-
normalizedReason = "MIN_AI_QUALITY";
|
|
1708
|
-
} else if (reason === "AI_QUALITY_UNAVAILABLE") {
|
|
1709
|
-
source = "AI";
|
|
1710
|
-
normalizedReason = "QUALITY_UNAVAILABLE";
|
|
1711
|
-
} else if (reason === "ML_RESULT_UNAVAILABLE") {
|
|
1712
|
-
source = "ML";
|
|
1713
|
-
normalizedReason = "RESULT_UNAVAILABLE";
|
|
1714
|
-
} else if (reason.startsWith("ML_THRESHOLD_NOT_MET")) {
|
|
1715
|
-
source = "ML";
|
|
1716
|
-
normalizedReason = "ML_THRESHOLD";
|
|
1717
|
-
} else if (reason.startsWith("HOOK_BEFORE_ENTRY_GATE:")) {
|
|
1718
|
-
source = "hook";
|
|
1719
|
-
normalizedReason = `BEFORE_ENTRY_GATE:${reason.slice(
|
|
1720
|
-
"HOOK_BEFORE_ENTRY_GATE:".length
|
|
1721
|
-
)}`;
|
|
1722
|
-
} else if (reason === "HOOK_BEFORE_ENTRY_GATE") {
|
|
1723
|
-
source = "hook";
|
|
1724
|
-
normalizedReason = "BEFORE_ENTRY_GATE";
|
|
1725
|
-
} else if (reason === "MAKE_ORDERS_DISABLED" || reason === "ENTRY_POLICY_BLOCKED") {
|
|
1726
|
-
source = "policy";
|
|
1727
|
-
}
|
|
1728
|
-
return {
|
|
1729
|
-
source: `skip from ${source}`,
|
|
1730
|
-
reason: normalizedReason
|
|
1731
|
-
};
|
|
1732
|
-
};
|
|
1733
|
-
var buildStatsReasonField = (source, reason) => `reason:${source}:${reason}`;
|
|
1734
|
-
var buildRuntimeSignalStatsIncrements = (evaluation) => {
|
|
1735
|
-
const increments = {
|
|
1736
|
-
evaluated: 1
|
|
1737
|
-
};
|
|
1738
|
-
if (evaluation.status === "signal") {
|
|
1739
|
-
increments.signals = 1;
|
|
1740
|
-
}
|
|
1741
|
-
let skipReason = null;
|
|
1742
|
-
let fallbackSource = "core";
|
|
1743
|
-
if (evaluation.status === "skip") {
|
|
1744
|
-
skipReason = evaluation.reason || "NO_SIGNAL";
|
|
1745
|
-
fallbackSource = "core";
|
|
1746
|
-
} else if (evaluation.status === "signal" && (evaluation.orderStatus === "skipped" || evaluation.orderStatus === "canceled")) {
|
|
1747
|
-
skipReason = evaluation.orderSkipReason || evaluation.reason || `orderStatus=${evaluation.orderStatus}`;
|
|
1748
|
-
fallbackSource = "runtime";
|
|
1749
|
-
} else if (evaluation.status === "error") {
|
|
1750
|
-
skipReason = evaluation.reason || "EVALUATION_ERROR";
|
|
1751
|
-
fallbackSource = "runtime";
|
|
1752
|
-
}
|
|
1753
|
-
if (skipReason) {
|
|
1754
|
-
const normalized = normalizeRuntimeSignalSkipReason(
|
|
1755
|
-
skipReason,
|
|
1756
|
-
fallbackSource
|
|
1757
|
-
);
|
|
1758
|
-
increments[buildStatsReasonField(normalized.source, normalized.reason)] = 1;
|
|
1759
|
-
}
|
|
1760
|
-
return increments;
|
|
1761
|
-
};
|
|
1762
|
-
var shouldStoreDetailedRuntimeSignalEvaluation = (evaluation) => evaluation.status !== "skip";
|
|
1763
|
-
|
|
1764
1660
|
// src/lib/marketData/windows.ts
|
|
1765
1661
|
var import_indicators = require("@tradejs/core/indicators");
|
|
1766
1662
|
var resolveOpenTimestamp = (timestamp, intervalMs2) => Number.isFinite(intervalMs2) && intervalMs2 > 0 ? Math.floor(timestamp / intervalMs2) * intervalMs2 : timestamp;
|
|
@@ -1820,6 +1716,8 @@ var invokeAfterSignalsHooks = async (hooks, params) => {
|
|
|
1820
1716
|
|
|
1821
1717
|
// src/lib/marketContextPrepare.ts
|
|
1822
1718
|
var import_chalk4 = __toESM(require("chalk"));
|
|
1719
|
+
var import_timescale5 = require("@tradejs/infra/timescale");
|
|
1720
|
+
var import_registry = require("@tradejs/node/registry");
|
|
1823
1721
|
|
|
1824
1722
|
// src/lib/binanceMarketContextBackfill.ts
|
|
1825
1723
|
var import_progress = __toESM(require("progress"));
|
|
@@ -2620,7 +2518,7 @@ var backfillBinanceMarketContextForReplay = (params) => backfillBinanceMarketCon
|
|
|
2620
2518
|
var import_chalk2 = __toESM(require("chalk"));
|
|
2621
2519
|
var import_progress2 = __toESM(require("progress"));
|
|
2622
2520
|
var import_async = require("@tradejs/core/async");
|
|
2623
|
-
var
|
|
2521
|
+
var import_constants = require("@tradejs/core/constants");
|
|
2624
2522
|
var import_indicators2 = require("@tradejs/core/indicators");
|
|
2625
2523
|
var import_timescale2 = require("@tradejs/infra/timescale");
|
|
2626
2524
|
var import_userSettings = require("@tradejs/infra/userSettings");
|
|
@@ -2655,7 +2553,7 @@ var normalizeSymbols = (symbols) => [
|
|
|
2655
2553
|
];
|
|
2656
2554
|
var isDerivativesTargetContextEnabled = () => parseBooleanFlag(process.env.DERIVATIVES_CONTEXT_TARGET_ENABLED, false);
|
|
2657
2555
|
var resolveDerivativesContextBackfillSymbols = (requestedSymbols = []) => {
|
|
2658
|
-
const referenceSymbols = (0,
|
|
2556
|
+
const referenceSymbols = (0, import_constants.resolveDerivativesContextReferenceSymbols)(
|
|
2659
2557
|
process.env.DERIVATIVES_CONTEXT_EXTRA_REFERENCE_SYMBOLS
|
|
2660
2558
|
);
|
|
2661
2559
|
return isDerivativesTargetContextEnabled() ? normalizeSymbols([...referenceSymbols, ...requestedSymbols]) : referenceSymbols;
|
|
@@ -5061,6 +4959,9 @@ var backfillHyperliquidWhaleContext = async (params) => {
|
|
|
5061
4959
|
};
|
|
5062
4960
|
|
|
5063
4961
|
// src/lib/marketContextPrepare.ts
|
|
4962
|
+
var uniqueReasons = (reasons) => [
|
|
4963
|
+
...new Set(reasons)
|
|
4964
|
+
];
|
|
5064
4965
|
var shouldPrepareDerivativesContextForRun = (params) => {
|
|
5065
4966
|
if (params.universe === "tradfi") return false;
|
|
5066
4967
|
if (params.mode === "signals") {
|
|
@@ -5068,6 +4969,9 @@ var shouldPrepareDerivativesContextForRun = (params) => {
|
|
|
5068
4969
|
cacheOnly: params.cacheOnly
|
|
5069
4970
|
});
|
|
5070
4971
|
}
|
|
4972
|
+
if (params.mode === "backtest" && !params.aiEnabled && !params.mlEnabled) {
|
|
4973
|
+
return false;
|
|
4974
|
+
}
|
|
5071
4975
|
return shouldBackfillDerivativesContextForBacktest({
|
|
5072
4976
|
aiEnabled: Boolean(params.aiEnabled),
|
|
5073
4977
|
cacheOnly: params.cacheOnly,
|
|
@@ -5077,6 +4981,7 @@ var shouldPrepareDerivativesContextForRun = (params) => {
|
|
|
5077
4981
|
var shouldPrepareBinanceMarketContextForRun = (params) => {
|
|
5078
4982
|
if (params.universe === "tradfi") return false;
|
|
5079
4983
|
if (params.mode === "backtest") {
|
|
4984
|
+
if (!params.aiEnabled && !params.mlEnabled) return false;
|
|
5080
4985
|
return shouldBackfillBinanceMarketContextForBacktest({
|
|
5081
4986
|
aiEnabled: Boolean(params.aiEnabled),
|
|
5082
4987
|
cacheOnly: params.cacheOnly,
|
|
@@ -5095,6 +5000,7 @@ var shouldPrepareBinanceMarketContextForRun = (params) => {
|
|
|
5095
5000
|
var shouldPrepareCoinMarketCapContextForRun = (params) => {
|
|
5096
5001
|
if (params.universe === "tradfi") return false;
|
|
5097
5002
|
if (params.mode === "backtest") {
|
|
5003
|
+
if (!params.aiEnabled && !params.mlEnabled) return false;
|
|
5098
5004
|
return shouldBackfillCoinMarketCapContextForBacktest({
|
|
5099
5005
|
aiEnabled: Boolean(params.aiEnabled),
|
|
5100
5006
|
cacheOnly: params.cacheOnly,
|
|
@@ -5110,10 +5016,8 @@ var shouldPrepareCoinMarketCapContextForRun = (params) => {
|
|
|
5110
5016
|
cacheOnly: params.cacheOnly
|
|
5111
5017
|
});
|
|
5112
5018
|
};
|
|
5113
|
-
var shouldPrepareHyperliquidWhaleContextForRun = (params) => {
|
|
5114
|
-
const strategyRequiresContext =
|
|
5115
|
-
"HyperliquidConsensus"
|
|
5116
|
-
);
|
|
5019
|
+
var shouldPrepareHyperliquidWhaleContextForRun = (params, coreContextSources = []) => {
|
|
5020
|
+
const strategyRequiresContext = coreContextSources.includes("hyperliquidWhales");
|
|
5117
5021
|
if (params.universe === "tradfi" || params.mode === "signals" || params.cacheOnly || !params.aiEnabled && !params.mlEnabled && !strategyRequiresContext) {
|
|
5118
5022
|
return false;
|
|
5119
5023
|
}
|
|
@@ -5124,6 +5028,64 @@ var shouldPrepareHyperliquidWhaleContextForRun = (params) => {
|
|
|
5124
5028
|
if (strategyRequiresContext) return true;
|
|
5125
5029
|
return ["1", "true", "yes", "on"].includes(configured);
|
|
5126
5030
|
};
|
|
5031
|
+
var resolveCoreContextSources = async (params) => {
|
|
5032
|
+
const strategyNames = [...new Set(params.strategyNames ?? [])];
|
|
5033
|
+
if (!strategyNames.length) return [];
|
|
5034
|
+
await (0, import_registry.ensureStrategyPluginsLoaded)(params.projectRoot);
|
|
5035
|
+
return [
|
|
5036
|
+
...new Set(
|
|
5037
|
+
strategyNames.flatMap(
|
|
5038
|
+
(strategyName) => (0, import_registry.getStrategyManifest)(strategyName, params.projectRoot)?.contextRequirements?.core ?? []
|
|
5039
|
+
)
|
|
5040
|
+
)
|
|
5041
|
+
];
|
|
5042
|
+
};
|
|
5043
|
+
var resolveMarketContextRunRequirements = async (params) => {
|
|
5044
|
+
const coreContextSources = await resolveCoreContextSources(params);
|
|
5045
|
+
const paramsWithCoreDemand = (source) => coreContextSources.includes(source) ? { ...params, aiEnabled: true } : params;
|
|
5046
|
+
const isCrypto = params.universe !== "tradfi";
|
|
5047
|
+
const capturesPayload = Boolean(params.aiEnabled || params.mlEnabled);
|
|
5048
|
+
const runtimeReadsContext = params.mode !== "backtest";
|
|
5049
|
+
const reasonsFor = (source) => uniqueReasons([
|
|
5050
|
+
...coreContextSources.includes(source) ? ["core"] : [],
|
|
5051
|
+
...params.aiEnabled ? ["ai"] : [],
|
|
5052
|
+
...params.mlEnabled ? ["ml"] : [],
|
|
5053
|
+
...runtimeReadsContext ? ["runtime"] : []
|
|
5054
|
+
]);
|
|
5055
|
+
const readsStandardContext = isCrypto && (capturesPayload || runtimeReadsContext);
|
|
5056
|
+
const readsHyperliquid = isCrypto && (readsStandardContext || coreContextSources.includes("hyperliquidWhales"));
|
|
5057
|
+
return {
|
|
5058
|
+
binance: {
|
|
5059
|
+
read: readsStandardContext || coreContextSources.includes("binance"),
|
|
5060
|
+
backfill: shouldPrepareBinanceMarketContextForRun(
|
|
5061
|
+
paramsWithCoreDemand("binance")
|
|
5062
|
+
),
|
|
5063
|
+
requiredBy: reasonsFor("binance")
|
|
5064
|
+
},
|
|
5065
|
+
coinmarketcap: {
|
|
5066
|
+
read: readsStandardContext || coreContextSources.includes("coinmarketcap"),
|
|
5067
|
+
backfill: shouldPrepareCoinMarketCapContextForRun(
|
|
5068
|
+
paramsWithCoreDemand("coinmarketcap")
|
|
5069
|
+
),
|
|
5070
|
+
requiredBy: reasonsFor("coinmarketcap")
|
|
5071
|
+
},
|
|
5072
|
+
derivatives: {
|
|
5073
|
+
read: readsStandardContext || coreContextSources.includes("derivatives"),
|
|
5074
|
+
backfill: shouldPrepareDerivativesContextForRun(
|
|
5075
|
+
paramsWithCoreDemand("derivatives")
|
|
5076
|
+
),
|
|
5077
|
+
requiredBy: reasonsFor("derivatives")
|
|
5078
|
+
},
|
|
5079
|
+
hyperliquidWhales: {
|
|
5080
|
+
read: readsHyperliquid,
|
|
5081
|
+
backfill: shouldPrepareHyperliquidWhaleContextForRun(
|
|
5082
|
+
params,
|
|
5083
|
+
coreContextSources
|
|
5084
|
+
),
|
|
5085
|
+
requiredBy: reasonsFor("hyperliquidWhales")
|
|
5086
|
+
}
|
|
5087
|
+
};
|
|
5088
|
+
};
|
|
5127
5089
|
var resolveCoinMarketCapBackfillForMode = (mode) => mode === "backtest" ? backfillCoinMarketCapContextForBacktest : mode === "signals" ? backfillCoinMarketCapContextForSignals : backfillCoinMarketCapContextForReplay;
|
|
5128
5090
|
var buildCoinMarketCapBackfillParams = (params) => ({
|
|
5129
5091
|
userName: params.userName,
|
|
@@ -5152,7 +5114,15 @@ var prepareMarketContextForRun = async (params) => {
|
|
|
5152
5114
|
console.log(import_chalk4.default.gray(message));
|
|
5153
5115
|
});
|
|
5154
5116
|
const timeOperation3 = (label, operation) => timeOperation(label, operation, log);
|
|
5155
|
-
|
|
5117
|
+
const requirements = await resolveMarketContextRunRequirements(params);
|
|
5118
|
+
const readableSources = Object.entries(requirements).filter(([, requirement]) => requirement.read).map(([source]) => source);
|
|
5119
|
+
if (readableSources.length) {
|
|
5120
|
+
await timeOperation3(
|
|
5121
|
+
"market context schema ensure",
|
|
5122
|
+
() => (0, import_timescale5.ensureMarketContextSchemas)(readableSources)
|
|
5123
|
+
);
|
|
5124
|
+
}
|
|
5125
|
+
if (requirements.derivatives.backfill) {
|
|
5156
5126
|
await timeOperation3(
|
|
5157
5127
|
"derivatives context backfill",
|
|
5158
5128
|
() => (params.mode === "signals" ? backfillDerivativesContextForSignals : backfillDerivativesContextForBacktest)(
|
|
@@ -5160,7 +5130,7 @@ var prepareMarketContextForRun = async (params) => {
|
|
|
5160
5130
|
)
|
|
5161
5131
|
);
|
|
5162
5132
|
}
|
|
5163
|
-
if (
|
|
5133
|
+
if (requirements.hyperliquidWhales.backfill) {
|
|
5164
5134
|
await timeOperation3(
|
|
5165
5135
|
"hyperliquid whale context backfill",
|
|
5166
5136
|
() => backfillHyperliquidWhaleContext({
|
|
@@ -5172,13 +5142,13 @@ var prepareMarketContextForRun = async (params) => {
|
|
|
5172
5142
|
})
|
|
5173
5143
|
);
|
|
5174
5144
|
}
|
|
5175
|
-
if (
|
|
5145
|
+
if (requirements.binance.backfill) {
|
|
5176
5146
|
await timeOperation3("binance market context backfill", () => {
|
|
5177
5147
|
const backfill = params.mode === "backtest" ? backfillBinanceMarketContextForBacktest : params.mode === "signals" ? backfillBinanceMarketContextForSignals : backfillBinanceMarketContextForReplay;
|
|
5178
5148
|
return backfill(buildBinanceMarketBackfillParams(params));
|
|
5179
5149
|
});
|
|
5180
5150
|
}
|
|
5181
|
-
if (
|
|
5151
|
+
if (requirements.coinmarketcap.backfill) {
|
|
5182
5152
|
await timeOperation3(
|
|
5183
5153
|
"coinmarketcap historical context backfill",
|
|
5184
5154
|
() => resolveCoinMarketCapBackfillForMode(params.mode)(
|
|
@@ -5186,6 +5156,7 @@ var prepareMarketContextForRun = async (params) => {
|
|
|
5186
5156
|
)
|
|
5187
5157
|
);
|
|
5188
5158
|
}
|
|
5159
|
+
return requirements;
|
|
5189
5160
|
};
|
|
5190
5161
|
|
|
5191
5162
|
// src/lib/marketData/historyPrepare.ts
|
|
@@ -5349,7 +5320,7 @@ var import_strategies3 = require("@tradejs/node/strategies");
|
|
|
5349
5320
|
|
|
5350
5321
|
// src/lib/runtimeRedis.ts
|
|
5351
5322
|
var import_redis = require("@tradejs/infra/redis");
|
|
5352
|
-
var
|
|
5323
|
+
var import_time = require("@tradejs/core/time");
|
|
5353
5324
|
var RESERVED_STRATEGY_NAMES = /* @__PURE__ */ new Set(["charts"]);
|
|
5354
5325
|
var RESERVED_CONFIG_IDS = /* @__PURE__ */ new Set(["results"]);
|
|
5355
5326
|
var resolveStrategyConfigIdentityByKey = (userName, key) => {
|
|
@@ -5493,6 +5464,111 @@ var loadRuntimeStrategies = async ({
|
|
|
5493
5464
|
// src/lib/signals/skipStats.ts
|
|
5494
5465
|
var import_chalk6 = __toESM(require("chalk"));
|
|
5495
5466
|
var import_logger2 = require("@tradejs/infra/logger");
|
|
5467
|
+
|
|
5468
|
+
// src/lib/runtimeSignalsStorage.ts
|
|
5469
|
+
var import_constants2 = require("@tradejs/core/constants");
|
|
5470
|
+
var import_time2 = require("@tradejs/core/time");
|
|
5471
|
+
var SECONDS_PER_DAY = 86400;
|
|
5472
|
+
var RUNTIME_SIGNAL_RETENTION_DAYS_ENV = "RUNTIME_SIGNAL_RETENTION_DAYS";
|
|
5473
|
+
var DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS = import_constants2.TTL_3D;
|
|
5474
|
+
var RUNTIME_LINEAGE_SCOPE_RETENTION_TTL_SECONDS = import_constants2.TTL_1M;
|
|
5475
|
+
var parsePositiveNumber = (value) => {
|
|
5476
|
+
if (value == null || value === "") {
|
|
5477
|
+
return null;
|
|
5478
|
+
}
|
|
5479
|
+
const parsed = Number(value);
|
|
5480
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
5481
|
+
};
|
|
5482
|
+
var getRuntimeSignalRetentionTtlSeconds = () => {
|
|
5483
|
+
const explicitDays = parsePositiveNumber(
|
|
5484
|
+
process.env[RUNTIME_SIGNAL_RETENTION_DAYS_ENV]
|
|
5485
|
+
);
|
|
5486
|
+
if (explicitDays != null) {
|
|
5487
|
+
return Math.ceil(explicitDays * SECONDS_PER_DAY);
|
|
5488
|
+
}
|
|
5489
|
+
return DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS;
|
|
5490
|
+
};
|
|
5491
|
+
var toRuntimeSignalBucketRef = (signal) => ({
|
|
5492
|
+
signalId: signal.signalId,
|
|
5493
|
+
symbol: signal.symbol,
|
|
5494
|
+
strategy: signal.strategy,
|
|
5495
|
+
timestamp: signal.timestamp,
|
|
5496
|
+
...signal.runtimeConfigId && signal.runtimeConfigId !== "config" ? { runtimeConfigId: signal.runtimeConfigId } : {}
|
|
5497
|
+
});
|
|
5498
|
+
var shouldStoreRuntimeSignalDiagnostics = (signal) => signal.orderStatus === "completed" || signal.orderStatus === "failed";
|
|
5499
|
+
var toStoredRuntimeSignal = (signal) => {
|
|
5500
|
+
if (shouldStoreRuntimeSignalDiagnostics(signal)) {
|
|
5501
|
+
return signal;
|
|
5502
|
+
}
|
|
5503
|
+
const stored = { ...signal };
|
|
5504
|
+
delete stored.figures;
|
|
5505
|
+
delete stored.indicators;
|
|
5506
|
+
delete stored.additionalIndicators;
|
|
5507
|
+
return stored;
|
|
5508
|
+
};
|
|
5509
|
+
var normalizeRuntimeSignalSkipReason = (reason, fallbackSource) => {
|
|
5510
|
+
let source = fallbackSource;
|
|
5511
|
+
let normalizedReason = reason;
|
|
5512
|
+
if (reason.startsWith("AI_QUALITY_BELOW_MIN")) {
|
|
5513
|
+
source = "AI";
|
|
5514
|
+
normalizedReason = "MIN_AI_QUALITY";
|
|
5515
|
+
} else if (reason === "AI_QUALITY_UNAVAILABLE") {
|
|
5516
|
+
source = "AI";
|
|
5517
|
+
normalizedReason = "QUALITY_UNAVAILABLE";
|
|
5518
|
+
} else if (reason === "ML_RESULT_UNAVAILABLE") {
|
|
5519
|
+
source = "ML";
|
|
5520
|
+
normalizedReason = "RESULT_UNAVAILABLE";
|
|
5521
|
+
} else if (reason.startsWith("ML_THRESHOLD_NOT_MET")) {
|
|
5522
|
+
source = "ML";
|
|
5523
|
+
normalizedReason = "ML_THRESHOLD";
|
|
5524
|
+
} else if (reason.startsWith("HOOK_BEFORE_ENTRY_GATE:")) {
|
|
5525
|
+
source = "hook";
|
|
5526
|
+
normalizedReason = `BEFORE_ENTRY_GATE:${reason.slice(
|
|
5527
|
+
"HOOK_BEFORE_ENTRY_GATE:".length
|
|
5528
|
+
)}`;
|
|
5529
|
+
} else if (reason === "HOOK_BEFORE_ENTRY_GATE") {
|
|
5530
|
+
source = "hook";
|
|
5531
|
+
normalizedReason = "BEFORE_ENTRY_GATE";
|
|
5532
|
+
} else if (reason === "MAKE_ORDERS_DISABLED" || reason === "ENTRY_POLICY_BLOCKED") {
|
|
5533
|
+
source = "policy";
|
|
5534
|
+
}
|
|
5535
|
+
return {
|
|
5536
|
+
source: `skip from ${source}`,
|
|
5537
|
+
reason: normalizedReason
|
|
5538
|
+
};
|
|
5539
|
+
};
|
|
5540
|
+
var buildStatsReasonField = (source, reason) => `reason:${source}:${reason}`;
|
|
5541
|
+
var buildRuntimeSignalStatsIncrements = (evaluation) => {
|
|
5542
|
+
const increments = {
|
|
5543
|
+
evaluated: 1
|
|
5544
|
+
};
|
|
5545
|
+
if (evaluation.status === "signal") {
|
|
5546
|
+
increments.signals = 1;
|
|
5547
|
+
}
|
|
5548
|
+
let skipReason = null;
|
|
5549
|
+
let fallbackSource = "core";
|
|
5550
|
+
if (evaluation.status === "skip") {
|
|
5551
|
+
skipReason = evaluation.reason || "NO_SIGNAL";
|
|
5552
|
+
fallbackSource = "core";
|
|
5553
|
+
} else if (evaluation.status === "signal" && (evaluation.orderStatus === "skipped" || evaluation.orderStatus === "canceled")) {
|
|
5554
|
+
skipReason = evaluation.orderSkipReason || evaluation.reason || `orderStatus=${evaluation.orderStatus}`;
|
|
5555
|
+
fallbackSource = "runtime";
|
|
5556
|
+
} else if (evaluation.status === "error") {
|
|
5557
|
+
skipReason = evaluation.reason || "EVALUATION_ERROR";
|
|
5558
|
+
fallbackSource = "runtime";
|
|
5559
|
+
}
|
|
5560
|
+
if (skipReason) {
|
|
5561
|
+
const normalized = normalizeRuntimeSignalSkipReason(
|
|
5562
|
+
skipReason,
|
|
5563
|
+
fallbackSource
|
|
5564
|
+
);
|
|
5565
|
+
increments[buildStatsReasonField(normalized.source, normalized.reason)] = 1;
|
|
5566
|
+
}
|
|
5567
|
+
return increments;
|
|
5568
|
+
};
|
|
5569
|
+
var shouldStoreDetailedRuntimeSignalEvaluation = (evaluation) => evaluation.status !== "skip";
|
|
5570
|
+
|
|
5571
|
+
// src/lib/signals/skipStats.ts
|
|
5496
5572
|
var createStrategySkipStats = (runtimeStrategies) => new Map(
|
|
5497
5573
|
runtimeStrategies.map(({ strategyName }) => [
|
|
5498
5574
|
strategyName,
|
|
@@ -5820,7 +5896,7 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
|
|
|
5820
5896
|
if (evaluations.length === 0) return;
|
|
5821
5897
|
const evaluationsByDay = /* @__PURE__ */ new Map();
|
|
5822
5898
|
for (const evaluation of evaluations) {
|
|
5823
|
-
const dayKey = (0,
|
|
5899
|
+
const dayKey = (0, import_time2.getRuntimeStorageDayKey)(evaluation.timestamp);
|
|
5824
5900
|
const dayEvaluations = evaluationsByDay.get(dayKey) ?? [];
|
|
5825
5901
|
dayEvaluations.push(evaluation);
|
|
5826
5902
|
evaluationsByDay.set(dayKey, dayEvaluations);
|
|
@@ -5928,13 +6004,47 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
|
|
|
5928
6004
|
};
|
|
5929
6005
|
var createRuntimeSignalEvaluationBuffer = () => {
|
|
5930
6006
|
const evaluations = [];
|
|
6007
|
+
const signals2 = [];
|
|
5931
6008
|
return {
|
|
5932
6009
|
save: async (evaluation) => {
|
|
5933
6010
|
evaluations.push(evaluation);
|
|
5934
6011
|
},
|
|
6012
|
+
saveSignal: (userName, signal) => {
|
|
6013
|
+
signals2.push({ userName, signal });
|
|
6014
|
+
},
|
|
5935
6015
|
flush: async () => {
|
|
5936
6016
|
const pending = evaluations.splice(0);
|
|
5937
|
-
|
|
6017
|
+
const pendingSignals = signals2.splice(0);
|
|
6018
|
+
const runtimeSignalRetentionTtl = getRuntimeSignalRetentionTtlSeconds();
|
|
6019
|
+
const signalRefsByBucket = /* @__PURE__ */ new Map();
|
|
6020
|
+
for (const { userName, signal } of pendingSignals) {
|
|
6021
|
+
const bucket = import_redis3.redisKeys.runtimeSignalBucket(
|
|
6022
|
+
userName,
|
|
6023
|
+
(0, import_time2.getRuntimeStorageDayKey)(signal.timestamp),
|
|
6024
|
+
signal.strategy
|
|
6025
|
+
);
|
|
6026
|
+
const entries = signalRefsByBucket.get(bucket) ?? [];
|
|
6027
|
+
entries.push({
|
|
6028
|
+
field: signal.signalId,
|
|
6029
|
+
data: toRuntimeSignalBucketRef(signal)
|
|
6030
|
+
});
|
|
6031
|
+
signalRefsByBucket.set(bucket, entries);
|
|
6032
|
+
}
|
|
6033
|
+
await Promise.all([
|
|
6034
|
+
flushRuntimeSignalEvaluations(pending),
|
|
6035
|
+
...pendingSignals.map(
|
|
6036
|
+
({ signal }) => (0, import_redis3.setData)(
|
|
6037
|
+
import_redis3.redisKeys.storeSignal(signal.symbol, signal.signalId),
|
|
6038
|
+
toStoredRuntimeSignal(signal),
|
|
6039
|
+
{ expire: runtimeSignalRetentionTtl }
|
|
6040
|
+
)
|
|
6041
|
+
),
|
|
6042
|
+
...[...signalRefsByBucket].map(
|
|
6043
|
+
([bucket, entries]) => (0, import_redis3.setHashJsonFields)(bucket, entries, {
|
|
6044
|
+
expire: runtimeSignalRetentionTtl
|
|
6045
|
+
})
|
|
6046
|
+
)
|
|
6047
|
+
]);
|
|
5938
6048
|
}
|
|
5939
6049
|
};
|
|
5940
6050
|
};
|
|
@@ -6167,7 +6277,7 @@ var runSignalsDaemon = async ({
|
|
|
6167
6277
|
// src/lib/signals/klineFeed.ts
|
|
6168
6278
|
var import_connectors5 = require("@tradejs/connectors");
|
|
6169
6279
|
var import_logger3 = require("@tradejs/infra/logger");
|
|
6170
|
-
var
|
|
6280
|
+
var import_timescale6 = require("@tradejs/infra/timescale");
|
|
6171
6281
|
|
|
6172
6282
|
// src/lib/marketData/klineEvents.ts
|
|
6173
6283
|
var import_ioredis = __toESM(require("ioredis"));
|
|
@@ -6187,9 +6297,9 @@ var createSignalsKlineFeed = async ({
|
|
|
6187
6297
|
publish = publishMarketKlineEvent,
|
|
6188
6298
|
persist = async (rows) => {
|
|
6189
6299
|
const intervalMinutes = Number(interval2);
|
|
6190
|
-
await (0,
|
|
6300
|
+
await (0, import_timescale6.upsertCandles)(
|
|
6191
6301
|
rows.flatMap(
|
|
6192
|
-
({ symbol, candle }) => (0,
|
|
6302
|
+
({ symbol, candle }) => (0, import_timescale6.toRows)("bybit", symbol, intervalMinutes, [candle])
|
|
6193
6303
|
)
|
|
6194
6304
|
);
|
|
6195
6305
|
},
|
|
@@ -6455,7 +6565,7 @@ var createSignalsSession = async (lifecycle, scope = {}) => {
|
|
|
6455
6565
|
lifecycle
|
|
6456
6566
|
};
|
|
6457
6567
|
};
|
|
6458
|
-
var findSignals = async (symbol, connectorName, connector, btcBinanceData, btcCoinbaseData, primaryBtcClosedData, primaryEthClosedData, primaryEthByTimestamp, runtimeStrategies, strategyStats, runtimeCloseNotifications, lifecycle, preloadStart, currentTimestamp, persistStrategyState, universe, interval2, intervalMs2, accountId, deploymentId, instrument, runtimeScopeUniverse, saveRuntimeSignalEvaluation) => {
|
|
6568
|
+
var findSignals = async (symbol, connectorName, connector, btcBinanceData, btcCoinbaseData, primaryBtcClosedData, primaryEthClosedData, primaryEthByTimestamp, runtimeStrategies, strategyStats, runtimeCloseNotifications, lifecycle, preloadStart, currentTimestamp, persistStrategyState, universe, interval2, intervalMs2, accountId, deploymentId, instrument, runtimeScopeUniverse, saveRuntimeSignalEvaluation, saveRuntimeSignal) => {
|
|
6459
6569
|
const strategySignals = [];
|
|
6460
6570
|
const loadCached = (cachedSymbol) => connector.kline({
|
|
6461
6571
|
symbol: cachedSymbol,
|
|
@@ -6648,26 +6758,7 @@ var findSignals = async (symbol, connectorName, connector, btcBinanceData, btcCo
|
|
|
6648
6758
|
);
|
|
6649
6759
|
}
|
|
6650
6760
|
strategySignals.push(signal);
|
|
6651
|
-
|
|
6652
|
-
await (0, import_redis5.setData)(
|
|
6653
|
-
import_redis5.redisKeys.storeSignal(symbol, signal.signalId),
|
|
6654
|
-
toStoredRuntimeSignal(signal),
|
|
6655
|
-
{
|
|
6656
|
-
expire: runtimeSignalRetentionTtl
|
|
6657
|
-
}
|
|
6658
|
-
);
|
|
6659
|
-
await (0, import_redis5.setHashJsonField)(
|
|
6660
|
-
import_redis5.redisKeys.runtimeSignalBucket(
|
|
6661
|
-
flags.user,
|
|
6662
|
-
(0, import_time.getRuntimeStorageDayKey)(signal.timestamp),
|
|
6663
|
-
signal.strategy
|
|
6664
|
-
),
|
|
6665
|
-
signal.signalId,
|
|
6666
|
-
toRuntimeSignalBucketRef(signal),
|
|
6667
|
-
{
|
|
6668
|
-
expire: runtimeSignalRetentionTtl
|
|
6669
|
-
}
|
|
6670
|
-
);
|
|
6761
|
+
saveRuntimeSignal?.(flags.user, signal);
|
|
6671
6762
|
await saveRuntimeSignalEvaluation?.({
|
|
6672
6763
|
evaluationId: buildRuntimeSignalEvaluationId({
|
|
6673
6764
|
strategyName,
|
|
@@ -6973,7 +7064,8 @@ var signals = async (options = {}) => {
|
|
|
6973
7064
|
deployment?.id,
|
|
6974
7065
|
instrumentsBySymbol.get(symbol),
|
|
6975
7066
|
sessionUniverse,
|
|
6976
|
-
runtimeSignalEvaluations.save
|
|
7067
|
+
runtimeSignalEvaluations.save,
|
|
7068
|
+
runtimeSignalEvaluations.saveSignal
|
|
6977
7069
|
);
|
|
6978
7070
|
if (strategySignals.length > 0) {
|
|
6979
7071
|
signals2.push(...strategySignals);
|