Fix: Signal date tracking and indicator real-time updates
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
This commit is contained in:
@ -1,5 +1,6 @@
|
|||||||
import { INTERVALS, COLORS } from '../core/index.js';
|
import { INTERVALS, COLORS } from '../core/index.js';
|
||||||
import { calculateAllIndicatorSignals, calculateSummarySignal } from './signals-calculator.js';
|
import { calculateAllIndicatorSignals, calculateSummarySignal } from './signals-calculator.js';
|
||||||
|
import { updateIndicatorCandles } from './indicators-panel-new.js';
|
||||||
|
|
||||||
function formatDate(timestamp) {
|
function formatDate(timestamp) {
|
||||||
const date = new Date(timestamp);
|
const date = new Date(timestamp);
|
||||||
@ -298,15 +299,21 @@ constructor() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
clearIndicatorCaches() {
|
clearIndicatorCaches(clearSignalState = false) {
|
||||||
const activeIndicators = window.getActiveIndicators?.() || [];
|
const activeIndicators = window.getActiveIndicators?.() || [];
|
||||||
activeIndicators.forEach(indicator => {
|
activeIndicators.forEach(indicator => {
|
||||||
|
// Always clear calculation caches
|
||||||
indicator.cachedResults = null;
|
indicator.cachedResults = null;
|
||||||
indicator.cachedMeta = null;
|
indicator.cachedMeta = 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.lastSignalTimestamp = null;
|
||||||
indicator.lastSignalType = 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() {
|
async loadInitialData() {
|
||||||
@ -396,8 +403,8 @@ async loadNewData() {
|
|||||||
|
|
||||||
if (isNewCandle) {
|
if (isNewCandle) {
|
||||||
console.log(`[NewData Load] New candle detected: ${this.lastCandleTimestamp} -> ${latest.time}`);
|
console.log(`[NewData Load] New candle detected: ${this.lastCandleTimestamp} -> ${latest.time}`);
|
||||||
// Clear indicator caches to force recalculation
|
// Clear indicator caches but preserve signal state
|
||||||
this.clearIndicatorCaches();
|
this.clearIndicatorCaches(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.lastCandleTimestamp = latest.time;
|
this.lastCandleTimestamp = latest.time;
|
||||||
@ -419,10 +426,11 @@ async loadNewData() {
|
|||||||
|
|
||||||
this.updateStats(latest);
|
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) {
|
if (window.drawIndicatorsOnChart) {
|
||||||
window.drawIndicatorsOnChart();
|
window.drawIndicatorsOnChart();
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.loadSignals();
|
await this.loadSignals();
|
||||||
|
|
||||||
// Refresh chart if new candle detected
|
// Refresh chart if new candle detected
|
||||||
@ -715,8 +723,8 @@ switchTimeframe(interval) {
|
|||||||
btn.classList.toggle('active', btn.dataset.interval === interval);
|
btn.classList.toggle('active', btn.dataset.interval === interval);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Clear indicator caches before switching timeframe
|
// Clear indicator caches and signal state before switching timeframe
|
||||||
this.clearIndicatorCaches();
|
this.clearIndicatorCaches(true);
|
||||||
|
|
||||||
// Clear old interval data, not new interval
|
// Clear old interval data, not new interval
|
||||||
this.allData.delete(oldInterval);
|
this.allData.delete(oldInterval);
|
||||||
|
|||||||
@ -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
|
// Chart drawing
|
||||||
export function drawIndicatorsOnChart() {
|
export function drawIndicatorsOnChart() {
|
||||||
if (!window.dashboard || !window.dashboard.chart) {
|
if (!window.dashboard || !window.dashboard.chart) {
|
||||||
|
|||||||
Reference in New Issue
Block a user