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:
co-authored by
Claude Opus 4.8
parent
6318817ac1
commit
027995d1e7
@@ -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 ~7–15 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 ~10–13 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
|
||||
|
||||
Reference in New Issue
Block a user