diff --git a/src/api/dashboard/static/js/indicators/rsi.js b/src/api/dashboard/static/js/indicators/rsi.js index 69c2f92..3f42337 100644 --- a/src/api/dashboard/static/js/indicators/rsi.js +++ b/src/api/dashboard/static/js/indicators/rsi.js @@ -32,8 +32,9 @@ class BaseIndicator { } // Signal calculation for RSI -function calculateRSISignal(indicator, lastCandle, prevCandle, values) { +function calculateRSISignal(indicator, lastCandle, prevCandle, values, prevValues) { const rsi = values?.rsi; + const prevRsi = prevValues?.rsi; const overbought = indicator.params?.overbought || 70; const oversold = indicator.params?.oversold || 30; @@ -43,13 +44,30 @@ function calculateRSISignal(indicator, lastCandle, prevCandle, values) { let signalType, strength, reasoning; - if (rsi < oversold) { + // BUY when RSI crosses UP through oversold band (bottom band) + // RSI was below oversold, now above oversold + if (prevRsi !== undefined && prevRsi !== null && prevRsi < oversold && rsi >= oversold) { signalType = SIGNAL_TYPES.BUY; - strength = Math.min(50 + (oversold - rsi) * 2, 100); - reasoning = `RSI (${rsi.toFixed(2)}) is oversold (<${oversold})`; - } else if (rsi > overbought) { + strength = Math.min(50 + (rsi - oversold) * 2, 100); + reasoning = `RSI (${rsi.toFixed(2)}) crossed up through oversold level (${oversold})`; + } + // SELL when RSI crosses DOWN through overbought band (top band) + // RSI was above overbought, now below overbought + else if (prevRsi !== undefined && prevRsi !== null && prevRsi > overbought && rsi <= overbought) { signalType = SIGNAL_TYPES.SELL; - strength = Math.min(50 + (rsi - overbought) * 2, 100); + strength = Math.min(50 + (overbought - rsi) * 2, 100); + reasoning = `RSI (${rsi.toFixed(2)}) crossed down through overbought level (${overbought})`; + } + // When RSI is in oversold territory but no crossover - strong BUY + else if (rsi < oversold) { + signalType = SIGNAL_TYPES.BUY; + strength = Math.min(40 + (oversold - rsi) * 1.5, 80); + reasoning = `RSI (${rsi.toFixed(2)}) is oversold (<${oversold})`; + } + // When RSI is in overbought territory but no crossover - strong SELL + else if (rsi > overbought) { + signalType = SIGNAL_TYPES.SELL; + strength = Math.min(40 + (rsi - overbought) * 1.5, 80); reasoning = `RSI (${rsi.toFixed(2)}) is overbought (>${overbought})`; } else { return null; diff --git a/src/api/dashboard/static/js/ui/signals-calculator.js b/src/api/dashboard/static/js/ui/signals-calculator.js index b69f805..29addd8 100644 --- a/src/api/dashboard/static/js/ui/signals-calculator.js +++ b/src/api/dashboard/static/js/ui/signals-calculator.js @@ -4,13 +4,14 @@ import { IndicatorRegistry, getSignalFunction } from '../indicators/index.js'; /** - * Calculate signal for a single indicator using its signal function - * @param {Object} indicator - Indicator object with type, params, etc. - * @param {Array} candles - Recent candle data + * Calculate signal for an indicator + * @param {Object} indicator - Indicator configuration + * @param {Array} candles - Candle data array * @param {Object} indicatorValues - Computed indicator values for last candle + * @param {Object} prevIndicatorValues - Computed indicator values for previous candle * @returns {Object} Signal object with type, strength, value, reasoning */ -function calculateIndicatorSignal(indicator, candles, indicatorValues) { +function calculateIndicatorSignal(indicator, candles, indicatorValues, prevIndicatorValues) { const signalFunction = getSignalFunction(indicator.type); if (!signalFunction) { @@ -21,6 +22,12 @@ function calculateIndicatorSignal(indicator, candles, indicatorValues) { const lastCandle = candles[candles.length - 1]; const prevCandle = candles[candles.length - 2]; + return signalFunction(indicator, lastCandle, prevCandle, indicatorValues, prevIndicatorValues); +} + + const lastCandle = candles[candles.length - 1]; + const prevCandle = candles[candles.length - 2]; + return signalFunction(indicator, lastCandle, prevCandle, indicatorValues); } @@ -216,22 +223,26 @@ export function calculateAllIndicatorSignals() { } const lastResult = results[results.length - 1]; + const prevResult = results[results.length - 2]; if (lastResult === null || lastResult === undefined) { console.log('[Signals] No valid last result for indicator:', indicator.type); continue; } let values; + let prevValues; if (typeof lastResult === 'object' && lastResult !== null && !Array.isArray(lastResult)) { values = lastResult; + prevValues = prevResult; } else if (typeof lastResult === 'number') { values = { ma: lastResult }; + prevValues = prevResult ? { ma: prevResult } : undefined; } else { console.log('[Signals] Unexpected result type for', indicator.type, ':', typeof lastResult); continue; } - const signal = calculateIndicatorSignal(indicator, candles, values); + const signal = calculateIndicatorSignal(indicator, candles, values, prevValues); let currentSignal = signal; let lastSignalDate = indicator.lastSignalTimestamp || null;