71 lines
2.4 KiB
JavaScript
71 lines
2.4 KiB
JavaScript
const { createApp, ref, computed, onMounted, onUnmounted } = Vue;
|
|
|
|
createApp({
|
|
setup() {
|
|
const status = ref({ stream: 'disconnected', bars_held: {} });
|
|
const price = ref(null);
|
|
const timeframe = ref('1m');
|
|
const timeframes = ['1m', '2m', '5m', '15m', '30m', '1h', '4h', '1d'];
|
|
const now = ref(Date.now());
|
|
let chartApi = null;
|
|
let socket = null;
|
|
let timer = null;
|
|
|
|
const barAge = computed(() => {
|
|
if (!status.value.last_bar_t) return '—';
|
|
const seconds = Math.max(0, Math.floor(now.value / 1000 - status.value.last_bar_t));
|
|
return seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}m`;
|
|
});
|
|
|
|
async function refreshStatus() {
|
|
const response = await fetch('/api/status');
|
|
if (response.ok) status.value = await response.json();
|
|
}
|
|
|
|
function connect() {
|
|
const protocol = location.protocol === 'https:' ? 'wss' : 'ws';
|
|
socket = new WebSocket(`${protocol}://${location.host}/ws`);
|
|
socket.onopen = () => socket.send(JSON.stringify({ type: 'subscribe', tf: timeframe.value }));
|
|
socket.onmessage = ({ data }) => {
|
|
const message = JSON.parse(data);
|
|
if (message.type === 'snapshot') {
|
|
chartApi.setBars(message.bars);
|
|
chartApi.syncLevels(message.levels || []);
|
|
price.value = message.price;
|
|
} else if (message.type === 'bar') {
|
|
chartApi.updateBar(message.bar);
|
|
price.value = message.bar.c;
|
|
status.value.last_bar_t = message.bar.t;
|
|
} else if (message.type === 'levels') {
|
|
chartApi.syncLevels(message.levels);
|
|
}
|
|
};
|
|
socket.onclose = () => {
|
|
status.value.stream = 'disconnected';
|
|
setTimeout(connect, 2000);
|
|
};
|
|
}
|
|
|
|
function selectTimeframe(tf) {
|
|
timeframe.value = tf;
|
|
if (socket?.readyState === WebSocket.OPEN) {
|
|
socket.send(JSON.stringify({ type: 'subscribe', tf }));
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
chartApi = new ConfluenceChart();
|
|
chartApi.create(document.getElementById('chart'));
|
|
refreshStatus();
|
|
connect();
|
|
timer = setInterval(() => { now.value = Date.now(); refreshStatus(); }, 5000);
|
|
});
|
|
onUnmounted(() => {
|
|
clearInterval(timer);
|
|
if (socket) socket.close();
|
|
if (chartApi) chartApi.destroy();
|
|
});
|
|
|
|
return { status, price, barAge, timeframe, timeframes, selectTimeframe };
|
|
},
|
|
}).mount('#app');
|