feat: implement persistence for settings and indicators
This commit is contained in:
@ -140,7 +140,10 @@ export class TradingDashboard {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.chart = null;
|
this.chart = null;
|
||||||
this.candleSeries = null;
|
this.candleSeries = null;
|
||||||
this.currentInterval = '1d';
|
// Load settings from local storage or defaults
|
||||||
|
this.symbol = localStorage.getItem('winterfail_symbol') || 'BTC';
|
||||||
|
this.currentInterval = localStorage.getItem('winterfail_interval') || '1d';
|
||||||
|
|
||||||
this.intervals = INTERVALS;
|
this.intervals = INTERVALS;
|
||||||
this.allData = new Map();
|
this.allData = new Map();
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
@ -164,7 +167,7 @@ constructor() {
|
|||||||
let candles = this.allData.get(interval);
|
let candles = this.allData.get(interval);
|
||||||
|
|
||||||
if (!candles || candles.length < 125) {
|
if (!candles || candles.length < 125) {
|
||||||
const response = await fetch(`${window.APP_CONFIG.API_BASE_URL}/candles?symbol=BTC&interval=${interval}&limit=1000`);
|
const response = await fetch(`${window.APP_CONFIG.API_BASE_URL}/candles?symbol=${this.symbol}&interval=${interval}&limit=1000`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (data.candles && data.candles.length > 0) {
|
if (data.candles && data.candles.length > 0) {
|
||||||
candles = data.candles.reverse().map(c => ({
|
candles = data.candles.reverse().map(c => ({
|
||||||
@ -1044,6 +1047,7 @@ switchTimeframe(interval) {
|
|||||||
|
|
||||||
const oldInterval = this.currentInterval;
|
const oldInterval = this.currentInterval;
|
||||||
this.currentInterval = interval;
|
this.currentInterval = interval;
|
||||||
|
localStorage.setItem('winterfail_interval', interval); // Save setting
|
||||||
this.hasInitialLoad = false;
|
this.hasInitialLoad = false;
|
||||||
|
|
||||||
document.querySelectorAll('.timeframe-btn').forEach(btn => {
|
document.querySelectorAll('.timeframe-btn').forEach(btn => {
|
||||||
|
|||||||
@ -3,6 +3,72 @@ import { getAvailableIndicators, IndicatorRegistry as IR } from '../indicators/i
|
|||||||
// State management
|
// State management
|
||||||
let activeIndicators = [];
|
let activeIndicators = [];
|
||||||
|
|
||||||
|
// Persistence Logic
|
||||||
|
function saveActiveIndicators() {
|
||||||
|
try {
|
||||||
|
const toSave = activeIndicators.map(ind => ({
|
||||||
|
id: ind.id,
|
||||||
|
type: ind.type,
|
||||||
|
name: ind.name,
|
||||||
|
params: ind.params,
|
||||||
|
visible: ind.visible,
|
||||||
|
paneHeight: ind.paneHeight
|
||||||
|
}));
|
||||||
|
console.log('[Persistence] Saving indicators:', toSave.length);
|
||||||
|
localStorage.setItem('winterfail_active_indicators', JSON.stringify(toSave));
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to save active indicators:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadActiveIndicators() {
|
||||||
|
try {
|
||||||
|
const saved = localStorage.getItem('winterfail_active_indicators');
|
||||||
|
console.log('[Persistence] Loading from storage:', saved ? 'data found' : 'empty');
|
||||||
|
if (!saved) return;
|
||||||
|
|
||||||
|
const parsed = JSON.parse(saved);
|
||||||
|
if (!Array.isArray(parsed)) return;
|
||||||
|
|
||||||
|
const restored = [];
|
||||||
|
|
||||||
|
parsed.forEach(savedInd => {
|
||||||
|
const IndicatorClass = IR?.[savedInd.type];
|
||||||
|
if (!IndicatorClass) {
|
||||||
|
console.warn(`[Persistence] Unknown indicator type: ${savedInd.type}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const instance = new IndicatorClass({ type: savedInd.type, params: savedInd.params, name: savedInd.name });
|
||||||
|
const metadata = instance.getMetadata();
|
||||||
|
|
||||||
|
restored.push({
|
||||||
|
id: savedInd.id,
|
||||||
|
type: savedInd.type,
|
||||||
|
name: savedInd.name || metadata.name,
|
||||||
|
params: savedInd.params,
|
||||||
|
plots: metadata.plots,
|
||||||
|
series: [],
|
||||||
|
visible: savedInd.visible !== undefined ? savedInd.visible : true,
|
||||||
|
paneHeight: savedInd.paneHeight || 120,
|
||||||
|
cachedResults: null,
|
||||||
|
cachedMeta: null
|
||||||
|
});
|
||||||
|
|
||||||
|
const parts = savedInd.id.split('_');
|
||||||
|
const idNum = parseInt(parts[parts.length - 1]);
|
||||||
|
if (!isNaN(idNum) && idNum >= nextInstanceId) {
|
||||||
|
nextInstanceId = idNum + 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
activeIndicators = restored;
|
||||||
|
console.log(`[Persistence] Successfully restored ${activeIndicators.length} indicators`);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to load active indicators:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
console.log('[Module] indicators-panel-new.js loaded - activeIndicators count:', activeIndicators?.length || 0);
|
console.log('[Module] indicators-panel-new.js loaded - activeIndicators count:', activeIndicators?.length || 0);
|
||||||
let configuringId = null;
|
let configuringId = null;
|
||||||
let searchQuery = '';
|
let searchQuery = '';
|
||||||
@ -96,7 +162,12 @@ function groupPlotsByColor(plots) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function initIndicatorPanel() {
|
export function initIndicatorPanel() {
|
||||||
|
loadActiveIndicators(); // Load persisted indicators
|
||||||
renderIndicatorPanel();
|
renderIndicatorPanel();
|
||||||
|
// Also trigger initial draw if dashboard exists (it might be too early, but safe to try)
|
||||||
|
if (window.dashboard && window.dashboard.hasInitialLoad) {
|
||||||
|
drawIndicatorsOnChart();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getActiveIndicators() {
|
export function getActiveIndicators() {
|
||||||
@ -107,6 +178,7 @@ export function setActiveIndicators(indicators) {
|
|||||||
console.warn('setActiveIndicators() called with', indicators.length, 'indicators - this will replace activeIndicators array!');
|
console.warn('setActiveIndicators() called with', indicators.length, 'indicators - this will replace activeIndicators array!');
|
||||||
console.trace('Call stack:');
|
console.trace('Call stack:');
|
||||||
activeIndicators = indicators;
|
activeIndicators = indicators;
|
||||||
|
saveActiveIndicators();
|
||||||
renderIndicatorPanel();
|
renderIndicatorPanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -557,6 +629,7 @@ window.updateIndicatorColor = function(id, index, color) {
|
|||||||
if (!indicator) return;
|
if (!indicator) return;
|
||||||
|
|
||||||
indicator.params[`_color_${index}`] = color;
|
indicator.params[`_color_${index}`] = color;
|
||||||
|
saveActiveIndicators();
|
||||||
drawIndicatorsOnChart();
|
drawIndicatorsOnChart();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -568,6 +641,7 @@ window.updateIndicatorSetting = function(id, key, value) {
|
|||||||
indicator.lastSignalTimestamp = null;
|
indicator.lastSignalTimestamp = null;
|
||||||
indicator.lastSignalType = null;
|
indicator.lastSignalType = null;
|
||||||
indicator.cachedResults = null; // Clear cache when params change
|
indicator.cachedResults = null; // Clear cache when params change
|
||||||
|
saveActiveIndicators();
|
||||||
drawIndicatorsOnChart();
|
drawIndicatorsOnChart();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -579,6 +653,7 @@ window.clearAllIndicators = function() {
|
|||||||
});
|
});
|
||||||
activeIndicators = [];
|
activeIndicators = [];
|
||||||
configuringId = null;
|
configuringId = null;
|
||||||
|
saveActiveIndicators();
|
||||||
renderIndicatorPanel();
|
renderIndicatorPanel();
|
||||||
drawIndicatorsOnChart();
|
drawIndicatorsOnChart();
|
||||||
}
|
}
|
||||||
@ -590,6 +665,7 @@ window.toggleAllIndicatorsVisibility = function() {
|
|||||||
ind.visible = !allVisible;
|
ind.visible = !allVisible;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
saveActiveIndicators();
|
||||||
drawIndicatorsOnChart();
|
drawIndicatorsOnChart();
|
||||||
renderIndicatorPanel();
|
renderIndicatorPanel();
|
||||||
}
|
}
|
||||||
@ -608,6 +684,7 @@ function removeIndicatorById(id) {
|
|||||||
configuringId = null;
|
configuringId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
saveActiveIndicators();
|
||||||
renderIndicatorPanel();
|
renderIndicatorPanel();
|
||||||
drawIndicatorsOnChart();
|
drawIndicatorsOnChart();
|
||||||
}
|
}
|
||||||
@ -808,7 +885,7 @@ function addIndicator(type) {
|
|||||||
paneHeight: 120 // default 120px
|
paneHeight: 120 // default 120px
|
||||||
});
|
});
|
||||||
|
|
||||||
// Don't set configuringId so indicators are NOT expanded by default
|
saveActiveIndicators();
|
||||||
renderIndicatorPanel();
|
renderIndicatorPanel();
|
||||||
drawIndicatorsOnChart();
|
drawIndicatorsOnChart();
|
||||||
};
|
};
|
||||||
@ -1286,6 +1363,7 @@ function resetIndicator(id) {
|
|||||||
indicator.params[input.name] = input.default;
|
indicator.params[input.name] = input.default;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
saveActiveIndicators();
|
||||||
renderIndicatorPanel();
|
renderIndicatorPanel();
|
||||||
drawIndicatorsOnChart();
|
drawIndicatorsOnChart();
|
||||||
}
|
}
|
||||||
@ -1302,6 +1380,7 @@ function toggleIndicatorVisibility(id) {
|
|||||||
|
|
||||||
indicator.visible = indicator.visible === false;
|
indicator.visible = indicator.visible === false;
|
||||||
|
|
||||||
|
saveActiveIndicators();
|
||||||
// Full redraw to ensure all indicators render correctly
|
// Full redraw to ensure all indicators render correctly
|
||||||
if (typeof drawIndicatorsOnChart === 'function') {
|
if (typeof drawIndicatorsOnChart === 'function') {
|
||||||
drawIndicatorsOnChart();
|
drawIndicatorsOnChart();
|
||||||
|
|||||||
Reference in New Issue
Block a user