@tradejs/cli 2.0.15 → 2.0.17
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 +2982 -994
- package/dist/lib/aiPocketSearch.js +493 -24
- package/dist/lib/aiTrainResearch.js +19 -2
- package/dist/lib/hyperliquidWhaleBackfill.js +37 -1
- package/dist/lib/marketContextPrepare.js +112 -9
- 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 +136 -11
- package/dist/scripts/hyperliquidWhaleBackfill.js +27 -1
- package/dist/scripts/hyperliquidWhalesUpdate.js +1 -0
- package/dist/scripts/replay.js +122 -12
- package/dist/scripts/replayRunner.js +122 -12
- 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 +110 -9
- package/dist/scripts/runtimeScorecard.js +731 -0
- package/dist/scripts/signals.js +263 -145
- package/dist/scripts/signalsDaemon.js +263 -145
- package/dist/scripts/signalsSummary.js +9 -0
- package/dist/workers/testerWorker.js +2 -0
- package/package.json +11 -9
|
@@ -1643,7 +1643,7 @@ module.exports = __toCommonJS(signalsDaemon_exports);
|
|
|
1643
1643
|
// src/scripts/signals.ts
|
|
1644
1644
|
var import_config = require("dotenv/config");
|
|
1645
1645
|
var import_args = __toESM(require("args"));
|
|
1646
|
-
var
|
|
1646
|
+
var import_progress4 = __toESM(require("progress"));
|
|
1647
1647
|
var import_chalk7 = __toESM(require("chalk"));
|
|
1648
1648
|
var import_connectors6 = require("@tradejs/node/connectors");
|
|
1649
1649
|
var import_cli2 = require("@tradejs/node/cli");
|
|
@@ -1653,112 +1653,8 @@ var import_constants3 = require("@tradejs/core/constants");
|
|
|
1653
1653
|
var import_strategies6 = require("@tradejs/node/strategies");
|
|
1654
1654
|
var import_time3 = require("@tradejs/core/time");
|
|
1655
1655
|
var import_logger4 = require("@tradejs/infra/logger");
|
|
1656
|
-
var import_redis5 = require("@tradejs/infra/redis");
|
|
1657
1656
|
var import_tradingAccounts2 = require("@tradejs/infra/tradingAccounts");
|
|
1658
1657
|
|
|
1659
|
-
// src/lib/runtimeSignalsStorage.ts
|
|
1660
|
-
var import_constants = require("@tradejs/core/constants");
|
|
1661
|
-
var import_time = require("@tradejs/core/time");
|
|
1662
|
-
var SECONDS_PER_DAY = 86400;
|
|
1663
|
-
var RUNTIME_SIGNAL_RETENTION_DAYS_ENV = "RUNTIME_SIGNAL_RETENTION_DAYS";
|
|
1664
|
-
var DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS = import_constants.TTL_3D;
|
|
1665
|
-
var RUNTIME_LINEAGE_SCOPE_RETENTION_TTL_SECONDS = import_constants.TTL_1M;
|
|
1666
|
-
var parsePositiveNumber = (value) => {
|
|
1667
|
-
if (value == null || value === "") {
|
|
1668
|
-
return null;
|
|
1669
|
-
}
|
|
1670
|
-
const parsed = Number(value);
|
|
1671
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
1672
|
-
};
|
|
1673
|
-
var getRuntimeSignalRetentionTtlSeconds = () => {
|
|
1674
|
-
const explicitDays = parsePositiveNumber(
|
|
1675
|
-
process.env[RUNTIME_SIGNAL_RETENTION_DAYS_ENV]
|
|
1676
|
-
);
|
|
1677
|
-
if (explicitDays != null) {
|
|
1678
|
-
return Math.ceil(explicitDays * SECONDS_PER_DAY);
|
|
1679
|
-
}
|
|
1680
|
-
return DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS;
|
|
1681
|
-
};
|
|
1682
|
-
var toRuntimeSignalBucketRef = (signal) => ({
|
|
1683
|
-
signalId: signal.signalId,
|
|
1684
|
-
symbol: signal.symbol,
|
|
1685
|
-
strategy: signal.strategy,
|
|
1686
|
-
timestamp: signal.timestamp,
|
|
1687
|
-
...signal.runtimeConfigId && signal.runtimeConfigId !== "config" ? { runtimeConfigId: signal.runtimeConfigId } : {}
|
|
1688
|
-
});
|
|
1689
|
-
var shouldStoreRuntimeSignalDiagnostics = (signal) => signal.orderStatus === "completed" || signal.orderStatus === "failed";
|
|
1690
|
-
var toStoredRuntimeSignal = (signal) => {
|
|
1691
|
-
if (shouldStoreRuntimeSignalDiagnostics(signal)) {
|
|
1692
|
-
return signal;
|
|
1693
|
-
}
|
|
1694
|
-
const stored = { ...signal };
|
|
1695
|
-
delete stored.figures;
|
|
1696
|
-
delete stored.indicators;
|
|
1697
|
-
delete stored.additionalIndicators;
|
|
1698
|
-
return stored;
|
|
1699
|
-
};
|
|
1700
|
-
var normalizeRuntimeSignalSkipReason = (reason, fallbackSource) => {
|
|
1701
|
-
let source = fallbackSource;
|
|
1702
|
-
let normalizedReason = reason;
|
|
1703
|
-
if (reason.startsWith("AI_QUALITY_BELOW_MIN")) {
|
|
1704
|
-
source = "AI";
|
|
1705
|
-
normalizedReason = "MIN_AI_QUALITY";
|
|
1706
|
-
} else if (reason === "AI_QUALITY_UNAVAILABLE") {
|
|
1707
|
-
source = "AI";
|
|
1708
|
-
normalizedReason = "QUALITY_UNAVAILABLE";
|
|
1709
|
-
} else if (reason === "ML_RESULT_UNAVAILABLE") {
|
|
1710
|
-
source = "ML";
|
|
1711
|
-
normalizedReason = "RESULT_UNAVAILABLE";
|
|
1712
|
-
} else if (reason.startsWith("ML_THRESHOLD_NOT_MET")) {
|
|
1713
|
-
source = "ML";
|
|
1714
|
-
normalizedReason = "ML_THRESHOLD";
|
|
1715
|
-
} else if (reason.startsWith("HOOK_BEFORE_ENTRY_GATE:")) {
|
|
1716
|
-
source = "hook";
|
|
1717
|
-
normalizedReason = `BEFORE_ENTRY_GATE:${reason.slice(
|
|
1718
|
-
"HOOK_BEFORE_ENTRY_GATE:".length
|
|
1719
|
-
)}`;
|
|
1720
|
-
} else if (reason === "HOOK_BEFORE_ENTRY_GATE") {
|
|
1721
|
-
source = "hook";
|
|
1722
|
-
normalizedReason = "BEFORE_ENTRY_GATE";
|
|
1723
|
-
} else if (reason === "MAKE_ORDERS_DISABLED" || reason === "ENTRY_POLICY_BLOCKED") {
|
|
1724
|
-
source = "policy";
|
|
1725
|
-
}
|
|
1726
|
-
return {
|
|
1727
|
-
source: `skip from ${source}`,
|
|
1728
|
-
reason: normalizedReason
|
|
1729
|
-
};
|
|
1730
|
-
};
|
|
1731
|
-
var buildStatsReasonField = (source, reason) => `reason:${source}:${reason}`;
|
|
1732
|
-
var buildRuntimeSignalStatsIncrements = (evaluation) => {
|
|
1733
|
-
const increments = {
|
|
1734
|
-
evaluated: 1
|
|
1735
|
-
};
|
|
1736
|
-
if (evaluation.status === "signal") {
|
|
1737
|
-
increments.signals = 1;
|
|
1738
|
-
}
|
|
1739
|
-
let skipReason = null;
|
|
1740
|
-
let fallbackSource = "core";
|
|
1741
|
-
if (evaluation.status === "skip") {
|
|
1742
|
-
skipReason = evaluation.reason || "NO_SIGNAL";
|
|
1743
|
-
fallbackSource = "core";
|
|
1744
|
-
} else if (evaluation.status === "signal" && (evaluation.orderStatus === "skipped" || evaluation.orderStatus === "canceled")) {
|
|
1745
|
-
skipReason = evaluation.orderSkipReason || evaluation.reason || `orderStatus=${evaluation.orderStatus}`;
|
|
1746
|
-
fallbackSource = "runtime";
|
|
1747
|
-
} else if (evaluation.status === "error") {
|
|
1748
|
-
skipReason = evaluation.reason || "EVALUATION_ERROR";
|
|
1749
|
-
fallbackSource = "runtime";
|
|
1750
|
-
}
|
|
1751
|
-
if (skipReason) {
|
|
1752
|
-
const normalized = normalizeRuntimeSignalSkipReason(
|
|
1753
|
-
skipReason,
|
|
1754
|
-
fallbackSource
|
|
1755
|
-
);
|
|
1756
|
-
increments[buildStatsReasonField(normalized.source, normalized.reason)] = 1;
|
|
1757
|
-
}
|
|
1758
|
-
return increments;
|
|
1759
|
-
};
|
|
1760
|
-
var shouldStoreDetailedRuntimeSignalEvaluation = (evaluation) => evaluation.status !== "skip";
|
|
1761
|
-
|
|
1762
1658
|
// src/lib/marketData/windows.ts
|
|
1763
1659
|
var import_indicators = require("@tradejs/core/indicators");
|
|
1764
1660
|
var resolveOpenTimestamp = (timestamp, intervalMs2) => Number.isFinite(intervalMs2) && intervalMs2 > 0 ? Math.floor(timestamp / intervalMs2) * intervalMs2 : timestamp;
|
|
@@ -1818,6 +1714,8 @@ var invokeAfterSignalsHooks = async (hooks, params) => {
|
|
|
1818
1714
|
|
|
1819
1715
|
// src/lib/marketContextPrepare.ts
|
|
1820
1716
|
var import_chalk4 = __toESM(require("chalk"));
|
|
1717
|
+
var import_timescale5 = require("@tradejs/infra/timescale");
|
|
1718
|
+
var import_registry = require("@tradejs/node/registry");
|
|
1821
1719
|
|
|
1822
1720
|
// src/lib/binanceMarketContextBackfill.ts
|
|
1823
1721
|
var import_progress = __toESM(require("progress"));
|
|
@@ -2618,7 +2516,7 @@ var backfillBinanceMarketContextForReplay = (params) => backfillBinanceMarketCon
|
|
|
2618
2516
|
var import_chalk2 = __toESM(require("chalk"));
|
|
2619
2517
|
var import_progress2 = __toESM(require("progress"));
|
|
2620
2518
|
var import_async = require("@tradejs/core/async");
|
|
2621
|
-
var
|
|
2519
|
+
var import_constants = require("@tradejs/core/constants");
|
|
2622
2520
|
var import_indicators2 = require("@tradejs/core/indicators");
|
|
2623
2521
|
var import_timescale2 = require("@tradejs/infra/timescale");
|
|
2624
2522
|
var import_userSettings = require("@tradejs/infra/userSettings");
|
|
@@ -2653,7 +2551,7 @@ var normalizeSymbols = (symbols) => [
|
|
|
2653
2551
|
];
|
|
2654
2552
|
var isDerivativesTargetContextEnabled = () => parseBooleanFlag(process.env.DERIVATIVES_CONTEXT_TARGET_ENABLED, false);
|
|
2655
2553
|
var resolveDerivativesContextBackfillSymbols = (requestedSymbols = []) => {
|
|
2656
|
-
const referenceSymbols = (0,
|
|
2554
|
+
const referenceSymbols = (0, import_constants.resolveDerivativesContextReferenceSymbols)(
|
|
2657
2555
|
process.env.DERIVATIVES_CONTEXT_EXTRA_REFERENCE_SYMBOLS
|
|
2658
2556
|
);
|
|
2659
2557
|
return isDerivativesTargetContextEnabled() ? normalizeSymbols([...referenceSymbols, ...requestedSymbols]) : referenceSymbols;
|
|
@@ -4653,6 +4551,7 @@ var backfillCoinMarketCapContextForReplay = backfillCoinMarketCapContext;
|
|
|
4653
4551
|
var backfillCoinMarketCapContextForSignals = (params) => backfillCoinMarketCapContext({ ...params, refreshStaleFearGreed: true });
|
|
4654
4552
|
|
|
4655
4553
|
// src/lib/hyperliquidWhaleBackfill.ts
|
|
4554
|
+
var import_progress3 = __toESM(require("progress"));
|
|
4656
4555
|
var import_timescale4 = require("@tradejs/infra/timescale");
|
|
4657
4556
|
var import_strategies2 = require("@tradejs/node/strategies");
|
|
4658
4557
|
|
|
@@ -5027,11 +4926,36 @@ var backfillHyperliquidWhaleContext = async (params) => {
|
|
|
5027
4926
|
toMs,
|
|
5028
4927
|
...identity
|
|
5029
4928
|
});
|
|
4929
|
+
const totalCoverageBuckets = Math.ceil(
|
|
4930
|
+
(toMs - fromMs) / HYPERLIQUID_WHALE_BUCKET_MS
|
|
4931
|
+
);
|
|
4932
|
+
const coverageBar = new import_progress3.default(
|
|
4933
|
+
"Hyperliquid coverage :current/:total [:bar][:percent] :eta(s) rows=:rows chunk=:chunk",
|
|
4934
|
+
{
|
|
4935
|
+
total: Math.max(1, totalCoverageBuckets),
|
|
4936
|
+
width: 24
|
|
4937
|
+
}
|
|
4938
|
+
);
|
|
4939
|
+
let displayedCoverageBuckets = 0;
|
|
5030
4940
|
const coverageBuckets = await (0, import_timescale4.rebuildHyperliquidWhaleCoverageRows)({
|
|
5031
4941
|
fromMs,
|
|
5032
4942
|
toMs,
|
|
5033
4943
|
expectedWhales: whales.addresses.length,
|
|
5034
|
-
...identity
|
|
4944
|
+
...identity,
|
|
4945
|
+
onProgress: (progress) => {
|
|
4946
|
+
const next = Math.max(
|
|
4947
|
+
displayedCoverageBuckets,
|
|
4948
|
+
Math.min(progress.completedBuckets, coverageBar.total)
|
|
4949
|
+
);
|
|
4950
|
+
const delta = next - displayedCoverageBuckets;
|
|
4951
|
+
if (delta > 0) {
|
|
4952
|
+
coverageBar.tick(delta, {
|
|
4953
|
+
rows: progress.rows,
|
|
4954
|
+
chunk: `${progress.chunkIndex}/${progress.totalChunks}`
|
|
4955
|
+
});
|
|
4956
|
+
displayedCoverageBuckets = next;
|
|
4957
|
+
}
|
|
4958
|
+
}
|
|
5035
4959
|
});
|
|
5036
4960
|
const result = {
|
|
5037
4961
|
cached: false,
|
|
@@ -5059,6 +4983,9 @@ var backfillHyperliquidWhaleContext = async (params) => {
|
|
|
5059
4983
|
};
|
|
5060
4984
|
|
|
5061
4985
|
// src/lib/marketContextPrepare.ts
|
|
4986
|
+
var uniqueReasons = (reasons) => [
|
|
4987
|
+
...new Set(reasons)
|
|
4988
|
+
];
|
|
5062
4989
|
var shouldPrepareDerivativesContextForRun = (params) => {
|
|
5063
4990
|
if (params.universe === "tradfi") return false;
|
|
5064
4991
|
if (params.mode === "signals") {
|
|
@@ -5066,6 +4993,9 @@ var shouldPrepareDerivativesContextForRun = (params) => {
|
|
|
5066
4993
|
cacheOnly: params.cacheOnly
|
|
5067
4994
|
});
|
|
5068
4995
|
}
|
|
4996
|
+
if (params.mode === "backtest" && !params.aiEnabled && !params.mlEnabled) {
|
|
4997
|
+
return false;
|
|
4998
|
+
}
|
|
5069
4999
|
return shouldBackfillDerivativesContextForBacktest({
|
|
5070
5000
|
aiEnabled: Boolean(params.aiEnabled),
|
|
5071
5001
|
cacheOnly: params.cacheOnly,
|
|
@@ -5075,6 +5005,7 @@ var shouldPrepareDerivativesContextForRun = (params) => {
|
|
|
5075
5005
|
var shouldPrepareBinanceMarketContextForRun = (params) => {
|
|
5076
5006
|
if (params.universe === "tradfi") return false;
|
|
5077
5007
|
if (params.mode === "backtest") {
|
|
5008
|
+
if (!params.aiEnabled && !params.mlEnabled) return false;
|
|
5078
5009
|
return shouldBackfillBinanceMarketContextForBacktest({
|
|
5079
5010
|
aiEnabled: Boolean(params.aiEnabled),
|
|
5080
5011
|
cacheOnly: params.cacheOnly,
|
|
@@ -5093,6 +5024,7 @@ var shouldPrepareBinanceMarketContextForRun = (params) => {
|
|
|
5093
5024
|
var shouldPrepareCoinMarketCapContextForRun = (params) => {
|
|
5094
5025
|
if (params.universe === "tradfi") return false;
|
|
5095
5026
|
if (params.mode === "backtest") {
|
|
5027
|
+
if (!params.aiEnabled && !params.mlEnabled) return false;
|
|
5096
5028
|
return shouldBackfillCoinMarketCapContextForBacktest({
|
|
5097
5029
|
aiEnabled: Boolean(params.aiEnabled),
|
|
5098
5030
|
cacheOnly: params.cacheOnly,
|
|
@@ -5108,10 +5040,8 @@ var shouldPrepareCoinMarketCapContextForRun = (params) => {
|
|
|
5108
5040
|
cacheOnly: params.cacheOnly
|
|
5109
5041
|
});
|
|
5110
5042
|
};
|
|
5111
|
-
var shouldPrepareHyperliquidWhaleContextForRun = (params) => {
|
|
5112
|
-
const strategyRequiresContext =
|
|
5113
|
-
"HyperliquidConsensus"
|
|
5114
|
-
);
|
|
5043
|
+
var shouldPrepareHyperliquidWhaleContextForRun = (params, coreContextSources = []) => {
|
|
5044
|
+
const strategyRequiresContext = coreContextSources.includes("hyperliquidWhales");
|
|
5115
5045
|
if (params.universe === "tradfi" || params.mode === "signals" || params.cacheOnly || !params.aiEnabled && !params.mlEnabled && !strategyRequiresContext) {
|
|
5116
5046
|
return false;
|
|
5117
5047
|
}
|
|
@@ -5122,6 +5052,64 @@ var shouldPrepareHyperliquidWhaleContextForRun = (params) => {
|
|
|
5122
5052
|
if (strategyRequiresContext) return true;
|
|
5123
5053
|
return ["1", "true", "yes", "on"].includes(configured);
|
|
5124
5054
|
};
|
|
5055
|
+
var resolveCoreContextSources = async (params) => {
|
|
5056
|
+
const strategyNames = [...new Set(params.strategyNames ?? [])];
|
|
5057
|
+
if (!strategyNames.length) return [];
|
|
5058
|
+
await (0, import_registry.ensureStrategyPluginsLoaded)(params.projectRoot);
|
|
5059
|
+
return [
|
|
5060
|
+
...new Set(
|
|
5061
|
+
strategyNames.flatMap(
|
|
5062
|
+
(strategyName) => (0, import_registry.getStrategyManifest)(strategyName, params.projectRoot)?.contextRequirements?.core ?? []
|
|
5063
|
+
)
|
|
5064
|
+
)
|
|
5065
|
+
];
|
|
5066
|
+
};
|
|
5067
|
+
var resolveMarketContextRunRequirements = async (params) => {
|
|
5068
|
+
const coreContextSources = await resolveCoreContextSources(params);
|
|
5069
|
+
const paramsWithCoreDemand = (source) => coreContextSources.includes(source) ? { ...params, aiEnabled: true } : params;
|
|
5070
|
+
const isCrypto = params.universe !== "tradfi";
|
|
5071
|
+
const capturesPayload = Boolean(params.aiEnabled || params.mlEnabled);
|
|
5072
|
+
const runtimeReadsContext = params.mode !== "backtest";
|
|
5073
|
+
const reasonsFor = (source) => uniqueReasons([
|
|
5074
|
+
...coreContextSources.includes(source) ? ["core"] : [],
|
|
5075
|
+
...params.aiEnabled ? ["ai"] : [],
|
|
5076
|
+
...params.mlEnabled ? ["ml"] : [],
|
|
5077
|
+
...runtimeReadsContext ? ["runtime"] : []
|
|
5078
|
+
]);
|
|
5079
|
+
const readsStandardContext = isCrypto && (capturesPayload || runtimeReadsContext);
|
|
5080
|
+
const readsHyperliquid = isCrypto && (readsStandardContext || coreContextSources.includes("hyperliquidWhales"));
|
|
5081
|
+
return {
|
|
5082
|
+
binance: {
|
|
5083
|
+
read: readsStandardContext || coreContextSources.includes("binance"),
|
|
5084
|
+
backfill: shouldPrepareBinanceMarketContextForRun(
|
|
5085
|
+
paramsWithCoreDemand("binance")
|
|
5086
|
+
),
|
|
5087
|
+
requiredBy: reasonsFor("binance")
|
|
5088
|
+
},
|
|
5089
|
+
coinmarketcap: {
|
|
5090
|
+
read: readsStandardContext || coreContextSources.includes("coinmarketcap"),
|
|
5091
|
+
backfill: shouldPrepareCoinMarketCapContextForRun(
|
|
5092
|
+
paramsWithCoreDemand("coinmarketcap")
|
|
5093
|
+
),
|
|
5094
|
+
requiredBy: reasonsFor("coinmarketcap")
|
|
5095
|
+
},
|
|
5096
|
+
derivatives: {
|
|
5097
|
+
read: readsStandardContext || coreContextSources.includes("derivatives"),
|
|
5098
|
+
backfill: shouldPrepareDerivativesContextForRun(
|
|
5099
|
+
paramsWithCoreDemand("derivatives")
|
|
5100
|
+
),
|
|
5101
|
+
requiredBy: reasonsFor("derivatives")
|
|
5102
|
+
},
|
|
5103
|
+
hyperliquidWhales: {
|
|
5104
|
+
read: readsHyperliquid,
|
|
5105
|
+
backfill: shouldPrepareHyperliquidWhaleContextForRun(
|
|
5106
|
+
params,
|
|
5107
|
+
coreContextSources
|
|
5108
|
+
),
|
|
5109
|
+
requiredBy: reasonsFor("hyperliquidWhales")
|
|
5110
|
+
}
|
|
5111
|
+
};
|
|
5112
|
+
};
|
|
5125
5113
|
var resolveCoinMarketCapBackfillForMode = (mode) => mode === "backtest" ? backfillCoinMarketCapContextForBacktest : mode === "signals" ? backfillCoinMarketCapContextForSignals : backfillCoinMarketCapContextForReplay;
|
|
5126
5114
|
var buildCoinMarketCapBackfillParams = (params) => ({
|
|
5127
5115
|
userName: params.userName,
|
|
@@ -5150,7 +5138,15 @@ var prepareMarketContextForRun = async (params) => {
|
|
|
5150
5138
|
console.log(import_chalk4.default.gray(message));
|
|
5151
5139
|
});
|
|
5152
5140
|
const timeOperation3 = (label, operation) => timeOperation(label, operation, log);
|
|
5153
|
-
|
|
5141
|
+
const requirements = await resolveMarketContextRunRequirements(params);
|
|
5142
|
+
const readableSources = Object.entries(requirements).filter(([, requirement]) => requirement.read).map(([source]) => source);
|
|
5143
|
+
if (readableSources.length) {
|
|
5144
|
+
await timeOperation3(
|
|
5145
|
+
"market context schema ensure",
|
|
5146
|
+
() => (0, import_timescale5.ensureMarketContextSchemas)(readableSources)
|
|
5147
|
+
);
|
|
5148
|
+
}
|
|
5149
|
+
if (requirements.derivatives.backfill) {
|
|
5154
5150
|
await timeOperation3(
|
|
5155
5151
|
"derivatives context backfill",
|
|
5156
5152
|
() => (params.mode === "signals" ? backfillDerivativesContextForSignals : backfillDerivativesContextForBacktest)(
|
|
@@ -5158,7 +5154,7 @@ var prepareMarketContextForRun = async (params) => {
|
|
|
5158
5154
|
)
|
|
5159
5155
|
);
|
|
5160
5156
|
}
|
|
5161
|
-
if (
|
|
5157
|
+
if (requirements.hyperliquidWhales.backfill) {
|
|
5162
5158
|
await timeOperation3(
|
|
5163
5159
|
"hyperliquid whale context backfill",
|
|
5164
5160
|
() => backfillHyperliquidWhaleContext({
|
|
@@ -5170,13 +5166,13 @@ var prepareMarketContextForRun = async (params) => {
|
|
|
5170
5166
|
})
|
|
5171
5167
|
);
|
|
5172
5168
|
}
|
|
5173
|
-
if (
|
|
5169
|
+
if (requirements.binance.backfill) {
|
|
5174
5170
|
await timeOperation3("binance market context backfill", () => {
|
|
5175
5171
|
const backfill = params.mode === "backtest" ? backfillBinanceMarketContextForBacktest : params.mode === "signals" ? backfillBinanceMarketContextForSignals : backfillBinanceMarketContextForReplay;
|
|
5176
5172
|
return backfill(buildBinanceMarketBackfillParams(params));
|
|
5177
5173
|
});
|
|
5178
5174
|
}
|
|
5179
|
-
if (
|
|
5175
|
+
if (requirements.coinmarketcap.backfill) {
|
|
5180
5176
|
await timeOperation3(
|
|
5181
5177
|
"coinmarketcap historical context backfill",
|
|
5182
5178
|
() => resolveCoinMarketCapBackfillForMode(params.mode)(
|
|
@@ -5184,6 +5180,7 @@ var prepareMarketContextForRun = async (params) => {
|
|
|
5184
5180
|
)
|
|
5185
5181
|
);
|
|
5186
5182
|
}
|
|
5183
|
+
return requirements;
|
|
5187
5184
|
};
|
|
5188
5185
|
|
|
5189
5186
|
// src/lib/marketData/historyPrepare.ts
|
|
@@ -5347,7 +5344,7 @@ var import_strategies3 = require("@tradejs/node/strategies");
|
|
|
5347
5344
|
|
|
5348
5345
|
// src/lib/runtimeRedis.ts
|
|
5349
5346
|
var import_redis = require("@tradejs/infra/redis");
|
|
5350
|
-
var
|
|
5347
|
+
var import_time = require("@tradejs/core/time");
|
|
5351
5348
|
var RESERVED_STRATEGY_NAMES = /* @__PURE__ */ new Set(["charts"]);
|
|
5352
5349
|
var RESERVED_CONFIG_IDS = /* @__PURE__ */ new Set(["results"]);
|
|
5353
5350
|
var resolveStrategyConfigIdentityByKey = (userName, key) => {
|
|
@@ -5491,6 +5488,111 @@ var loadRuntimeStrategies = async ({
|
|
|
5491
5488
|
// src/lib/signals/skipStats.ts
|
|
5492
5489
|
var import_chalk6 = __toESM(require("chalk"));
|
|
5493
5490
|
var import_logger2 = require("@tradejs/infra/logger");
|
|
5491
|
+
|
|
5492
|
+
// src/lib/runtimeSignalsStorage.ts
|
|
5493
|
+
var import_constants2 = require("@tradejs/core/constants");
|
|
5494
|
+
var import_time2 = require("@tradejs/core/time");
|
|
5495
|
+
var SECONDS_PER_DAY = 86400;
|
|
5496
|
+
var RUNTIME_SIGNAL_RETENTION_DAYS_ENV = "RUNTIME_SIGNAL_RETENTION_DAYS";
|
|
5497
|
+
var DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS = import_constants2.TTL_3D;
|
|
5498
|
+
var RUNTIME_LINEAGE_SCOPE_RETENTION_TTL_SECONDS = import_constants2.TTL_1M;
|
|
5499
|
+
var parsePositiveNumber = (value) => {
|
|
5500
|
+
if (value == null || value === "") {
|
|
5501
|
+
return null;
|
|
5502
|
+
}
|
|
5503
|
+
const parsed = Number(value);
|
|
5504
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
5505
|
+
};
|
|
5506
|
+
var getRuntimeSignalRetentionTtlSeconds = () => {
|
|
5507
|
+
const explicitDays = parsePositiveNumber(
|
|
5508
|
+
process.env[RUNTIME_SIGNAL_RETENTION_DAYS_ENV]
|
|
5509
|
+
);
|
|
5510
|
+
if (explicitDays != null) {
|
|
5511
|
+
return Math.ceil(explicitDays * SECONDS_PER_DAY);
|
|
5512
|
+
}
|
|
5513
|
+
return DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS;
|
|
5514
|
+
};
|
|
5515
|
+
var toRuntimeSignalBucketRef = (signal) => ({
|
|
5516
|
+
signalId: signal.signalId,
|
|
5517
|
+
symbol: signal.symbol,
|
|
5518
|
+
strategy: signal.strategy,
|
|
5519
|
+
timestamp: signal.timestamp,
|
|
5520
|
+
...signal.runtimeConfigId && signal.runtimeConfigId !== "config" ? { runtimeConfigId: signal.runtimeConfigId } : {}
|
|
5521
|
+
});
|
|
5522
|
+
var shouldStoreRuntimeSignalDiagnostics = (signal) => signal.orderStatus === "completed" || signal.orderStatus === "failed";
|
|
5523
|
+
var toStoredRuntimeSignal = (signal) => {
|
|
5524
|
+
if (shouldStoreRuntimeSignalDiagnostics(signal)) {
|
|
5525
|
+
return signal;
|
|
5526
|
+
}
|
|
5527
|
+
const stored = { ...signal };
|
|
5528
|
+
delete stored.figures;
|
|
5529
|
+
delete stored.indicators;
|
|
5530
|
+
delete stored.additionalIndicators;
|
|
5531
|
+
return stored;
|
|
5532
|
+
};
|
|
5533
|
+
var normalizeRuntimeSignalSkipReason = (reason, fallbackSource) => {
|
|
5534
|
+
let source = fallbackSource;
|
|
5535
|
+
let normalizedReason = reason;
|
|
5536
|
+
if (reason.startsWith("AI_QUALITY_BELOW_MIN")) {
|
|
5537
|
+
source = "AI";
|
|
5538
|
+
normalizedReason = "MIN_AI_QUALITY";
|
|
5539
|
+
} else if (reason === "AI_QUALITY_UNAVAILABLE") {
|
|
5540
|
+
source = "AI";
|
|
5541
|
+
normalizedReason = "QUALITY_UNAVAILABLE";
|
|
5542
|
+
} else if (reason === "ML_RESULT_UNAVAILABLE") {
|
|
5543
|
+
source = "ML";
|
|
5544
|
+
normalizedReason = "RESULT_UNAVAILABLE";
|
|
5545
|
+
} else if (reason.startsWith("ML_THRESHOLD_NOT_MET")) {
|
|
5546
|
+
source = "ML";
|
|
5547
|
+
normalizedReason = "ML_THRESHOLD";
|
|
5548
|
+
} else if (reason.startsWith("HOOK_BEFORE_ENTRY_GATE:")) {
|
|
5549
|
+
source = "hook";
|
|
5550
|
+
normalizedReason = `BEFORE_ENTRY_GATE:${reason.slice(
|
|
5551
|
+
"HOOK_BEFORE_ENTRY_GATE:".length
|
|
5552
|
+
)}`;
|
|
5553
|
+
} else if (reason === "HOOK_BEFORE_ENTRY_GATE") {
|
|
5554
|
+
source = "hook";
|
|
5555
|
+
normalizedReason = "BEFORE_ENTRY_GATE";
|
|
5556
|
+
} else if (reason === "MAKE_ORDERS_DISABLED" || reason === "ENTRY_POLICY_BLOCKED") {
|
|
5557
|
+
source = "policy";
|
|
5558
|
+
}
|
|
5559
|
+
return {
|
|
5560
|
+
source: `skip from ${source}`,
|
|
5561
|
+
reason: normalizedReason
|
|
5562
|
+
};
|
|
5563
|
+
};
|
|
5564
|
+
var buildStatsReasonField = (source, reason) => `reason:${source}:${reason}`;
|
|
5565
|
+
var buildRuntimeSignalStatsIncrements = (evaluation) => {
|
|
5566
|
+
const increments = {
|
|
5567
|
+
evaluated: 1
|
|
5568
|
+
};
|
|
5569
|
+
if (evaluation.status === "signal") {
|
|
5570
|
+
increments.signals = 1;
|
|
5571
|
+
}
|
|
5572
|
+
let skipReason = null;
|
|
5573
|
+
let fallbackSource = "core";
|
|
5574
|
+
if (evaluation.status === "skip") {
|
|
5575
|
+
skipReason = evaluation.reason || "NO_SIGNAL";
|
|
5576
|
+
fallbackSource = "core";
|
|
5577
|
+
} else if (evaluation.status === "signal" && (evaluation.orderStatus === "skipped" || evaluation.orderStatus === "canceled")) {
|
|
5578
|
+
skipReason = evaluation.orderSkipReason || evaluation.reason || `orderStatus=${evaluation.orderStatus}`;
|
|
5579
|
+
fallbackSource = "runtime";
|
|
5580
|
+
} else if (evaluation.status === "error") {
|
|
5581
|
+
skipReason = evaluation.reason || "EVALUATION_ERROR";
|
|
5582
|
+
fallbackSource = "runtime";
|
|
5583
|
+
}
|
|
5584
|
+
if (skipReason) {
|
|
5585
|
+
const normalized = normalizeRuntimeSignalSkipReason(
|
|
5586
|
+
skipReason,
|
|
5587
|
+
fallbackSource
|
|
5588
|
+
);
|
|
5589
|
+
increments[buildStatsReasonField(normalized.source, normalized.reason)] = 1;
|
|
5590
|
+
}
|
|
5591
|
+
return increments;
|
|
5592
|
+
};
|
|
5593
|
+
var shouldStoreDetailedRuntimeSignalEvaluation = (evaluation) => evaluation.status !== "skip";
|
|
5594
|
+
|
|
5595
|
+
// src/lib/signals/skipStats.ts
|
|
5494
5596
|
var createStrategySkipStats = (runtimeStrategies) => new Map(
|
|
5495
5597
|
runtimeStrategies.map(({ strategyName }) => [
|
|
5496
5598
|
strategyName,
|
|
@@ -5818,7 +5920,7 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
|
|
|
5818
5920
|
if (evaluations.length === 0) return;
|
|
5819
5921
|
const evaluationsByDay = /* @__PURE__ */ new Map();
|
|
5820
5922
|
for (const evaluation of evaluations) {
|
|
5821
|
-
const dayKey = (0,
|
|
5923
|
+
const dayKey = (0, import_time2.getRuntimeStorageDayKey)(evaluation.timestamp);
|
|
5822
5924
|
const dayEvaluations = evaluationsByDay.get(dayKey) ?? [];
|
|
5823
5925
|
dayEvaluations.push(evaluation);
|
|
5824
5926
|
evaluationsByDay.set(dayKey, dayEvaluations);
|
|
@@ -5926,13 +6028,47 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
|
|
|
5926
6028
|
};
|
|
5927
6029
|
var createRuntimeSignalEvaluationBuffer = () => {
|
|
5928
6030
|
const evaluations = [];
|
|
6031
|
+
const signals2 = [];
|
|
5929
6032
|
return {
|
|
5930
6033
|
save: async (evaluation) => {
|
|
5931
6034
|
evaluations.push(evaluation);
|
|
5932
6035
|
},
|
|
6036
|
+
saveSignal: (userName, signal) => {
|
|
6037
|
+
signals2.push({ userName, signal });
|
|
6038
|
+
},
|
|
5933
6039
|
flush: async () => {
|
|
5934
6040
|
const pending = evaluations.splice(0);
|
|
5935
|
-
|
|
6041
|
+
const pendingSignals = signals2.splice(0);
|
|
6042
|
+
const runtimeSignalRetentionTtl = getRuntimeSignalRetentionTtlSeconds();
|
|
6043
|
+
const signalRefsByBucket = /* @__PURE__ */ new Map();
|
|
6044
|
+
for (const { userName, signal } of pendingSignals) {
|
|
6045
|
+
const bucket = import_redis3.redisKeys.runtimeSignalBucket(
|
|
6046
|
+
userName,
|
|
6047
|
+
(0, import_time2.getRuntimeStorageDayKey)(signal.timestamp),
|
|
6048
|
+
signal.strategy
|
|
6049
|
+
);
|
|
6050
|
+
const entries = signalRefsByBucket.get(bucket) ?? [];
|
|
6051
|
+
entries.push({
|
|
6052
|
+
field: signal.signalId,
|
|
6053
|
+
data: toRuntimeSignalBucketRef(signal)
|
|
6054
|
+
});
|
|
6055
|
+
signalRefsByBucket.set(bucket, entries);
|
|
6056
|
+
}
|
|
6057
|
+
await Promise.all([
|
|
6058
|
+
flushRuntimeSignalEvaluations(pending),
|
|
6059
|
+
...pendingSignals.map(
|
|
6060
|
+
({ signal }) => (0, import_redis3.setData)(
|
|
6061
|
+
import_redis3.redisKeys.storeSignal(signal.symbol, signal.signalId),
|
|
6062
|
+
toStoredRuntimeSignal(signal),
|
|
6063
|
+
{ expire: runtimeSignalRetentionTtl }
|
|
6064
|
+
)
|
|
6065
|
+
),
|
|
6066
|
+
...[...signalRefsByBucket].map(
|
|
6067
|
+
([bucket, entries]) => (0, import_redis3.setHashJsonFields)(bucket, entries, {
|
|
6068
|
+
expire: runtimeSignalRetentionTtl
|
|
6069
|
+
})
|
|
6070
|
+
)
|
|
6071
|
+
]);
|
|
5936
6072
|
}
|
|
5937
6073
|
};
|
|
5938
6074
|
};
|
|
@@ -6165,7 +6301,7 @@ var runSignalsDaemon = async ({
|
|
|
6165
6301
|
// src/lib/signals/klineFeed.ts
|
|
6166
6302
|
var import_connectors5 = require("@tradejs/connectors");
|
|
6167
6303
|
var import_logger3 = require("@tradejs/infra/logger");
|
|
6168
|
-
var
|
|
6304
|
+
var import_timescale6 = require("@tradejs/infra/timescale");
|
|
6169
6305
|
|
|
6170
6306
|
// src/lib/marketData/klineEvents.ts
|
|
6171
6307
|
var import_ioredis = __toESM(require("ioredis"));
|
|
@@ -6185,9 +6321,9 @@ var createSignalsKlineFeed = async ({
|
|
|
6185
6321
|
publish = publishMarketKlineEvent,
|
|
6186
6322
|
persist = async (rows) => {
|
|
6187
6323
|
const intervalMinutes = Number(interval2);
|
|
6188
|
-
await (0,
|
|
6324
|
+
await (0, import_timescale6.upsertCandles)(
|
|
6189
6325
|
rows.flatMap(
|
|
6190
|
-
({ symbol, candle }) => (0,
|
|
6326
|
+
({ symbol, candle }) => (0, import_timescale6.toRows)("bybit", symbol, intervalMinutes, [candle])
|
|
6191
6327
|
)
|
|
6192
6328
|
);
|
|
6193
6329
|
},
|
|
@@ -6453,7 +6589,7 @@ var createSignalsSession = async (lifecycle, scope = {}) => {
|
|
|
6453
6589
|
lifecycle
|
|
6454
6590
|
};
|
|
6455
6591
|
};
|
|
6456
|
-
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) => {
|
|
6592
|
+
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) => {
|
|
6457
6593
|
const strategySignals = [];
|
|
6458
6594
|
const loadCached = (cachedSymbol) => connector.kline({
|
|
6459
6595
|
symbol: cachedSymbol,
|
|
@@ -6646,26 +6782,7 @@ var findSignals = async (symbol, connectorName, connector, btcBinanceData, btcCo
|
|
|
6646
6782
|
);
|
|
6647
6783
|
}
|
|
6648
6784
|
strategySignals.push(signal);
|
|
6649
|
-
|
|
6650
|
-
await (0, import_redis5.setData)(
|
|
6651
|
-
import_redis5.redisKeys.storeSignal(symbol, signal.signalId),
|
|
6652
|
-
toStoredRuntimeSignal(signal),
|
|
6653
|
-
{
|
|
6654
|
-
expire: runtimeSignalRetentionTtl
|
|
6655
|
-
}
|
|
6656
|
-
);
|
|
6657
|
-
await (0, import_redis5.setHashJsonField)(
|
|
6658
|
-
import_redis5.redisKeys.runtimeSignalBucket(
|
|
6659
|
-
flags.user,
|
|
6660
|
-
(0, import_time.getRuntimeStorageDayKey)(signal.timestamp),
|
|
6661
|
-
signal.strategy
|
|
6662
|
-
),
|
|
6663
|
-
signal.signalId,
|
|
6664
|
-
toRuntimeSignalBucketRef(signal),
|
|
6665
|
-
{
|
|
6666
|
-
expire: runtimeSignalRetentionTtl
|
|
6667
|
-
}
|
|
6668
|
-
);
|
|
6785
|
+
saveRuntimeSignal?.(flags.user, signal);
|
|
6669
6786
|
await saveRuntimeSignalEvaluation?.({
|
|
6670
6787
|
evaluationId: buildRuntimeSignalEvaluationId({
|
|
6671
6788
|
strategyName,
|
|
@@ -6936,7 +7053,7 @@ var signals = async (options = {}) => {
|
|
|
6936
7053
|
return;
|
|
6937
7054
|
}
|
|
6938
7055
|
const strategyStats = createStrategySkipStats(runtimeStrategies);
|
|
6939
|
-
const bar = new
|
|
7056
|
+
const bar = new import_progress4.default(
|
|
6940
7057
|
":current/:total [:bar][:percent] :found :eta(s) :symbol",
|
|
6941
7058
|
{
|
|
6942
7059
|
total: tickers.length,
|
|
@@ -6971,7 +7088,8 @@ var signals = async (options = {}) => {
|
|
|
6971
7088
|
deployment?.id,
|
|
6972
7089
|
instrumentsBySymbol.get(symbol),
|
|
6973
7090
|
sessionUniverse,
|
|
6974
|
-
runtimeSignalEvaluations.save
|
|
7091
|
+
runtimeSignalEvaluations.save,
|
|
7092
|
+
runtimeSignalEvaluations.saveSignal
|
|
6975
7093
|
);
|
|
6976
7094
|
if (strategySignals.length > 0) {
|
|
6977
7095
|
signals2.push(...strategySignals);
|
|
@@ -370,6 +370,15 @@ var syncRuntimeTrades = async ({
|
|
|
370
370
|
nextTrade,
|
|
371
371
|
{ expire: import_constants.TTL_1M }
|
|
372
372
|
),
|
|
373
|
+
(0, import_redis2.setHashJsonField)(
|
|
374
|
+
import_redis2.redisKeys.runtimeClosedTradeBucket(
|
|
375
|
+
userName,
|
|
376
|
+
(0, import_time2.getRuntimeStorageDayKey)(nextTrade.exitTimestamp)
|
|
377
|
+
),
|
|
378
|
+
trade.orderId,
|
|
379
|
+
nextTrade,
|
|
380
|
+
{ expire: import_constants.TTL_1M }
|
|
381
|
+
),
|
|
373
382
|
...isCurrentActiveTrade ? [(0, import_redis2.delKey)(activeTradeKey)] : []
|
|
374
383
|
]);
|
|
375
384
|
syncedTrades.push(nextTrade);
|
|
@@ -8,7 +8,9 @@ var import_ai = require("@tradejs/infra/ai");
|
|
|
8
8
|
var import_logger = require("@tradejs/infra/logger");
|
|
9
9
|
var import_ml = require("@tradejs/infra/ml");
|
|
10
10
|
var import_redis = require("@tradejs/infra/redis");
|
|
11
|
+
var import_timescale = require("@tradejs/infra/timescale");
|
|
11
12
|
var isProcessing = false;
|
|
13
|
+
(0, import_timescale.configureTimescaleMarketContextSchemaMode)("verify");
|
|
12
14
|
var resolveTestSuite = async ({
|
|
13
15
|
chunk,
|
|
14
16
|
chunkId,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tradejs/cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.17",
|
|
4
4
|
"description": "Official CLI for the TradeJS TypeScript framework: infra setup, backtests, signals, bots, and ML workflows.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tradejs",
|
|
@@ -29,14 +29,14 @@
|
|
|
29
29
|
"tradejs": "dist/cli.js"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@tradejs/base": "^2.0.
|
|
33
|
-
"@tradejs/connectors": "^2.0.
|
|
34
|
-
"@tradejs/core": "^2.0.
|
|
35
|
-
"@tradejs/indicators": "^2.0.
|
|
36
|
-
"@tradejs/infra": "^2.0.
|
|
37
|
-
"@tradejs/node": "^2.0.
|
|
38
|
-
"@tradejs/strategies": "^2.0.
|
|
39
|
-
"@tradejs/types": "^2.0.
|
|
32
|
+
"@tradejs/base": "^2.0.17",
|
|
33
|
+
"@tradejs/connectors": "^2.0.17",
|
|
34
|
+
"@tradejs/core": "^2.0.17",
|
|
35
|
+
"@tradejs/indicators": "^2.0.17",
|
|
36
|
+
"@tradejs/infra": "^2.0.17",
|
|
37
|
+
"@tradejs/node": "^2.0.17",
|
|
38
|
+
"@tradejs/strategies": "^2.0.17",
|
|
39
|
+
"@tradejs/types": "^2.0.17",
|
|
40
40
|
"args": "^5.0.3",
|
|
41
41
|
"bcryptjs": "^2.4.3",
|
|
42
42
|
"chalk": "4.1.2",
|
|
@@ -77,6 +77,8 @@
|
|
|
77
77
|
"results": "yarn run run:dist results",
|
|
78
78
|
"replay-runtime-evidence": "yarn run run:dist replay-runtime-evidence",
|
|
79
79
|
"runtime-evidence": "yarn run run:dist runtime-evidence",
|
|
80
|
+
"runtime-evidence-sync": "yarn run run:dist runtime-evidence-sync",
|
|
81
|
+
"runtime-scorecard": "yarn run run:dist runtime-scorecard",
|
|
80
82
|
"continuity": "yarn run run:dist continuity",
|
|
81
83
|
"derivatives:ingest": "yarn run run:dist derivatives:ingest",
|
|
82
84
|
"derivatives:ingest:coinalyze:all": "yarn run run:dist derivatives:ingest:coinalyze:all",
|