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