From 0dd2e524b77298ce68ad0d82a1e7b9adf0a0308d Mon Sep 17 00:00:00 2001 From: Axel Hocks Date: Fri, 21 Aug 2026 10:47:44 +0200 Subject: [PATCH] S/R-Auto-Close komplett entfernt + Circuit-Breaker als runder CB-Knopf (v=210) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (1) S/R-AUTO-CLOSE RAUS (User-Vorgabe: "die brauche ich nicht mehr"). Der Schalter stand seit dem 19.08. ohnehin auf AUS - abgeschaltet, nachdem analyze_pbreak_live.py das P(break)-Modell live als INVERTIERT gemessen hatte (AUC 0,452, Klassen monoton rueckwaerts). Ohne funktionierendes Gate ist der S/R-Close faktisch der PAUSCHALE, und der ist 2x gemessen und verworfen (Gewinner-Kappen). Die Entfernung vollzieht den gemessenen Stand nach. WEG: _check_sr_close, set_sr_autoclose, set_sr_close_min_gain, die Konstruktor-Felder, die %-Armierung des Gewinn-Close, drei Snapshot-Felder, beide runtime_state-Schluessel, die Reichweiten- und Systemstatus-Zeilen, /api/srclose und /api/srclose_min, der Knopf S/R, das Gewinn-Close-Feld, der Toast "Bot hat am Level geschlossen". BLEIBT: `_sr_close_hint` (App-Hinweis, P(break)-Chartlinien, Copilot-Kontext, pbreak_predictions-Tracking), `stop_approach`, das Modell selbst und die closed_by=sr_close-Historie in der DB. Live gegengeprueft: die drei Felder sind aus dem Snapshot verschwunden, sr_close_hint und stop_approach sind da, beide Endpunkte antworten 405. Sicherung: .removed_backup/sr_auto_close_2026-08-21.py ⚠ Der Close-Alarm gilt jetzt schlicht ab jedem Gewinn (`minGain = 0`) - die Schwelle gab es nur fuer den Auto-Close. Das ist der Zustand von vor 2026-07. ⚠ Der Test test_automatik_zeile_nennt_alle_schalter war MITGEZAEHLT und faellt korrekt - er ist angepasst, nicht entfernt: jetzt zwei Schalter statt drei, und die Negativ-Pruefungen ("SIG"/"S/R-Close" duerfen NICHT auftauchen) sind der eigentliche Wert, denn ein wieder auftauchender Pfad waere ein stiller Rueckbau. (2) CIRCUIT BREAKER als runder Knopf "CB" rechts neben BALANCE. ⚠ Farb-Logik wie vorgegeben: GRUEN = inaktiv (aus), ROT = aktiv (scharf). Bewusst NICHT die uebliche "gruen = geschuetzt"-Lesart - hier signalisiert Rot, dass eine Automatik eingreifen kann. Im Code und im Markup steht das dran, damit es niemand spaeter "korrigiert". ⚠ VERSCHOBEN, nicht kopiert: zwei Elemente mit derselben ID waeren ein stiller Fehler, weil getElementById nur das erste findet. ⚠ Beim Entfernen zwei eigene Fehler: (a) ein Slice ueber `rindex("try:")` schnitt durch fremde Konstruktor-Bloecke und riss Kommentar-Fragmente heraus - zurueckgesetzt und zeilenbasiert von hinten neu gemacht; (b) beim Toast-Block eine Klammer zu viel entfernt, von `node --check` sofort gefangen. Pipeline A-G gruen, 97 Tests, 14 render*-Funktionen, deployt und am ausgelieferten HTML/Snapshot verifiziert. Co-Authored-By: Claude Opus 5 --- core/engine.py | 134 ++++-------------------------------- server.py | 19 ----- tests/test_system_status.py | 16 +++-- web/app.js | 74 +++++--------------- web/index.html | 26 +++++-- web/style.css | 15 ++++ 6 files changed, 75 insertions(+), 209 deletions(-) diff --git a/core/engine.py b/core/engine.py index aa34bf3..8325687 100644 --- a/core/engine.py +++ b/core/engine.py @@ -444,14 +444,11 @@ class TradingEngine: self._close_alert_ts = 0.0 # letzter CLOSE-Telegram-Push (5-min-Cooldown) self._close_alert_ticket = None # Ticket, auf das sich der Cooldown bezieht self._close_alert_count = 0 # wie oft der Flip-Close-Alarm gefeuert hat - self._auto_sr_close = self.cfg["trading"].get("auto_sr_close", "true").lower() == "true" try: self._sr_close_pbreak = float(self.cfg["trading"].get("sr_close_pbreak", "0.60")) except (TypeError, ValueError): self._sr_close_pbreak = 0.60 self._sr_close_pbreak = max(0.30, min(0.90, self._sr_close_pbreak)) # sinnvolles Band - self._sr_close_count = 0 # wie oft der Bot am S/R automatisch schloss - self._sr_close_last_ticket = None # verhindert Doppel-Auslösung je Ticket # Autonomer Entry auf den gemessen-validierten Squeeze-Ausbruch (nur FLAT). # ⚠ ECHTE Order — Default AUS (Autonomie nie stillschweigend an). `[trading] # auto_squeeze=true` aktiviert. Dedup über (dir, level) des Ausbruchs. @@ -575,24 +572,6 @@ class TradingEngine: self._flip_close_count = 0 self._pos_close_dir = None # Art) → Squeeze-Wiedereinstiegs-Sperre (gegen die # Open→Instant-Close-Kaskade bei zu engem Notfall-Stop) - self._sr_close_min_gain_armed_ticket = None # Ticket, für das die %-Schwelle schon - # aus echter Margin berechnet wurde (Retry-Schutz, - # s. `_check_auto_close`) - try: - self._sr_close_min_gain = max(0.0, float( - self.cfg["trading"].get("sr_close_min_gain", "0"))) - except (TypeError, ValueError): - self._sr_close_min_gain = 0.0 - # Gewinn-Close automatisch auf X% des Einsatzes (Margin) DIESER Position setzen, - # sobald sie eröffnet wird (User-Vorgabe 2026-07-23) — überschreibt den manuell - # gemerkten/persistierten Wert bei jeder ECHT NEUEN Position (nicht beim - # Wiedererkennen nach Neustart, s. `_check_auto_close`). 0 = aus (dann bleibt der - # manuell gesetzte/gemerkte Wert wie bisher maßgeblich). - try: - self._sr_close_min_gain_pct = max(0.0, float( - self.cfg["trading"].get("sr_close_min_gain_pct", "1.0"))) - except (TypeError, ValueError): - self._sr_close_min_gain_pct = 1.0 self._pb_state = None # geglättetes P(break) + Hysterese-Zone je Level self._stop_pb_state = None # dito für die Gegen-/Stop-Seiten-Anzeige # (gemessen `analyze_pbreak_flicker.py`: mom3 der @@ -946,7 +925,6 @@ class TradingEngine: self._check_slot_reichweite() # meldet ungedeckte 2. Position self._check_adverse15() # 15-Min-Regel (nur Bot-Trades) self._check_flip_close() # Empfehlung dreht → schließen - self._check_sr_close() # ZUERST die ruhenden Stop-Orders pflegen (setzen/stornieren), # DANN der Market-Fallback: so kann eine bereits liegende Order # den Ausbruch am Level abfangen, bevor nachgejagt würde. @@ -1202,8 +1180,6 @@ class TradingEngine: or (getattr(self, "_emergency_pct", 0) or 0) > 0 or (getattr(self, "_emergency_remember", 0) or 0) > 0): offen.append("Notfall-Stop") - if getattr(self, "_auto_sr_close", False): - offen.append("S/R-Close") if (getattr(self, "_adverse15_atr", 0) or 0) > 0: offen.append("15-Min-Regel") if getattr(self, "_flip_close", False): @@ -1321,7 +1297,6 @@ class TradingEngine: # bei offenem Trade kappte sonst die von Hand gesetzten −25 € auf −19,42). self._emergency_loss = self._emergency_state_loss self._takeprofit = self._emergency_state_tp - self._sr_close_min_gain_armed_ticket = ticket # wiederhergestellter Wert # gilt als final — NICHT auf 1% zurücksetzen self._emergency_margin_armed_ticket = ticket # dito für den margin-%-Stop log.info(f"Auto-Close wiederhergestellt (Pos {ticket}): Stop " @@ -1359,16 +1334,8 @@ class TradingEngine: # umgesetzt, der Ticket-Wechsel-Zweig feuert nur 1×) — konnte den Trade zu # früh (falscher, zu niedriger Alt-Wert) ODER dauerhaft mit falscher Schwelle # laufen lassen. - if (self._sr_close_min_gain_pct > 0 - and self._sr_close_min_gain_armed_ticket != ticket): - margin = ps.get("margin") or 0.0 - if margin > 0: - self._sr_close_min_gain = round( - self._sr_close_min_gain_pct / 100.0 * margin, 2) - self._sr_close_min_gain_armed_ticket = ticket - self._save_runtime_state() - log.info(f"Gewinn-Close armiert (Pos {ticket}): " - f"+{self._sr_close_min_gain:.2f} ({self._sr_close_min_gain_pct:.0f}% Margin {margin:.2f})") + # ⚠ Die automatische %-Armierung des Gewinn-Close ist am 2026-08-21 + # mit dem S/R-Auto-Close entfallen — sie diente ausschliesslich ihm. # Notfall-Stop als % der Margin — gleiches Retry-Muster (margin-Wert kann im # ersten Tick nach dem Öffnen noch 0 sein). Hat Vorrang vor Balance-%/Fixwert # (die im "echt neue Position"-Zweig oben bewusst übersprungen wurden, wenn @@ -1430,62 +1397,17 @@ class TradingEngine: self._takeprofit = None return - def _check_sr_close(self): - """Automatischer S/R-Close (User-Vorgabe): schließt eine Position **im Plus** - selbstständig, wenn die gemessene Bedingung greift (Kurs klebt am gegenüber- - liegenden Level + schwacher Anlauf → Durchbruch ~26 %, Close schlägt Laufen- - lassen; `backtest_srbreak.py`). Nur bei `auto_sr_close=true`, genau 1×/Ticket, - wirkt auch bei gesperrtem Handy. Fail-safe.""" - if not self._auto_sr_close: - return - if self._in_startup_grace(): # Startup-Schonfrist: kein Sofort-Close nach Neustart - return - ps = self.trader.snapshot() - ticket = ps.get("ticket") - if not ticket: - self._sr_close_last_ticket = None - return - if ticket == self._sr_close_last_ticket: # bereits ausgelöst → nicht erneut - return - pnl = ps.get("pnl") - if pnl is None or pnl <= 0: # NUR bei Trade im Plus (User-Vorgabe) - return - # Optionaler Mindestgewinn (User-Vorgabe; Default 0=aus — gemessen ist ohne - # besser): unter der Schwelle wird am Level NICHT geschlossen, Trade läuft. - if self._sr_close_min_gain > 0 and pnl < self._sr_close_min_gain: - return - hint = self._sr_close_hint(self.data.snapshot(), ps, self.wave.snapshot()) - if not hint or not hint.get("close"): # am Level, aber P(break) ≥ Schwelle → laufen lassen - return - self._sr_close_last_ticket = ticket # vor dem Close markieren (kein Doppel) - sym = self.data.symbol or "" - _confirm = hint.get("confirm") - log.info(f"🎯 S/R-AUTO-CLOSE: {sym} P&L {pnl:+.2f} — am Level {hint['level']}, " - f"Durchbruch nur ~{hint['p_break']}% (< {self._sr_close_pbreak:.0%})" - + (f" — Bestätigung erst ab {_confirm}" if _confirm else "") - + " → Gewinn sichern") - err = self.trader.close(reason="sr_close") - tg = self.cfg["telegram"] - tg_on = (tg.get("enabled", "false").lower() == "true" and tg.get("bot_token")) - if err: - log.error(f"S/R-Auto-Close fehlgeschlagen: {err}") - self._sr_close_last_ticket = None # Fehlschlag → nächster Tick erneut - if tg_on: - try: send_telegram(f"⚠ S/R-Close FEHLGESCHLAGEN\n{sym} P&L {pnl:+.2f} " - f"— bitte MANUELL schließen! ({err})", - tg["bot_token"], tg["chat_id"]) - except Exception as e: log.warning(f"S/R-Close-Telegram (Fehler): {e}") - return - # Richtung + Zeit merken → Auto-Squeeze-Cooldown gegen Gleichrichtungs-Roundtrip - self._sr_close_dir = "LONG" if ps.get("order_type") == 0 else "SHORT" - self._sr_close_ts = time.time() - self._sr_close_count += 1 - if tg_on: - try: send_telegram(f"🎯 S/R-Auto-Close\n{sym} bei P&L {pnl:+.2f} geschlossen " - f"— Kurs klebte am Level {hint['level']} mit schwachem Anlauf " - f"(Durchbruch ~{hint['p_break']}% unwahrscheinlich).", - tg["bot_token"], tg["chat_id"]) - except Exception as e: log.warning(f"S/R-Close-Telegram: {e}") + # ⚠⚠ `_check_sr_close` ENTFERNT am 2026-08-21 (User-Vorgabe). Der Schalter + # stand seit dem 19.08. auf AUS — abgeschaltet, nachdem + # `analyze_pbreak_live.py` das P(break)-Modell live als INVERTIERT gemessen + # hatte (AUC 0,452, Klassen monoton rueckwaerts). Ohne funktionierendes + # Gate ist der S/R-Close faktisch der PAUSCHALE, und der ist 2x gemessen + # und verworfen (Gewinner-Kappen). Die Entfernung vollzieht den gemessenen + # Stand nach; sie schaltet nichts ab, was noch lief. + # ⚠ BLEIBT: `_sr_close_hint` (App-Hinweis, P(break)-Chartlinien, + # Copilot-Kontext, `pbreak_predictions`-Tracking), `stop_approach`, das + # Modell selbst und die `closed_by=sr_close`-Historie in der DB. + # Sicherung: `.removed_backup/sr_auto_close_2026-08-21.py`. def _auto_guard(self, pfad: str) -> str | None: """Die NICHT richtungsabhängigen Gates für autonome Entries. @@ -2174,24 +2096,6 @@ class TradingEngine: tg["bot_token"], tg["chat_id"]) except Exception as e: log.warning(f"Adverse15-Telegram: {e}") - def set_sr_autoclose(self, on: bool) -> bool: - """Automatischen S/R-Close an/aus (UI/Config). Rückgabe = neuer Zustand.""" - self._auto_sr_close = bool(on) - log.info(f"Auto-S/R-Close {'AN' if self._auto_sr_close else 'AUS'}") - self._save_runtime_state() - return self._auto_sr_close - - def set_sr_close_min_gain(self, value) -> float: - """Mindestgewinn (EUR) für den S/R-Auto-Close. 0/leer = aus.""" - try: - v = max(0.0, float(value)) if value not in (None, "") else 0.0 - except (TypeError, ValueError): - v = 0.0 - self._sr_close_min_gain = v - log.info(f"S/R-Close-Mindestgewinn {('+' + format(v, '.2f')) if v > 0 else 'aus'}") - self._save_runtime_state() # neustart-fest (Fix 2026-07-22 — war vergessen) - return self._sr_close_min_gain - def set_sltp(self, sl=None, tp=None) -> dict: """Manuelles SL/TP der offenen Position setzen. **Deaktiviert das Trailing** (User-Vorgabe) — sonst zöge es SL/TP beim nächsten Tick wieder um. Rückgabe @@ -2731,10 +2635,6 @@ class TradingEngine: # ⚠ `auto_signal` wird seit 2026-08-12 NICHT mehr gelesen (Pfad # entfernt). Ein Altbestand in der Datei schadet nicht — er wird # beim naechsten Schreiben ohnehin nicht mehr mitgeschrieben. - if "auto_sr_close" in st: - self._auto_sr_close = bool(st["auto_sr_close"]) - if "sr_close_min_gain" in st: - self._sr_close_min_gain = max(0.0, float(st["sr_close_min_gain"] or 0)) if "manual_margin" in st: from core.config import set_manual_margin as _set self._manual_margin = _set(st["manual_margin"] or 0) @@ -2783,8 +2683,6 @@ class TradingEngine: self._cone_skalen = {int(k): float(v) for k, v in (st["cone_skalen"] or {}).items()} log.info(f"Runtime-State geladen: BRK={'AN' if self._auto_squeeze else 'aus'} · " - f"S/R-Close={'AN' if self._auto_sr_close else 'aus'} · " - f"MinGewinn={self._sr_close_min_gain:+.2f} · " f"Einsatz={'auto' if not getattr(self, '_manual_margin', 0) else f'{self._manual_margin:.2f}'}" f" · Einsatz-%={getattr(self, '_margin_pct', 0):.0f}") except FileNotFoundError: @@ -2798,8 +2696,6 @@ class TradingEngine: tmp = self._runtime_state_path + ".tmp" with open(tmp, "w", encoding="utf-8") as f: json.dump({"auto_squeeze": self._auto_squeeze, - "auto_sr_close": self._auto_sr_close, - "sr_close_min_gain": self._sr_close_min_gain, "manual_margin": getattr(self, "_manual_margin", 0.0), "margin_pct": getattr(self, "_margin_pct", 0.0), # Circuit-Breaker als PROZENTZAHL (nicht bool): @@ -5258,10 +5154,7 @@ class TradingEngine: "entry_gap": self._entry_gap_cached(), "sr_close_hint": sr_close_hint, "stop_approach": stop_approach, - "auto_sr_close": self._auto_sr_close, - "sr_close_min_gain": self._sr_close_min_gain, "sr_close_pbreak": self._sr_close_pbreak, - "sr_close_count": self._sr_close_count, # Schwelle der Close-Benachrichtigung — sichtbar, damit belegbar ist, # dass sie im LAUFENDEN Prozess angekommen ist. „Erfolg melden, wo # nichts geprueft wurde" ist im Projekt bereits viermal passiert. @@ -5488,7 +5381,6 @@ class TradingEngine: try: an = lambda b: "AN" if b else "aus" # noqa: E731 z.append(f"Automatik: BRK {an(self._auto_squeeze)} · " - f"S/R-Close {an(self._auto_sr_close)} · " f"Flip {an(getattr(self, '_flip_close', False))}") except Exception as e: z.append(f"Automatik: nicht lesbar ({e})") diff --git a/server.py b/server.py index e6b5fbf..ce5dc97 100644 --- a/server.py +++ b/server.py @@ -346,15 +346,6 @@ async def set_sltp(payload: dict = Body(...), return res -@app.post("/api/srclose") -async def toggle_srclose(x_auth_token: str | None = Header(default=None)): - """Automatischen S/R-Close (P(break)-Regel) an/aus.""" - _require_auth(x_auth_token) - eng = app.state.engine - state = eng.set_sr_autoclose(not eng._auto_sr_close) - return {"ok": True, "enabled": state} - - @app.post("/api/autom15") async def toggle_autom15(x_auth_token: str | None = Header(default=None)): """M15-Karten-Auto-Entry an/aus (User-Test 2026-08-19, gemessen negativ).""" @@ -464,16 +455,6 @@ async def set_margin_pct(body: dict, # Sicherung: `.removed_backup/auto_signal_2026-08-12.py` + Git-Historie. -@app.post("/api/srclose_min") -async def srclose_min(payload: dict = Body(...), - x_auth_token: str | None = Header(default=None)): - """Mindestgewinn (EUR) für den S/R-Auto-Close setzen. 0/leer = aus.""" - _require_auth(x_auth_token) - get_logger("server").info(f"[WEB] Mindestgewinn angefordert: {payload.get('value')!r}") - cur = app.state.engine.set_sr_close_min_gain(payload.get("value")) - return {"ok": True, "min_gain": cur} - - @app.post("/api/trail") async def toggle_trail(x_auth_token: str | None = Header(default=None)): _require_auth(x_auth_token) diff --git a/tests/test_system_status.py b/tests/test_system_status.py index 745952f..909e35f 100644 --- a/tests/test_system_status.py +++ b/tests/test_system_status.py @@ -46,14 +46,18 @@ def _text(z): def test_automatik_zeile_nennt_alle_schalter(): - """⚠ Seit dem 2026-08-12 sind es DREI — der Auto-Signal-Pfad (SIG) ist - entfernt. Der Test zaehlt sie bewusst einzeln auf: verschwindet einer - stillschweigend aus der Zeile, faellt es hier auf und nicht erst, wenn - monatelang niemand merkt, dass ein Schalter aus ist (real geschehen: - `auto_sr_close` stand 5 Tage aus, gemessener Wert +350,81 EUR).""" + """⚠ Seit dem 2026-08-21 sind es ZWEI. Entfernt wurden der Auto-Signal-Pfad + (SIG, am 12.08.) und der S/R-Auto-Close (am 21.08., User-Vorgabe — er stand + seit dem 19.08. ohnehin aus, weil das P(break)-Modell live INVERTIERT misst). + Der Test zaehlt sie bewusst einzeln auf: verschwindet einer stillschweigend + aus der Zeile, faellt es hier auf und nicht erst, wenn monatelang niemand + merkt, dass ein Schalter aus ist. + ⚠ Die beiden Negativ-Pruefungen sind der eigentliche Wert: ein entfernter + Pfad, der wieder auftaucht, waere ein stiller Rueckbau.""" z = _text(_Stub(brk=True, src=True, flip=False)._system_status()) - assert "BRK AN" in z and "S/R-Close AN" in z and "Flip aus" in z + assert "BRK AN" in z and "Flip aus" in z assert "SIG" not in z, "der entfernte Pfad darf nicht wieder auftauchen" + assert "S/R-Close" not in z, "der S/R-Auto-Close ist entfernt (21.08.)" def test_toter_logger_wird_gemeldet(): diff --git a/web/app.js b/web/app.js index 02038f7..b2d6778 100644 --- a/web/app.js +++ b/web/app.js @@ -94,7 +94,7 @@ function beepCloseAlert() { // unterscheidbar). Sofort beim Signal-Wechsel + WIEDERHOLUNG alle 30 s, solange // der Button blinkt (Empfehlung aktiv & flat) — einmaliger Piep war überhörbar. const _REC_REBEEP_MS = 30000; -let _lastRecBeep = 0, _lastRecSignal = null, _audioHintShown = false, _lastSrCloseCount = null; +let _lastRecBeep = 0, _lastRecSignal = null, _audioHintShown = false; // ── Laufzeit-Uhr des offenen Trades ─────────────────────────────────────────── // Öffnungszeit kommt als echte Epoch (Broker-Offset serverseitig korrigiert) im @@ -1519,16 +1519,13 @@ function render(d) { + " Entries). ACHTUNG: gemessen negativ -- reiner Test." : "Auto-Entry auf die gruene M15-Karte AUS (gemessen negativ)."; } - const srb = $streng("btn-sr"); - if (srb) { - srb.classList.toggle("on", !!d.auto_sr_close); - srb.title = d.auto_sr_close - ? "S/R-Auto-Close AN: schliesst am Level, wenn P(break) unter der Schwelle liegt." - : "S/R-Auto-Close AUS -- der Bot schliesst nicht mehr selbst am Level."; - } const cbb = $streng("btn-cb"); if (cbb) { - cbb.classList.toggle("on", !!cb.enabled); + // ⚠ Vorgabe des Nutzers: GRUEN = inaktiv (aus), ROT = aktiv (scharf). + // Bewusst nicht die uebliche "gruen = geschuetzt"-Lesart — Rot + // signalisiert hier, dass eine Automatik eingreifen kann. + cbb.classList.toggle("aktiv", !!cb.enabled); + cbb.classList.toggle("inaktiv", !cb.enabled); cbb.title = cb.enabled ? "Circuit Breaker AN: schliesst bei " + fmt(cb.limit_eur, 2) + " € (" + cb.limit_pct + " % der Balance). Heute: " + fmt(cb.day_total, 2) + " €." @@ -1559,7 +1556,9 @@ function render(d) { // Deckt sich mit dem Auto-Close-Gate im Backend (unter der Schwelle schließt der // Bot ohnehin nicht → dann auch kein Alarm/Ton). const posLong = p.order_type === 0; - const minGain = d.sr_close_min_gain || 0; + // ⚠ Der Mindestgewinn ist mit dem S/R-Auto-Close entfallen; der + // Close-Alarm gilt jetzt schlicht ab jedem Gewinn (wie vor 2026-07). + const minGain = 0; const gainOK = hasPos && (p.pnl || 0) > minGain; const flipClose = gainOK && ((posLong && w.signal === "SHORT") || (!posLong && w.signal === "LONG")); @@ -1567,7 +1566,8 @@ function render(d) { // schwacher Anlauf → Level hält ~74 % → Close schlägt Laufenlassen. Text-Hinweis // in der Position-Kachel immer; Blink/Ton nur über dem Mindestgewinn. const srHint = d.sr_close_hint || null; // vorhanden = Kurs am Level - const srAuto = !!d.auto_sr_close; + // ⚠ Der Bot schliesst seit dem 2026-08-21 NICHT mehr selbst am Level. + const srAuto = false; const srClose = !!(srHint && srHint.close); // P(break) < Schwelle → schließen const closeAlert = flipClose || (gainOK && srClose); // ⚠ Der Alarm gilt der Position in Slot 1 (dort entsteht `sr_close_hint` und @@ -1686,23 +1686,9 @@ function render(d) { flipEl.className = "news-conflict hidden"; flipEl.textContent = ""; } } - // ⚠ Der Auto-Close-Schalter der Trade-Leiste ist am 2026-08-20 entfernt - // (User-Wunsch) — er war doppelt: der Knopf S/R in der Aktionsleiste - // schaltet dasselbe und ist auch sichtbar, wenn KEINE Position offen ist. - // `srAuto` wird weiter gebraucht (Knopf `btn-sr`, Hinweistexte). - const srMin = d.sr_close_min_gain || 0; - const smi = $("srmin-input"); - if (smi && document.activeElement !== smi) { - smi.value = srMin > 0 ? String(srMin) : ""; - } - // Einblendung: der Bot hat am S/R automatisch geschlossen (Zähler stieg) - const src = d.sr_close_count; - if (src != null) { - if (_lastSrCloseCount != null && src > _lastSrCloseCount) { - toast("🎯 Bot hat am Level geschlossen (schwacher Anlauf, Gewinn gesichert)", true); - } - _lastSrCloseCount = src; - } + // ⚠ Am 2026-08-21 entfernt: der Auto-Close-Schalter der Trade-Leiste, das + // Mindestgewinn-Feld und die Toast-Meldung "Bot hat am Level + // geschlossen" — es gibt keinen Auto-Close am Level mehr. // ── Letzte Trades ─────────────────────── // 5 → 10 (User-Wunsch 2026-08-04). Das Backend liefert bereits 10 @@ -2116,37 +2102,13 @@ _updateMuteBtn(); // Toggle-Buttons (AUTO/TRAIL/S-R-Close) — ohne Bestätigung $("btn-cb").onclick = () => toggle("api/circuit", "Circuit Breaker"); -$("btn-sr").onclick = () => toggle("api/srclose", "S/R-Auto-Close"); $("btn-m15").onclick = () => toggle("api/autom15", "M15-Auto-Entry (gemessen negativ!)"); // (🎯 SIG entfernt 2026-08-12 — s. renderOrderButtons oben) $("btn-trail").onclick = () => toggle("api/trail", "Trailing"); -// Mindestgewinn für den S/R-Auto-Close (0/leer = aus) -async function setSrMin() { - const raw = $("srmin-input").value; - const v = raw === "" ? 0 : parseFloat(raw); - if (isNaN(v) || v < 0) { toast("Bitte Betrag ≥ 0 eingeben (0 = aus)", false); return; } - const tok = await ensureToken(); - if (tok === null) return; - $("srmin-input").blur(); - try { - const r = await fetch("api/srclose_min", { method: "POST", - headers: { "X-Auth-Token": tok, "Content-Type": "application/json" }, - body: JSON.stringify({ value: v }) }); - if (r.status === 401) { clearToken(); throw new Error("401"); } - toast(v > 0 ? `Mindestgewinn +${v} gesetzt` : "Mindestgewinn aus", true); - } catch (e) { - toast(e.message === "401" ? "Token abgelaufen — neu eingeben" : "Netzwerkfehler", false); - } -} -// Feld sitzt in der Button-Zeile: setzt beim Verlassen/Enter automatisch -$("srmin-input").addEventListener("change", setSrMin); -// Enter NUR blurren → das change-Event feuert setSrMin genau EINMAL (und nur, wenn -// der Wert wirklich geändert wurde). Vorher: keydown + change = doppelter Send, -// und Enter auf leerem Feld schickte ungewollt 0 (Fix 2026-07-22). -$("srmin-input").addEventListener("keydown", e => { - if (e.key === "Enter") { e.preventDefault(); e.target.blur(); } -}); + // ⚠ Der Mindestgewinn-Sender ist am 2026-08-21 mit dem S/R-Auto-Close + // entfallen — die Schwelle gab es nur fuer ihn (Endpoint, Feld und + // Backend-Zustand sind ebenfalls weg). // Einsatz-Felder ENTFERNT (User 2026-08-19). Das BACKEND bleibt vollstaendig: // beide Endpunkte sind weiter erreichbar, die Werte leben in @@ -2519,7 +2481,7 @@ async function closeSlot(slot, label, pnl) { // und der Browser zeigte Altes. Statt das jedes Mal von Hand zu diagnostizieren, // sagt die Seite es jetzt selbst. // ⚠ Bewusst SICHTBAR statt nur Konsole: die Konsole sieht am Handy niemand. -const _JS_V = "208"; +const _JS_V = "210"; { const htmlV = document.body && document.body.dataset ? document.body.dataset.htmlV : null; diff --git a/web/index.html b/web/index.html index e216683..8e7a6e5 100644 --- a/web/index.html +++ b/web/index.html @@ -12,9 +12,9 @@ ⚠ LOKAL eingebunden, kein CDN: die PWA laeuft ueber WireGuard und muss offline funktionieren. --> - + - +