207 lines
6.5 KiB
HTML
207 lines
6.5 KiB
HTML
<!-- templates/index.html -->
|
|
<!DOCTYPE html>
|
|
<html>
|
|
|
|
<head>
|
|
<title>Realtime Candlestick Chart</title>
|
|
<script src="https://cdn.jsdelivr.net/npm/luxon@3.4.4"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@1.3.1"></script>
|
|
<script src="{{ url_for('static', path='js/chartjs-chart-financial.js') }}"></script>
|
|
<link href="https://cdn.jsdelivr.net/npm/flowbite@2.5.2/dist/flowbite.min.css" rel="stylesheet" />
|
|
<script src="https://cdn.jsdelivr.net/npm/flowbite@2.5.2/dist/flowbite.min.js"></script>
|
|
</head>
|
|
|
|
<body>
|
|
<div class="flex w-full mt-8">
|
|
<canvas id="chart"></canvas>
|
|
</div>
|
|
<script>
|
|
var barCount = 100;
|
|
var barData = [];
|
|
var lineData = [];
|
|
|
|
var ctx = document.getElementById('chart').getContext('2d');
|
|
ctx.canvas.width = 1000;
|
|
ctx.canvas.height = 250;
|
|
|
|
var chart = new Chart(ctx, {
|
|
type: 'candlestick',
|
|
data: {
|
|
datasets: [{
|
|
label: '{{coin}}',
|
|
data: barData
|
|
}, {
|
|
label: 'Close price',
|
|
type: 'line',
|
|
data: lineData,
|
|
hidden: true
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
scales: {
|
|
x: {
|
|
type: 'time',
|
|
time: {
|
|
unit: 'minute',
|
|
displayFormats: {
|
|
minute: 'HH:mm'
|
|
}
|
|
},
|
|
adapters: {
|
|
date: {
|
|
zone: 'UTC'
|
|
}
|
|
}
|
|
},
|
|
y: {
|
|
type: 'linear',
|
|
position: 'right'
|
|
}
|
|
},
|
|
animation: false
|
|
}
|
|
});
|
|
|
|
async function fetchHistoricalData() {
|
|
try {
|
|
const response = await fetch('/data/{{coin}}/get-candle/all');
|
|
const data = await response.json();
|
|
return data;
|
|
} catch (error) {
|
|
console.error('Error fetching historical data:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async function fetchLatestData() {
|
|
try {
|
|
const response = await fetch('/data/{{coin}}/get-candle');
|
|
const data = await response.json();
|
|
return data;
|
|
} catch (error) {
|
|
console.error('Error fetching latest data:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async function initializeChart() {
|
|
// Fetch historical data first
|
|
const historicalData = await fetchHistoricalData();
|
|
if (!historicalData) return;
|
|
|
|
// Process historical data
|
|
historicalData.forEach(data => {
|
|
const candleData = {
|
|
x: data.time,
|
|
o: data.open,
|
|
h: data.high,
|
|
l: data.low,
|
|
c: data.close,
|
|
v: data.volume,
|
|
vwap: data.vwap,
|
|
trades: data.trades
|
|
};
|
|
|
|
barData.push(candleData);
|
|
lineData.push({
|
|
x: data.time,
|
|
y: data.close
|
|
});
|
|
});
|
|
|
|
chart.update();
|
|
|
|
// Start real-time updates
|
|
setInterval(updateChart, 5000);
|
|
}
|
|
|
|
async function updateChart() {
|
|
const newData = await fetchLatestData();
|
|
if (!newData) return;
|
|
|
|
if (barData.length >= barCount) {
|
|
barData.shift();
|
|
lineData.shift();
|
|
}
|
|
|
|
const newBarData = {
|
|
x: newData.time,
|
|
o: newData.open,
|
|
h: newData.high,
|
|
l: newData.low,
|
|
c: newData.close,
|
|
v: newData.volume,
|
|
vwap: newData.vwap,
|
|
trades: newData.trades
|
|
};
|
|
|
|
// Check if the last candle's timestamp matches the new data
|
|
const lastCandle = barData[barData.length - 1];
|
|
if (lastCandle && lastCandle.x === newData.time) {
|
|
// Update the existing candle
|
|
Object.assign(lastCandle, newBarData);
|
|
lineData[lineData.length - 1].y = newData.close;
|
|
} else {
|
|
// Add new candle
|
|
barData.push(newBarData);
|
|
lineData.push({
|
|
x: newData.time,
|
|
y: newData.close
|
|
});
|
|
}
|
|
|
|
chart.update();
|
|
}
|
|
|
|
// Initialize the chart when the page loads
|
|
initializeChart();
|
|
|
|
var update = function () {
|
|
var dataset = chart.config.data.datasets[0];
|
|
|
|
// candlestick vs ohlc
|
|
var type = document.getElementById('type').value;
|
|
chart.config.type = type;
|
|
|
|
// linear vs log
|
|
var scaleType = document.getElementById('scale-type').value;
|
|
chart.config.options.scales.y.type = scaleType;
|
|
|
|
// color
|
|
var colorScheme = document.getElementById('color-scheme').value;
|
|
if (colorScheme === 'neon') {
|
|
chart.config.data.datasets[0].backgroundColors = {
|
|
up: '#01ff01',
|
|
down: '#fe0000',
|
|
unchanged: '#999',
|
|
};
|
|
} else {
|
|
delete chart.config.data.datasets[0].backgroundColors;
|
|
}
|
|
|
|
// border
|
|
var border = document.getElementById('border').value;
|
|
if (border === 'false') {
|
|
dataset.borderColors = 'rgba(0, 0, 0, 0)';
|
|
} else {
|
|
delete dataset.borderColors;
|
|
}
|
|
|
|
// mixed charts
|
|
var mixed = document.getElementById('mixed').value;
|
|
if (mixed === 'true') {
|
|
chart.config.data.datasets[1].hidden = false;
|
|
} else {
|
|
chart.config.data.datasets[1].hidden = true;
|
|
}
|
|
|
|
chart.update();
|
|
};
|
|
|
|
[...document.getElementsByTagName('select')].forEach(element => element.addEventListener('change', update));
|
|
</script>
|
|
</body>
|
|
|
|
</html> |