added RSI crossover signals: BUY when crosses up oversold, SELL when crosses down overbought
This commit is contained in:
@ -32,8 +32,9 @@ class BaseIndicator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Signal calculation for RSI
|
// Signal calculation for RSI
|
||||||
function calculateRSISignal(indicator, lastCandle, prevCandle, values) {
|
function calculateRSISignal(indicator, lastCandle, prevCandle, values, prevValues) {
|
||||||
const rsi = values?.rsi;
|
const rsi = values?.rsi;
|
||||||
|
const prevRsi = prevValues?.rsi;
|
||||||
const overbought = indicator.params?.overbought || 70;
|
const overbought = indicator.params?.overbought || 70;
|
||||||
const oversold = indicator.params?.oversold || 30;
|
const oversold = indicator.params?.oversold || 30;
|
||||||
|
|
||||||
@ -43,13 +44,30 @@ function calculateRSISignal(indicator, lastCandle, prevCandle, values) {
|
|||||||
|
|
||||||
let signalType, strength, reasoning;
|
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;
|
signalType = SIGNAL_TYPES.BUY;
|
||||||
strength = Math.min(50 + (oversold - rsi) * 2, 100);
|
strength = Math.min(50 + (rsi - oversold) * 2, 100);
|
||||||
reasoning = `RSI (${rsi.toFixed(2)}) is oversold (<${oversold})`;
|
reasoning = `RSI (${rsi.toFixed(2)}) crossed up through oversold level (${oversold})`;
|
||||||
} else if (rsi > overbought) {
|
}
|
||||||
|
// 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;
|
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})`;
|
reasoning = `RSI (${rsi.toFixed(2)}) is overbought (>${overbought})`;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -4,13 +4,14 @@
|
|||||||
import { IndicatorRegistry, getSignalFunction } from '../indicators/index.js';
|
import { IndicatorRegistry, getSignalFunction } from '../indicators/index.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate signal for a single indicator using its signal function
|
* Calculate signal for an indicator
|
||||||
* @param {Object} indicator - Indicator object with type, params, etc.
|
* @param {Object} indicator - Indicator configuration
|
||||||
* @param {Array} candles - Recent candle data
|
* @param {Array} candles - Candle data array
|
||||||
* @param {Object} indicatorValues - Computed indicator values for last candle
|
* @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
|
* @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);
|
const signalFunction = getSignalFunction(indicator.type);
|
||||||
|
|
||||||
if (!signalFunction) {
|
if (!signalFunction) {
|
||||||
@ -21,6 +22,12 @@ function calculateIndicatorSignal(indicator, candles, indicatorValues) {
|
|||||||
const lastCandle = candles[candles.length - 1];
|
const lastCandle = candles[candles.length - 1];
|
||||||
const prevCandle = candles[candles.length - 2];
|
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);
|
return signalFunction(indicator, lastCandle, prevCandle, indicatorValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,22 +223,26 @@ export function calculateAllIndicatorSignals() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const lastResult = results[results.length - 1];
|
const lastResult = results[results.length - 1];
|
||||||
|
const prevResult = results[results.length - 2];
|
||||||
if (lastResult === null || lastResult === undefined) {
|
if (lastResult === null || lastResult === undefined) {
|
||||||
console.log('[Signals] No valid last result for indicator:', indicator.type);
|
console.log('[Signals] No valid last result for indicator:', indicator.type);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let values;
|
let values;
|
||||||
|
let prevValues;
|
||||||
if (typeof lastResult === 'object' && lastResult !== null && !Array.isArray(lastResult)) {
|
if (typeof lastResult === 'object' && lastResult !== null && !Array.isArray(lastResult)) {
|
||||||
values = lastResult;
|
values = lastResult;
|
||||||
|
prevValues = prevResult;
|
||||||
} else if (typeof lastResult === 'number') {
|
} else if (typeof lastResult === 'number') {
|
||||||
values = { ma: lastResult };
|
values = { ma: lastResult };
|
||||||
|
prevValues = prevResult ? { ma: prevResult } : undefined;
|
||||||
} else {
|
} else {
|
||||||
console.log('[Signals] Unexpected result type for', indicator.type, ':', typeof lastResult);
|
console.log('[Signals] Unexpected result type for', indicator.type, ':', typeof lastResult);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const signal = calculateIndicatorSignal(indicator, candles, values);
|
const signal = calculateIndicatorSignal(indicator, candles, values, prevValues);
|
||||||
|
|
||||||
let currentSignal = signal;
|
let currentSignal = signal;
|
||||||
let lastSignalDate = indicator.lastSignalTimestamp || null;
|
let lastSignalDate = indicator.lastSignalTimestamp || null;
|
||||||
|
|||||||
Reference in New Issue
Block a user