@tradejs/core 2.0.15 → 2.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7982,19 +7982,9 @@ var buildBaseContextSnapshot = ({
7982
7982
  const btcCloseSeries = fullBtcCloseSeries.slice(-BASE_CONTEXT_CANDLE_WINDOW);
7983
7983
  const atr2 = toNullable(baseResult.atr);
7984
7984
  const bbWidthPct = baseResult.bbUpper != null && baseResult.bbLower != null && baseResult.bbMiddle != null && baseResult.bbMiddle !== 0 ? (baseResult.bbUpper - baseResult.bbLower) / baseResult.bbMiddle * 100 : null;
7985
- const atrPctSeries = materializeNumericHistory(
7986
- indicatorHistory.atrPct ?? createNumericHistoryBuffer()
7987
- );
7988
- const macdHistogramSeries = materializeNumericHistory(
7989
- indicatorHistory.macdHistogram ?? createNumericHistoryBuffer()
7990
- );
7991
- const spreadSeries = materializeNumericHistory(
7992
- indicatorHistory.spread ?? createNumericHistoryBuffer()
7993
- );
7994
7985
  const recent20 = candlesHistory.slice(-20);
7995
7986
  const prior20 = candlesHistory.slice(-21, -1);
7996
7987
  const structureWindow = candlesHistory.slice(-STRUCTURE_LOOKBACK);
7997
- const session = buildSessionContext(candle.timestamp);
7998
7988
  const recent20High = recent20.length > 0 ? Math.max(...recent20.map((item) => item.high)) : null;
7999
7989
  const recent20Low = recent20.length > 0 ? Math.min(...recent20.map((item) => item.low)) : null;
8000
7990
  const avgVolume20 = recent20.length > 0 ? recent20.reduce((sum2, item) => sum2 + item.volume, 0) / recent20.length : null;
@@ -8023,38 +8013,7 @@ var buildBaseContextSnapshot = ({
8023
8013
  );
8024
8014
  const maStackScore = baseResult.maFast == null || baseResult.maMedium == null || baseResult.maSlow == null ? null : Math.sign(baseResult.maFast - baseResult.maMedium) + Math.sign(baseResult.maMedium - baseResult.maSlow);
8025
8015
  const trendBias = maStackScore == null ? "neutral" : maStackScore > 0 ? "bull" : maStackScore < 0 ? "bear" : "neutral";
8026
- const persistenceWindow = closeSeries.slice(-10);
8027
- const directionalMoves = persistenceWindow.slice(1).map((value, index) => value - persistenceWindow[index]);
8028
- const persistence = directionalMoves.length === 0 ? null : directionalMoves.filter(
8029
- (delta) => trendBias === "bull" ? delta > 0 : trendBias === "bear" ? delta < 0 : delta === 0
8030
- ).length / directionalMoves.length;
8031
- const atrPctZScore = calculateZScore(
8032
- atrPctSeries,
8033
- toNullable(baseResult.atrPct)
8034
- );
8035
- const atrSlope = calculateLineSlope(atrPctSeries, 5);
8036
- const compressionScore = safeDivide(
8037
- toNullable(baseResult.atrPct),
8038
- getLastFiniteValue(atrPctSeries.slice(0, -1))
8039
- );
8040
- const expansionScore = compressionScore == null || compressionScore === 0 ? null : 1 / compressionScore;
8041
- const bbWidthPctSeries = calculateRecentBbWidthPctSeries(closeSeries, 100);
8042
- const rawAtrPctSeries = calculateRecentAtrPctSeries(candlesHistory, 100);
8043
- const rawAtrPct = safeDivide(atr2, candle.close);
8044
- const realizedVolatility = calculateRealizedVolatility(closeSeries);
8045
- const realizedVolatilitySeries = calculateRecentRealizedVolatilitySeries(
8046
- closeSeries,
8047
- 100
8048
- );
8049
- const rangeExpansionSeries = calculateRecentRangeExpansionSeries(
8050
- candlesHistory,
8051
- 20
8052
- );
8053
- const rangeExpansion = rangeExpansionSeries[rangeExpansionSeries.length - 1] ?? null;
8054
- const volatilityState = compressionScore == null ? "unknown" : compressionScore <= 0.9 ? "compressed" : compressionScore >= 1.1 ? "expanded" : "normal";
8055
8016
  const highLowRange = candle.high - candle.low;
8056
- const bodyStrength = highLowRange > 0 ? Math.abs(candle.close - candle.open) / highLowRange : null;
8057
- const closeLocationInRange = highLowRange > 0 ? (candle.close - candle.low) / highLowRange : null;
8058
8017
  const breakoutState = baseResult.highLevel == null || baseResult.lowLevel == null ? "unknown" : candle.close > baseResult.highLevel ? prevCandle != null && prevCandle.close <= baseResult.highLevel ? "above_high_level" : "failed_high_breakout" : candle.close < baseResult.lowLevel ? prevCandle != null && prevCandle.close >= baseResult.lowLevel ? "below_low_level" : "failed_low_breakout" : "inside_range";
8059
8018
  const touchTolerance = atr2 != null && Number.isFinite(atr2) && atr2 > 0 ? atr2 * 0.15 : null;
8060
8019
  const highLevel = baseResult.highLevel;
@@ -8087,253 +8046,347 @@ var buildBaseContextSnapshot = ({
8087
8046
  distanceScore * 0.45 + wickSupport * 0.25 + closeAcceptance * 0.3
8088
8047
  );
8089
8048
  })();
8090
- const recentFalseBreakoutDensity = highLevel == null || lowLevel == null || recent20.length < 2 ? null : recent20.reduce((count, item, index) => {
8091
- if (index === 0) {
8092
- return count;
8093
- }
8094
- const prevItem = recent20[index - 1];
8095
- if (!prevItem) {
8096
- return count;
8097
- }
8098
- if (prevItem.close > highLevel && item.close <= highLevel) {
8099
- return count + 1;
8100
- }
8101
- if (prevItem.close < lowLevel && item.close >= lowLevel) {
8102
- return count + 1;
8103
- }
8104
- return count;
8105
- }, 0) / (recent20.length - 1);
8106
8049
  const rejectionWickScore = trendBias === "bull" ? lowerWick : trendBias === "bear" ? upperWick : Math.max(upperWick ?? 0, lowerWick ?? 0);
8107
- const adxContext = buildAdxContext(adxValue) ?? calculateAdxContext(candlesHistory);
8108
- const rsiContext = buildRsiContext(rsiValue) ?? calculateRsiContext(closeSeries);
8109
- const benchmarkMaFast = averageLastN(btcCloseSeries, indicatorPeriods.maFast);
8110
- const benchmarkMaSlow = averageLastN(btcCloseSeries, indicatorPeriods.maSlow);
8111
- const coin1h = coinResampledCandles.h1;
8112
- const btc1h = btcResampledCandles.h1;
8113
- const btc4h = btcResampledCandles.h4;
8114
- const btc1d = btcResampledCandles.d1;
8115
- const eth1h = ethResampledCandles?.h1 ?? [];
8116
- const eth4h = ethResampledCandles?.h4 ?? [];
8117
- const eth1d = ethResampledCandles?.d1 ?? [];
8118
8050
  const coin4h = coinResampledCandles.h4;
8119
8051
  const coin1d = coinResampledCandles.d1;
8120
- const targetVsBtc = buildTargetVsBtcContext({
8121
- coin1h,
8122
- btc1h,
8123
- coin4h,
8124
- btc4h,
8125
- coin1d,
8126
- btc1d,
8127
- coinCandles: candlesHistory,
8128
- btcCandles: btcCandlesHistory
8129
- });
8130
- const targetVsEth = ethCandlesHistory.length >= 2 ? buildTargetVsEthContext({
8131
- coin1h,
8132
- eth1h,
8133
- coin4h,
8134
- eth4h,
8135
- coin1d,
8136
- eth1d,
8137
- coinCandles: candlesHistory,
8138
- ethCandles: ethCandlesHistory
8139
- }) : null;
8140
- const btcPsychologicalLevels = buildPsychologicalLevelAssetContext(
8141
- btcCandlesHistory,
8142
- 1e3
8143
- );
8144
- const ethPsychologicalLevels = buildPsychologicalLevelAssetContext(
8145
- ethCandlesHistory,
8146
- 100
8147
- );
8148
- const referencePsychologicalLevels = btcPsychologicalLevels != null || ethPsychologicalLevels != null ? {
8149
- ...btcPsychologicalLevels != null ? { BTCUSDT: btcPsychologicalLevels } : {},
8150
- ...ethPsychologicalLevels != null ? { ETHUSDT: ethPsychologicalLevels } : {}
8151
- } : null;
8152
- const relativeStrength1h = getRelativeChange(
8153
- baseResult.price1hPcnt,
8154
- btc1h.length >= 2 ? percentChange(
8155
- btc1h[btc1h.length - 1].close,
8156
- btc1h[Math.max(0, btc1h.length - 2)].close
8157
- ) : null
8158
- );
8159
- const relativeStrength4h = getRelativeChange(
8160
- coin4h.length >= 2 ? percentChange(
8161
- coin4h[coin4h.length - 1].close,
8162
- coin4h[coin4h.length - 2].close
8163
- ) : null,
8164
- btc4h.length >= 2 ? percentChange(
8165
- btc4h[btc4h.length - 1].close,
8166
- btc4h[btc4h.length - 2].close
8167
- ) : null
8168
- );
8169
- const relativeStrength1d = getRelativeChange(
8170
- coin1d.length >= 2 ? percentChange(
8171
- coin1d[coin1d.length - 1].close,
8172
- coin1d[coin1d.length - 2].close
8173
- ) : null,
8174
- btc1d.length >= 2 ? percentChange(
8175
- btc1d[btc1d.length - 1].close,
8176
- btc1d[btc1d.length - 2].close
8177
- ) : null
8178
- );
8179
- const benchmarkBias = btc1h.length >= 2 ? btc1h[btc1h.length - 1].close > btc1h[btc1h.length - 2].close ? "bull" : btc1h[btc1h.length - 1].close < btc1h[btc1h.length - 2].close ? "bear" : "neutral" : "neutral";
8180
- const trendAlignment = trendBias === "neutral" || benchmarkBias === "neutral" ? "neutral" : trendBias === benchmarkBias ? trendBias === "bull" ? "aligned_bull" : "aligned_bear" : "against_benchmark";
8181
- const benchmarkTrendBias = benchmarkMaFast == null || benchmarkMaSlow == null ? "neutral" : benchmarkMaFast > benchmarkMaSlow ? "bull" : benchmarkMaFast < benchmarkMaSlow ? "bear" : "neutral";
8182
- const structurePivots = detectConfirmedPivots(structureWindow, atr2);
8183
- const structureZones = buildPriceZones(structureWindow, structurePivots, atr2);
8184
- const swingContext = buildSwingContext(structurePivots);
8185
- const pivotContext = buildPivotContext(
8186
- structurePivots,
8187
- structureWindow.length,
8188
- atr2
8189
- );
8190
- const nearestSupport = getNearestZone(
8191
- structureZones,
8192
- "support",
8193
- candle.close
8194
- );
8195
- const nearestResistance = getNearestZone(
8196
- structureZones,
8197
- "resistance",
8198
- candle.close
8199
- );
8200
- const totalStructureVolume = structureWindow.reduce(
8201
- (sum2, item) => sum2 + item.volume,
8202
- 0
8203
- );
8204
- const priceInSupportZone = nearestSupport == null ? null : candle.close >= nearestSupport.lower && candle.close <= nearestSupport.upper;
8205
- const priceInResistanceZone = nearestResistance == null ? null : candle.close >= nearestResistance.lower && candle.close <= nearestResistance.upper;
8206
- const activeZoneType = priceInSupportZone ? "support" : priceInResistanceZone ? "resistance" : null;
8207
- const priceInZone = priceInSupportZone == null && priceInResistanceZone == null ? null : Boolean(priceInSupportZone || priceInResistanceZone);
8208
- const resistanceVolumeShare = safeDivide(
8209
- nearestResistance?.volume ?? null,
8210
- totalStructureVolume
8211
- );
8212
- const supportVolumeShare = safeDivide(
8213
- nearestSupport?.volume ?? null,
8214
- totalStructureVolume
8215
- );
8216
- const sweepState = nearestResistance == null && nearestSupport == null ? "unknown" : nearestResistance != null && candle.high > nearestResistance.upper && candle.close < nearestResistance.level ? "swept_high" : nearestSupport != null && candle.low < nearestSupport.lower && candle.close > nearestSupport.level ? "swept_low" : nearestResistance != null && candle.close > nearestResistance.upper ? "broken_high" : nearestSupport != null && candle.close < nearestSupport.lower ? "broken_low" : "none";
8217
- const liquiditySide = sweepState === "swept_high" || sweepState === "broken_high" ? "high" : sweepState === "swept_low" || sweepState === "broken_low" ? "low" : null;
8218
- const referenceZoneSide = liquiditySide === "high" ? "resistance" : liquiditySide === "low" ? "support" : null;
8219
- const prior20High = prior20.length > 0 ? Math.max(...prior20.map((item) => item.high)) : null;
8220
- const prior20Low = prior20.length > 0 ? Math.min(...prior20.map((item) => item.low)) : null;
8221
- const sweepHigh20 = prior20High == null ? null : candle.high > prior20High && candle.close < prior20High;
8222
- const sweepLow20 = prior20Low == null ? null : candle.low < prior20Low && candle.close > prior20Low;
8223
- const closeBackInsideRange = sweepHigh20 == null && sweepLow20 == null ? null : Boolean(sweepHigh20 || sweepLow20);
8224
- const stopRunDirection = sweepHigh20 ? "up" : sweepLow20 ? "down" : null;
8225
- const sweepWickPct = stopRunDirection === "up" ? upperWick : stopRunDirection === "down" ? lowerWick : null;
8226
- const recent3 = candlesHistory.slice(-3);
8227
- const recent5 = candlesHistory.slice(-5);
8228
- const closesAboveHighLevel3 = highLevel == null ? null : recent3.filter((item) => item.close > highLevel).length;
8229
- const closesBelowLowLevel3 = lowLevel == null ? null : recent3.filter((item) => item.close < lowLevel).length;
8230
- const failedAcceptanceBars = highLevel == null || lowLevel == null ? null : recent5.filter(
8231
- (item) => item.high > highLevel && item.close <= highLevel || item.low < lowLevel && item.close >= lowLevel
8232
- ).length;
8233
- const acceptanceScore = closesAboveHighLevel3 == null || closesBelowLowLevel3 == null ? null : (closesAboveHighLevel3 - closesBelowLowLevel3) / Math.max(1, recent3.length);
8234
- const breakoutBodyAtr = safeDivide(Math.abs(candle.close - candle.open), atr2);
8235
- const priceVolumeProfile = buildPriceVolumeProfileContext(
8236
- structureWindow,
8237
- candle.close,
8238
- atr2
8239
- );
8240
- const volumeStructure = buildVolumeStructureContext(
8241
- candlesHistory,
8242
- candle.close,
8243
- atr2
8244
- );
8245
- const srZones = buildSrZonesContext(
8246
- structureWindow,
8247
- candle.close,
8248
- prevCandle?.close ?? null,
8249
- atr2
8250
- );
8251
- const liquidityZones = buildLiquidityZonesContext(
8252
- candlesHistory.slice(-180),
8253
- candle.close,
8254
- prevCandle?.close ?? null,
8255
- atr2
8256
- );
8257
- const liquidityTails = buildLiquidityTailsContext(
8258
- candlesHistory.slice(-180),
8259
- candle.close,
8260
- atr2
8261
- );
8262
- const trendFollow = buildTrendFollowContext(
8263
- candlesHistory.slice(-220),
8264
- candle.close,
8265
- atr2
8266
- );
8267
- const structureZonesContext = buildStructureZonesContext(
8268
- swingContext,
8269
- pivotContext,
8270
- candle.close,
8271
- atr2,
8272
- structureWindow
8273
- );
8274
- const hl2Series = precomputedMaLayers === void 0 ? candlesHistory.map((item) => (item.high + item.low) / 2) : [];
8275
- const maLayers = buildMaLayersContext(hl2Series, precomputedMaLayers);
8276
- const contextMa = buildContextMaContext(
8277
- closeSeries,
8278
- candle.close,
8279
- atr2,
8280
- precomputedContextMa
8281
- );
8282
- const adaptiveChannel = buildAdaptiveChannelContext(
8283
- candlesHistory,
8284
- candle.close,
8285
- atr2,
8286
- precomputedAdaptiveChannel
8287
- );
8288
- const deltaContext = buildDeltaContext(structureWindow);
8289
- const snapshot = {
8290
- candle,
8291
- prevCandle,
8292
- raw: {
8293
- trend: {
8294
- maFast: baseResult.maFast,
8295
- maMedium: baseResult.maMedium,
8296
- maSlow: baseResult.maSlow
8052
+ const buildRelativeSnapshot = () => {
8053
+ const spreadSeries = materializeNumericHistory(
8054
+ indicatorHistory.spread ?? createNumericHistoryBuffer()
8055
+ );
8056
+ const benchmarkMaFast = averageLastN(
8057
+ btcCloseSeries,
8058
+ indicatorPeriods.maFast
8059
+ );
8060
+ const benchmarkMaSlow = averageLastN(
8061
+ btcCloseSeries,
8062
+ indicatorPeriods.maSlow
8063
+ );
8064
+ const coin1h = coinResampledCandles.h1;
8065
+ const btc1h = btcResampledCandles.h1;
8066
+ const btc4h = btcResampledCandles.h4;
8067
+ const btc1d = btcResampledCandles.d1;
8068
+ const eth1h = ethResampledCandles?.h1 ?? [];
8069
+ const eth4h = ethResampledCandles?.h4 ?? [];
8070
+ const eth1d = ethResampledCandles?.d1 ?? [];
8071
+ const targetVsBtc = buildTargetVsBtcContext({
8072
+ coin1h,
8073
+ btc1h,
8074
+ coin4h,
8075
+ btc4h,
8076
+ coin1d,
8077
+ btc1d,
8078
+ coinCandles: candlesHistory,
8079
+ btcCandles: btcCandlesHistory
8080
+ });
8081
+ const targetVsEth = ethCandlesHistory.length >= 2 ? buildTargetVsEthContext({
8082
+ coin1h,
8083
+ eth1h,
8084
+ coin4h,
8085
+ eth4h,
8086
+ coin1d,
8087
+ eth1d,
8088
+ coinCandles: candlesHistory,
8089
+ ethCandles: ethCandlesHistory
8090
+ }) : null;
8091
+ const btcPsychologicalLevels = buildPsychologicalLevelAssetContext(
8092
+ btcCandlesHistory,
8093
+ 1e3
8094
+ );
8095
+ const ethPsychologicalLevels = buildPsychologicalLevelAssetContext(
8096
+ ethCandlesHistory,
8097
+ 100
8098
+ );
8099
+ const referencePsychologicalLevels = btcPsychologicalLevels != null || ethPsychologicalLevels != null ? {
8100
+ ...btcPsychologicalLevels != null ? { BTCUSDT: btcPsychologicalLevels } : {},
8101
+ ...ethPsychologicalLevels != null ? { ETHUSDT: ethPsychologicalLevels } : {}
8102
+ } : null;
8103
+ const relativeStrength1h = getRelativeChange(
8104
+ baseResult.price1hPcnt,
8105
+ btc1h.length >= 2 ? percentChange(
8106
+ btc1h[btc1h.length - 1].close,
8107
+ btc1h[Math.max(0, btc1h.length - 2)].close
8108
+ ) : null
8109
+ );
8110
+ const relativeStrength4h = getRelativeChange(
8111
+ coin4h.length >= 2 ? percentChange(
8112
+ coin4h[coin4h.length - 1].close,
8113
+ coin4h[coin4h.length - 2].close
8114
+ ) : null,
8115
+ btc4h.length >= 2 ? percentChange(
8116
+ btc4h[btc4h.length - 1].close,
8117
+ btc4h[btc4h.length - 2].close
8118
+ ) : null
8119
+ );
8120
+ const relativeStrength1d = getRelativeChange(
8121
+ coin1d.length >= 2 ? percentChange(
8122
+ coin1d[coin1d.length - 1].close,
8123
+ coin1d[coin1d.length - 2].close
8124
+ ) : null,
8125
+ btc1d.length >= 2 ? percentChange(
8126
+ btc1d[btc1d.length - 1].close,
8127
+ btc1d[btc1d.length - 2].close
8128
+ ) : null
8129
+ );
8130
+ const benchmarkBias = btc1h.length >= 2 ? btc1h[btc1h.length - 1].close > btc1h[btc1h.length - 2].close ? "bull" : btc1h[btc1h.length - 1].close < btc1h[btc1h.length - 2].close ? "bear" : "neutral" : "neutral";
8131
+ const trendAlignment = trendBias === "neutral" || benchmarkBias === "neutral" ? "neutral" : trendBias === benchmarkBias ? trendBias === "bull" ? "aligned_bull" : "aligned_bear" : "against_benchmark";
8132
+ const benchmarkTrendBias = benchmarkMaFast == null || benchmarkMaSlow == null ? "neutral" : benchmarkMaFast > benchmarkMaSlow ? "bull" : benchmarkMaFast < benchmarkMaSlow ? "bear" : "neutral";
8133
+ return {
8134
+ benchmark: {
8135
+ maFast: benchmarkMaFast,
8136
+ maSlow: benchmarkMaSlow,
8137
+ bias: benchmarkTrendBias,
8138
+ relativeStrength1h,
8139
+ relativeStrength4h,
8140
+ relativeStrength1d,
8141
+ trendAlignment
8297
8142
  },
8298
- volatility: {
8299
- atr: atr2,
8300
- atrPct: toNullable(baseResult.atrPct),
8301
- bbUpper: baseResult.bbUpper,
8302
- bbMiddle: baseResult.bbMiddle,
8303
- bbLower: baseResult.bbLower,
8304
- bbWidthPct
8143
+ execution: {
8144
+ venueSpread: baseResult.spread,
8145
+ venueSpreadZScore: calculateZScore(
8146
+ spreadSeries,
8147
+ toNullable(baseResult.spread)
8148
+ )
8305
8149
  },
8306
- momentum: {
8307
- macd: toNullable(baseResult.macd),
8308
- macdSignal: toNullable(baseResult.macdSignal),
8309
- macdHistogram: toNullable(baseResult.macdHistogram)
8150
+ targetVsBtc,
8151
+ ...targetVsEth != null ? { targetVsEth } : {},
8152
+ ...referencePsychologicalLevels != null ? { referencePsychologicalLevels } : {}
8153
+ };
8154
+ };
8155
+ const buildStructureSnapshot = () => {
8156
+ const structurePivots = detectConfirmedPivots(structureWindow, atr2);
8157
+ const structureZones = buildPriceZones(
8158
+ structureWindow,
8159
+ structurePivots,
8160
+ atr2
8161
+ );
8162
+ const swingContext = buildSwingContext(structurePivots);
8163
+ const pivotContext = buildPivotContext(
8164
+ structurePivots,
8165
+ structureWindow.length,
8166
+ atr2
8167
+ );
8168
+ const nearestSupport = getNearestZone(
8169
+ structureZones,
8170
+ "support",
8171
+ candle.close
8172
+ );
8173
+ const nearestResistance = getNearestZone(
8174
+ structureZones,
8175
+ "resistance",
8176
+ candle.close
8177
+ );
8178
+ const totalStructureVolume = structureWindow.reduce(
8179
+ (sum2, item) => sum2 + item.volume,
8180
+ 0
8181
+ );
8182
+ const priceInSupportZone = nearestSupport == null ? null : candle.close >= nearestSupport.lower && candle.close <= nearestSupport.upper;
8183
+ const priceInResistanceZone = nearestResistance == null ? null : candle.close >= nearestResistance.lower && candle.close <= nearestResistance.upper;
8184
+ const activeZoneType = priceInSupportZone ? "support" : priceInResistanceZone ? "resistance" : null;
8185
+ const priceInZone = priceInSupportZone == null && priceInResistanceZone == null ? null : Boolean(priceInSupportZone || priceInResistanceZone);
8186
+ const resistanceVolumeShare = safeDivide(
8187
+ nearestResistance?.volume ?? null,
8188
+ totalStructureVolume
8189
+ );
8190
+ const supportVolumeShare = safeDivide(
8191
+ nearestSupport?.volume ?? null,
8192
+ totalStructureVolume
8193
+ );
8194
+ const sweepState = nearestResistance == null && nearestSupport == null ? "unknown" : nearestResistance != null && candle.high > nearestResistance.upper && candle.close < nearestResistance.level ? "swept_high" : nearestSupport != null && candle.low < nearestSupport.lower && candle.close > nearestSupport.level ? "swept_low" : nearestResistance != null && candle.close > nearestResistance.upper ? "broken_high" : nearestSupport != null && candle.close < nearestSupport.lower ? "broken_low" : "none";
8195
+ const liquiditySide = sweepState === "swept_high" || sweepState === "broken_high" ? "high" : sweepState === "swept_low" || sweepState === "broken_low" ? "low" : null;
8196
+ const referenceZoneSide = liquiditySide === "high" ? "resistance" : liquiditySide === "low" ? "support" : null;
8197
+ const prior20High = prior20.length > 0 ? Math.max(...prior20.map((item) => item.high)) : null;
8198
+ const prior20Low = prior20.length > 0 ? Math.min(...prior20.map((item) => item.low)) : null;
8199
+ const sweepHigh20 = prior20High == null ? null : candle.high > prior20High && candle.close < prior20High;
8200
+ const sweepLow20 = prior20Low == null ? null : candle.low < prior20Low && candle.close > prior20Low;
8201
+ const closeBackInsideRange = sweepHigh20 == null && sweepLow20 == null ? null : Boolean(sweepHigh20 || sweepLow20);
8202
+ const stopRunDirection = sweepHigh20 ? "up" : sweepLow20 ? "down" : null;
8203
+ const sweepWickPct = stopRunDirection === "up" ? upperWick : stopRunDirection === "down" ? lowerWick : null;
8204
+ const recent3 = candlesHistory.slice(-3);
8205
+ const recent5 = candlesHistory.slice(-5);
8206
+ const closesAboveHighLevel3 = highLevel == null ? null : recent3.filter((item) => item.close > highLevel).length;
8207
+ const closesBelowLowLevel3 = lowLevel == null ? null : recent3.filter((item) => item.close < lowLevel).length;
8208
+ const failedAcceptanceBars = highLevel == null || lowLevel == null ? null : recent5.filter(
8209
+ (item) => item.high > highLevel && item.close <= highLevel || item.low < lowLevel && item.close >= lowLevel
8210
+ ).length;
8211
+ const acceptanceScore = closesAboveHighLevel3 == null || closesBelowLowLevel3 == null ? null : (closesAboveHighLevel3 - closesBelowLowLevel3) / Math.max(1, recent3.length);
8212
+ const breakoutBodyAtr = safeDivide(
8213
+ Math.abs(candle.close - candle.open),
8214
+ atr2
8215
+ );
8216
+ const srZones = buildSrZonesContext(
8217
+ structureWindow,
8218
+ candle.close,
8219
+ prevCandle?.close ?? null,
8220
+ atr2
8221
+ );
8222
+ const liquidityZones = buildLiquidityZonesContext(
8223
+ candlesHistory.slice(-180),
8224
+ candle.close,
8225
+ prevCandle?.close ?? null,
8226
+ atr2
8227
+ );
8228
+ const liquidityTails = buildLiquidityTailsContext(
8229
+ candlesHistory.slice(-180),
8230
+ candle.close,
8231
+ atr2
8232
+ );
8233
+ const structureZonesContext = buildStructureZonesContext(
8234
+ swingContext,
8235
+ pivotContext,
8236
+ candle.close,
8237
+ atr2,
8238
+ structureWindow
8239
+ );
8240
+ return {
8241
+ swing: swingContext,
8242
+ zones: {
8243
+ support: {
8244
+ level: nearestSupport?.level ?? null,
8245
+ lower: nearestSupport?.lower ?? null,
8246
+ upper: nearestSupport?.upper ?? null,
8247
+ touches: nearestSupport?.touches ?? null,
8248
+ ageBars: nearestSupport?.ageBars ?? null,
8249
+ volumeShare: supportVolumeShare,
8250
+ distanceAtr: safeDivide(
8251
+ nearestSupport == null ? null : candle.close - nearestSupport.level,
8252
+ atr2
8253
+ )
8254
+ },
8255
+ resistance: {
8256
+ level: nearestResistance?.level ?? null,
8257
+ lower: nearestResistance?.lower ?? null,
8258
+ upper: nearestResistance?.upper ?? null,
8259
+ touches: nearestResistance?.touches ?? null,
8260
+ ageBars: nearestResistance?.ageBars ?? null,
8261
+ volumeShare: resistanceVolumeShare,
8262
+ distanceAtr: safeDivide(
8263
+ nearestResistance == null ? null : nearestResistance.level - candle.close,
8264
+ atr2
8265
+ )
8266
+ },
8267
+ active: {
8268
+ side: activeZoneType,
8269
+ priceInZone
8270
+ }
8310
8271
  },
8311
- volume: {
8312
- volume: candle.volume,
8313
- turnover: candle.turnover,
8314
- obv: baseResult.obv,
8315
- obvSma: baseResult.smaObv,
8316
- volume1h: baseResult.volume1h,
8317
- volume24h: baseResult.volume24h
8272
+ srZones,
8273
+ liquidity: {
8274
+ sweepState,
8275
+ side: liquiditySide,
8276
+ referenceZoneSide,
8277
+ sweepHigh20,
8278
+ sweepLow20,
8279
+ closeBackInsideRange,
8280
+ stopRunDirection,
8281
+ sweepWickPct
8318
8282
  },
8319
- price: {
8320
- prevClose: baseResult.prevClose,
8321
- price1hPct: baseResult.price1hPcnt,
8322
- price24hPct: baseResult.price24hPcnt,
8323
- highPrice1h: baseResult.highPrice1h,
8324
- lowPrice1h: baseResult.lowPrice1h,
8325
- highPrice24h: baseResult.highPrice24h,
8326
- lowPrice24h: baseResult.lowPrice24h
8283
+ liquidityZones,
8284
+ liquidityTails,
8285
+ structureZones: structureZonesContext,
8286
+ pivots: pivotContext,
8287
+ acceptance: {
8288
+ closesAboveHighLevel3,
8289
+ closesBelowLowLevel3,
8290
+ failedAcceptanceBars,
8291
+ acceptanceScore,
8292
+ breakoutBodyAtr
8293
+ },
8294
+ localRange: {
8295
+ rangePosition20: calculateRangePosition(
8296
+ candle.close,
8297
+ recent20Low,
8298
+ recent20High
8299
+ ),
8300
+ distanceToHighLevelAtr,
8301
+ distanceToLowLevelAtr,
8302
+ breakoutState,
8303
+ barsSinceBreakout: breakoutRuntimeState.barsSinceBreakout,
8304
+ breakoutRetestQuality
8327
8305
  },
8328
8306
  levels: {
8329
- highLevel: baseResult.highLevel,
8330
- lowLevel: baseResult.lowLevel
8307
+ highTouchCount20,
8308
+ lowTouchCount20,
8309
+ dominantTouchCount20
8331
8310
  },
8332
- crossAsset: {
8333
- btcCorrelation: baseResult.correlation
8311
+ candleQuality: {
8312
+ upperWickPct: upperWick,
8313
+ lowerWickPct: lowerWick,
8314
+ rejectionWickScore
8334
8315
  }
8335
- },
8336
- regime: {
8316
+ };
8317
+ };
8318
+ const buildRegimeSnapshot = () => {
8319
+ const atrPctSeries = materializeNumericHistory(
8320
+ indicatorHistory.atrPct ?? createNumericHistoryBuffer()
8321
+ );
8322
+ const macdHistogramSeries = materializeNumericHistory(
8323
+ indicatorHistory.macdHistogram ?? createNumericHistoryBuffer()
8324
+ );
8325
+ const persistenceWindow = closeSeries.slice(-10);
8326
+ const directionalMoves = persistenceWindow.slice(1).map((value, index) => value - persistenceWindow[index]);
8327
+ const persistence = directionalMoves.length === 0 ? null : directionalMoves.filter(
8328
+ (delta) => trendBias === "bull" ? delta > 0 : trendBias === "bear" ? delta < 0 : delta === 0
8329
+ ).length / directionalMoves.length;
8330
+ const atrPctZScore = calculateZScore(
8331
+ atrPctSeries,
8332
+ toNullable(baseResult.atrPct)
8333
+ );
8334
+ const atrSlope = calculateLineSlope(atrPctSeries, 5);
8335
+ const compressionScore = safeDivide(
8336
+ toNullable(baseResult.atrPct),
8337
+ getLastFiniteValue(atrPctSeries.slice(0, -1))
8338
+ );
8339
+ const expansionScore = compressionScore == null || compressionScore === 0 ? null : 1 / compressionScore;
8340
+ const bbWidthPctSeries = calculateRecentBbWidthPctSeries(closeSeries, 100);
8341
+ const rawAtrPctSeries = calculateRecentAtrPctSeries(candlesHistory, 100);
8342
+ const rawAtrPct = safeDivide(atr2, candle.close);
8343
+ const realizedVolatility = calculateRealizedVolatility(closeSeries);
8344
+ const realizedVolatilitySeries = calculateRecentRealizedVolatilitySeries(
8345
+ closeSeries,
8346
+ 100
8347
+ );
8348
+ const rangeExpansionSeries = calculateRecentRangeExpansionSeries(
8349
+ candlesHistory,
8350
+ 20
8351
+ );
8352
+ const rangeExpansion = rangeExpansionSeries[rangeExpansionSeries.length - 1] ?? null;
8353
+ const volatilityState = compressionScore == null ? "unknown" : compressionScore <= 0.9 ? "compressed" : compressionScore >= 1.1 ? "expanded" : "normal";
8354
+ const bodyStrength = highLowRange > 0 ? Math.abs(candle.close - candle.open) / highLowRange : null;
8355
+ const closeLocationInRange = highLowRange > 0 ? (candle.close - candle.low) / highLowRange : null;
8356
+ const recentFalseBreakoutDensity = highLevel == null || lowLevel == null || recent20.length < 2 ? null : recent20.reduce((count, item, index) => {
8357
+ if (index === 0) return count;
8358
+ const prevItem = recent20[index - 1];
8359
+ if (!prevItem) return count;
8360
+ if (prevItem.close > highLevel && item.close <= highLevel) {
8361
+ return count + 1;
8362
+ }
8363
+ if (prevItem.close < lowLevel && item.close >= lowLevel) {
8364
+ return count + 1;
8365
+ }
8366
+ return count;
8367
+ }, 0) / (recent20.length - 1);
8368
+ const adxContext = buildAdxContext(adxValue) ?? calculateAdxContext(candlesHistory);
8369
+ const rsiContext = buildRsiContext(rsiValue) ?? calculateRsiContext(closeSeries);
8370
+ const trendFollow = buildTrendFollowContext(
8371
+ candlesHistory.slice(-220),
8372
+ candle.close,
8373
+ atr2
8374
+ );
8375
+ const hl2Series = precomputedMaLayers === void 0 ? candlesHistory.map((item) => (item.high + item.low) / 2) : [];
8376
+ const maLayers = buildMaLayersContext(hl2Series, precomputedMaLayers);
8377
+ const contextMa = buildContextMaContext(
8378
+ closeSeries,
8379
+ candle.close,
8380
+ atr2,
8381
+ precomputedContextMa
8382
+ );
8383
+ const adaptiveChannel = buildAdaptiveChannelContext(
8384
+ candlesHistory,
8385
+ candle.close,
8386
+ atr2,
8387
+ precomputedAdaptiveChannel
8388
+ );
8389
+ return {
8337
8390
  trend: {
8338
8391
  bias: trendBias,
8339
8392
  maStackScore,
@@ -8406,89 +8459,17 @@ var buildBaseContextSnapshot = ({
8406
8459
  upCloseStreak: closeStreaks.up,
8407
8460
  downCloseStreak: closeStreaks.down
8408
8461
  },
8409
- session,
8462
+ session: buildSessionContext(candle.timestamp),
8410
8463
  memory: {
8411
8464
  recentFalseBreakoutDensity
8412
8465
  }
8413
- },
8414
- structure: {
8415
- swing: swingContext,
8416
- zones: {
8417
- support: {
8418
- level: nearestSupport?.level ?? null,
8419
- lower: nearestSupport?.lower ?? null,
8420
- upper: nearestSupport?.upper ?? null,
8421
- touches: nearestSupport?.touches ?? null,
8422
- ageBars: nearestSupport?.ageBars ?? null,
8423
- volumeShare: supportVolumeShare,
8424
- distanceAtr: safeDivide(
8425
- nearestSupport == null ? null : candle.close - nearestSupport.level,
8426
- atr2
8427
- )
8428
- },
8429
- resistance: {
8430
- level: nearestResistance?.level ?? null,
8431
- lower: nearestResistance?.lower ?? null,
8432
- upper: nearestResistance?.upper ?? null,
8433
- touches: nearestResistance?.touches ?? null,
8434
- ageBars: nearestResistance?.ageBars ?? null,
8435
- volumeShare: resistanceVolumeShare,
8436
- distanceAtr: safeDivide(
8437
- nearestResistance == null ? null : nearestResistance.level - candle.close,
8438
- atr2
8439
- )
8440
- },
8441
- active: {
8442
- side: activeZoneType,
8443
- priceInZone
8444
- }
8445
- },
8446
- srZones,
8447
- liquidity: {
8448
- sweepState,
8449
- side: liquiditySide,
8450
- referenceZoneSide,
8451
- sweepHigh20,
8452
- sweepLow20,
8453
- closeBackInsideRange,
8454
- stopRunDirection,
8455
- sweepWickPct
8456
- },
8457
- liquidityZones,
8458
- liquidityTails,
8459
- structureZones: structureZonesContext,
8460
- pivots: pivotContext,
8461
- acceptance: {
8462
- closesAboveHighLevel3,
8463
- closesBelowLowLevel3,
8464
- failedAcceptanceBars,
8465
- acceptanceScore,
8466
- breakoutBodyAtr
8467
- },
8468
- localRange: {
8469
- rangePosition20: calculateRangePosition(
8470
- candle.close,
8471
- recent20Low,
8472
- recent20High
8473
- ),
8474
- distanceToHighLevelAtr,
8475
- distanceToLowLevelAtr,
8476
- breakoutState,
8477
- barsSinceBreakout: breakoutRuntimeState.barsSinceBreakout,
8478
- breakoutRetestQuality
8479
- },
8480
- levels: {
8481
- highTouchCount20,
8482
- lowTouchCount20,
8483
- dominantTouchCount20
8484
- },
8485
- candleQuality: {
8486
- upperWickPct: upperWick,
8487
- lowerWickPct: lowerWick,
8488
- rejectionWickScore
8489
- }
8490
- },
8491
- participation: {
8466
+ };
8467
+ };
8468
+ const buildParticipationSnapshot = () => {
8469
+ let cachedPriceVolumeProfile;
8470
+ let cachedVolumeStructure;
8471
+ let cachedDelta;
8472
+ return {
8492
8473
  volume: {
8493
8474
  volumeRel20,
8494
8475
  turnoverRel20,
@@ -8501,30 +8482,89 @@ var buildBaseContextSnapshot = ({
8501
8482
  ),
8502
8483
  effortVsResult
8503
8484
  },
8504
- priceVolumeProfile,
8505
- volumeStructure,
8506
- delta: deltaContext
8507
- },
8508
- relative: {
8509
- benchmark: {
8510
- maFast: benchmarkMaFast,
8511
- maSlow: benchmarkMaSlow,
8512
- bias: benchmarkTrendBias,
8513
- relativeStrength1h,
8514
- relativeStrength4h,
8515
- relativeStrength1d,
8516
- trendAlignment
8485
+ get priceVolumeProfile() {
8486
+ return cachedPriceVolumeProfile ??= buildPriceVolumeProfileContext(
8487
+ structureWindow,
8488
+ candle.close,
8489
+ atr2
8490
+ );
8517
8491
  },
8518
- execution: {
8519
- venueSpread: baseResult.spread,
8520
- venueSpreadZScore: calculateZScore(
8521
- spreadSeries,
8522
- toNullable(baseResult.spread)
8523
- )
8492
+ get volumeStructure() {
8493
+ return cachedVolumeStructure ??= buildVolumeStructureContext(
8494
+ candlesHistory,
8495
+ candle.close,
8496
+ atr2
8497
+ );
8524
8498
  },
8525
- targetVsBtc,
8526
- ...targetVsEth != null ? { targetVsEth } : {},
8527
- ...referencePsychologicalLevels != null ? { referencePsychologicalLevels } : {}
8499
+ get delta() {
8500
+ return cachedDelta ??= buildDeltaContext(
8501
+ structureWindow
8502
+ );
8503
+ }
8504
+ };
8505
+ };
8506
+ let cachedStructureSnapshot;
8507
+ let cachedRegimeSnapshot;
8508
+ let cachedParticipationSnapshot;
8509
+ let cachedRelativeSnapshot;
8510
+ const snapshot = {
8511
+ candle,
8512
+ prevCandle,
8513
+ raw: {
8514
+ trend: {
8515
+ maFast: baseResult.maFast,
8516
+ maMedium: baseResult.maMedium,
8517
+ maSlow: baseResult.maSlow
8518
+ },
8519
+ volatility: {
8520
+ atr: atr2,
8521
+ atrPct: toNullable(baseResult.atrPct),
8522
+ bbUpper: baseResult.bbUpper,
8523
+ bbMiddle: baseResult.bbMiddle,
8524
+ bbLower: baseResult.bbLower,
8525
+ bbWidthPct
8526
+ },
8527
+ momentum: {
8528
+ macd: toNullable(baseResult.macd),
8529
+ macdSignal: toNullable(baseResult.macdSignal),
8530
+ macdHistogram: toNullable(baseResult.macdHistogram)
8531
+ },
8532
+ volume: {
8533
+ volume: candle.volume,
8534
+ turnover: candle.turnover,
8535
+ obv: baseResult.obv,
8536
+ obvSma: baseResult.smaObv,
8537
+ volume1h: baseResult.volume1h,
8538
+ volume24h: baseResult.volume24h
8539
+ },
8540
+ price: {
8541
+ prevClose: baseResult.prevClose,
8542
+ price1hPct: baseResult.price1hPcnt,
8543
+ price24hPct: baseResult.price24hPcnt,
8544
+ highPrice1h: baseResult.highPrice1h,
8545
+ lowPrice1h: baseResult.lowPrice1h,
8546
+ highPrice24h: baseResult.highPrice24h,
8547
+ lowPrice24h: baseResult.lowPrice24h
8548
+ },
8549
+ levels: {
8550
+ highLevel: baseResult.highLevel,
8551
+ lowLevel: baseResult.lowLevel
8552
+ },
8553
+ crossAsset: {
8554
+ btcCorrelation: baseResult.correlation
8555
+ }
8556
+ },
8557
+ get regime() {
8558
+ return cachedRegimeSnapshot ??= buildRegimeSnapshot();
8559
+ },
8560
+ get structure() {
8561
+ return cachedStructureSnapshot ??= buildStructureSnapshot();
8562
+ },
8563
+ get participation() {
8564
+ return cachedParticipationSnapshot ??= buildParticipationSnapshot();
8565
+ },
8566
+ get relative() {
8567
+ return cachedRelativeSnapshot ??= buildRelativeSnapshot();
8528
8568
  }
8529
8569
  };
8530
8570
  let cachedMtfSnapshot = null;
@@ -10599,6 +10639,22 @@ var createIndicators = (data, btcData = [], options = {}) => {
10599
10639
  const last = value[value.length - 1];
10600
10640
  return typeof last === "number" ? last : void 0;
10601
10641
  },
10642
+ latestNumbers: (key, count) => {
10643
+ const normalizedCount = Number.isFinite(count) ? Math.max(0, Math.trunc(count)) : 0;
10644
+ if (normalizedCount === 0) return [];
10645
+ const buffer = indicatorHistory[key];
10646
+ if (buffer) {
10647
+ const resultSize = Math.min(normalizedCount, buffer.size);
10648
+ const result = new Array(resultSize);
10649
+ const firstOffset = buffer.size - resultSize;
10650
+ for (let index = 0; index < resultSize; index += 1) {
10651
+ result[index] = buffer.values[(buffer.start + firstOffset + index) % ML_BASE_CANDLES_WINDOW];
10652
+ }
10653
+ return result;
10654
+ }
10655
+ const value = getHistoryResult()[key];
10656
+ return Array.isArray(value) ? value.slice(-normalizedCount).filter((item) => typeof item === "number") : [];
10657
+ },
10602
10658
  result: () => {
10603
10659
  return cloneHistorySnapshot(
10604
10660
  getHistoryResult()
@@ -10938,7 +10994,8 @@ var createStrategyIndicatorsState = ({
10938
10994
  ensureInitializedWithCurrentBar: ensureControllerInitialized,
10939
10995
  snapshot: (options) => ensureControllerInitialized().snapshot(options),
10940
10996
  latestSnapshot: () => ensureControllerInitialized().latestSnapshot(),
10941
- latestNumber: (key) => ensureControllerInitialized().latestNumber(key)
10997
+ latestNumber: (key) => ensureControllerInitialized().latestNumber(key),
10998
+ latestNumbers: (key, count) => ensureControllerInitialized().latestNumbers(key, count)
10942
10999
  };
10943
11000
  };
10944
11001
 
@@ -11257,19 +11314,6 @@ var toRankBucket = (value) => {
11257
11314
  if (value <= 20) return "low";
11258
11315
  return "normal";
11259
11316
  };
11260
- var toRangePositionBucket = (value) => {
11261
- if (value == null) return "unknown";
11262
- if (value <= 0.2) return "low";
11263
- if (value >= 0.8) return "high";
11264
- return "middle";
11265
- };
11266
- var toVolumeBucket = (value) => {
11267
- if (value == null) return "unknown";
11268
- if (value < 0.8) return "thin";
11269
- if (value < 1.5) return "normal";
11270
- if (value < 3) return "elevated";
11271
- return "spike";
11272
- };
11273
11317
  var toVenueSpreadSeverity = (value) => {
11274
11318
  if (value == null) return "unknown";
11275
11319
  const abs = Math.abs(value);
@@ -11382,7 +11426,6 @@ var buildBaseContextGateFeatures = ({
11382
11426
  const atrPctZScore = asFiniteNumberOrNull(volatility?.atrPctZScore);
11383
11427
  const breakoutState = localRange?.breakoutState ?? "unknown";
11384
11428
  const rangePosition20 = asFiniteNumberOrNull(localRange?.rangePosition20);
11385
- const rangePositionBucket = toRangePositionBucket(rangePosition20);
11386
11429
  const breakoutWithDirection = toDirectionalAlignment({
11387
11430
  direction,
11388
11431
  bullValue: "above_high_level",
@@ -11501,22 +11544,12 @@ var buildBaseContextGateFeatures = ({
11501
11544
  targetVsBtc?.ratioReturn24h
11502
11545
  );
11503
11546
  const targetVsBtcAlpha24h = asFiniteNumberOrNull(targetVsBtc?.alphaVsBtc24h);
11504
- const targetVsBtcBeta20 = asFiniteNumberOrNull(targetVsBtc?.betaToBtc20);
11505
- const targetVsBtcCorrelation20 = asFiniteNumberOrNull(
11506
- targetVsBtc?.correlationToBtc20
11507
- );
11508
- const targetVsBtcRatioTrend = targetVsBtc?.ratioTrend ?? "unknown";
11509
11547
  const targetVsBtcDirectionValue = targetVsBtcRatioReturn24h ?? targetVsBtcAlpha24h;
11510
11548
  const targetVsBtcAligned = direction == null || targetVsBtcDirectionValue == null ? null : direction === "LONG" ? targetVsBtcDirectionValue >= 0 : targetVsBtcDirectionValue <= 0;
11511
11549
  const targetVsEthRatioReturn24h = asFiniteNumberOrNull(
11512
11550
  targetVsEth?.ratioReturn24h
11513
11551
  );
11514
11552
  const targetVsEthAlpha24h = asFiniteNumberOrNull(targetVsEth?.alphaVsEth24h);
11515
- const targetVsEthBeta20 = asFiniteNumberOrNull(targetVsEth?.betaToEth20);
11516
- const targetVsEthCorrelation20 = asFiniteNumberOrNull(
11517
- targetVsEth?.correlationToEth20
11518
- );
11519
- const targetVsEthRatioTrend = targetVsEth?.ratioTrend ?? "unknown";
11520
11553
  const targetVsEthDirectionValue = targetVsEthRatioReturn24h ?? targetVsEthAlpha24h;
11521
11554
  const targetVsEthAligned = direction == null || targetVsEthDirectionValue == null ? null : direction === "LONG" ? targetVsEthDirectionValue >= 0 : targetVsEthDirectionValue <= 0;
11522
11555
  const btcAltRegimeValue = btcAltRegime?.regime ?? "unknown";
@@ -11525,20 +11558,15 @@ var buildBaseContextGateFeatures = ({
11525
11558
  const btcVsAltReturn24h = asFiniteNumberOrNull(
11526
11559
  btcAltRegime?.btcVsAltReturn24h
11527
11560
  );
11528
- const btcTurnoverShare24h = asFiniteNumberOrNull(
11529
- btcAltRegime?.btcTurnoverShare24h
11530
- );
11531
11561
  const venueSpreadZScore = asFiniteNumberOrNull(execution?.venueSpreadZScore);
11532
11562
  const venueSpreadSeverity = toVenueSpreadSeverity(venueSpreadZScore);
11533
11563
  const higherTimeframeConflict = mtfAlignmentForDirection === "unknown" ? null : mtfAlignmentForDirection === "against" || mtfAlignmentForDirection === "mixed";
11534
11564
  const extremeVolatilityRisk = Math.abs(atrPctZScore ?? 0) >= 2 || atrPctRankBucket === "extreme";
11535
- const compressionBreakoutSupport = (volatility?.state === "compressed" || bbWidthRankBucket === "low") && breakoutState !== "inside_range" && breakoutState !== "unknown";
11536
11565
  const benchmarkConflict = benchmarkAligned === false || relativeStrengthBucket.endsWith("_against");
11537
11566
  const derivativesSummary = baseContext.derivatives?.summary;
11538
11567
  const derivativesDirectionAligned = typeof derivativesSummary?.directionAligned === "boolean" ? derivativesSummary.directionAligned : null;
11539
11568
  const derivativesRiskFlags = Array.isArray(derivativesSummary?.riskFlags) ? derivativesSummary.riskFlags : [];
11540
11569
  const derivativesCrowdedForDirection = direction === "LONG" ? derivativesRiskFlags.includes("crowded_long") : direction === "SHORT" ? derivativesRiskFlags.includes("crowded_short") : false;
11541
- const derivativesCrowdedAny = derivativesRiskFlags.includes("crowded_long") || derivativesRiskFlags.includes("crowded_short");
11542
11570
  const atr2 = asFiniteNumberOrNull(baseContext.raw?.volatility?.atr);
11543
11571
  const currentPrice = asFiniteNumberOrNull(prices?.currentPrice);
11544
11572
  const takeProfitPrice = asFiniteNumberOrNull(prices?.takeProfitPrice);
@@ -11698,142 +11726,57 @@ var buildBaseContextGateFeatures = ({
11698
11726
  scores.execution,
11699
11727
  scores.derivatives
11700
11728
  ]);
11701
- const volatilityRisk = extremeVolatilityRisk ? "high" : atrPctRankBucket === "high" || bbWidthRankBucket === "high" ? "medium" : atrPctRankBucket === "unknown" && bbWidthRankBucket === "unknown" ? "unknown" : "low";
11702
11729
  const liquidityRisk = venueSpreadSeverity === "wide" || cmcExchangeLiquidityAligned === false ? "high" : venueSpreadSeverity === "elevated" ? "medium" : venueSpreadSeverity === "unknown" && cmcExchangeLiquidityAligned == null ? "unknown" : "low";
11703
- const regimeRisk = higherTimeframeConflict === true ? "high" : mtfAlignmentForDirection === "neutral" || mtfAlignmentForDirection === "unknown" ? "medium" : "low";
11704
- const crowdingRisk = derivativesCrowdedForDirection ? "high" : derivativesCrowdedAny ? "medium" : derivativesSummary ? "low" : "unknown";
11705
- const chaseRisk = direction === "LONG" && rangePositionBucket === "high" && breakoutWithDirection !== true || direction === "SHORT" && rangePositionBucket === "low" && breakoutWithDirection !== true ? "high" : tpDistanceAtr != null && tpDistanceAtr < 1 || rangePositionBucket === "high" || rangePositionBucket === "low" ? "medium" : rangePositionBucket === "unknown" ? "unknown" : "low";
11706
11730
  const primaryIssue = derivativesCrowdedForDirection ? "crowded_derivatives" : higherTimeframeConflict === true ? "mtf_conflict" : venueSpreadSeverity === "wide" || cmcExchangeLiquidityAligned === false ? "bad_execution" : extremeVolatilityRisk ? "extreme_volatility" : benchmarkConflict || marketBreadthAligned === false || cmcAltLiquidityAligned === false || cmcEthBtcAligned === false || cmcFearGreedAligned === false || cmcIndexAligned === false || targetVsBtcAligned === false || targetVsEthAligned === false || btcAltRegimeAligned === false ? "market_context_against" : (scores.participation ?? 100) < 45 ? "weak_participation" : (scores.structure ?? 100) < 45 ? "weak_structure" : "none";
11707
- const needsExtraConfirmation = conflicts.length > 0 || scores.totalContext != null && scores.totalContext < 60;
11708
11731
  const approveBias = conflicts.length >= 3 || primaryIssue === "crowded_derivatives" || primaryIssue === "bad_execution" || primaryIssue === "mtf_conflict" ? "reject" : confirmations.length >= 3 && conflicts.length === 0 ? "support" : "neutral";
11709
- const maxReasonableQuality = approveBias === "reject" ? 2 : conflicts.length >= 2 || needsExtraConfirmation ? 3 : approveBias === "support" ? 5 : 4;
11710
11732
  return {
11711
- direction,
11712
11733
  setup: {
11713
- riskRatio: asFiniteNumberOrNull(prices?.riskRatio),
11714
11734
  rewardToVolatility: tpDistanceAtr,
11715
11735
  stopDistanceAtr,
11716
11736
  tpDistanceAtr,
11717
11737
  entryLocation
11718
11738
  },
11719
- scores,
11720
- confirmations: {
11721
- count: confirmations.length,
11722
- items: confirmations
11739
+ scores: {
11740
+ structure: scores.structure,
11741
+ participation: scores.participation,
11742
+ execution: scores.execution,
11743
+ totalContext: scores.totalContext
11723
11744
  },
11724
11745
  conflicts: {
11725
- count: conflicts.length,
11726
- items: conflicts
11746
+ count: conflicts.length
11727
11747
  },
11728
11748
  risk: {
11729
- regimeRisk,
11730
- liquidityRisk,
11731
- volatilityRisk,
11732
- crowdingRisk,
11733
- chaseRisk
11749
+ liquidityRisk
11734
11750
  },
11735
11751
  decisionHints: {
11736
11752
  approveBias,
11737
- maxReasonableQuality,
11738
- needsExtraConfirmation,
11739
11753
  primaryIssue
11740
11754
  },
11741
11755
  mtf: {
11742
- alignmentForDirection: mtfAlignmentForDirection,
11743
- higherTimeframeConflict,
11744
- h1TrendBias: mtfSummary?.h1TrendBias ?? "unknown",
11745
- h4TrendBias: mtfSummary?.h4TrendBias ?? "unknown",
11746
- d1TrendBias: mtfSummary?.d1TrendBias ?? "unknown",
11747
- h1RangePosition: asFiniteNumberOrNull(mtfSummary?.h1RangePosition),
11748
- h4VolatilityState: mtfSummary?.h4VolatilityState ?? "unknown"
11756
+ higherTimeframeConflict
11749
11757
  },
11750
11758
  volatility: {
11751
11759
  state: volatility?.state ?? "unknown",
11752
- atrPctZScore,
11753
11760
  atrPctRankBucket,
11754
- bbWidthRankBucket,
11755
- extremeVolatilityRisk,
11756
- compressionBreakoutSupport
11757
- },
11758
- structure: {
11759
- breakoutState,
11760
- rangePositionBucket,
11761
- breakoutWithDirection,
11762
- failedBreakoutForDirection,
11763
- liquiditySweepForDirection,
11764
- nearPointOfControl
11761
+ bbWidthRankBucket
11765
11762
  },
11766
11763
  participation: {
11767
- volumeRel20,
11768
- volumeBucket: toVolumeBucket(volumeRel20),
11769
- deltaBias,
11770
- deltaAligned,
11771
- tradeFlowBuyPressurePct,
11772
- tradeFlowAligned,
11773
- hyperliquidWhaleBuySharePct,
11774
- hyperliquidWhaleNetNotionalUsd,
11775
- hyperliquidWhaleUniqueCount,
11776
- hyperliquidWhaleCoveredCount,
11777
- hyperliquidWhaleExpectedCount,
11778
- hyperliquidWhaleCoveragePct,
11779
- hyperliquidWhaleCoverageSufficient,
11780
- hyperliquidWhaleNotionalUsd,
11781
- hyperliquidWhaleSufficientActivity,
11782
- hyperliquidWhaleFlowAligned,
11783
- hyperliquidWhaleFlowStale,
11784
- referenceTradeFlowBuyPressurePct,
11785
- referenceTradeFlowAligned,
11786
11764
  volumeStructureAligned
11787
11765
  },
11788
11766
  relative: {
11789
- benchmarkTrendAlignment,
11790
- benchmarkAligned,
11791
11767
  benchmarkConflict,
11792
- relativeStrength1h,
11793
- relativeStrengthBucket,
11794
11768
  marketBreadthReturn,
11795
- marketBreadthAligned,
11796
11769
  marketBreadthStale: typeof marketBreadth?.stale === "boolean" ? marketBreadth.stale : null,
11797
- cmcAltLiquidityRegime,
11798
- cmcAltLiquidityAligned,
11799
- cmcAltLiquidityStale,
11800
- cmcEthBtcReferenceRegime,
11801
- cmcEthBtcAligned,
11802
- cmcEthBtcStale,
11803
- cmcExchangeLiquidityRegime,
11804
11770
  cmcExchangeLiquidityAligned,
11805
11771
  cmcExchangeLiquidityStale,
11806
11772
  cmcExchangeLiquidityVolumeChange24hPct,
11807
11773
  cmcFearGreedValue,
11808
11774
  cmcFearGreedValueChange24h,
11809
- cmcFearGreedRegime,
11810
- cmcFearGreedAligned,
11811
11775
  cmcFearGreedStale,
11812
- cmcIndexRegime,
11813
- cmcIndexAligned,
11814
- cmcIndexStale,
11815
11776
  cmc20ToCmc100RatioChange24hPct,
11816
- targetVsBtcRatioReturn24h,
11817
- targetVsBtcAlpha24h,
11818
- targetVsBtcBeta20,
11819
- targetVsBtcCorrelation20,
11820
- targetVsBtcRatioTrend,
11821
- targetVsBtcAligned,
11822
- targetVsEthRatioReturn24h,
11823
- targetVsEthAlpha24h,
11824
- targetVsEthBeta20,
11825
- targetVsEthCorrelation20,
11826
- targetVsEthRatioTrend,
11827
- targetVsEthAligned,
11828
11777
  btcAltRegime: btcAltRegimeValue,
11829
- btcAltRegimeAligned,
11830
11778
  btcAltRegimeStale,
11831
- btcVsAltReturn24h,
11832
- btcTurnoverShare24h
11833
- },
11834
- execution: {
11835
- venueSpreadZScore,
11836
- venueSpreadSeverity
11779
+ btcVsAltReturn24h
11837
11780
  }
11838
11781
  };
11839
11782
  };
@@ -11841,6 +11784,40 @@ var cloneBaseContextData = (baseContext, direction, prices) => {
11841
11784
  const clone = cloneSignalPayloadDataProperties(
11842
11785
  baseContext
11843
11786
  );
11787
+ clone.regime = cloneSignalPayloadDataProperties(
11788
+ baseContext.regime
11789
+ );
11790
+ clone.structure = cloneSignalPayloadDataProperties(
11791
+ baseContext.structure
11792
+ );
11793
+ const participation = baseContext.participation;
11794
+ if (participation) {
11795
+ const participationClone = cloneSignalPayloadDataProperties(
11796
+ participation
11797
+ );
11798
+ const priceVolumeProfile = participation.priceVolumeProfile;
11799
+ const volumeStructure = participation.volumeStructure;
11800
+ const delta = participation.delta;
11801
+ if (priceVolumeProfile != null) {
11802
+ participationClone.priceVolumeProfile = cloneSignalPayloadDataProperties(
11803
+ priceVolumeProfile
11804
+ );
11805
+ }
11806
+ if (volumeStructure != null) {
11807
+ participationClone.volumeStructure = cloneSignalPayloadDataProperties(
11808
+ volumeStructure
11809
+ );
11810
+ }
11811
+ if (delta != null) {
11812
+ participationClone.delta = cloneSignalPayloadDataProperties(
11813
+ delta
11814
+ );
11815
+ }
11816
+ clone.participation = participationClone;
11817
+ }
11818
+ clone.relative = cloneSignalPayloadDataProperties(
11819
+ baseContext.relative
11820
+ );
11844
11821
  const compactMtf = cloneCompactMtfContext(baseContext);
11845
11822
  if (compactMtf) {
11846
11823
  clone.mtf = compactMtf;