This commit is contained in:
2026-09-10 13:01:11 +02:00
parent 7c68220404
commit d5e5b78334
5 changed files with 554 additions and 17 deletions
+71 -1
View File
@@ -3,9 +3,10 @@ import logging
import os
from datetime import datetime
import httpx
import websockets
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.responses import FileResponse, HTMLResponse
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
logging.basicConfig(level=logging.INFO)
@@ -38,6 +39,7 @@ HOME_HTML = """\
<link rel="stylesheet" href="/css/output.css">
<script src="https://unpkg.com/htmx.org@2/dist/htmx.min.js"></script>
<script src="https://unpkg.com/htmx-ext-ws@2/ws.js"></script>
<script src="https://unpkg.com/lightweight-charts@4/dist/lightweight-charts.standalone.production.js"></script>
</head>
<body>
<div class="mx-auto max-w-lg px-4 py-6">
@@ -58,8 +60,62 @@ HOME_HTML = """\
</div>
</div>
</div>
<div class="mt-4">
<div class="bg-white rounded-2xl shadow-sm border border-gray-200 p-6">
<p class="text-sm text-gray-500 mb-3">1h Candles (1m)</p>
<div id="chart-minute" style="height:300px"></div>
</div>
</div>
<div class="mt-4">
<div class="bg-white rounded-2xl shadow-sm border border-gray-200 p-6">
<p class="text-sm text-gray-500 mb-3">24h Candles (1h)</p>
<div id="chart-hour" style="height:300px"></div>
</div>
</div>
<div class="hidden text-4xl text-2xl font-bold text-green-600 text-red-600 text-gray-900 text-xs text-gray-400 mt-2 flex justify-center justify-between items-center gap-4 mt-1 mt-3 text-sm text-gray-600 text-gray-500 mb-3 py-1 grid grid-cols-2 gap-x-6 gap-y-1" aria-hidden="true"></div>
</div>
<script>
(function() {
function initChart(containerId, endpoint, intervalMs) {
const container = document.getElementById(containerId);
const chart = LightweightCharts.createChart(container, {
layout: { background: { type: 'solid', color: '#ffffff' }, textColor: '#6b7280' },
grid: { vertLines: { color: '#f3f4f6' }, horzLines: { color: '#f3f4f6' } },
rightPriceScale: { borderColor: '#e5e7eb' },
timeScale: { borderColor: '#e5e7eb', timeVisible: true, secondsVisible: false },
});
const series = chart.addCandlestickSeries({
upColor: '#16a34a', downColor: '#dc2626',
borderUpColor: '#16a34a', borderDownColor: '#dc2626',
wickUpColor: '#16a34a', wickDownColor: '#dc2626',
});
function loadData() {
fetch(endpoint)
.then(r => r.json())
.then(candles => {
const data = candles.map(c => ({
time: c.ts,
open: parseFloat(c.open),
high: parseFloat(c.high),
low: parseFloat(c.low),
close: parseFloat(c.close),
})).sort((a, b) => a.time - b.time);
series.setData(data);
chart.timeScale().fitContent();
})
.catch(e => console.error('Chart fetch error:', e));
}
loadData();
setInterval(loadData, intervalMs);
new ResizeObserver(() => {
chart.applyOptions({ width: container.clientWidth });
}).observe(container);
}
initChart('chart-minute', '/api/kraken-minute', 60 * 1000);
initChart('chart-hour', '/api/kraken-hour', 5 * 60 * 1000);
})();
</script>
</body>
</html>"""
@@ -77,6 +133,20 @@ async def health():
return {"status": "up"}
@app.get("/api/kraken-hour")
async def kraken_hour():
async with httpx.AsyncClient() as client:
resp = await client.get(f"{BTCDATA_URL}/api/kraken-hour", timeout=10)
return JSONResponse(content=resp.json(), status_code=resp.status_code)
@app.get("/api/kraken-minute")
async def kraken_minute():
async with httpx.AsyncClient() as client:
resp = await client.get(f"{BTCDATA_URL}/api/kraken-minute", timeout=10)
return JSONResponse(content=resp.json(), status_code=resp.status_code)
# --- Formatting helpers (Danish locale) ---