@tradejs/cli 1.0.8 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/cli.js +29748 -13641
  2. package/dist/lib/aiPocketSearch.js +1316 -0
  3. package/dist/lib/aiTrainCharts.js +711 -0
  4. package/dist/lib/aiTrainDataset.js +40 -0
  5. package/dist/lib/aiTrainEvaluationDump.js +76 -0
  6. package/dist/lib/aiTrainMetrics.js +148 -8
  7. package/dist/lib/aiTrainOptions.js +130 -0
  8. package/dist/lib/aiTrainQuarantine.js +199 -0
  9. package/dist/lib/aiTrainResearch.js +467 -0
  10. package/dist/lib/binanceMarketContextBackfill.js +841 -0
  11. package/dist/lib/binanceMarketData.js +392 -0
  12. package/dist/lib/coinMarketCapContextBackfill.js +1088 -0
  13. package/dist/lib/continuity.js +60 -0
  14. package/dist/lib/derivativesContextBackfill.js +490 -77
  15. package/dist/lib/executionCalibration.js +705 -0
  16. package/dist/lib/marketContextPrepare.js +4403 -0
  17. package/dist/lib/paritySummary.js +101 -0
  18. package/dist/lib/runBot.js +1 -0
  19. package/dist/lib/runEnvironment.js +2254 -0
  20. package/dist/lib/runFormatting.js +1657 -0
  21. package/dist/lib/runtimeDebugEvidence.js +526 -0
  22. package/dist/lib/runtimeModeConfig.js +53 -0
  23. package/dist/lib/runtimeParity.js +88 -11
  24. package/dist/lib/runtimeParityDetails.js +751 -0
  25. package/dist/lib/runtimeRedis.js +157 -0
  26. package/dist/lib/runtimeSignalsLoader.js +187 -0
  27. package/dist/lib/runtimeSignalsStorage.js +185 -0
  28. package/dist/lib/runtimeStrategyBacktest.js +134 -0
  29. package/dist/lib/runtimeTradeSync.js +263 -0
  30. package/dist/lib/telegramReports.js +9 -0
  31. package/dist/lib/tickerUniverseCache.js +116 -0
  32. package/dist/lib/timeWindow.js +3 -3
  33. package/dist/scripts/agentRun.js +28 -8
  34. package/dist/scripts/aiExport.js +1721 -7
  35. package/dist/scripts/aiExportSelect.js +16 -4
  36. package/dist/scripts/aiPocketSearch.js +4190 -0
  37. package/dist/scripts/aiTrain.js +1866 -87
  38. package/dist/scripts/backtest.js +4509 -818
  39. package/dist/scripts/binanceMarketIngest.js +565 -0
  40. package/dist/scripts/bot.js +16 -2
  41. package/dist/scripts/candlesMigrateProvider.js +32 -22
  42. package/dist/scripts/cleanDir.js +15 -2
  43. package/dist/scripts/cleanRedis.js +15 -2
  44. package/dist/scripts/cleanTests.js +38 -2
  45. package/dist/scripts/cleanupMarketContext.js +69 -0
  46. package/dist/scripts/continuity.js +148 -67
  47. package/dist/scripts/derivativesIngest.js +14 -4
  48. package/dist/scripts/derivativesIngestCoinalyzeAll.js +14 -4
  49. package/dist/scripts/doctor.js +14 -4
  50. package/dist/scripts/executionCalibration.js +792 -0
  51. package/dist/scripts/infraCommon.js +10 -0
  52. package/dist/scripts/infraDown.js +14 -4
  53. package/dist/scripts/infraInit.js +24 -4
  54. package/dist/scripts/infraUp.js +14 -4
  55. package/dist/scripts/marketWs.js +222 -0
  56. package/dist/scripts/migration.js +15 -2
  57. package/dist/scripts/mlExport.js +1698 -10
  58. package/dist/scripts/mlExportSelect.js +16 -4
  59. package/dist/scripts/mlInspect.js +13 -3
  60. package/dist/scripts/mlTrainLatestSelect.js +14 -4
  61. package/dist/scripts/replay.js +8824 -0
  62. package/dist/scripts/replayRunner.js +8827 -0
  63. package/dist/scripts/replayRuntimeEvidence.js +868 -0
  64. package/dist/scripts/researchAuto.js +289 -60
  65. package/dist/scripts/results.js +3 -3
  66. package/dist/scripts/runtimeEvidence.js +605 -0
  67. package/dist/scripts/runtimeParity.js +6360 -1004
  68. package/dist/scripts/serverHealth.js +588 -0
  69. package/dist/scripts/signals.js +5623 -486
  70. package/dist/scripts/signalsDaemon.js +6167 -0
  71. package/dist/scripts/signalsSummary.js +954 -312
  72. package/dist/scripts/test-ml.js +25 -3
  73. package/dist/scripts/test.js +26 -1
  74. package/dist/scripts/user-add.js +15 -2
  75. package/dist/workers/testerWorker.js +130 -21
  76. package/package.json +18 -12
