WebUI: Trading beenden, ohne die Anwendung zu beenden

Umsetzung von #563.

Entscheidung Richard, 13.09.2026: Beenden soll ueber das WebUI gehen, aber
beschraenkt auf das Trading. Ein Ende der ganzen Anwendung naehme den Webserver
mit; danach waere das WebUI nicht mehr erreichbar.

POST /api/dashboard/stop-trading setzt Live- und Demo-Modus auf Inaktiv und
schaltet die Pause ein. Damit halten CopyTrading und ResolutionFarming (#569)
an, wie beim Ende der Anwendung. Offene Orders an der Boerse werden bewusst
nicht storniert: Das tut auch das Beenden der App nicht, und ein Eingriff in
die CLOB-Integration gehoert nicht in einen Notschalter. Der Dialog sagt das
ausdruecklich.

Die Route verlangt {"confirmed": true}. Ein Aufruf ohne Absicht, etwa aus einem
alten Skript, beendet nichts. Der Vorgang wird als Warnung protokolliert, mit
den Modi vorher.

Im Dashboard steht unter der Notfall-Pause jetzt "Trading beenden ...". Der
Dialog ist wie im Desktop 10 Sekunden gesperrt, Abbrechen und Esc gehen
jederzeit. Wieder aufgenommen wird nur ueber die Modus-Schalter. Die Pause
aufzuheben genuegt nicht, das haelt ein Test fest.

Offen bis #570: Laufende Verkaufsleitern und der REST-Pre-Redeem pruefen die
Pause nicht. Der Inaktiv-Modus, den dieser Knopf setzt, sperrt den Pre-Redeem
bereits; die Leitern friert erst #570 ein.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-09-13 13:51:24 +02:00
co-authored by Claude Opus 5
parent 99172a6be2
commit d33d695db5
4 changed files with 228 additions and 0 deletions
+61
View File
@@ -6,6 +6,9 @@ let allLogs = [];
// Init on DOM ready
document.addEventListener('DOMContentLoaded', () => {
document.addEventListener('keydown', e => {
if (e.key === 'Escape' && document.getElementById('modalStopTrading')?.style.display === 'flex') closeStopTradingModal();
});
initNavMenus();
refreshNavModules();
initSseLogs();
@@ -269,6 +272,64 @@ async function togglePause() {
} catch (e) {}
}
// ==================== Trading beenden (#563) ====================
const STOP_TRADING_LOCK_SECONDS = 10;
let stopTradingTimer = null;
function openStopTradingModal() {
const modal = document.getElementById('modalStopTrading');
const btn = document.getElementById('btnConfirmStopTrading');
const err = document.getElementById('stopTradingError');
if (!modal || !btn) return;
err.hidden = true;
let remaining = STOP_TRADING_LOCK_SECONDS;
btn.disabled = true;
btn.textContent = `Trading beenden (${remaining} s)`;
modal.style.display = 'flex';
clearInterval(stopTradingTimer);
stopTradingTimer = setInterval(() => {
remaining--;
if (remaining > 0) {
btn.textContent = `Trading beenden (${remaining} s)`;
return;
}
clearInterval(stopTradingTimer);
stopTradingTimer = null;
btn.textContent = 'Trading beenden';
btn.disabled = false;
}, 1000);
}
function closeStopTradingModal() {
clearInterval(stopTradingTimer);
stopTradingTimer = null;
const modal = document.getElementById('modalStopTrading');
if (modal) modal.style.display = 'none';
}
async function confirmStopTrading() {
const btn = document.getElementById('btnConfirmStopTrading');
const err = document.getElementById('stopTradingError');
btn.disabled = true;
try {
const res = await fetch('/api/dashboard/stop-trading', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ confirmed: true })
});
if (!res.ok) throw new Error(await errorText(res));
closeStopTradingModal();
refreshDashboard();
} catch (e) {
err.textContent = `Trading konnte nicht beendet werden: ${e.message}`;
err.hidden = false;
btn.disabled = false;
}
}
// Dashboard Account-Übersicht
async function loadDashboardAccounts() {
try {