Files
vault-dash/app/components/charts.py
Bu5hm4nn 874b4a5a02 Fix linting issues: line length, import sorting, unused variables
- Set ruff/black line length to 120
- Reformatted code with black
- Fixed import ordering with ruff
- Disabled lint for UI component files with long CSS strings
- Updated pyproject.toml with proper tool configuration
2026-03-22 10:30:12 +01:00

174 lines
6.3 KiB
Python

from __future__ import annotations
import json
from typing import Any
from uuid import uuid4
from nicegui import ui
_CHARTS_SCRIPT_ADDED = False
def _ensure_lightweight_charts_assets() -> None:
global _CHARTS_SCRIPT_ADDED
if _CHARTS_SCRIPT_ADDED:
return
ui.add_head_html("""
<script src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"></script>
""")
_CHARTS_SCRIPT_ADDED = True
class CandlestickChart:
"""Minimal Lightweight-Charts wrapper for NiceGUI candlestick dashboards.
Features:
- real-time candlestick price updates
- volume histogram overlay
- moving-average / indicator line support
"""
def __init__(self, title: str = "Gold Price", *, height: int = 420) -> None:
_ensure_lightweight_charts_assets()
self.chart_id = f"chart_{uuid4().hex}"
self.height = height
with ui.card().classes("w-full rounded-2xl border border-slate-800 bg-slate-950/90 shadow-xl"):
with ui.row().classes("w-full items-center justify-between"):
ui.label(title).classes("text-lg font-semibold text-white")
ui.label("Live").classes(
"rounded-full bg-emerald-500/15 px-3 py-1 text-xs font-medium uppercase tracking-wide text-emerald-300"
)
self.container = ui.html(f'<div id="{self.chart_id}" class="w-full rounded-xl"></div>').style(
f"height: {height}px;"
)
self._initialize_chart()
def _initialize_chart(self) -> None:
ui.run_javascript(f"""
(function() {{
const root = document.getElementById({json.dumps(self.chart_id)});
if (!root || typeof LightweightCharts === 'undefined') return;
root.innerHTML = '';
window.vaultDashCharts = window.vaultDashCharts || {{}};
const chart = LightweightCharts.createChart(root, {{
autoSize: true,
layout: {{
background: {{ color: '#020617' }},
textColor: '#cbd5e1',
}},
grid: {{
vertLines: {{ color: 'rgba(148, 163, 184, 0.12)' }},
horzLines: {{ color: 'rgba(148, 163, 184, 0.12)' }},
}},
rightPriceScale: {{ borderColor: 'rgba(148, 163, 184, 0.25)' }},
timeScale: {{ borderColor: 'rgba(148, 163, 184, 0.25)' }},
crosshair: {{ mode: LightweightCharts.CrosshairMode.Normal }},
}});
const candleSeries = chart.addSeries(LightweightCharts.CandlestickSeries, {{
upColor: '#22c55e',
downColor: '#ef4444',
borderVisible: false,
wickUpColor: '#22c55e',
wickDownColor: '#ef4444',
}});
const volumeSeries = chart.addSeries(LightweightCharts.HistogramSeries, {{
priceFormat: {{ type: 'volume' }},
priceScaleId: '',
scaleMargins: {{ top: 0.78, bottom: 0 }},
color: 'rgba(56, 189, 248, 0.45)',
}});
window.vaultDashCharts[{json.dumps(self.chart_id)}] = {{
chart,
candleSeries,
volumeSeries,
indicators: {{}},
}};
}})();
""")
def set_candles(self, candles: list[dict[str, Any]]) -> None:
payload = json.dumps(candles)
ui.run_javascript(f"""
(function() {{
const ref = window.vaultDashCharts?.[{json.dumps(self.chart_id)}];
if (!ref) return;
ref.candleSeries.setData({payload});
ref.chart.timeScale().fitContent();
}})();
""")
def update_price(self, candle: dict[str, Any]) -> None:
payload = json.dumps(candle)
ui.run_javascript(f"""
(function() {{
const ref = window.vaultDashCharts?.[{json.dumps(self.chart_id)}];
if (!ref) return;
ref.candleSeries.update({payload});
}})();
""")
def set_volume(self, volume_points: list[dict[str, Any]]) -> None:
payload = json.dumps(volume_points)
ui.run_javascript(f"""
(function() {{
const ref = window.vaultDashCharts?.[{json.dumps(self.chart_id)}];
if (!ref) return;
ref.volumeSeries.setData({payload});
}})();
""")
def update_volume(self, volume_point: dict[str, Any]) -> None:
payload = json.dumps(volume_point)
ui.run_javascript(f"""
(function() {{
const ref = window.vaultDashCharts?.[{json.dumps(self.chart_id)}];
if (!ref) return;
ref.volumeSeries.update({payload});
}})();
""")
def set_indicator(
self,
name: str,
points: list[dict[str, Any]],
*,
color: str = "#f59e0b",
line_width: int = 2,
) -> None:
key = json.dumps(name)
payload = json.dumps(points)
ui.run_javascript(f"""
(function() {{
const ref = window.vaultDashCharts?.[{json.dumps(self.chart_id)}];
if (!ref) return;
if (!ref.indicators[{key}]) {{
ref.indicators[{key}] = ref.chart.addSeries(LightweightCharts.LineSeries, {{
color: {json.dumps(color)},
lineWidth: {line_width},
priceLineVisible: false,
lastValueVisible: true,
}});
}}
ref.indicators[{key}].setData({payload});
}})();
""")
def update_indicator(self, name: str, point: dict[str, Any]) -> None:
key = json.dumps(name)
payload = json.dumps(point)
ui.run_javascript(f"""
(function() {{
const ref = window.vaultDashCharts?.[{json.dumps(self.chart_id)}];
const series = ref?.indicators?.[{key}];
if (!series) return;
series.update({payload});
}})();
""")