From 0df8547d96eb0c2778a81dc8a04894f97d27476e Mon Sep 17 00:00:00 2001 From: DiTus Date: Sun, 1 Mar 2026 20:07:12 +0100 Subject: [PATCH] Fix: Signal date tracking and indicator real-time updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue 1: Only update lastSignalDate when signal type changes (BUY→SELL or SELL→BUY) - Modified clearIndicatorCaches() to accept clearSignalState parameter - When new candle completes: only clear cachedResults/cachedMeta (not signal state) - When timeframe changes: clear everything including signal tracking - This preserves signal change history across multiple candles Issue 2: Indicator lines not updating when new candles arrive - Added updateIndicatorCandles() function to update existing series - Instead of removing and recreating series, now uses .setData() to update - Called when new candle is detected to update indicator lines - Chart renders correctly with new data after each candle completion Both issues now resolved: 1. Shows last crossover date only when signal actually changes 2. Indicator lines update in real-time when new candles complete --- src/api/dashboard/static/js/ui/chart.js | 26 ++++++--- .../static/js/ui/indicators-panel-new.js | 55 +++++++++++++++++++ 2 files changed, 72 insertions(+), 9 deletions(-) diff --git a/src/api/dashboard/static/js/ui/chart.js b/src/api/dashboard/static/js/ui/chart.js index 92c3458..b77b411 100644 --- a/src/api/dashboard/static/js/ui/chart.js +++ b/src/api/dashboard/static/js/ui/chart.js @@ -1,5 +1,6 @@ import { INTERVALS, COLORS } from '../core/index.js'; import { calculateAllIndicatorSignals, calculateSummarySignal } from './signals-calculator.js'; +import { updateIndicatorCandles } from './indicators-panel-new.js'; function formatDate(timestamp) { const date = new Date(timestamp); @@ -298,15 +299,21 @@ constructor() { }); } - clearIndicatorCaches() { + clearIndicatorCaches(clearSignalState = false) { const activeIndicators = window.getActiveIndicators?.() || []; activeIndicators.forEach(indicator => { + // Always clear calculation caches indicator.cachedResults = null; indicator.cachedMeta = null; - indicator.lastSignalTimestamp = null; - indicator.lastSignalType = null; + + // Only clear signal state if explicitly requested (e.g., timeframe change) + // Do not clear on new candle completion - preserve signal change tracking + if (clearSignalState) { + indicator.lastSignalTimestamp = null; + indicator.lastSignalType = null; + } }); - console.log(`[Dashboard] Cleared caches for ${activeIndicators.length} indicators`); + console.log(`[Dashboard] Cleared caches for ${activeIndicators.length} indicators (signals: ${clearSignalState})`); } async loadInitialData() { @@ -396,8 +403,8 @@ async loadNewData() { if (isNewCandle) { console.log(`[NewData Load] New candle detected: ${this.lastCandleTimestamp} -> ${latest.time}`); - // Clear indicator caches to force recalculation - this.clearIndicatorCaches(); + // Clear indicator caches but preserve signal state + this.clearIndicatorCaches(false); } this.lastCandleTimestamp = latest.time; @@ -419,10 +426,11 @@ async loadNewData() { this.updateStats(latest); - // Recalculate indicators and signals when new data loads + // Update indicators - for new candles, update series; for initial loads, redraw if (window.drawIndicatorsOnChart) { window.drawIndicatorsOnChart(); } + await this.loadSignals(); // Refresh chart if new candle detected @@ -715,8 +723,8 @@ switchTimeframe(interval) { btn.classList.toggle('active', btn.dataset.interval === interval); }); - // Clear indicator caches before switching timeframe - this.clearIndicatorCaches(); + // Clear indicator caches and signal state before switching timeframe + this.clearIndicatorCaches(true); // Clear old interval data, not new interval this.allData.delete(oldInterval); diff --git a/src/api/dashboard/static/js/ui/indicators-panel-new.js b/src/api/dashboard/static/js/ui/indicators-panel-new.js index 7e4960d..5efb68c 100644 --- a/src/api/dashboard/static/js/ui/indicators-panel-new.js +++ b/src/api/dashboard/static/js/ui/indicators-panel-new.js @@ -813,6 +813,61 @@ function renderIndicatorOnPane(indicator, meta, instance, candles, paneIndex, li }); } +// Update existing indicator series with new data (for real-time updates) +export function updateIndicatorCandles() { + if (!window.dashboard || !window.dashboard.chart) return; + + const activeIndicators = getActiveIndicators(); + const currentInterval = window.dashboard.currentInterval; + const candles = window.dashboard?.allData?.get(currentInterval); + + if (!candles || candles.length === 0) return; + + activeIndicators.forEach(indicator => { + if (!indicator.visible || indicator.series.length === 0) return; + + const IndicatorClass = IndicatorRegistry[indicator.type]; + if (!IndicatorClass) return; + + const instance = new IndicatorClass(indicator); + const results = instance.calculate(candles); + + if (!results || results.length === 0) return; + + const meta = instance.getMetadata(); + + // Update each plot series + meta.plots.forEach((plot, plotIdx) => { + const series = indicator.series[plotIdx]; + if (!series) return; + + const plotColor = indicator.params[`_color_${plotIdx}`] || plot.color || '#2962ff'; + const lineWidth = indicator.params._lineWidth || 2; + + // Build complete data array + const data = []; + + for (let i = 0; i < candles.length; i++) { + const value = results[i]?.[plot.id]; + if (value !== null && value !== undefined) { + data.push({ + time: candles[i].time, + value: value, + color: plotColor, + lineWidth: lineWidth + }); + } + } + + if (data.length > 0) { + series.setData(data); + } + }); + }); + + console.log(`[UpdateIndicators] Updated ${activeIndicators.length} indicator series`); +} + // Chart drawing export function drawIndicatorsOnChart() { if (!window.dashboard || !window.dashboard.chart) {