diff --git a/src/api/dashboard/static/index.html b/src/api/dashboard/static/index.html
index 445cde9..4e7d19a 100644
--- a/src/api/dashboard/static/index.html
+++ b/src/api/dashboard/static/index.html
@@ -523,7 +523,77 @@
z-index: 9999;
}
- /* Price Scale Controls */
+ /* Chart Settings Menu */
+ .chart-settings-btn {
+ width: 20px;
+ height: 20px;
+ background: rgba(42, 46, 57, 0.9);
+ border: 1px solid #363c4e;
+ color: #d1d4dc;
+ font-size: 12px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 2px;
+ transition: all 0.2s;
+ padding: 0;
+ }
+
+ .chart-settings-btn:hover {
+ background: #363c4e;
+ border-color: #4a4f5e;
+ }
+
+ /* Settings Popup */
+ .chart-settings-popup {
+ position: absolute;
+ right: 0;
+ top: 26px;
+ background: #1e222d;
+ border: 1px solid #363c4e;
+ border-radius: 6px;
+ padding: 12px;
+ z-index: 100;
+ min-width: 200px;
+ box-shadow: 0 4px 12px rgba(0,0,0,0.4);
+ display: none;
+ }
+
+ .chart-settings-popup.show {
+ display: block;
+ }
+
+ .settings-group {
+ margin-bottom: 12px;
+ }
+
+ .settings-group:last-child {
+ margin-bottom: 0;
+ }
+
+ .settings-label {
+ display: block;
+ font-size: 11px;
+ color: #787b86;
+ margin-bottom: 4px;
+ text-transform: uppercase;
+ }
+
+ .settings-select {
+ width: 100%;
+ background: #131722;
+ border: 1px solid #363c4e;
+ color: #d1d4dc;
+ padding: 6px 8px;
+ border-radius: 4px;
+ font-size: 12px;
+ }
+
+ .settings-select:focus {
+ outline: none;
+ border-color: #2962ff;
+ }
.price-scale-controls {
position: absolute;
right: 10px;
@@ -1355,8 +1425,26 @@
+
+
diff --git a/src/api/dashboard/static/js/app.js b/src/api/dashboard/static/js/app.js
index 908d7a0..33a7ba3 100644
--- a/src/api/dashboard/static/js/app.js
+++ b/src/api/dashboard/static/js/app.js
@@ -9,12 +9,14 @@ import {
removeIndicatorById
} from './ui/indicators-panel-new.js';
import { IndicatorRegistry } from './indicators/index.js';
+import { TimezoneConfig } from './config/timezone.js';
window.dashboard = null;
window.toggleSidebar = toggleSidebar;
window.refreshTA = refreshTA;
window.openAIAnalysis = openAIAnalysis;
+window.TimezoneConfig = TimezoneConfig;
window.renderIndicatorList = function() {
// This function is no longer needed for sidebar indicators
};
@@ -35,6 +37,39 @@ document.addEventListener('DOMContentLoaded', async () => {
toggleBtn.addEventListener('click', toggleSidebar);
}
+ // Initialize timezone selector
+ const timezoneSelect = document.getElementById('timezoneSelect');
+ const settingsPopup = document.getElementById('settingsPopup');
+ const settingsBtn = document.getElementById('btnSettings');
+
+ if (timezoneSelect) {
+ timezoneSelect.value = TimezoneConfig.getTimezone();
+ timezoneSelect.addEventListener('change', (e) => {
+ TimezoneConfig.setTimezone(e.target.value);
+ settingsPopup.classList.remove('show');
+ // Redraw chart and indicators
+ if (window.dashboard) {
+ window.drawIndicatorsOnChart?.();
+ }
+ });
+ }
+
+ // Toggle settings popup
+ if (settingsBtn && settingsPopup) {
+ settingsBtn.addEventListener('click', (e) => {
+ e.stopPropagation();
+ settingsPopup.classList.toggle('show');
+ });
+
+ settingsPopup.addEventListener('click', (e) => {
+ e.stopPropagation();
+ });
+
+ document.addEventListener('click', () => {
+ settingsPopup.classList.remove('show');
+ });
+ }
+
window.dashboard = new TradingDashboard();
restoreSidebarState();
restoreSidebarTabState();
diff --git a/src/api/dashboard/static/js/config/timezone.js b/src/api/dashboard/static/js/config/timezone.js
new file mode 100644
index 0000000..b3965cb
--- /dev/null
+++ b/src/api/dashboard/static/js/config/timezone.js
@@ -0,0 +1,68 @@
+const TimezoneConfig = {
+ timezone: localStorage.getItem('timezone') || 'Europe/Warsaw',
+
+ availableTimezones: [
+ { value: 'UTC', label: 'UTC', offset: 0 },
+ { value: 'Europe/London', label: 'London (GMT/BST)', offset: 0 },
+ { value: 'Europe/Paris', label: 'Central Europe (CET/CEST)', offset: 1 },
+ { value: 'Europe/Warsaw', label: 'Warsaw (CET/CEST)', offset: 1 },
+ { value: 'America/New_York', label: 'New York (EST/EDT)', offset: -5 },
+ { value: 'America/Chicago', label: 'Chicago (CST/CDT)', offset: -6 },
+ { value: 'America/Los_Angeles', label: 'Los Angeles (PST/PDT)', offset: -8 },
+ { value: 'Asia/Tokyo', label: 'Tokyo (JST)', offset: 9 },
+ { value: 'Asia/Shanghai', label: 'Shanghai (CST)', offset: 8 },
+ { value: 'Australia/Sydney', label: 'Sydney (AEST/AEDT)', offset: 10 },
+ ],
+
+ setTimezone(tz) {
+ this.timezone = tz;
+ localStorage.setItem('timezone', tz);
+ document.dispatchEvent(new CustomEvent('timezone-changed', { detail: tz }));
+ },
+
+ getTimezone() {
+ return this.timezone;
+ },
+
+ getOffsetHours(tz = this.timezone) {
+ const now = new Date();
+ const tzDate = new Date(now.toLocaleString('en-US', { timeZone: tz }));
+ const utcDate = new Date(now.toLocaleString('en-US', { timeZone: 'UTC' }));
+ return (tzDate - utcDate) / 3600000;
+ },
+
+ formatDate(timestamp) {
+ const date = new Date(timestamp);
+ const tz = this.timezone;
+
+ const options = {
+ timeZone: tz,
+ year: 'numeric', month: '2-digit', day: '2-digit',
+ hour: '2-digit', minute: '2-digit', second: '2-digit',
+ hour12: false
+ };
+
+ const formatter = new Intl.DateTimeFormat('en-GB', options);
+ const parts = formatter.formatToParts(date);
+ const get = (type) => parts.find(p => p.type === type).value;
+
+ return `${get('day')}/${get('month')}/${get('year').slice(-2)} ${get('hour')}:${get('minute')}`;
+ },
+
+ formatTickMark(timestamp) {
+ const date = new Date(timestamp * 1000);
+ const tz = this.timezone;
+
+ const options = {
+ timeZone: tz,
+ month: '2-digit', day: '2-digit',
+ hour: '2-digit', minute: '2-digit',
+ hour12: false
+ };
+
+ const formatter = new Intl.DateTimeFormat('en-US', options);
+ return formatter.format(date).replace(',', '');
+ }
+};
+
+export { TimezoneConfig };
diff --git a/src/api/dashboard/static/js/ui/chart.js b/src/api/dashboard/static/js/ui/chart.js
index d3c07eb..6fd1c10 100644
--- a/src/api/dashboard/static/js/ui/chart.js
+++ b/src/api/dashboard/static/js/ui/chart.js
@@ -1,15 +1,10 @@
import { INTERVALS, COLORS } from '../core/index.js';
import { calculateAllIndicatorSignals, calculateSummarySignal } from './signals-calculator.js';
import { updateIndicatorCandles } from './indicators-panel-new.js';
+import { TimezoneConfig } from '../config/timezone.js';
function formatDate(timestamp) {
- const date = new Date(timestamp);
- const day = String(date.getUTCDate()).padStart(2, '0');
- const month = String(date.getUTCMonth() + 1).padStart(2, '0');
- const year = String(date.getUTCFullYear()).slice(-2);
- const hours = String(date.getUTCHours()).padStart(2, '0');
- const minutes = String(date.getUTCMinutes()).padStart(2, '0');
- return `${day}/${month}/${year} ${hours}:${minutes}`;
+ return TimezoneConfig.formatDate(timestamp);
}
export class TradingDashboard {
@@ -98,8 +93,7 @@ timeScale: {
rightOffset: 12,
barSpacing: 10,
tickMarkFormatter: (time, tickMarkType, locale) => {
- const date = new Date((time + 3600) * 1000);
- return `${String(date.getUTCMonth() + 1).padStart(2, '0')}/${String(date.getUTCDate()).padStart(2, '0')} ${String(date.getUTCHours()).padStart(2, '0')}:${String(date.getUTCMinutes()).padStart(2, '0')}`;
+ return TimezoneConfig.formatTickMark(time);
},
},
handleScroll: {
@@ -419,7 +413,7 @@ async loadNewData() {
const existingData = this.allData.get(this.currentInterval) || [];
this.allData.set(this.currentInterval, this.mergeData(existingData, chartData));
- console.log(`[NewData Load] Added ${chartData.length} new candles, total in dataset: ${this.allData.get(this.currentInterval).length}`);
+ //console.log(`[NewData Load] Added ${chartData.length} new candles, total in dataset: ${this.allData.get(this.currentInterval).length}`);
if (atEdge) {
this.chart.timeScale().scrollToRealTime();
@@ -427,7 +421,7 @@ async loadNewData() {
this.updateStats(latest);
- console.log('[Chart] Calling drawIndicatorsOnChart after new data');
+ //console.log('[Chart] Calling drawIndicatorsOnChart after new data');
window.drawIndicatorsOnChart?.();
window.updateIndicatorCandles?.();
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 a6bcb7e..9bacdac 100644
--- a/src/api/dashboard/static/js/ui/indicators-panel-new.js
+++ b/src/api/dashboard/static/js/ui/indicators-panel-new.js
@@ -854,22 +854,22 @@ export function drawIndicatorsOnChart() {
const candles = window.dashboard?.allData?.get(currentInterval);
if (!candles || candles.length === 0) {
- console.log('[Indicators] No candles available');
+ //console.log('[Indicators] No candles available');
return;
}
- console.log(`[Indicators] ========== drawIndicatorsOnChart START ==========`);
- console.log(`[Indicators] Candles from allData: ${candles.length}`);
- console.log(`[Indicators] First candle time: ${candles[0]?.time} (${new Date(candles[0]?.time * 1000).toLocaleDateString()})`);
- console.log(`[Indicators] Last candle time: ${candles[candles.length - 1]?.time} (${new Date(candles[candles.length - 1]?.time * 1000).toLocaleDateString()})`);
+ // console.log(`[Indicators] ========== drawIndicatorsOnChart START ==========`);
+ // console.log(`[Indicators] Candles from allData: ${candles.length}`);
+ // console.log(`[Indicators] First candle time: ${candles[0]?.time} (${new Date(candles[0]?.time * 1000).toLocaleDateString()})`);
+ // console.log(`[Indicators] Last candle time: ${candles[candles.length - 1]?.time} (${new Date(candles[candles.length - 1]?.time * 1000).toLocaleDateString()})`);
const oldestTime = candles[0]?.time;
const newestTime = candles[candles.length - 1]?.time;
const oldestDate = oldestTime ? new Date(oldestTime * 1000).toLocaleDateString() : 'N/A';
const newestDate = newestTime ? new Date(newestTime * 1000).toLocaleDateString() : 'N/A';
- console.log(`[Indicators] ========== Redrawing ==========`);
- console.log(`[Indicators] Candles: ${candles.length} | Time range: ${oldestDate} (${oldestTime}) to ${newestDate} (${newestTime})`);
+ //console.log(`[Indicators] ========== Redrawing ==========`);
+ // console.log(`[Indicators] Candles: ${candles.length} | Time range: ${oldestDate} (${oldestTime}) to ${newestDate} (${newestTime})`);
const activeIndicators = getActiveIndicators();
@@ -929,14 +929,14 @@ export function drawIndicatorsOnChart() {
window.dashboard.chart.panes()[0]?.setHeight(mainPaneHeight);
- console.log(`[Indicators] ========== Rendering Indicators ==========`);
- console.log(`[Indicators] Input candles: ${candles.length} | Panel count: ${totalPanes}`);
+ //console.log(`[Indicators] ========== Rendering Indicators ==========`);
+ //console.log(`[Indicators] Input candles: ${candles.length} | Panel count: ${totalPanes}`);
overlayIndicators.forEach(({ indicator, meta, instance }) => {
- console.log(`[Indicators] Processing overlay: ${indicator.name}`);
- console.log(`[Indicators] Before renderIndicatorOnPane: indicator.cachedResults length = ${indicator.cachedResults?.length || 0}`);
+ //console.log(`[Indicators] Processing overlay: ${indicator.name}`);
+ //console.log(`[Indicators] Before renderIndicatorOnPane: indicator.cachedResults length = ${indicator.cachedResults?.length || 0}`);
renderIndicatorOnPane(indicator, meta, instance, candles, 0, lineStyleMap);
- console.log(`[Indicators] After renderIndicatorOnPane: indicator.cachedResults length = ${indicator.cachedResults?.length || 0}`);
+ //console.log(`[Indicators] After renderIndicatorOnPane: indicator.cachedResults length = ${indicator.cachedResults?.length || 0}`);
});
paneIndicators.forEach(({ indicator, meta, instance }, idx) => {
@@ -947,10 +947,10 @@ export function drawIndicatorsOnChart() {
indicatorPanes.set(indicator.id, paneIndex);
}
- console.log(`[Indicators] Processing pane: ${indicator.name} (pane ${paneIndex})`);
- console.log(`[Indicators] Before renderIndicatorOnPane: indicator.cachedResults length = ${indicator.cachedResults?.length || 0}`);
+ //console.log(`[Indicators] Processing pane: ${indicator.name} (pane ${paneIndex})`);
+ //console.log(`[Indicators] Before renderIndicatorOnPane: indicator.cachedResults length = ${indicator.cachedResults?.length || 0}`);
renderIndicatorOnPane(indicator, meta, instance, candles, paneIndex, lineStyleMap);
- console.log(`[Indicators] After renderIndicatorOnPane: indicator.cachedResults length = ${indicator.cachedResults?.length || 0}`);
+ //console.log(`[Indicators] After renderIndicatorOnPane: indicator.cachedResults length = ${indicator.cachedResults?.length || 0}`);
const pane = window.dashboard.chart.panes()[paneIndex];
if (pane) {
@@ -959,27 +959,10 @@ export function drawIndicatorsOnChart() {
parseInt(localStorage.getItem(`pane_height_${indicator.type}`)) ||
120;
pane.setHeight(storedHeight);
-
- // Subscribe to pane height changes to save to localStorage
- const originalHeight = storedHeight;
- const paneElement = pane.getHTMLElement && pane.getHTMLElement();
- if (paneElement) {
- const resizeObserver = new ResizeObserver((entries) => {
- for (const entry of entries) {
- const newHeight = Math.round(entry.contentRect.height);
- if (newHeight !== originalHeight && newHeight > 50) {
- indicator.paneHeight = newHeight;
- localStorage.setItem(`pane_height_${indicator.type}`, newHeight);
- console.log(`[Indicators] Saved pane height for ${indicator.type}: ${newHeight}px`);
- }
- }
- });
- resizeObserver.observe(paneElement);
- }
}
});
- console.log(`[Indicators] ========== drawIndicatorsOnChart END ==========`);
+ //console.log(`[Indicators] ========== drawIndicatorsOnChart END ==========`);
} catch (error) {
console.error('[Indicators] Error drawing indicators:', error);
}
diff --git a/src/api/dashboard/static/js/ui/signals-calculator.js b/src/api/dashboard/static/js/ui/signals-calculator.js
index 533c6a4..b69f805 100644
--- a/src/api/dashboard/static/js/ui/signals-calculator.js
+++ b/src/api/dashboard/static/js/ui/signals-calculator.js
@@ -173,16 +173,16 @@ export function calculateAllIndicatorSignals() {
const activeIndicators = window.getActiveIndicators?.() || [];
const candles = window.dashboard?.allData?.get(window.dashboard?.currentInterval);
- console.log('[Signals] ========== calculateAllIndicatorSignals START ==========');
+ //console.log('[Signals] ========== calculateAllIndicatorSignals START ==========');
console.log('[Signals] Active indicators:', activeIndicators.length, 'Candles:', candles?.length || 0);
if (!candles || candles.length < 2) {
- console.log('[Signals] Insufficient candles available:', candles?.length || 0);
+ //console.log('[Signals] Insufficient candles available:', candles?.length || 0);
return [];
}
if (!activeIndicators || activeIndicators.length === 0) {
- console.log('[Signals] No active indicators');
+ //console.log('[Signals] No active indicators');
return [];
}
@@ -283,7 +283,7 @@ export function calculateAllIndicatorSignals() {
});
}
- console.log('[Signals] ========== calculateAllIndicatorSignals END ==========');
+ //console.log('[Signals] ========== calculateAllIndicatorSignals END ==========');
console.log('[Signals] Total signals calculated:', signals.length);
return signals;
}
\ No newline at end of file