94 lines
3.2 KiB
HTML
94 lines
3.2 KiB
HTML
{% extends "layout.html" %}
|
|
|
|
{% block title %}Real-Time Chart — IBKR Dashboard{% endblock %}
|
|
|
|
{% block extra_head %}
|
|
<script src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"></script>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="chart-section">
|
|
<div id="controls">
|
|
<div class="control-group">
|
|
<input type="text" id="symbolInput" placeholder="Enter symbol (e.g. AAPL)" value="{{ symbol }}">
|
|
<button onclick="subscribeSymbol()">Load Chart</button>
|
|
</div>
|
|
<div class="loading-indicator" id="loadingIndicator">Loading...</div>
|
|
</div>
|
|
<div id="chart"></div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_scripts %}
|
|
<script>
|
|
const chart = LightweightCharts.createChart(document.getElementById('chart'), {
|
|
width: document.getElementById('chart').clientWidth,
|
|
height: 600,
|
|
layout: { background: { color: '#ffffff' }, textColor: '#333' },
|
|
grid: { vertLines: { color: '#f0f0f0' }, horzLines: { color: '#f0f0f0' } },
|
|
timeScale: { timeVisible: true, secondsVisible: false }
|
|
});
|
|
const series = chart.addCandlestickSeries();
|
|
|
|
let eventSource = null;
|
|
|
|
function cleanupConnections() {
|
|
if (eventSource) {
|
|
console.log('Closing EventSource connection');
|
|
eventSource.close();
|
|
eventSource = null;
|
|
}
|
|
}
|
|
|
|
async function subscribeSymbol() {
|
|
cleanupConnections();
|
|
const symbol = document.getElementById("symbolInput").value.toUpperCase();
|
|
document.getElementById("loadingIndicator").classList.add("show");
|
|
document.getElementById("chart").classList.add("loading");
|
|
|
|
const historyResponse = await fetch(`/history?symbol=${symbol}`);
|
|
const historicalData = await historyResponse.json();
|
|
series.setData(historicalData);
|
|
|
|
await fetch("/subscribe", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ symbol })
|
|
});
|
|
|
|
eventSource = new EventSource("/stream");
|
|
|
|
eventSource.addEventListener("candle", (event) => {
|
|
const candleData = JSON.parse(event.data);
|
|
series.update(candleData);
|
|
});
|
|
|
|
eventSource.addEventListener("error", (event) => {
|
|
console.error('SSE Error:', event);
|
|
});
|
|
|
|
eventSource.addEventListener("open", () => {
|
|
console.log('SSE Connection Opened');
|
|
document.getElementById("loadingIndicator").classList.remove("show");
|
|
document.getElementById("chart").classList.remove("loading");
|
|
});
|
|
}
|
|
|
|
window.addEventListener('DOMContentLoaded', () => {
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const symbol = urlParams.get('symbol');
|
|
if (symbol) {
|
|
document.getElementById("symbolInput").value = symbol;
|
|
subscribeSymbol();
|
|
}
|
|
});
|
|
|
|
window.addEventListener('resize', () => {
|
|
chart.applyOptions({ width: document.getElementById('chart').clientWidth });
|
|
});
|
|
|
|
window.addEventListener('beforeunload', cleanupConnections);
|
|
window.addEventListener('pagehide', cleanupConnections);
|
|
</script>
|
|
{% endblock %}
|