Provider-Failover bei hartem Fehlschlag + Einsatz-Prozentfeld im Dashboard
1) LLM-Failover (core/agent.py): ein hart gescheiterter Provider wird zeitweise uebersprungen, analyze() nimmt den naechsten aus der Kette. Hart = 401/402/403/ 404 (Key/Guthaben/Modell) -> 30 min, nicht erreichbar -> 5 min. Timeout/429/5xx loesen BEWUSST keinen Wechsel aus (voruebergehend; sonst kostet jede Lastspitze die volle Timeout-Summe aller Anbieter). Neu im Snapshot: agent.provider_dead mit Restminuten - der DeepSeek-Ausfall stand vorher nur im Log und blieb deshalb 18 h unbemerkt. 9 Szenarien getestet. 2) Einsatz-Prozentfeld (web, v=137): margin_buffer_pct hatte bisher keine UI. Neues Feld "Einsatz %" neben "Einsatz EUR", POST /api/marginpct -> engine.set_margin_pct -> config.set_margin_buffer, Snapshot margin_pct, neustart-fest. Der feste EUR-Betrag hat Vorrang; das Prozentfeld wird dann ausgegraut, damit nicht unklar bleibt was gilt. Auf 1-99 % geklemmt. Ende-zu-Ende getestet (50, 150->99, 0 und -5 abgelehnt, 95, persistiert). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
7e9b0196a5
commit
cc4f1a0863
+31
-2
@@ -22,6 +22,7 @@ import MetaTrader5 as mt5
|
||||
|
||||
from core.config import (
|
||||
HISTORY_DB_FILE, MAGIC, load_config, set_margin_buffer, set_risk_per_trade,
|
||||
get_margin_buffer as _cfg_margin_buffer,
|
||||
)
|
||||
# ⚠ `parse_zones` wurde am 2026-07-31 hier entfernt (letzter Nutzer war `_sr_levels`,
|
||||
# s. dort): die `[zones]`-Config stammte aus einem ~10 $ tieferen Kursregime und
|
||||
@@ -1845,10 +1846,15 @@ class TradingEngine:
|
||||
if "manual_margin" in st:
|
||||
from core.config import set_manual_margin as _set
|
||||
self._manual_margin = _set(st["manual_margin"] or 0)
|
||||
if "margin_pct" in st:
|
||||
from core.config import set_margin_buffer as _setp, get_margin_buffer
|
||||
_setp(float(st["margin_pct"] or 0) or 95.0)
|
||||
self._margin_pct = round(get_margin_buffer() * 100.0, 1)
|
||||
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={'auto' if not getattr(self, '_manual_margin', 0) else f'{self._manual_margin:.2f}'}"
|
||||
f" · Einsatz-%={getattr(self, '_margin_pct', 0):.0f}")
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
except Exception as e:
|
||||
@@ -1863,7 +1869,8 @@ class TradingEngine:
|
||||
"auto_signal": self._auto_signal,
|
||||
"auto_sr_close": self._auto_sr_close,
|
||||
"sr_close_min_gain": self._sr_close_min_gain,
|
||||
"manual_margin": getattr(self, "_manual_margin", 0.0)}, f)
|
||||
"manual_margin": getattr(self, "_manual_margin", 0.0),
|
||||
"margin_pct": getattr(self, "_margin_pct", 0.0)}, f)
|
||||
os.replace(tmp, self._runtime_state_path)
|
||||
except Exception as e:
|
||||
log.warning(f"runtime_state speichern: {e}")
|
||||
@@ -2781,6 +2788,25 @@ class TradingEngine:
|
||||
self._save_runtime_state()
|
||||
return v
|
||||
|
||||
def set_margin_pct(self, pct: float) -> float:
|
||||
"""Einsatz als PROZENT der freien Margin (User-Wunsch 2026-08-05).
|
||||
|
||||
Das ist die zweite Hälfte des Einsatz-Felds: `set_manual_margin` gibt einen
|
||||
FESTEN Betrag vor, das hier den ANTEIL. Beides existierte bisher nur in der
|
||||
`ini` (`margin_buffer_pct`, live 95) — die Prozent-Schraube hatte keine UI.
|
||||
|
||||
⚠ VORRANG: ein fester Betrag (`manual_margin > 0`) schlägt den Prozentsatz —
|
||||
`calc_lots` prüft ihn zuerst. Der Prozentsatz bleibt dabei die Obergrenze
|
||||
(freie Margin × pct), ein zu hoher Fixbetrag wird darauf gedeckelt.
|
||||
⚠ Wirkt auf ALLE neuen Positionen, auch die autonomen. Neustart-fest.
|
||||
⚠ `set_margin_buffer` klemmt auf 1–99 % — 100 % wäre ohnehin unhaltbar
|
||||
(kein Puffer für Spread/Swap → Margin-Call beim Öffnen)."""
|
||||
from core.config import set_margin_buffer as _set, get_margin_buffer
|
||||
_set(pct)
|
||||
self._margin_pct = round(get_margin_buffer() * 100.0, 1)
|
||||
self._save_runtime_state()
|
||||
return self._margin_pct
|
||||
|
||||
def _manual_stats_cached(self) -> dict | None:
|
||||
"""Kennzahlen der manuellen Trades für die Karte „Manuelle Trades"
|
||||
(2026-08-04). Gleiches Cache-Muster wie `_alignment_cached` — die
|
||||
@@ -3342,6 +3368,9 @@ class TradingEngine:
|
||||
"manual_stats": self._manual_stats_cached(),
|
||||
# Manuell vorgegebene Einsatz-Margin (0 = automatisch, s. set_manual_margin)
|
||||
"manual_margin": getattr(self, "_manual_margin", 0.0),
|
||||
# Einsatz als % der freien Margin (`margin_buffer_pct`). Greift, solange
|
||||
# kein fester Betrag gesetzt ist — der hat Vorrang (s. set_margin_pct).
|
||||
"margin_pct": round(_cfg_margin_buffer() * 100.0, 1),
|
||||
# Live-Handelskosten relativ zur Vola (2026-07-24): Spread/ATR(M5).
|
||||
# Gemessen (`backtest_realcosts.py`): Ø 0,265×ATR, Nacht 0,32–0,50 =
|
||||
# Kostenfalle, 15–17 Uhr ~0,13 = günstig. Reine Anzeige (Chip) — die
|
||||
|
||||
Reference in New Issue
Block a user