@tradejs/cli 2.0.6 → 2.0.7
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 +2448 -1374
- package/dist/lib/aiExport.js +1822 -0
- package/dist/lib/binanceMarketContextBackfill.js +97 -88
- package/dist/lib/binanceMarketData.js +6 -3
- package/dist/lib/marketContextPrepare.js +97 -88
- package/dist/lib/runEnvironment.js +48 -18
- package/dist/lib/runtimeLineage.js +255 -0
- package/dist/lib/runtimeParityDetails.js +24 -1
- package/dist/lib/runtimeSignalsLoader.js +19 -0
- package/dist/lib/runtimeSignalsStorage.js +3 -0
- package/dist/lib/runtimeTradeSync.js +40 -11
- package/dist/lib/timeWindow.js +44 -16
- package/dist/scripts/aiExport.js +200 -88
- package/dist/scripts/backtest.js +170 -107
- package/dist/scripts/binanceBreadthUniversesUpdate.js +120 -0
- package/dist/scripts/binanceMarketIngest.js +32 -33
- package/dist/scripts/mlExport.js +5 -2
- package/dist/scripts/replay.js +988 -218
- package/dist/scripts/replayRunner.js +988 -218
- package/dist/scripts/replayRuntimeEvidence.js +3 -1
- package/dist/scripts/resolveExportStrategy.js +7 -2
- package/dist/scripts/runtimeParity.js +141 -104
- package/dist/scripts/signals.js +381 -107
- package/dist/scripts/signalsDaemon.js +381 -107
- package/dist/scripts/signalsSummary.js +40 -11
- package/package.json +10 -9
|
@@ -1648,9 +1648,9 @@ var import_chalk7 = __toESM(require("chalk"));
|
|
|
1648
1648
|
var import_connectors6 = require("@tradejs/node/connectors");
|
|
1649
1649
|
var import_cli2 = require("@tradejs/node/cli");
|
|
1650
1650
|
var import_async3 = require("@tradejs/core/async");
|
|
1651
|
-
var
|
|
1651
|
+
var import_strategies3 = require("@tradejs/core/strategies");
|
|
1652
1652
|
var import_constants3 = require("@tradejs/core/constants");
|
|
1653
|
-
var
|
|
1653
|
+
var import_strategies4 = require("@tradejs/node/strategies");
|
|
1654
1654
|
var import_time3 = require("@tradejs/core/time");
|
|
1655
1655
|
var import_logger4 = require("@tradejs/infra/logger");
|
|
1656
1656
|
var import_redis5 = require("@tradejs/infra/redis");
|
|
@@ -1662,6 +1662,7 @@ var import_time = require("@tradejs/core/time");
|
|
|
1662
1662
|
var SECONDS_PER_DAY = 86400;
|
|
1663
1663
|
var RUNTIME_SIGNAL_RETENTION_DAYS_ENV = "RUNTIME_SIGNAL_RETENTION_DAYS";
|
|
1664
1664
|
var DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS = import_constants.TTL_3D;
|
|
1665
|
+
var RUNTIME_LINEAGE_SCOPE_RETENTION_TTL_SECONDS = import_constants.TTL_1M;
|
|
1665
1666
|
var parsePositiveNumber = (value) => {
|
|
1666
1667
|
if (value == null || value === "") {
|
|
1667
1668
|
return null;
|
|
@@ -1823,6 +1824,7 @@ var import_progress = __toESM(require("progress"));
|
|
|
1823
1824
|
var import_chalk = __toESM(require("chalk"));
|
|
1824
1825
|
var import_connectors = require("@tradejs/connectors");
|
|
1825
1826
|
var import_connectors2 = require("@tradejs/node/connectors");
|
|
1827
|
+
var import_strategies = require("@tradejs/node/strategies");
|
|
1826
1828
|
var import_timescale = require("@tradejs/infra/timescale");
|
|
1827
1829
|
|
|
1828
1830
|
// src/lib/binanceMarketData.ts
|
|
@@ -1832,14 +1834,6 @@ var MARKET_FEATURE_INTERVAL_MS = {
|
|
|
1832
1834
|
"15m": 9e5,
|
|
1833
1835
|
"1h": 36e5
|
|
1834
1836
|
};
|
|
1835
|
-
var STABLE_QUOTE_SYMBOLS = /* @__PURE__ */ new Set([
|
|
1836
|
-
"USDCUSDT",
|
|
1837
|
-
"FDUSDUSDT",
|
|
1838
|
-
"TUSDUSDT",
|
|
1839
|
-
"BUSDUSDT",
|
|
1840
|
-
"USDPUSDT",
|
|
1841
|
-
"DAIUSDT"
|
|
1842
|
-
]);
|
|
1843
1837
|
var safeDivide = (num, den) => Number.isFinite(num) && Number.isFinite(den) && den !== 0 ? num / den : null;
|
|
1844
1838
|
var mean = (values) => values.length ? values.reduce((sum, value) => sum + value, 0) / values.length : null;
|
|
1845
1839
|
var standardDeviation = (values) => {
|
|
@@ -1883,9 +1877,6 @@ var classifyBtcAltRegime = ({
|
|
|
1883
1877
|
if (btcVsAltReturn24h < -5e-3) return "alt_lead";
|
|
1884
1878
|
return "neutral";
|
|
1885
1879
|
};
|
|
1886
|
-
var selectBreadthUniverseFromTickers = (tickers, limit) => tickers.filter(
|
|
1887
|
-
(ticker) => ticker.symbol.endsWith("USDT") && ticker.symbol !== "BTCUSDT" && !STABLE_QUOTE_SYMBOLS.has(ticker.symbol) && Number.isFinite(ticker.turnover24h) && ticker.turnover24h > 0
|
|
1888
|
-
).sort((a, b) => b.turnover24h - a.turnover24h).slice(0, Math.max(0, limit)).map((ticker) => ticker.symbol);
|
|
1889
1880
|
var aggregateAggTradesToRows = ({
|
|
1890
1881
|
symbol,
|
|
1891
1882
|
interval: interval2,
|
|
@@ -2367,10 +2358,6 @@ var backfillBinanceMarketContext = async (params, enabled) => {
|
|
|
2367
2358
|
process.env.BINANCE_MARKET_CONTEXT_TRADE_FLOW_SYMBOL_LIMIT,
|
|
2368
2359
|
2
|
|
2369
2360
|
);
|
|
2370
|
-
const breadthLimit = asInt(
|
|
2371
|
-
process.env.BINANCE_MARKET_CONTEXT_BREADTH_LIMIT,
|
|
2372
|
-
30
|
|
2373
|
-
);
|
|
2374
2361
|
const breadthChunkDays = asFloat(
|
|
2375
2362
|
process.env.BINANCE_MARKET_CONTEXT_BREADTH_CHUNK_DAYS,
|
|
2376
2363
|
30
|
|
@@ -2493,88 +2480,113 @@ var backfillBinanceMarketContext = async (params, enabled) => {
|
|
|
2493
2480
|
}
|
|
2494
2481
|
}
|
|
2495
2482
|
if (includeBreadth) {
|
|
2496
|
-
const
|
|
2497
|
-
const breadthSymbols =
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
);
|
|
2501
|
-
const universe = `binance_top${breadthSymbols.length}_usdt`;
|
|
2502
|
-
const coverage = await (0, import_timescale.getMarketBreadthCoverage)({
|
|
2503
|
-
universe,
|
|
2504
|
-
interval: interval2,
|
|
2505
|
-
startMs: breadthStartMs,
|
|
2506
|
-
endMs
|
|
2507
|
-
});
|
|
2508
|
-
const hasBtcAltMetrics = (coverage?.btcAltMetricsRows ?? 0) >= Math.floor(Math.max(1, coverage?.rows ?? 0) * 0.9);
|
|
2509
|
-
if (!hasCoverage({
|
|
2510
|
-
coverage,
|
|
2483
|
+
const breadthUniverses = (0, import_strategies.getBinanceBreadthUniverses)();
|
|
2484
|
+
const breadthSymbols = breadthUniverses.at(-1)?.symbols ?? [];
|
|
2485
|
+
const connectorInterval = marketIntervalToConnectorInterval(interval2);
|
|
2486
|
+
const chunks = buildBreadthBackfillChunks({
|
|
2511
2487
|
startMs: breadthStartMs,
|
|
2512
2488
|
endMs,
|
|
2513
|
-
intervalMs: intervalMs2
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
chunks,
|
|
2524
|
-
coverage,
|
|
2525
|
-
intervalMs: intervalMs2
|
|
2526
|
-
});
|
|
2527
|
-
const bar = new import_progress.default(
|
|
2528
|
-
"breadth :current/:total [:bar][:percent] :etas(s) candles=:candles skip=:skip chunk=:chunk :symbol",
|
|
2529
|
-
{
|
|
2530
|
-
total: Math.max(1, missingChunks.length * breadthSymbols.length),
|
|
2531
|
-
width: 24
|
|
2532
|
-
}
|
|
2533
|
-
);
|
|
2534
|
-
let candlesRead = 0;
|
|
2535
|
-
let skippedBreadthChunks = chunks.length - missingChunks.length;
|
|
2536
|
-
if (!missingChunks.length) {
|
|
2537
|
-
bar.tick(1, {
|
|
2538
|
-
candles: 0,
|
|
2539
|
-
skip: skippedBreadthChunks,
|
|
2540
|
-
chunk: "cached",
|
|
2541
|
-
symbol: universe
|
|
2489
|
+
intervalMs: intervalMs2,
|
|
2490
|
+
chunkDays: breadthChunkDays
|
|
2491
|
+
});
|
|
2492
|
+
const plans = await Promise.all(
|
|
2493
|
+
breadthUniverses.map(async (definition) => {
|
|
2494
|
+
const coverage = await (0, import_timescale.getMarketBreadthCoverage)({
|
|
2495
|
+
universe: definition.universe,
|
|
2496
|
+
interval: interval2,
|
|
2497
|
+
startMs: breadthStartMs,
|
|
2498
|
+
endMs
|
|
2542
2499
|
});
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
candlesBySymbol[symbol] = candles;
|
|
2559
|
-
candlesRead += candles.length;
|
|
2560
|
-
bar.tick(1, {
|
|
2561
|
-
candles: candlesRead,
|
|
2562
|
-
skip: skippedBreadthChunks,
|
|
2563
|
-
chunk: `${originalChunkIndex + 1}/${chunks.length}`,
|
|
2564
|
-
symbol
|
|
2565
|
-
});
|
|
2500
|
+
const hasBtcAltMetrics = (coverage?.btcAltMetricsRows ?? 0) >= Math.floor(Math.max(1, coverage?.rows ?? 0) * 0.9);
|
|
2501
|
+
const missingChunks2 = !hasCoverage({
|
|
2502
|
+
coverage,
|
|
2503
|
+
startMs: breadthStartMs,
|
|
2504
|
+
endMs,
|
|
2505
|
+
intervalMs: intervalMs2
|
|
2506
|
+
}) || !hasBtcAltMetrics ? !hasBtcAltMetrics ? chunks : filterMissingBreadthBackfillChunks({
|
|
2507
|
+
chunks,
|
|
2508
|
+
coverage,
|
|
2509
|
+
intervalMs: intervalMs2
|
|
2510
|
+
}) : [];
|
|
2511
|
+
if (!missingChunks2.length) {
|
|
2512
|
+
console.log(
|
|
2513
|
+
import_chalk.default.gray(`breadth cached: universe=${definition.universe}`)
|
|
2514
|
+
);
|
|
2566
2515
|
}
|
|
2567
|
-
|
|
2568
|
-
|
|
2516
|
+
return {
|
|
2517
|
+
definition,
|
|
2518
|
+
missingChunkKeys: new Set(
|
|
2519
|
+
missingChunks2.map((chunk) => `${chunk.startMs}:${chunk.endMs}`)
|
|
2520
|
+
)
|
|
2521
|
+
};
|
|
2522
|
+
})
|
|
2523
|
+
);
|
|
2524
|
+
const missingChunks = chunks.filter((chunk) => {
|
|
2525
|
+
const key = `${chunk.startMs}:${chunk.endMs}`;
|
|
2526
|
+
return plans.some((plan) => plan.missingChunkKeys.has(key));
|
|
2527
|
+
});
|
|
2528
|
+
const bar = new import_progress.default(
|
|
2529
|
+
"breadth :current/:total [:bar][:percent] :etas(s) candles=:candles skip=:skip chunk=:chunk :symbol",
|
|
2530
|
+
{
|
|
2531
|
+
total: Math.max(1, missingChunks.length * breadthSymbols.length),
|
|
2532
|
+
width: 24
|
|
2533
|
+
}
|
|
2534
|
+
);
|
|
2535
|
+
let candlesRead = 0;
|
|
2536
|
+
const skippedBreadthChunks = chunks.length - missingChunks.length;
|
|
2537
|
+
if (!missingChunks.length) {
|
|
2538
|
+
bar.tick(1, {
|
|
2539
|
+
candles: 0,
|
|
2540
|
+
skip: skippedBreadthChunks,
|
|
2541
|
+
chunk: "cached",
|
|
2542
|
+
symbol: "top5/top10/top30/top50/top100"
|
|
2543
|
+
});
|
|
2544
|
+
}
|
|
2545
|
+
for (let chunkIndex = 0; chunkIndex < missingChunks.length; chunkIndex += 1) {
|
|
2546
|
+
const chunk = missingChunks[chunkIndex];
|
|
2547
|
+
const originalChunkIndex = chunks.findIndex(
|
|
2548
|
+
(item) => item.startMs === chunk.startMs && item.endMs === chunk.endMs
|
|
2549
|
+
);
|
|
2550
|
+
const candlesBySymbol = {};
|
|
2551
|
+
for (const symbol of breadthSymbols) {
|
|
2552
|
+
const candles = await connector.kline({
|
|
2553
|
+
symbol,
|
|
2569
2554
|
interval: connectorInterval,
|
|
2570
2555
|
start: chunk.fetchStartMs,
|
|
2571
2556
|
end: chunk.endMs,
|
|
2572
2557
|
silent: true
|
|
2573
2558
|
});
|
|
2559
|
+
candlesBySymbol[symbol] = candles;
|
|
2560
|
+
candlesRead += candles.length;
|
|
2561
|
+
bar.tick(1, {
|
|
2562
|
+
candles: candlesRead,
|
|
2563
|
+
skip: skippedBreadthChunks,
|
|
2564
|
+
chunk: `${originalChunkIndex + 1}/${chunks.length}`,
|
|
2565
|
+
symbol
|
|
2566
|
+
});
|
|
2567
|
+
}
|
|
2568
|
+
const btcCandles = await connector.kline({
|
|
2569
|
+
symbol: "BTCUSDT",
|
|
2570
|
+
interval: connectorInterval,
|
|
2571
|
+
start: chunk.fetchStartMs,
|
|
2572
|
+
end: chunk.endMs,
|
|
2573
|
+
silent: true
|
|
2574
|
+
});
|
|
2575
|
+
const chunkKey = `${chunk.startMs}:${chunk.endMs}`;
|
|
2576
|
+
for (const plan of plans) {
|
|
2577
|
+
if (!plan.missingChunkKeys.has(chunkKey)) {
|
|
2578
|
+
continue;
|
|
2579
|
+
}
|
|
2580
|
+
const symbolSet = new Set(plan.definition.symbols);
|
|
2581
|
+
const universeCandles = Object.fromEntries(
|
|
2582
|
+
Object.entries(candlesBySymbol).filter(
|
|
2583
|
+
([symbol]) => symbolSet.has(symbol)
|
|
2584
|
+
)
|
|
2585
|
+
);
|
|
2574
2586
|
const rows = buildMarketBreadthRows({
|
|
2575
|
-
universe,
|
|
2587
|
+
universe: plan.definition.universe,
|
|
2576
2588
|
interval: interval2,
|
|
2577
|
-
candlesBySymbol,
|
|
2589
|
+
candlesBySymbol: universeCandles,
|
|
2578
2590
|
btcCandles
|
|
2579
2591
|
}).filter((row) => {
|
|
2580
2592
|
const ts = row.ts.getTime();
|
|
@@ -2583,8 +2595,6 @@ var backfillBinanceMarketContext = async (params, enabled) => {
|
|
|
2583
2595
|
await (0, import_timescale.upsertMarketBreadthRows)(rows);
|
|
2584
2596
|
breadthRows += rows.length;
|
|
2585
2597
|
}
|
|
2586
|
-
} else {
|
|
2587
|
-
console.log(import_chalk.default.gray(`breadth cached: universe=${universe}`));
|
|
2588
2598
|
}
|
|
2589
2599
|
}
|
|
2590
2600
|
console.log(
|
|
@@ -3809,27 +3819,27 @@ var createCoinMarketCapFetchStats = () => ({
|
|
|
3809
3819
|
totalCredits: 0,
|
|
3810
3820
|
byPath: /* @__PURE__ */ new Map()
|
|
3811
3821
|
});
|
|
3812
|
-
var getCoinMarketCapPathLabel = (
|
|
3813
|
-
if (
|
|
3814
|
-
if (
|
|
3815
|
-
if (
|
|
3816
|
-
if (
|
|
3817
|
-
if (
|
|
3818
|
-
if (
|
|
3819
|
-
return
|
|
3820
|
-
};
|
|
3821
|
-
var recordCoinMarketCapFetchStats = (stats,
|
|
3822
|
+
var getCoinMarketCapPathLabel = (path2) => {
|
|
3823
|
+
if (path2.includes("global-metrics")) return "global";
|
|
3824
|
+
if (path2.includes("cryptocurrency/quotes")) return "reference";
|
|
3825
|
+
if (path2.includes("exchange/quotes")) return "exchangeLiquidity";
|
|
3826
|
+
if (path2.includes("fear-and-greed")) return "fearGreed";
|
|
3827
|
+
if (path2.includes("cmc100")) return "cmc100";
|
|
3828
|
+
if (path2.includes("cmc20")) return "cmc20";
|
|
3829
|
+
return path2;
|
|
3830
|
+
};
|
|
3831
|
+
var recordCoinMarketCapFetchStats = (stats, path2, creditCount) => {
|
|
3822
3832
|
if (!stats) return;
|
|
3823
|
-
const existing = stats.byPath.get(
|
|
3833
|
+
const existing = stats.byPath.get(path2) ?? { requests: 0, credits: 0 };
|
|
3824
3834
|
existing.requests += 1;
|
|
3825
3835
|
existing.credits += creditCount ?? 0;
|
|
3826
3836
|
stats.requests += 1;
|
|
3827
3837
|
stats.totalCredits += creditCount ?? 0;
|
|
3828
|
-
stats.byPath.set(
|
|
3838
|
+
stats.byPath.set(path2, existing);
|
|
3829
3839
|
};
|
|
3830
3840
|
var formatCoinMarketCapFetchStats = (stats) => {
|
|
3831
3841
|
const byPath = [...stats.byPath.entries()].map(
|
|
3832
|
-
([
|
|
3842
|
+
([path2, item]) => `${getCoinMarketCapPathLabel(path2)}=${item.requests}req/${item.credits}cr`
|
|
3833
3843
|
).join(" ");
|
|
3834
3844
|
return `apiRequests=${stats.requests} apiCredits=${stats.totalCredits}${byPath ? ` ${byPath}` : ""}`;
|
|
3835
3845
|
};
|
|
@@ -4901,7 +4911,7 @@ var updateBtcReferenceHistory = async ({
|
|
|
4901
4911
|
var import_logger = require("@tradejs/infra/logger");
|
|
4902
4912
|
var import_redis2 = require("@tradejs/infra/redis");
|
|
4903
4913
|
var import_tradingAccounts = require("@tradejs/infra/tradingAccounts");
|
|
4904
|
-
var
|
|
4914
|
+
var import_strategies2 = require("@tradejs/node/strategies");
|
|
4905
4915
|
|
|
4906
4916
|
// src/lib/runtimeRedis.ts
|
|
4907
4917
|
var import_redis = require("@tradejs/infra/redis");
|
|
@@ -5001,7 +5011,7 @@ var loadRuntimeStrategies = async ({
|
|
|
5001
5011
|
});
|
|
5002
5012
|
const effectiveAccountId = resolvedAccount?.id ?? requestedAccountId;
|
|
5003
5013
|
const [strategyCreator, strategyResults] = await Promise.all([
|
|
5004
|
-
(0,
|
|
5014
|
+
(0, import_strategies2.getStrategyCreator)(strategyName, projectRoot2),
|
|
5005
5015
|
(0, import_redis2.getData)(import_redis2.redisKeys.strategyResults(userName, strategyName), {})
|
|
5006
5016
|
]);
|
|
5007
5017
|
if (!strategyCreator) {
|
|
@@ -5095,6 +5105,214 @@ var logStrategySkipStats = (runtimeStrategies, strategyStats) => {
|
|
|
5095
5105
|
|
|
5096
5106
|
// src/lib/signals/evaluations.ts
|
|
5097
5107
|
var import_redis3 = require("@tradejs/infra/redis");
|
|
5108
|
+
|
|
5109
|
+
// src/lib/runtimeLineage.ts
|
|
5110
|
+
var import_node_child_process = require("child_process");
|
|
5111
|
+
var import_node_crypto = require("crypto");
|
|
5112
|
+
var import_promises = __toESM(require("fs/promises"));
|
|
5113
|
+
var import_node_path = __toESM(require("path"));
|
|
5114
|
+
var RUNTIME_CONTEXT_ENV_KEYS = [
|
|
5115
|
+
"AI_MODE",
|
|
5116
|
+
"MIN_AI_QUALITY",
|
|
5117
|
+
"INTERVAL",
|
|
5118
|
+
"DERIVATIVES_CONTEXT_ENABLED",
|
|
5119
|
+
"DERIVATIVES_CONTEXT_TARGET_ENABLED",
|
|
5120
|
+
"DERIVATIVES_CONTEXT_LOOKBACK_HOURS",
|
|
5121
|
+
"DERIVATIVES_CONTEXT_EXTRA_REFERENCE_SYMBOLS",
|
|
5122
|
+
"DERIVATIVES_CONTEXT_EXCHANGE_PRIORITY",
|
|
5123
|
+
"COINMARKETCAP_CONTEXT_ENABLED",
|
|
5124
|
+
"COINMARKETCAP_CONTEXT_MAX_AGE_MS",
|
|
5125
|
+
"COINMARKETCAP_CONTEXT_EXCHANGE_LIQUIDITY_ENABLED",
|
|
5126
|
+
"COINMARKETCAP_CONTEXT_EXCHANGE_SLUGS",
|
|
5127
|
+
"COINMARKETCAP_CONTEXT_FEAR_GREED_ENABLED",
|
|
5128
|
+
"COINMARKETCAP_CONTEXT_HISTORICAL_ACCESS_MONTHS",
|
|
5129
|
+
"COINMARKETCAP_CONTEXT_BACKFILL_ENABLED",
|
|
5130
|
+
"COINMARKETCAP_CONTEXT_BACKFILL_MAX_DAYS",
|
|
5131
|
+
"COINMARKETCAP_CONTEXT_BACKFILL_WARMUP_DAYS"
|
|
5132
|
+
];
|
|
5133
|
+
var RUNTIME_CONTEXT_DATA_MODEL = {
|
|
5134
|
+
derivativesSourceIntervals: "15m",
|
|
5135
|
+
derivativesDerivedIntervals: "1h",
|
|
5136
|
+
derivativesHourlyFallback: "stored-1h",
|
|
5137
|
+
derivativesDataModelVersion: 2,
|
|
5138
|
+
coinMarketCapAvailabilityModel: "closed-interval-all-daily",
|
|
5139
|
+
coinMarketCapDataModelVersion: 3,
|
|
5140
|
+
binanceBreadthUniverseModel: "fixed-versioned-top5-top10-top30-top50-top100",
|
|
5141
|
+
binanceBreadthDataModelVersion: 2
|
|
5142
|
+
};
|
|
5143
|
+
var normalizeForStableJson = (value) => {
|
|
5144
|
+
if (Array.isArray(value)) {
|
|
5145
|
+
return value.map(normalizeForStableJson);
|
|
5146
|
+
}
|
|
5147
|
+
if (value && typeof value === "object") {
|
|
5148
|
+
return Object.fromEntries(
|
|
5149
|
+
Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, normalizeForStableJson(entry)])
|
|
5150
|
+
);
|
|
5151
|
+
}
|
|
5152
|
+
return value;
|
|
5153
|
+
};
|
|
5154
|
+
var fingerprintRuntimeValue = (value) => (0, import_node_crypto.createHash)("sha256").update(JSON.stringify(normalizeForStableJson(value))).digest("hex").slice(0, 16);
|
|
5155
|
+
var gitLineageCache = /* @__PURE__ */ new Map();
|
|
5156
|
+
var gateFingerprintCache = /* @__PURE__ */ new Map();
|
|
5157
|
+
var binanceBreadthFingerprintCache = /* @__PURE__ */ new Map();
|
|
5158
|
+
var resolveGitLineage = (projectRoot2) => {
|
|
5159
|
+
const cached = gitLineageCache.get(projectRoot2);
|
|
5160
|
+
if (cached) {
|
|
5161
|
+
return cached;
|
|
5162
|
+
}
|
|
5163
|
+
const envSha = String(process.env.TRADEJS_GIT_SHA ?? "").trim();
|
|
5164
|
+
if (envSha && envSha !== "unknown") {
|
|
5165
|
+
const result = {
|
|
5166
|
+
gitSha: envSha,
|
|
5167
|
+
gitDirty: false
|
|
5168
|
+
};
|
|
5169
|
+
gitLineageCache.set(projectRoot2, result);
|
|
5170
|
+
return result;
|
|
5171
|
+
}
|
|
5172
|
+
try {
|
|
5173
|
+
const gitSha = (0, import_node_child_process.execFileSync)("git", ["rev-parse", "HEAD"], {
|
|
5174
|
+
cwd: projectRoot2,
|
|
5175
|
+
encoding: "utf8",
|
|
5176
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
5177
|
+
}).trim();
|
|
5178
|
+
const status = (0, import_node_child_process.execFileSync)(
|
|
5179
|
+
"git",
|
|
5180
|
+
["status", "--porcelain", "--untracked-files=normal"],
|
|
5181
|
+
{
|
|
5182
|
+
cwd: projectRoot2,
|
|
5183
|
+
encoding: "utf8",
|
|
5184
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
5185
|
+
}
|
|
5186
|
+
).trim();
|
|
5187
|
+
const result = {
|
|
5188
|
+
gitSha: gitSha || null,
|
|
5189
|
+
gitDirty: status.length > 0
|
|
5190
|
+
};
|
|
5191
|
+
gitLineageCache.set(projectRoot2, result);
|
|
5192
|
+
return result;
|
|
5193
|
+
} catch {
|
|
5194
|
+
const result = {
|
|
5195
|
+
gitSha: null,
|
|
5196
|
+
gitDirty: null
|
|
5197
|
+
};
|
|
5198
|
+
gitLineageCache.set(projectRoot2, result);
|
|
5199
|
+
return result;
|
|
5200
|
+
}
|
|
5201
|
+
};
|
|
5202
|
+
var readOptionalFile = async (filePath) => {
|
|
5203
|
+
try {
|
|
5204
|
+
return await import_promises.default.readFile(filePath);
|
|
5205
|
+
} catch {
|
|
5206
|
+
return null;
|
|
5207
|
+
}
|
|
5208
|
+
};
|
|
5209
|
+
var resolveBinanceBreadthFingerprint = async (projectRoot2) => {
|
|
5210
|
+
if (binanceBreadthFingerprintCache.has(projectRoot2)) {
|
|
5211
|
+
return binanceBreadthFingerprintCache.get(projectRoot2) ?? null;
|
|
5212
|
+
}
|
|
5213
|
+
const content = await readOptionalFile(
|
|
5214
|
+
import_node_path.default.join(
|
|
5215
|
+
projectRoot2,
|
|
5216
|
+
"packages/node/src/config/binanceBreadthUniverses.json"
|
|
5217
|
+
)
|
|
5218
|
+
);
|
|
5219
|
+
let result = null;
|
|
5220
|
+
if (content) {
|
|
5221
|
+
try {
|
|
5222
|
+
const parsed = JSON.parse(content.toString("utf8"));
|
|
5223
|
+
result = typeof parsed.fingerprint === "string" && parsed.fingerprint.trim() ? parsed.fingerprint.trim() : null;
|
|
5224
|
+
} catch {
|
|
5225
|
+
result = null;
|
|
5226
|
+
}
|
|
5227
|
+
}
|
|
5228
|
+
binanceBreadthFingerprintCache.set(projectRoot2, result);
|
|
5229
|
+
return result;
|
|
5230
|
+
};
|
|
5231
|
+
var resolveGateFingerprint = async ({
|
|
5232
|
+
projectRoot: projectRoot2,
|
|
5233
|
+
strategyName,
|
|
5234
|
+
gitSha
|
|
5235
|
+
}) => {
|
|
5236
|
+
const cacheKey = `${projectRoot2}:${strategyName}:${gitSha ?? "unknown"}`;
|
|
5237
|
+
const cached = gateFingerprintCache.get(cacheKey);
|
|
5238
|
+
if (cached) {
|
|
5239
|
+
return cached;
|
|
5240
|
+
}
|
|
5241
|
+
const relativeCandidates = [
|
|
5242
|
+
`packages/strategies/src/${strategyName}/adapters/ai.ts`,
|
|
5243
|
+
`packages/strategies/src/${strategyName}/guardrails.ts`,
|
|
5244
|
+
`packages/strategies/src/${strategyName}/pockets.ts`,
|
|
5245
|
+
`packages/strategies/src/${strategyName}/config.ts`,
|
|
5246
|
+
"packages/node/src/ai.ts"
|
|
5247
|
+
];
|
|
5248
|
+
const optionalEntries = await Promise.all(
|
|
5249
|
+
relativeCandidates.map(async (relativePath) => {
|
|
5250
|
+
const content = await readOptionalFile(
|
|
5251
|
+
import_node_path.default.join(projectRoot2, relativePath)
|
|
5252
|
+
);
|
|
5253
|
+
return content == null ? null : { relativePath, content };
|
|
5254
|
+
})
|
|
5255
|
+
);
|
|
5256
|
+
const sourceEntries = [];
|
|
5257
|
+
for (const entry of optionalEntries) {
|
|
5258
|
+
if (entry != null) {
|
|
5259
|
+
sourceEntries.push(entry);
|
|
5260
|
+
}
|
|
5261
|
+
}
|
|
5262
|
+
const hash = (0, import_node_crypto.createHash)("sha256");
|
|
5263
|
+
hash.update(strategyName);
|
|
5264
|
+
if (sourceEntries.length) {
|
|
5265
|
+
for (const entry of sourceEntries) {
|
|
5266
|
+
hash.update(entry.relativePath);
|
|
5267
|
+
hash.update(entry.content);
|
|
5268
|
+
}
|
|
5269
|
+
} else {
|
|
5270
|
+
hash.update(gitSha ?? "unknown-git-sha");
|
|
5271
|
+
}
|
|
5272
|
+
const fingerprint = hash.digest("hex").slice(0, 16);
|
|
5273
|
+
gateFingerprintCache.set(cacheKey, fingerprint);
|
|
5274
|
+
return fingerprint;
|
|
5275
|
+
};
|
|
5276
|
+
var buildRuntimeLineage = async ({
|
|
5277
|
+
projectRoot: projectRoot2,
|
|
5278
|
+
strategyName,
|
|
5279
|
+
config,
|
|
5280
|
+
runContext,
|
|
5281
|
+
env = process.env
|
|
5282
|
+
}) => {
|
|
5283
|
+
const git = resolveGitLineage(projectRoot2);
|
|
5284
|
+
const context = {
|
|
5285
|
+
...Object.fromEntries(
|
|
5286
|
+
RUNTIME_CONTEXT_ENV_KEYS.map((key) => [key, env[key] ?? null])
|
|
5287
|
+
),
|
|
5288
|
+
...RUNTIME_CONTEXT_DATA_MODEL,
|
|
5289
|
+
binanceBreadthUniverseFingerprint: await resolveBinanceBreadthFingerprint(projectRoot2),
|
|
5290
|
+
...runContext
|
|
5291
|
+
};
|
|
5292
|
+
return {
|
|
5293
|
+
schemaVersion: 1,
|
|
5294
|
+
...git,
|
|
5295
|
+
gateFingerprint: await resolveGateFingerprint({
|
|
5296
|
+
projectRoot: projectRoot2,
|
|
5297
|
+
strategyName,
|
|
5298
|
+
gitSha: git.gitSha
|
|
5299
|
+
}),
|
|
5300
|
+
configFingerprint: fingerprintRuntimeValue(config),
|
|
5301
|
+
contextFingerprint: fingerprintRuntimeValue(context)
|
|
5302
|
+
};
|
|
5303
|
+
};
|
|
5304
|
+
var runtimeLineageKey = (lineage) => [
|
|
5305
|
+
lineage.schemaVersion,
|
|
5306
|
+
lineage.gitSha ?? "unknown",
|
|
5307
|
+
lineage.gitDirty == null ? "unknown" : lineage.gitDirty ? "dirty" : "clean",
|
|
5308
|
+
lineage.gateFingerprint,
|
|
5309
|
+
lineage.configFingerprint,
|
|
5310
|
+
lineage.contextFingerprint
|
|
5311
|
+
].join(":");
|
|
5312
|
+
|
|
5313
|
+
// src/lib/signals/evaluations.ts
|
|
5314
|
+
var runtimeLineageScopeCache = /* @__PURE__ */ new Map();
|
|
5315
|
+
var runtimeLineageScopeCacheDayKey = "";
|
|
5098
5316
|
var buildRuntimeSignalEvaluationId = ({
|
|
5099
5317
|
strategyName,
|
|
5100
5318
|
symbol,
|
|
@@ -5103,7 +5321,46 @@ var buildRuntimeSignalEvaluationId = ({
|
|
|
5103
5321
|
}) => runtimeConfigId && runtimeConfigId !== "config" ? [strategyName, runtimeConfigId, symbol, timestamp].join(":") : [strategyName, symbol, timestamp].join(":");
|
|
5104
5322
|
var saveRuntimeSignalEvaluation = async (evaluation) => {
|
|
5105
5323
|
const dayKey = (0, import_time.getRuntimeStorageDayKey)(evaluation.timestamp);
|
|
5324
|
+
if (runtimeLineageScopeCacheDayKey !== dayKey) {
|
|
5325
|
+
runtimeLineageScopeCache.clear();
|
|
5326
|
+
runtimeLineageScopeCacheDayKey = dayKey;
|
|
5327
|
+
}
|
|
5106
5328
|
const runtimeSignalRetentionTtl = getRuntimeSignalRetentionTtlSeconds();
|
|
5329
|
+
if (evaluation.runtimeLineage) {
|
|
5330
|
+
const lineageField = [
|
|
5331
|
+
evaluation.strategy,
|
|
5332
|
+
evaluation.runtimeConfigId ?? "config",
|
|
5333
|
+
evaluation.symbol,
|
|
5334
|
+
runtimeLineageKey(evaluation.runtimeLineage)
|
|
5335
|
+
].join(":");
|
|
5336
|
+
const lineageBucket = import_redis3.redisKeys.runtimeLineageScopeBucket(
|
|
5337
|
+
evaluation.userName,
|
|
5338
|
+
dayKey
|
|
5339
|
+
);
|
|
5340
|
+
const cacheKey = `${lineageBucket}:${lineageField}`;
|
|
5341
|
+
const existing = runtimeLineageScopeCache.get(cacheKey) ?? await (0, import_redis3.getHashJsonField)(
|
|
5342
|
+
lineageBucket,
|
|
5343
|
+
lineageField
|
|
5344
|
+
);
|
|
5345
|
+
const scope = {
|
|
5346
|
+
strategy: evaluation.strategy,
|
|
5347
|
+
symbol: evaluation.symbol,
|
|
5348
|
+
runtimeConfigId: evaluation.runtimeConfigId,
|
|
5349
|
+
lineage: evaluation.runtimeLineage,
|
|
5350
|
+
firstTimestamp: Math.min(
|
|
5351
|
+
existing?.firstTimestamp ?? evaluation.timestamp,
|
|
5352
|
+
evaluation.timestamp
|
|
5353
|
+
),
|
|
5354
|
+
lastTimestamp: Math.max(
|
|
5355
|
+
existing?.lastTimestamp ?? evaluation.timestamp,
|
|
5356
|
+
evaluation.timestamp
|
|
5357
|
+
)
|
|
5358
|
+
};
|
|
5359
|
+
runtimeLineageScopeCache.set(cacheKey, scope);
|
|
5360
|
+
await (0, import_redis3.setHashJsonField)(lineageBucket, lineageField, scope, {
|
|
5361
|
+
expire: RUNTIME_LINEAGE_SCOPE_RETENTION_TTL_SECONDS
|
|
5362
|
+
});
|
|
5363
|
+
}
|
|
5107
5364
|
if (shouldStoreDetailedRuntimeSignalEvaluation(evaluation)) {
|
|
5108
5365
|
await (0, import_redis3.setHashJsonField)(
|
|
5109
5366
|
import_redis3.redisKeys.runtimeSignalEvaluationBucket(
|
|
@@ -5563,7 +5820,7 @@ var resolveSignalsKlineWsWaitMs = () => resolveNonNegativeInteger(process.env.SI
|
|
|
5563
5820
|
var createDefaultSignalsLifecycle = (runtimeInterval = interval) => createSignalsStrategyLifecycle({
|
|
5564
5821
|
intervalMs: Number(runtimeInterval) * 6e4,
|
|
5565
5822
|
maxLiveBars: resolveSignalsDaemonMaxLiveBars(runtimeInterval),
|
|
5566
|
-
releaseState:
|
|
5823
|
+
releaseState: import_strategies3.releaseStrategyReplayCache
|
|
5567
5824
|
});
|
|
5568
5825
|
var timeOperation2 = (label, operation) => timeOperation(
|
|
5569
5826
|
label,
|
|
@@ -5708,6 +5965,20 @@ var findSignals = async (symbol, connectorName, connector, btcBinanceData, btcCo
|
|
|
5708
5965
|
interval: interval2,
|
|
5709
5966
|
makeOrders: flags.makeOrders
|
|
5710
5967
|
});
|
|
5968
|
+
const runtimeLineage = await buildRuntimeLineage({
|
|
5969
|
+
projectRoot,
|
|
5970
|
+
strategyName,
|
|
5971
|
+
config: {
|
|
5972
|
+
configId,
|
|
5973
|
+
strategyConfig,
|
|
5974
|
+
symbolResultConfig: strategyResults?.[symbol]?.config ?? null
|
|
5975
|
+
},
|
|
5976
|
+
runContext: {
|
|
5977
|
+
connectorName: connectorName.toLowerCase(),
|
|
5978
|
+
interval: String(interval2),
|
|
5979
|
+
universe
|
|
5980
|
+
}
|
|
5981
|
+
});
|
|
5711
5982
|
const lifecycleKey = buildSignalsStrategyLifecycleKey({
|
|
5712
5983
|
connectorName,
|
|
5713
5984
|
universe: runtimeScopeUniverse,
|
|
@@ -5789,6 +6060,7 @@ var findSignals = async (symbol, connectorName, connector, btcBinanceData, btcCo
|
|
|
5789
6060
|
userName: flags.user,
|
|
5790
6061
|
strategy: strategyName,
|
|
5791
6062
|
runtimeConfigId: configId,
|
|
6063
|
+
runtimeLineage,
|
|
5792
6064
|
symbol,
|
|
5793
6065
|
interval: interval2,
|
|
5794
6066
|
universe,
|
|
@@ -5807,10 +6079,11 @@ var findSignals = async (symbol, connectorName, connector, btcBinanceData, btcCo
|
|
|
5807
6079
|
stats.signals += 1;
|
|
5808
6080
|
}
|
|
5809
6081
|
signal.runtimeConfigId = configId;
|
|
6082
|
+
signal.runtimeLineage = runtimeLineage;
|
|
5810
6083
|
if (configId && configId !== "config" && !signal.signalId.endsWith(`:${configId}`)) {
|
|
5811
6084
|
signal.signalId = `${signal.signalId}:${configId}`;
|
|
5812
6085
|
}
|
|
5813
|
-
await (0,
|
|
6086
|
+
await (0, import_strategies4.enrichSignalWithBinanceMarketContext)({
|
|
5814
6087
|
signal,
|
|
5815
6088
|
env: "CRON"
|
|
5816
6089
|
});
|
|
@@ -5853,6 +6126,7 @@ var findSignals = async (symbol, connectorName, connector, btcBinanceData, btcCo
|
|
|
5853
6126
|
userName: flags.user,
|
|
5854
6127
|
strategy: strategyName,
|
|
5855
6128
|
runtimeConfigId: configId,
|
|
6129
|
+
runtimeLineage,
|
|
5856
6130
|
symbol,
|
|
5857
6131
|
interval: interval2,
|
|
5858
6132
|
universe,
|