@@ -0,0 +1,392 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/lib/binanceMarketData.ts
21
+ var binanceMarketData_exports = {};
22
+ __export(binanceMarketData_exports, {
23
+ MARKET_FEATURE_INTERVAL_MS: () => MARKET_FEATURE_INTERVAL_MS,
24
+ aggregateAggTradesToRows: () => aggregateAggTradesToRows,
25
+ buildKlineTradeFlowRows: () => buildKlineTradeFlowRows,
26
+ buildMarketBreadthRows: () => buildMarketBreadthRows,
27
+ classifyBtcAltRegime: () => classifyBtcAltRegime,
28
+ estimateBinanceMarketDataVolume: () => estimateBinanceMarketDataVolume,
29
+ normalizeBinanceSymbols: () => normalizeBinanceSymbols,
30
+ normalizeMarketFeatureInterval: () => normalizeMarketFeatureInterval,
31
+ selectBreadthUniverseFromTickers: () => selectBreadthUniverseFromTickers
32
+ });
33
+ module.exports = __toCommonJS(binanceMarketData_exports);
34
+ var MARKET_FEATURE_INTERVAL_MS = {
35
+ "1m": 6e4,
36
+ "5m": 3e5,
37
+ "15m": 9e5,
38
+ "1h": 36e5
39
+ };
40
+ var STABLE_QUOTE_SYMBOLS = /* @__PURE__ */ new Set([
41
+ "USDCUSDT",
42
+ "FDUSDUSDT",
43
+ "TUSDUSDT",
44
+ "BUSDUSDT",
45
+ "USDPUSDT",
46
+ "DAIUSDT"
47
+ ]);
48
+ var safeDivide = (num, den) => Number.isFinite(num) && Number.isFinite(den) && den !== 0 ? num / den : null;
49
+ var mean = (values) => values.length ? values.reduce((sum, value) => sum + value, 0) / values.length : null;
50
+ var standardDeviation = (values) => {
51
+ const avg = mean(values);
52
+ if (avg == null || values.length < 2) return null;
53
+ const variance = values.reduce((sum, value) => sum + (value - avg) ** 2, 0) / values.length;
54
+ return Math.sqrt(variance);
55
+ };
56
+ var buildPrefixSum = (values) => {
57
+ const prefix = [0];
58
+ for (const value of values) prefix.push(prefix[prefix.length - 1] + value);
59
+ return prefix;
60
+ };
61
+ var sumPrefixRange = (prefix, startIndex, endIndexInclusive) => {
62
+ if (endIndexInclusive < startIndex) return 0;
63
+ const start = Math.max(0, startIndex);
64
+ const end = Math.min(prefix.length - 2, endIndexInclusive);
65
+ if (end < start) return 0;
66
+ return prefix[end + 1] - prefix[start];
67
+ };
68
+ var windowReturn = (candles, index, lookbackBars) => {
69
+ const previous = candles[index - lookbackBars];
70
+ const current = candles[index];
71
+ return previous && previous.close > 0 ? (current.close - previous.close) / previous.close : null;
72
+ };
73
+ var classifyBtcAltRegime = ({
74
+ btcReturn24h,
75
+ altBasketReturn24h,
76
+ btcVsAltReturn24h
77
+ }) => {
78
+ if (btcReturn24h == null || altBasketReturn24h == null || btcVsAltReturn24h == null) {
79
+ return "unknown";
80
+ }
81
+ if (btcReturn24h < -0.015 && altBasketReturn24h < -0.025) {
82
+ return "risk_off";
83
+ }
84
+ if (btcReturn24h > 5e-3 && altBasketReturn24h > 5e-3 && altBasketReturn24h > btcReturn24h) {
85
+ return "risk_on";
86
+ }
87
+ if (btcVsAltReturn24h > 5e-3) return "btc_lead";
88
+ if (btcVsAltReturn24h < -5e-3) return "alt_lead";
89
+ return "neutral";
90
+ };
91
+ var normalizeMarketFeatureInterval = (value) => {
92
+ const normalized = String(value || "15m").trim().toLowerCase();
93
+ if (normalized === "1" || normalized === "1m") return "1m";
94
+ if (normalized === "5" || normalized === "5m") return "5m";
95
+ if (normalized === "15" || normalized === "15m") return "15m";
96
+ if (normalized === "60" || normalized === "1h") return "1h";
97
+ return "15m";
98
+ };
99
+ var normalizeBinanceSymbols = (value) => String(value || "").split(",").map((item) => item.trim().toUpperCase()).filter(Boolean);
100
+ var selectBreadthUniverseFromTickers = (tickers, limit) => tickers.filter(
101
+ (ticker) => ticker.symbol.endsWith("USDT") && ticker.symbol !== "BTCUSDT" && !STABLE_QUOTE_SYMBOLS.has(ticker.symbol) && Number.isFinite(ticker.turnover24h) && ticker.turnover24h > 0
102
+ ).sort((a, b) => b.turnover24h - a.turnover24h).slice(0, Math.max(0, limit)).map((ticker) => ticker.symbol);
103
+ var estimateBinanceMarketDataVolume = ({
104
+ symbols,
105
+ days,
106
+ interval,
107
+ includeAggTrades,
108
+ includeBreadth,
109
+ breadthLimit
110
+ }) => {
111
+ const intervalMs = MARKET_FEATURE_INTERVAL_MS[interval];
112
+ const bucketRowsPerSymbol = Math.ceil(days * 864e5 / intervalMs);
113
+ const aggTradeBucketRows = includeAggTrades ? symbols.length * bucketRowsPerSymbol : 0;
114
+ const breadthSymbols = includeBreadth ? Math.max(0, breadthLimit) : 0;
115
+ const breadthCandleRows = breadthSymbols * bucketRowsPerSymbol;
116
+ const breadthRows = includeBreadth ? bucketRowsPerSymbol : 0;
117
+ return {
118
+ interval,
119
+ days,
120
+ symbols: symbols.length,
121
+ bucketRowsPerSymbol,
122
+ aggTradeBucketRows,
123
+ breadthSymbols,
124
+ breadthCandleRows,
125
+ breadthRows,
126
+ estimatedStoredRows: aggTradeBucketRows + breadthRows
127
+ };
128
+ };
129
+ var aggregateAggTradesToRows = ({
130
+ symbol,
131
+ interval,
132
+ trades,
133
+ source = "binance_agg_trades"
134
+ }) => {
135
+ const intervalMs = MARKET_FEATURE_INTERVAL_MS[interval];
136
+ const buckets = /* @__PURE__ */ new Map();
137
+ for (const trade of trades) {
138
+ const bucketTs = Math.floor(trade.timestamp / intervalMs) * intervalMs;
139
+ const row = buckets.get(bucketTs) ?? {
140
+ symbol,
141
+ interval,
142
+ ts: new Date(bucketTs),
143
+ trades: 0,
144
+ buyBaseVolume: 0,
145
+ sellBaseVolume: 0,
146
+ buyQuoteVolume: 0,
147
+ sellQuoteVolume: 0,
148
+ netBaseDelta: 0,
149
+ netQuoteDelta: 0,
150
+ buyPressurePct: null,
151
+ source
152
+ };
153
+ const quote = trade.price * trade.quantity;
154
+ const isAggressiveSell = trade.isBuyerMaker;
155
+ row.trades += 1;
156
+ if (isAggressiveSell) {
157
+ row.sellBaseVolume = (row.sellBaseVolume ?? 0) + trade.quantity;
158
+ row.sellQuoteVolume = (row.sellQuoteVolume ?? 0) + quote;
159
+ } else {
160
+ row.buyBaseVolume = (row.buyBaseVolume ?? 0) + trade.quantity;
161
+ row.buyQuoteVolume = (row.buyQuoteVolume ?? 0) + quote;
162
+ }
163
+ row.netBaseDelta = (row.buyBaseVolume ?? 0) - (row.sellBaseVolume ?? 0);
164
+ row.netQuoteDelta = (row.buyQuoteVolume ?? 0) - (row.sellQuoteVolume ?? 0);
165
+ row.buyPressurePct = safeDivide(
166
+ row.buyBaseVolume ?? 0,
167
+ (row.buyBaseVolume ?? 0) + (row.sellBaseVolume ?? 0)
168
+ );
169
+ buckets.set(bucketTs, row);
170
+ }
171
+ return [...buckets.values()].sort((a, b) => a.ts.getTime() - b.ts.getTime());
172
+ };
173
+ var buildKlineTradeFlowRows = ({
174
+ symbol,
175
+ interval,
176
+ candles,
177
+ source = "binance_klines"
178
+ }) => candles.map((candle) => {
179
+ const buyBaseVolume = candle.takerBuyBaseVolume;
180
+ const buyQuoteVolume = candle.takerBuyQuoteVolume;
181
+ if (buyBaseVolume == null || buyQuoteVolume == null || !Number.isFinite(buyBaseVolume) || !Number.isFinite(buyQuoteVolume)) {
182
+ return null;
183
+ }
184
+ const sellBaseVolume = candle.takerSellBaseVolume != null && Number.isFinite(candle.takerSellBaseVolume) ? candle.takerSellBaseVolume : Math.max(0, candle.volume - buyBaseVolume);
185
+ const sellQuoteVolume = candle.takerSellQuoteVolume != null && Number.isFinite(candle.takerSellQuoteVolume) ? candle.takerSellQuoteVolume : Math.max(0, candle.turnover - buyQuoteVolume);
186
+ const trades = candle.trades != null && Number.isFinite(candle.trades) ? Math.max(0, Math.trunc(candle.trades)) : 0;
187
+ return {
188
+ symbol,
189
+ interval,
190
+ ts: new Date(candle.timestamp),
191
+ trades,
192
+ buyBaseVolume,
193
+ sellBaseVolume,
194
+ buyQuoteVolume,
195
+ sellQuoteVolume,
196
+ netBaseDelta: buyBaseVolume - sellBaseVolume,
197
+ netQuoteDelta: buyQuoteVolume - sellQuoteVolume,
198
+ buyPressurePct: safeDivide(
199
+ buyBaseVolume,
200
+ buyBaseVolume + sellBaseVolume
201
+ ),
202
+ source
203
+ };
204
+ }).filter((row) => row != null);
205
+ var buildMarketBreadthRows = ({
206
+ universe,
207
+ interval,
208
+ candlesBySymbol,
209
+ btcCandles,
210
+ source = "binance_klines"
211
+ }) => {
212
+ const timestamps = /* @__PURE__ */ new Set();
213
+ const indexedSymbols = Object.values(candlesBySymbol).map((candles) => ({
214
+ candles,
215
+ byTimestamp: new Map(
216
+ candles.map((candle, index) => [candle.timestamp, index])
217
+ ),
218
+ turnoverPrefix: buildPrefixSum(
219
+ candles.map((candle) => Math.max(0, candle.turnover ?? 0))
220
+ ),
221
+ closePrefix: buildPrefixSum(candles.map((candle) => candle.close))
222
+ }));
223
+ const btcIndexed = btcCandles ? {
224
+ candles: btcCandles,
225
+ byTimestamp: new Map(
226
+ btcCandles.map((candle, index) => [candle.timestamp, index])
227
+ ),
228
+ turnoverPrefix: buildPrefixSum(
229
+ btcCandles.map((candle) => Math.max(0, candle.turnover ?? 0))
230
+ )
231
+ } : null;
232
+ const intervalMs = MARKET_FEATURE_INTERVAL_MS[interval];
233
+ const bars1h = Math.max(1, Math.round(36e5 / intervalMs));
234
+ const bars4h = Math.max(1, Math.round(144e5 / intervalMs));
235
+ const bars24h = Math.max(1, Math.round(864e5 / intervalMs));
236
+ for (const candles of Object.values(candlesBySymbol)) {
237
+ for (const candle of candles) timestamps.add(candle.timestamp);
238
+ }
239
+ return [...timestamps].sort((a, b) => a - b).map((timestamp) => {
240
+ const returns = [];
241
+ const weightedReturns = [];
242
+ let advancers = 0;
243
+ let decliners = 0;
244
+ let unchanged = 0;
245
+ let aboveMa20 = 0;
246
+ let aboveMa20Eligible = 0;
247
+ let aboveMa50 = 0;
248
+ let aboveMa50Eligible = 0;
249
+ const altReturns1h = [];
250
+ const altReturns4h = [];
251
+ const altReturns24h = [];
252
+ let altTurnover1h = 0;
253
+ let altTurnover24h = 0;
254
+ let previousAltTurnover24h = 0;
255
+ for (const indexed of indexedSymbols) {
256
+ const { candles, byTimestamp, turnoverPrefix, closePrefix } = indexed;
257
+ const index = byTimestamp.get(timestamp);
258
+ if (index == null) continue;
259
+ const candle = candles[index];
260
+ const previous = candles[index - 1];
261
+ const ret = previous && previous.close > 0 ? (candle.close - previous.close) / previous.close : 0;
262
+ if (ret > 0) advancers += 1;
263
+ else if (ret < 0) decliners += 1;
264
+ else unchanged += 1;
265
+ returns.push(ret);
266
+ weightedReturns.push({
267
+ value: ret,
268
+ weight: Math.max(0, candle.turnover ?? 0)
269
+ });
270
+ const ma20Start = index - 19;
271
+ if (ma20Start >= 0) {
272
+ aboveMa20Eligible += 1;
273
+ const ma20 = sumPrefixRange(closePrefix, ma20Start, index) / 20;
274
+ if (candle.close > ma20) aboveMa20 += 1;
275
+ }
276
+ const ma50Start = index - 49;
277
+ if (ma50Start >= 0) {
278
+ aboveMa50Eligible += 1;
279
+ const ma50 = sumPrefixRange(closePrefix, ma50Start, index) / 50;
280
+ if (candle.close > ma50) aboveMa50 += 1;
281
+ }
282
+ const altReturn1h = windowReturn(candles, index, bars1h);
283
+ const altReturn4h = windowReturn(candles, index, bars4h);
284
+ const altReturn24h = windowReturn(candles, index, bars24h);
285
+ if (altReturn1h != null) altReturns1h.push(altReturn1h);
286
+ if (altReturn4h != null) altReturns4h.push(altReturn4h);
287
+ if (altReturn24h != null) altReturns24h.push(altReturn24h);
288
+ altTurnover1h += sumPrefixRange(
289
+ turnoverPrefix,
290
+ index - bars1h + 1,
291
+ index
292
+ );
293
+ altTurnover24h += sumPrefixRange(
294
+ turnoverPrefix,
295
+ index - bars24h + 1,
296
+ index
297
+ );
298
+ previousAltTurnover24h += sumPrefixRange(
299
+ turnoverPrefix,
300
+ index - bars24h * 2 + 1,
301
+ index - bars24h
302
+ );
303
+ }
304
+ const weightSum = weightedReturns.reduce(
305
+ (sum, item) => sum + item.weight,
306
+ 0
307
+ );
308
+ const volumeWeightedReturn = weightSum > 0 ? weightedReturns.reduce(
309
+ (sum, item) => sum + item.value * item.weight,
310
+ 0
311
+ ) / weightSum : null;
312
+ const btcIndex = btcIndexed?.byTimestamp.get(timestamp);
313
+ const btcReturn1h = btcIndexed && btcIndex != null ? windowReturn(btcIndexed.candles, btcIndex, bars1h) : null;
314
+ const btcReturn4h = btcIndexed && btcIndex != null ? windowReturn(btcIndexed.candles, btcIndex, bars4h) : null;
315
+ const btcReturn24h = btcIndexed && btcIndex != null ? windowReturn(btcIndexed.candles, btcIndex, bars24h) : null;
316
+ const btcTurnover1h = btcIndexed && btcIndex != null ? sumPrefixRange(
317
+ btcIndexed.turnoverPrefix,
318
+ btcIndex - bars1h + 1,
319
+ btcIndex
320
+ ) : null;
321
+ const btcTurnover24h = btcIndexed && btcIndex != null ? sumPrefixRange(
322
+ btcIndexed.turnoverPrefix,
323
+ btcIndex - bars24h + 1,
324
+ btcIndex
325
+ ) : null;
326
+ const previousBtcTurnover24h = btcIndexed && btcIndex != null ? sumPrefixRange(
327
+ btcIndexed.turnoverPrefix,
328
+ btcIndex - bars24h * 2 + 1,
329
+ btcIndex - bars24h
330
+ ) : null;
331
+ const altBasketReturn1h = mean(altReturns1h);
332
+ const altBasketReturn4h = mean(altReturns4h);
333
+ const altBasketReturn24h = mean(altReturns24h);
334
+ const btcVsAltReturn1h = btcReturn1h == null || altBasketReturn1h == null ? null : btcReturn1h - altBasketReturn1h;
335
+ const btcVsAltReturn4h = btcReturn4h == null || altBasketReturn4h == null ? null : btcReturn4h - altBasketReturn4h;
336
+ const btcVsAltReturn24h = btcReturn24h == null || altBasketReturn24h == null ? null : btcReturn24h - altBasketReturn24h;
337
+ const btcTurnoverShare1h = btcTurnover1h == null ? null : safeDivide(btcTurnover1h, btcTurnover1h + altTurnover1h);
338
+ const btcTurnoverShare24h = btcTurnover24h == null ? null : safeDivide(btcTurnover24h, btcTurnover24h + altTurnover24h);
339
+ const previousBtcTurnoverShare24h = previousBtcTurnover24h == null ? null : safeDivide(
340
+ previousBtcTurnover24h,
341
+ previousBtcTurnover24h + previousAltTurnover24h
342
+ );
343
+ const btcTurnoverShareChange24h = btcTurnoverShare24h == null || previousBtcTurnoverShare24h == null ? null : btcTurnoverShare24h - previousBtcTurnoverShare24h;
344
+ return {
345
+ universe,
346
+ interval,
347
+ ts: new Date(timestamp),
348
+ symbolsCount: returns.length,
349
+ advancers,
350
+ decliners,
351
+ unchanged,
352
+ advanceDeclineRatio: safeDivide(advancers, decliners || 1),
353
+ pctAboveMa20: safeDivide(aboveMa20, aboveMa20Eligible),
354
+ pctAboveMa50: safeDivide(aboveMa50, aboveMa50Eligible),
355
+ equalWeightedReturn: mean(returns),
356
+ volumeWeightedReturn,
357
+ dispersion: standardDeviation(returns),
358
+ btcReturn1h,
359
+ btcReturn4h,
360
+ btcReturn24h,
361
+ altBasketReturn1h,
362
+ altBasketReturn4h,
363
+ altBasketReturn24h,
364
+ btcVsAltReturn1h,
365
+ btcVsAltReturn4h,
366
+ btcVsAltReturn24h,
367
+ btcTurnoverShare1h,
368
+ btcTurnoverShare24h,
369
+ btcTurnoverShareChange24h,
370
+ altVolToBtcVol24h: btcTurnover24h == null ? null : safeDivide(altTurnover24h, btcTurnover24h),
371
+ altDispersion24h: standardDeviation(altReturns24h),
372
+ btcAltRegime: classifyBtcAltRegime({
373
+ btcReturn24h,
374
+ altBasketReturn24h,
375
+ btcVsAltReturn24h
376
+ }),
377
+ source
378
+ };
379
+ }).filter((row) => row.symbolsCount > 0);
380
+ };
381
+ // Annotate the CommonJS export names for ESM import in node:
382
+ 0 && (module.exports = {
383
+ MARKET_FEATURE_INTERVAL_MS,
384
+ aggregateAggTradesToRows,
385
+ buildKlineTradeFlowRows,
386
+ buildMarketBreadthRows,
387
+ classifyBtcAltRegime,
388
+ estimateBinanceMarketDataVolume,
389
+ normalizeBinanceSymbols,
390
+ normalizeMarketFeatureInterval,
391
+ selectBreadthUniverseFromTickers
392
+ });