Avg Volatility
points per session
Highest Session
most volatile
Total Sessions
analyzed
Avg Range
high - low

Session Volatility Overview

Volatility by Day of Week

Session Heatmap

Volatility Data

Date DOW Session Open High Low Close Volatility Range
No data available. Load test data to see volatility information.

Session Volatility Overview

Session Heatmap

Session Statistics

Session Avg Volatility Avg Range Max Volatility Min Volatility Count
No data available. Load test data to see volatility information.

Volatility by Day of Week

Most Volatile Day
highest average
Least Volatile Day
lowest average
Monday Avg
points
Friday Avg
points

Day of Week Statistics

Day of Week Avg Volatility Avg Range Max Volatility Min Volatility Sessions
No data available. Load test data to see volatility information.

Monthly Volatility Trend

Current Month
average volatility
Previous Month
average volatility
Month Change
vs previous
Highest Month
most volatile

Monthly Statistics

Month Avg Volatility Avg Range Max Volatility Min Volatility Sessions
No data available. Load test data to see volatility information.

Sign In

Enter your credentials to access your journal

// ── RENDER FUNCTIONS ── function renderVolatilityKPI() { const sessions = volatilityState.sessions; if (sessions.length === 0) { document.getElementById('volAvgVolatility').textContent = '—'; document.getElementById('volHighestSession').textContent = '—'; document.getElementById('volTotalSessions').textContent = '—'; document.getElementById('volAvgRange').textContent = '—'; return; } const avgVol = (sessions.reduce((sum, s) => sum + s.volatility, 0) / sessions.length).toFixed(0); const sessionAvgs = {}; sessions.forEach(s => { if (!sessionAvgs[s.session]) sessionAvgs[s.session] = []; sessionAvgs[s.session].push(s.volatility); }); let highestSession = ''; let highestAvg = 0; Object.keys(sessionAvgs).forEach(session => { const avg = sessionAvgs[session].reduce((a, b) => a + b, 0) / sessionAvgs[session].length; if (avg > highestAvg) { highestAvg = avg; highestSession = session; } }); const avgRange = (sessions.reduce((sum, s) => sum + s.range, 0) / sessions.length).toFixed(0); document.getElementById('volAvgVolatility').textContent = avgVol; document.getElementById('volHighestSession').textContent = highestSession; document.getElementById('volTotalSessions').textContent = sessions.length; document.getElementById('volAvgRange').textContent = avgRange; } function renderVolatilityMain() { const canvas = document.getElementById('volatilityMainChart'); if (!canvas) return; const ctx = canvas.getContext('2d'); if (volatilityState.charts.main) { volatilityState.charts.main.destroy(); } const sessions = volatilityState.sessions; if (sessions.length === 0) return; // Group by session and calculate average const sessionNames = ['Asian', 'London', 'Win1', 'PreM', 'News', 'AM1', 'AM2', 'AM3', 'AM4', 'Lunch', 'Win2', 'PM1', 'PM2', 'WinF']; const sessionAvgs = {}; sessionNames.forEach(name => { const sessionData = sessions.filter(s => s.session === name); if (sessionData.length > 0) { sessionAvgs[name] = sessionData.reduce((sum, s) => sum + s.volatility, 0) / sessionData.length; } }); const labels = Object.keys(sessionAvgs); const data = Object.values(sessionAvgs); const colors = labels.map(session => sessionColors[session]); volatilityState.charts.main = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Avg Volatility', data: data, backgroundColor: colors, borderWidth: 0 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, tooltip: { backgroundColor: 'rgba(0, 0, 0, 0.8)', padding: 12, callbacks: { label: function(context) { return 'Avg: ' + context.parsed.y.toFixed(1) + ' pts'; } } } }, scales: { x: { grid: { display: false }, ticks: { font: { size: 11 } } }, y: { title: { display: true, text: 'Volatility (points)' }, grid: { color: '#e8e4df' }, beginAtZero: true } } } }); } function renderVolatilityDOW() { const canvas = document.getElementById('volatilityDOWChart'); if (!canvas) return; const ctx = canvas.getContext('2d'); if (volatilityState.charts.dow) { volatilityState.charts.dow.destroy(); } const sessions = volatilityState.sessions; if (sessions.length === 0) return; const dows = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri']; const dowAvgs = {}; dows.forEach(dow => { const dowData = sessions.filter(s => s.dow === dow); if (dowData.length > 0) { dowAvgs[dow] = dowData.reduce((sum, s) => sum + s.volatility, 0) / dowData.length; } }); const labels = Object.keys(dowAvgs); const data = Object.values(dowAvgs); volatilityState.charts.dow = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Avg Volatility', data: data, backgroundColor: '#4a7fa5', borderWidth: 0 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, tooltip: { backgroundColor: 'rgba(0, 0, 0, 0.8)', padding: 12, callbacks: { label: function(context) { return 'Avg: ' + context.parsed.y.toFixed(1) + ' pts'; } } } }, scales: { x: { grid: { display: false } }, y: { title: { display: true, text: 'Volatility (points)' }, grid: { color: '#e8e4df' }, beginAtZero: true } } } }); } function renderVolatilityHeatmap() { const canvas = document.getElementById('volatilityHeatmapChart'); if (!canvas) return; const ctx = canvas.getContext('2d'); if (volatilityState.charts.heatmap) { volatilityState.charts.heatmap.destroy(); } const sessions = volatilityState.sessions; if (sessions.length === 0) return; // Create matrix: DOW x Session const dows = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri']; const sessionNames = ['Asian', 'London', 'PreM', 'News', 'AM1', 'AM2', 'Lunch', 'PM1', 'PM2']; const matrix = {}; dows.forEach(dow => { matrix[dow] = {}; sessionNames.forEach(session => { const data = sessions.filter(s => s.dow === dow && s.session === session); if (data.length > 0) { matrix[dow][session] = data.reduce((sum, s) => sum + s.volatility, 0) / data.length; } else { matrix[dow][session] = 0; } }); }); // Convert to bubble chart data const bubbleData = []; dows.forEach((dow, dowIndex) => { sessionNames.forEach((session, sessionIndex) => { const value = matrix[dow][session]; if (value > 0) { bubbleData.push({ x: sessionIndex, y: dowIndex, r: Math.max(5, Math.min(20, value / 5)), value: value }); } }); }); volatilityState.charts.heatmap = new Chart(ctx, { type: 'bubble', data: { datasets: [{ label: 'Volatility', data: bubbleData, backgroundColor: 'rgba(74, 127, 165, 0.6)', borderColor: '#4a7fa5', borderWidth: 1 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, tooltip: { backgroundColor: 'rgba(0, 0, 0, 0.8)', padding: 12, callbacks: { title: function(context) { const point = context[0].raw; return dows[point.y] + ' - ' + sessionNames[point.x]; }, label: function(context) { return 'Volatility: ' + context.raw.value.toFixed(1) + ' pts'; } } } }, scales: { x: { type: 'linear', ticks: { callback: function(value) { return sessionNames[value] || ''; }, stepSize: 1 }, grid: { display: false } }, y: { type: 'linear', ticks: { callback: function(value) { return dows[value] || ''; }, stepSize: 1 }, grid: { color: '#e8e4df' } } } } }); } function renderVolatilityTable() { const tbody = document.getElementById('volatilityTableBody'); const sessions = [...volatilityState.sessions].sort((a, b) => new Date(b.date) - new Date(a.date) ); if (sessions.length === 0) { tbody.innerHTML = 'No data available. Load test data to see volatility information.'; return; } tbody.innerHTML = sessions.map(session => ` ${session.date} ${session.dow} ${session.session} ${session.open.toFixed(2)} ${session.high.toFixed(2)} ${session.low.toFixed(2)} ${session.close.toFixed(2)} ${session.volatility} ${session.range} `).join(''); } // ═══ SESSIONS PAGE ═══ function renderVolatilitySessions() { const sessions = volatilityState.sessions; if (sessions.length === 0) return; // Render Sessions Chart const canvas = document.getElementById('volatilitySessionsChart'); if (canvas) { const ctx = canvas.getContext('2d'); if (volatilityState.charts.sessions) { volatilityState.charts.sessions.destroy(); } const sessionNames = ['Asian', 'London', 'Win1', 'PreM', 'News', 'AM1', 'AM2', 'AM3', 'AM4', 'Lunch', 'Win2', 'PM1', 'PM2', 'WinF']; const sessionAvgs = {}; sessionNames.forEach(name => { const sessionData = sessions.filter(s => s.session === name); if (sessionData.length > 0) { sessionAvgs[name] = sessionData.reduce((sum, s) => sum + s.volatility, 0) / sessionData.length; } }); const labels = Object.keys(sessionAvgs); const data = Object.values(sessionAvgs); const colors = labels.map(session => sessionColors[session]); volatilityState.charts.sessions = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Avg Volatility', data: data, backgroundColor: colors, borderWidth: 0 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, tooltip: { backgroundColor: 'rgba(0, 0, 0, 0.8)', padding: 12 } }, scales: { x: { grid: { display: false } }, y: { title: { display: true, text: 'Volatility (points)' }, grid: { color: '#e8e4df' }, beginAtZero: true } } } }); } // Render Sessions Heatmap const heatmapCanvas = document.getElementById('volatilitySessionsHeatmap'); if (heatmapCanvas) { const ctx = heatmapCanvas.getContext('2d'); if (volatilityState.charts.sessionsHeatmap) { volatilityState.charts.sessionsHeatmap.destroy(); } const dows = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri']; const sessionNames = ['Asian', 'London', 'PreM', 'News', 'AM1', 'AM2', 'Lunch', 'PM1', 'PM2']; const bubbleData = []; dows.forEach((dow, dowIndex) => { sessionNames.forEach((session, sessionIndex) => { const data = sessions.filter(s => s.dow === dow && s.session === session); if (data.length > 0) { const value = data.reduce((sum, s) => sum + s.volatility, 0) / data.length; bubbleData.push({ x: sessionIndex, y: dowIndex, r: Math.max(5, Math.min(20, value / 5)), value: value }); } }); }); volatilityState.charts.sessionsHeatmap = new Chart(ctx, { type: 'bubble', data: { datasets: [{ label: 'Volatility', data: bubbleData, backgroundColor: 'rgba(74, 127, 165, 0.6)', borderColor: '#4a7fa5', borderWidth: 1 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, tooltip: { backgroundColor: 'rgba(0, 0, 0, 0.8)', padding: 12, callbacks: { title: function(context) { const point = context[0].raw; return dows[point.y] + ' - ' + sessionNames[point.x]; }, label: function(context) { return 'Volatility: ' + context.raw.value.toFixed(1) + ' pts'; } } } }, scales: { x: { type: 'linear', ticks: { callback: function(value) { return sessionNames[value] || ''; }, stepSize: 1 }, grid: { display: false } }, y: { type: 'linear', ticks: { callback: function(value) { return dows[value] || ''; }, stepSize: 1 }, grid: { color: '#e8e4df' } } } } }); } // Render Sessions Stats Table const tbody = document.getElementById('sessionsStatsTableBody'); if (tbody) { const sessionNames = ['Asian', 'London', 'Win1', 'PreM', 'News', 'AM1', 'AM2', 'AM3', 'AM4', 'Lunch', 'Win2', 'PM1', 'PM2', 'WinF']; const stats = []; sessionNames.forEach(name => { const sessionData = sessions.filter(s => s.session === name); if (sessionData.length > 0) { const vols = sessionData.map(s => s.volatility); const ranges = sessionData.map(s => s.range); stats.push({ session: name, avgVol: (vols.reduce((a, b) => a + b, 0) / vols.length).toFixed(1), avgRange: (ranges.reduce((a, b) => a + b, 0) / ranges.length).toFixed(1), maxVol: Math.max(...vols), minVol: Math.min(...vols), count: sessionData.length }); } }); tbody.innerHTML = stats.map(stat => ` ${stat.session} ${stat.avgVol} ${stat.avgRange} ${stat.maxVol} ${stat.minVol} ${stat.count} `).join(''); } } // ═══ BY DAYS PAGE ═══ function renderVolatilityByDays() { const sessions = volatilityState.sessions; if (sessions.length === 0) return; // Render By Days Chart const canvas = document.getElementById('volatilityByDaysChart'); if (canvas) { const ctx = canvas.getContext('2d'); if (volatilityState.charts.byDays) { volatilityState.charts.byDays.destroy(); } const dows = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri']; const dowAvgs = {}; dows.forEach(dow => { const dowData = sessions.filter(s => s.dow === dow); if (dowData.length > 0) { dowAvgs[dow] = dowData.reduce((sum, s) => sum + s.volatility, 0) / dowData.length; } }); const labels = Object.keys(dowAvgs); const data = Object.values(dowAvgs); volatilityState.charts.byDays = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Avg Volatility', data: data, backgroundColor: '#4a7fa5', borderWidth: 0 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, tooltip: { backgroundColor: 'rgba(0, 0, 0, 0.8)', padding: 12 } }, scales: { x: { grid: { display: false } }, y: { title: { display: true, text: 'Volatility (points)' }, grid: { color: '#e8e4df' }, beginAtZero: true } } } }); // Update KPI cards const sortedDows = Object.entries(dowAvgs).sort((a, b) => b[1] - a[1]); document.getElementById('mostVolatileDay').textContent = sortedDows[0][0]; document.getElementById('leastVolatileDay').textContent = sortedDows[sortedDows.length - 1][0]; document.getElementById('mondayAvg').textContent = dowAvgs['Mon']?.toFixed(0) || '—'; document.getElementById('fridayAvg').textContent = dowAvgs['Fri']?.toFixed(0) || '—'; } // Render DOW Stats Table const tbody = document.getElementById('dowStatsTableBody'); if (tbody) { const dows = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri']; const stats = []; dows.forEach(dow => { const dowData = sessions.filter(s => s.dow === dow); if (dowData.length > 0) { const vols = dowData.map(s => s.volatility); const ranges = dowData.map(s => s.range); stats.push({ dow: dow, avgVol: (vols.reduce((a, b) => a + b, 0) / vols.length).toFixed(1), avgRange: (ranges.reduce((a, b) => a + b, 0) / ranges.length).toFixed(1), maxVol: Math.max(...vols), minVol: Math.min(...vols), count: dowData.length }); } }); tbody.innerHTML = stats.map(stat => ` ${stat.dow} ${stat.avgVol} ${stat.avgRange} ${stat.maxVol} ${stat.minVol} ${stat.count} `).join(''); } } // ═══ BY MONTHS PAGE ═══ function renderVolatilityByMonths() { const sessions = volatilityState.sessions; if (sessions.length === 0) return; // Group by month const monthlyData = {}; sessions.forEach(s => { const date = new Date(s.date); const monthKey = date.getFullYear() + '-' + String(date.getMonth() + 1).padStart(2, '0'); if (!monthlyData[monthKey]) { monthlyData[monthKey] = []; } monthlyData[monthKey].push(s); }); // Render By Months Chart const canvas = document.getElementById('volatilityByMonthsChart'); if (canvas) { const ctx = canvas.getContext('2d'); if (volatilityState.charts.byMonths) { volatilityState.charts.byMonths.destroy(); } const sortedMonths = Object.keys(monthlyData).sort(); const monthLabels = sortedMonths.map(m => { const date = new Date(m + '-01'); return date.toLocaleDateString(currentLang === 'ru' ? 'ru-RU' : 'en-US', { year: 'numeric', month: 'short' }); }); const monthAvgs = sortedMonths.map(month => { const data = monthlyData[month]; return data.reduce((sum, s) => sum + s.volatility, 0) / data.length; }); volatilityState.charts.byMonths = new Chart(ctx, { type: 'line', data: { labels: monthLabels, datasets: [{ label: 'Avg Volatility', data: monthAvgs, borderColor: '#4a7fa5', backgroundColor: 'rgba(74, 127, 165, 0.1)', borderWidth: 2, fill: true, tension: 0.4 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, tooltip: { backgroundColor: 'rgba(0, 0, 0, 0.8)', padding: 12 } }, scales: { x: { grid: { display: false } }, y: { title: { display: true, text: 'Volatility (points)' }, grid: { color: '#e8e4df' }, beginAtZero: true } } } }); // Update KPI cards const currentMonth = sortedMonths[sortedMonths.length - 1]; const previousMonth = sortedMonths[sortedMonths.length - 2]; const currentAvg = monthAvgs[monthAvgs.length - 1]; const previousAvg = monthAvgs[monthAvgs.length - 2]; const change = previousAvg ? ((currentAvg - previousAvg) / previousAvg * 100).toFixed(1) : 0; const maxMonth = sortedMonths[monthAvgs.indexOf(Math.max(...monthAvgs))]; const maxMonthLabel = new Date(maxMonth + '-01').toLocaleDateString(currentLang === 'ru' ? 'ru-RU' : 'en-US', { year: 'numeric', month: 'short' }); document.getElementById('currentMonthVol').textContent = currentAvg.toFixed(0); document.getElementById('previousMonthVol').textContent = previousAvg?.toFixed(0) || '—'; document.getElementById('monthChange').textContent = change + '%'; document.getElementById('highestMonth').textContent = maxMonthLabel; } // Render Month Stats Table const tbody = document.getElementById('monthStatsTableBody'); if (tbody) { const sortedMonths = Object.keys(monthlyData).sort().reverse(); const stats = []; sortedMonths.forEach(month => { const data = monthlyData[month]; const vols = data.map(s => s.volatility); const ranges = data.map(s => s.range); const date = new Date(month + '-01'); const monthLabel = date.toLocaleDateString(currentLang === 'ru' ? 'ru-RU' : 'en-US', { year: 'numeric', month: 'long' }); stats.push({ month: monthLabel, avgVol: (vols.reduce((a, b) => a + b, 0) / vols.length).toFixed(1), avgRange: (ranges.reduce((a, b) => a + b, 0) / ranges.length).toFixed(1), maxVol: Math.max(...vols), minVol: Math.min(...vols), count: data.length }); }); tbody.innerHTML = stats.map(stat => ` ${stat.month} ${stat.avgVol} ${stat.avgRange} ${stat.maxVol} ${stat.minVol} ${stat.count} `).join(''); } }