fixed deutsche boerse
All checks were successful
Deployment / deploy-docker (push) Successful in 17s
All checks were successful
Deployment / deploy-docker (push) Successful in 17s
This commit is contained in:
@@ -71,7 +71,7 @@
|
||||
</div>
|
||||
|
||||
<div class="glass p-8 mb-8">
|
||||
<h3 class="text-lg font-bold mb-6 text-slate-300">Moving Average: Tradezahlen & Volumen je Exchange</h3>
|
||||
<h3 class="text-lg font-bold mb-6 text-slate-300">Moving Average: Tradezahlen & Volumen (alle Exchanges)</h3>
|
||||
<div class="h-96"><canvas id="movingAverageChart"></canvas></div>
|
||||
</div>
|
||||
|
||||
@@ -251,61 +251,81 @@
|
||||
if (charts.movingAverage) charts.movingAverage.destroy();
|
||||
|
||||
const dateIdx = columns.findIndex(c => c.name === 'date' || c.name === 'timestamp');
|
||||
const exchangeIdx = columns.findIndex(c => c.name === 'exchange');
|
||||
const countIdx = columns.findIndex(c => c.name === 'trade_count');
|
||||
const volumeIdx = columns.findIndex(c => c.name === 'volume');
|
||||
const maCountIdx = columns.findIndex(c => c.name === 'ma_count');
|
||||
const maVolumeIdx = columns.findIndex(c => c.name === 'ma_volume');
|
||||
|
||||
const exchanges = [...new Set(data.map(r => r[exchangeIdx]))];
|
||||
// Alle Daten nach Datum aggregieren (über alle Exchanges summieren)
|
||||
const dates = [...new Set(data.map(r => r[dateIdx]))].sort();
|
||||
|
||||
const datasets = [];
|
||||
// Erweiterte Farben für mehr Exchanges (EIX, LS, XETRA, FRA, GETTEX, STU, QUOTRIX)
|
||||
const colors = ['#38bdf8', '#f43f5e', '#10b981', '#fbbf24', '#8b5cf6', '#f97316', '#ec4899', '#14b8a6', '#84cc16', '#a855f7'];
|
||||
|
||||
exchanges.forEach((exchange, idx) => {
|
||||
datasets.push({
|
||||
label: `${exchange} - Trade Count`,
|
||||
data: dates.map(d => {
|
||||
const row = data.find(r => r[dateIdx] === d && r[exchangeIdx] === exchange);
|
||||
return row ? (row[countIdx] || 0) : 0;
|
||||
}),
|
||||
borderColor: colors[idx % colors.length],
|
||||
backgroundColor: colors[idx % colors.length] + '33',
|
||||
borderWidth: 2,
|
||||
yAxisID: 'y',
|
||||
tension: 0.3
|
||||
});
|
||||
|
||||
datasets.push({
|
||||
label: `${exchange} - MA Count`,
|
||||
data: dates.map(d => {
|
||||
const row = data.find(r => r[dateIdx] === d && r[exchangeIdx] === exchange);
|
||||
return row ? (row[maCountIdx] || 0) : 0;
|
||||
}),
|
||||
borderColor: colors[idx % colors.length],
|
||||
backgroundColor: 'transparent',
|
||||
borderWidth: 2,
|
||||
borderDash: [5, 5],
|
||||
yAxisID: 'y',
|
||||
tension: 0.3
|
||||
});
|
||||
|
||||
datasets.push({
|
||||
label: `${exchange} - Volume`,
|
||||
data: dates.map(d => {
|
||||
const row = data.find(r => r[dateIdx] === d && r[exchangeIdx] === exchange);
|
||||
return row ? (row[volumeIdx] || 0) : 0;
|
||||
}),
|
||||
borderColor: colors[(idx + 2) % colors.length],
|
||||
backgroundColor: colors[(idx + 2) % colors.length] + '33',
|
||||
borderWidth: 2,
|
||||
yAxisID: 'y1',
|
||||
tension: 0.3
|
||||
});
|
||||
// Summiere Trade Count und Volume pro Tag (alle Exchanges zusammen)
|
||||
const dailyTotals = {};
|
||||
dates.forEach(date => {
|
||||
const dayRows = data.filter(r => r[dateIdx] === date);
|
||||
dailyTotals[date] = {
|
||||
tradeCount: dayRows.reduce((sum, r) => sum + (r[countIdx] || 0), 0),
|
||||
volume: dayRows.reduce((sum, r) => sum + (r[volumeIdx] || 0), 0)
|
||||
};
|
||||
});
|
||||
|
||||
// Moving Average berechnen (7-Tage gleitender Durchschnitt)
|
||||
const maWindow = 7;
|
||||
const tradeCountData = dates.map(d => dailyTotals[d].tradeCount);
|
||||
const volumeData = dates.map(d => dailyTotals[d].volume);
|
||||
|
||||
const calculateMA = (data, window) => {
|
||||
return data.map((val, idx) => {
|
||||
if (idx < window - 1) return null;
|
||||
const slice = data.slice(idx - window + 1, idx + 1);
|
||||
return slice.reduce((a, b) => a + b, 0) / window;
|
||||
});
|
||||
};
|
||||
|
||||
const maTradeCount = calculateMA(tradeCountData, maWindow);
|
||||
const maVolume = calculateMA(volumeData, maWindow);
|
||||
|
||||
const datasets = [
|
||||
{
|
||||
label: 'Trades (täglich)',
|
||||
data: tradeCountData,
|
||||
borderColor: '#38bdf8',
|
||||
backgroundColor: '#38bdf833',
|
||||
borderWidth: 1,
|
||||
yAxisID: 'y',
|
||||
tension: 0.3,
|
||||
pointRadius: 2
|
||||
},
|
||||
{
|
||||
label: `Trades (${maWindow}-Tage MA)`,
|
||||
data: maTradeCount,
|
||||
borderColor: '#38bdf8',
|
||||
backgroundColor: 'transparent',
|
||||
borderWidth: 3,
|
||||
yAxisID: 'y',
|
||||
tension: 0.4,
|
||||
pointRadius: 0
|
||||
},
|
||||
{
|
||||
label: 'Volumen (täglich)',
|
||||
data: volumeData,
|
||||
borderColor: '#10b981',
|
||||
backgroundColor: '#10b98133',
|
||||
borderWidth: 1,
|
||||
yAxisID: 'y1',
|
||||
tension: 0.3,
|
||||
pointRadius: 2
|
||||
},
|
||||
{
|
||||
label: `Volumen (${maWindow}-Tage MA)`,
|
||||
data: maVolume,
|
||||
borderColor: '#10b981',
|
||||
backgroundColor: 'transparent',
|
||||
borderWidth: 3,
|
||||
yAxisID: 'y1',
|
||||
tension: 0.4,
|
||||
pointRadius: 0
|
||||
}
|
||||
];
|
||||
|
||||
charts.movingAverage = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
@@ -321,7 +341,7 @@
|
||||
type: 'linear',
|
||||
display: true,
|
||||
position: 'left',
|
||||
title: { display: true, text: 'Trade Count', color: '#94a3b8' },
|
||||
title: { display: true, text: 'Anzahl Trades', color: '#94a3b8' },
|
||||
grid: { color: 'rgba(255,255,255,0.05)' },
|
||||
ticks: { color: '#64748b' }
|
||||
},
|
||||
@@ -329,13 +349,20 @@
|
||||
type: 'linear',
|
||||
display: true,
|
||||
position: 'right',
|
||||
title: { display: true, text: 'Volume (€)', color: '#94a3b8' },
|
||||
title: { display: true, text: 'Volumen (€)', color: '#94a3b8' },
|
||||
grid: { drawOnChartArea: false },
|
||||
ticks: { color: '#64748b' }
|
||||
ticks: {
|
||||
color: '#64748b',
|
||||
callback: function(value) {
|
||||
if (value >= 1e6) return (value / 1e6).toFixed(1) + 'M';
|
||||
if (value >= 1e3) return (value / 1e3).toFixed(0) + 'k';
|
||||
return value;
|
||||
}
|
||||
}
|
||||
},
|
||||
x: {
|
||||
grid: { display: false },
|
||||
ticks: { color: '#64748b' }
|
||||
ticks: { color: '#64748b', maxRotation: 45 }
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
@@ -343,6 +370,22 @@
|
||||
display: true,
|
||||
position: 'bottom',
|
||||
labels: { color: '#94a3b8', boxWidth: 12, usePointStyle: true, padding: 15 }
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function(context) {
|
||||
let label = context.dataset.label || '';
|
||||
if (label) label += ': ';
|
||||
if (context.parsed.y !== null) {
|
||||
if (context.dataset.yAxisID === 'y1') {
|
||||
label += '€' + context.parsed.y.toLocaleString();
|
||||
} else {
|
||||
label += context.parsed.y.toLocaleString();
|
||||
}
|
||||
}
|
||||
return label;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user