simplified crossover detection: find first candle where price crosses MA

This commit is contained in:
DiTus
2026-03-01 22:16:12 +01:00
parent 12689ad8f3
commit af79268621

View File

@ -94,9 +94,6 @@ export function calculateSummarySignal(signals) {
function calculateHistoricalCrossovers(activeIndicators, candles) { function calculateHistoricalCrossovers(activeIndicators, candles) {
activeIndicators.forEach(indicator => { activeIndicators.forEach(indicator => {
const indicatorType = indicator.type || indicator.indicatorType; const indicatorType = indicator.type || indicator.indicatorType;
const SignalFunction = getSignalFunction(indicatorType);
if (!SignalFunction) return;
// Recalculate indicator values for all candles // Recalculate indicator values for all candles
const IndicatorClass = IndicatorRegistry[indicatorType]; const IndicatorClass = IndicatorRegistry[indicatorType];
@ -107,46 +104,43 @@ function calculateHistoricalCrossovers(activeIndicators, candles) {
if (!results || results.length === 0) return; if (!results || results.length === 0) return;
// Track the last crossover timestamp // Find the most recent crossover by going backwards from the newest candle
let lastCrossoverTimestamp = indicator.lastSignalTimestamp || null; // candles are sorted oldest first, newest last
let crossoverCount = 0; let lastCrossoverTimestamp = null;
// Iterate through candles to find crossovers (from newest to oldest) for (let i = candles.length - 1; i > 0; i--) {
// We start from the end and go backwards to find the most recent crossover const candle = candles[i]; // newer candle
for (let i = candles.length - 2; i >= 0; i--) { const prevCandle = candles[i-1]; // older candle
// Skip if we don't have data for these candles
if (!results[i] || !results[i + 1]) continue;
const candle = candles[i]; const result = results[i];
const prevCandle = candles[i + 1]; const prevResult = results[i-1];
const valuesThis = typeof results[i] === 'object' ? results[i] : { ma: results[i] }; if (!result || !prevResult) continue;
const valuesPrev = typeof results[i + 1] === 'object' ? results[i + 1] : { ma: results[i + 1] };
const closeThis = candles[i].close; // Get MA value (handle both object and number formats)
const closePrev = candles[i + 1].close; const ma = result.ma !== undefined ? result.ma : result;
const maThis = valuesThis.ma; const prevMa = prevResult.ma !== undefined ? prevResult.ma : prevResult;
const maPrev = valuesPrev.ma;
// Check for BUY→SELL crossover (was above, now below) if (ma === undefined || prevMa === undefined) continue;
if (closePrev > maPrev && closeThis < maThis) {
console.log(`[HistoricalCross] ${indicatorType} BUY→SELL crossover at candle ${i}, time: ${prevCandle.time}`); // Check crossover: price was on one side of MA, now on the other side
lastCrossoverTimestamp = prevCandle.time; const priceAbovePrev = prevCandle.close > prevMa;
crossoverCount++; const priceAboveNow = candle.close > ma;
break; // Found most recent crossover
// SELL signal: price crossed from above to below MA
if (priceAbovePrev && !priceAboveNow) {
lastCrossoverTimestamp = candle.time;
break;
} }
// Check for SELL→BUY crossover (was below, now above) // BUY signal: price crossed from below to above MA
else if (closePrev < maPrev && closeThis > maThis) { if (!priceAbovePrev && priceAboveNow) {
console.log(`[HistoricalCross] ${indicatorType} SELL→BUY crossover at candle ${i}, time: ${prevCandle.time}`); lastCrossoverTimestamp = candle.time;
lastCrossoverTimestamp = prevCandle.time; break;
crossoverCount++;
break; // Found most recent crossover
} }
} }
if (crossoverCount > 0) { if (lastCrossoverTimestamp) {
console.log(`[HistoricalCross] ${indicatorType}: Found crossover at ${new Date(lastCrossoverTimestamp * 1000).toLocaleString()}`); console.log(`[HistoricalCross] ${indicatorType}: Found crossover at ${new Date(lastCrossoverTimestamp * 1000).toLocaleString()}`);
// Update the indicator's lastSignalTimestamp
indicator.lastSignalTimestamp = lastCrossoverTimestamp; indicator.lastSignalTimestamp = lastCrossoverTimestamp;
} }
}); });