Gegen-/Stop-Seiten-Anzeige P(Durchbruch) (stop_approach, v=109)

Zeigt die Durchbruchwahrscheinlichkeit fuer das Level, dem sich der Kurs auf der
Gegen-/Stop-Seite der offenen Position naehert (LONG->Support darunter,
SHORT->Resistance darueber) — reine Anzeige, kein Auto-Close. Vorher per
backtest_pbreak_calibration.py belegt: Modell haelt auf der Gegen-Seite
out-of-sample (AUC 0,68/0,72 in beiden Haelften), niedriger P-Bereich kalibriert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-07-24 16:19:39 +02:00
co-authored by Claude Opus 4.8
parent 6318817ac1
commit 027995d1e7
6 changed files with 274 additions and 6 deletions
+63
View File
@@ -236,6 +236,7 @@ class TradingEngine:
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
# laufenden Kerze ließ P ~715 Pp je M5-Bar springen)
# UI-Einstellungen neustart-fest (Fix 2026-07-19): Toggles/Werte, die über
@@ -1862,6 +1863,7 @@ class TradingEngine:
day_pl = {"realized": round(day_realized, 2), "open": round(open_pnl, 2),
"total": round(day_realized + open_pnl, 2)}
sr_close_hint = self._sr_close_hint(market, position, wave_snap)
stop_approach = self._stop_approach_hint(market, position, wave_snap)
return {
"ts": time.time(),
"connected": self._connected,
@@ -1902,6 +1904,7 @@ class TradingEngine:
"close_alert_count": self._close_alert_count,
"news_storm": self.news.storm_state(),
"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,
@@ -2016,3 +2019,63 @@ class TradingEngine:
except Exception as e:
log.debug(f"sr_close_hint: {e}")
return None
def _stop_approach_hint(self, market, position, wave_snap) -> dict | None:
"""REINE ANZEIGE: P(Durchbruch) für das Level, dem sich der Kurs auf der
**Gegen-/Stop-Seite** der offenen Position nähert (LONG → Support darunter,
SHORT → Resistance darüber). Beantwortet die User-Frage „mein Trade läuft auf
ein Level zu — bricht es durch (Verlust läuft) oder prallt es ab (erholt sich)?".
NICHT der S/R-Auto-Close (der wirkt nur auf der Ziel-Seite im Plus) — hier wird
NICHTS geschlossen. Kalibrierung der Gegen-Seite gemessen
(`backtest_pbreak_calibration.py`, 2026-07-24): AUC 0,68/0,72 out-of-sample in
beiden Hälften, im niedrigen P-Bereich sauber kalibriert; im hohen (≥0,5) gegen
den Trend leicht KONSERVATIV (unterschätzt die Bruch-Rate ~1013 Pp) → die Zahl
ist eine belastbare, eher vorsichtige Schätzung. Feature-/Glättungs-Logik
identisch zu `_sr_close_hint`, nur Level = Gegen-Level und Richtung = -d."""
try:
if not position.get("ticket"):
self._stop_pb_state = None
return None
is_long = position.get("order_type") == 0
bid = (market or {}).get("bid")
pf = (wave_snap or {}).get("pb_feats") or {}
atr = pf.get("atr"); mom6 = pf.get("mom6"); mom3 = pf.get("mom3")
ediff = pf.get("ema_diff")
entry = position.get("entry_price") or bid
dl = self._draw_levels()
# Gegen-Level: LONG → Support darunter, SHORT → Resistance darüber
if is_long:
level = (dl.get("sup") or [None])[0]
else:
level = (dl.get("res") or [None])[0]
if not (bid and atr and level) or mom6 is None or mom3 is None or ediff is None:
self._stop_pb_state = None
return None
dd = -1 if is_long else 1 # Break-Richtung GEGEN den Trade
gap = (level - bid) * dd # >0 solange Kurs noch vor dem Level
if gap <= 0 or gap > 0.6 * atr: # weit weg / schon durch → kein Hinweis
self._stop_pb_state = None
return None
wt = 1.0 if ediff * dd > 0 else 0.0
dist = abs(level - entry) / atr
p_raw = _p_break(mom6 * dd, mom3 * dd, wt, dist)
import math as _m
now = time.time(); st = self._stop_pb_state
if not st or abs(st["level"] - level) > 1e-9:
st = {"level": level, "p": p_raw, "ts": now}
else:
a = 1.0 - _m.exp(-max(0.0, now - st["ts"]) / 60.0)
st["p"] += a * (p_raw - st["p"]); st["ts"] = now
self._stop_pb_state = st
p_s = st["p"]
confirm = round(level + dd * 0.5 * atr, 3) # Durchbruch bestätigt ab
reject = round(level - dd * 0.5 * atr, 3) # Abprall bestätigt ab
return {"level": round(level, 3),
"side": ("sup" if is_long else "res"),
"p_break": round(100 * p_s),
"at_level": gap <= 0.15 * atr,
"gap_atr": round(gap / atr, 2),
"confirm": confirm, "reject": reject}
except Exception as e:
log.debug(f"stop_approach_hint: {e}")
return None