pyflakes eingebunden — zwei echte Fehler in core/trader.py gefunden
Auf die Frage nach einem Lint-Modul: pyflakes installiert und als Stufe A2 in tools/check_nfalle.py eingebunden. py_compile prueft nur die SYNTAX; ein Tippfehler in einem NAMEN ist syntaktisch einwandfrei — und dieses Projekt hat sehr viele breite except-Exception-Bloecke, in denen ein NameError unsichtbar bleibt. Beim ERSTEN Lauf zwei echte Fehler, beide in core/trader.py, beide nutzten log. (den Namen gibt es dort nicht, nur log_trade/log_hist): · _broker_offset_s(): der NameError lief in das except-pass UND nahm die darunter stehende Zuweisung self._boff = int(off) mit -> ein echter Broker-Zeitzonen-Wechsel waere nie uebernommen worden, dauerhaft und lautlos. Ausgerechnet der Zweig, der als Deployment-Drift Fall 4 gebaut wurde (falscher Offset -> Time-Stop-Alter negativ). · modify_sltp(): steht NICHT in einem try -> der Fehler lief bis in /api/sltp. Der Broker hatte SL/TP bereits geaendert, der User bekam trotzdem eine Fehlermeldung, und set_sltp kam nie bis trail.deactivate() — das Trailing blieb an und haette die Handeingabe zurueckgezogen. Seit dem Initial-Commit drin. Beide behoben (log_trade). Zusaetzlich die Annotation 'HistoryLogger | None' ueber if TYPE_CHECKING sauber importiert (String-Annotation, kein Laufzeit-Import, kein Zirkel). Projektweit jetzt 0 undefinierte Namen. Schweregrad-Trennung ist Absicht: nur undefined name / syntax error werden gemeldet, die ~105 kosmetischen Hinweise unterdrueckt. Auch 'redefinition of unused' bleibt draussen — es trifft das legitime Muster hook = None + bedingtes def hook (in zwei Backtests geprueft, beide korrekt). Eine Pruefung mit Dauer-Treffern wird ignoriert, und mit ihr die eine echte. Backtest-Dateien nicht angefasst (Zahlen). Neustart verifiziert, Log sauber. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
394786b312
commit
e69907edbe
@@ -275,6 +275,39 @@ dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche:
|
|||||||
als offenes Literal (gültiges JS mit einem Anführungszeichen darin — real in
|
als offenes Literal (gültiges JS mit einem Anführungszeichen darin — real in
|
||||||
`app.js`). Der Scan verfolgt jetzt zeichenweise den AKTIVEN Quote-Typ.
|
`app.js`). Der Scan verfolgt jetzt zeichenweise den AKTIVEN Quote-Typ.
|
||||||
Erster vollständiger Lauf: **161 Python-Dateien, `app.js`, alle HTML/MD sauber.**
|
Erster vollständiger Lauf: **161 Python-Dateien, `app.js`, alle HTML/MD sauber.**
|
||||||
|
- **⚠⚠ LINTER: `pyflakes` (seit 2026-08-07 installiert, Stufe A2 in
|
||||||
|
`tools/check_nfalle.py`) — und er hat beim ERSTEN Lauf zwei echte Fehler
|
||||||
|
gefunden.** `py_compile` prüft **nur die Syntax**; ein Tippfehler in einem
|
||||||
|
**Namen** ist syntaktisch einwandfrei. Dieses Projekt ist dafür besonders
|
||||||
|
anfällig, weil es sehr viele breite `except Exception`-Blöcke hat (bewusst, als
|
||||||
|
Fail-safe) — ein `NameError` darin ist **unsichtbar**: keine Meldung, kein
|
||||||
|
Logeintrag, nur ein Zweig, der nichts tut. Genau das war am 04.08. schon einmal
|
||||||
|
fast passiert (`wave_snap`), gefunden damals nur durch Lesen.
|
||||||
|
**Die beiden Funde in `core/trader.py`** — beide nutzten `log.`, den Namen gibt
|
||||||
|
es dort NICHT (nur `log_trade`/`log_hist`):
|
||||||
|
· **`_broker_offset_s()`** (seit dem Fix vom 01.08. drin): der `NameError` lief in
|
||||||
|
das `except Exception: pass` **und nahm die darunter stehende Zuweisung
|
||||||
|
`self._boff = int(off)` mit** → ein echter **Broker-Zeitzonen-Wechsel wäre nie
|
||||||
|
übernommen worden**, dauerhaft und lautlos. Ausgerechnet der Zweig, der als
|
||||||
|
Deployment-Drift **Fall 4** gebaut wurde (falscher Offset → `open_time` in der
|
||||||
|
Zukunft, **Time-Stop-Alter negativ**).
|
||||||
|
· **`modify_sltp()`** (seit dem **Initial-Commit**): steht **nicht** in einem
|
||||||
|
`try` → der Fehler lief bis in `/api/sltp` durch. Folge: der Broker hatte SL/TP
|
||||||
|
**bereits geändert**, der User bekam trotzdem eine Fehlermeldung, und
|
||||||
|
`set_sltp` kam nie bis `trail.deactivate()` — **das Trailing blieb an** und hätte
|
||||||
|
die Handeingabe beim nächsten Tick zurückgezogen, also genau das, was die
|
||||||
|
User-Vorgabe verhindern soll. Aufgefangen hat das nur die separate
|
||||||
|
Extern-Erkennung in `trailing._do_modify`, einen Tick später.
|
||||||
|
✅ Beide behoben (`log_trade`), dazu die Annotation `'HistoryLogger | None'` über
|
||||||
|
`if TYPE_CHECKING` sauber importiert. **Projektweit jetzt 0 undefinierte Namen.**
|
||||||
|
⚠ **Schweregrad-Trennung ist Absicht:** gemeldet werden nur `undefined name` /
|
||||||
|
`syntax error`; die ~105 kosmetischen Hinweise (ungenutzte Importe, f-Strings
|
||||||
|
ohne Platzhalter) werden unterdrückt. Auch `redefinition of unused` steht bewusst
|
||||||
|
NICHT in der Liste — es trifft das legitime Muster `hook = None` + bedingtes
|
||||||
|
`def hook` (geprüft in zwei Backtests, beide korrekt). **Eine Prüfung mit
|
||||||
|
Dauer-Treffern wird ignoriert, und mit ihr die eine echte Meldung.**
|
||||||
|
⚠ Backtest-Dateien wurden **nicht** angefasst — eine Änderung dort könnte ihre
|
||||||
|
dokumentierten Zahlen verschieben.
|
||||||
- **Konsole:** UTF-8 erzwingen (`PYTHONIOENCODING=utf-8`, `-X utf8`).
|
- **Konsole:** UTF-8 erzwingen (`PYTHONIOENCODING=utf-8`, `-X utf8`).
|
||||||
- **Uvicorn-Access-Log AUS** (`access_log=False` in `uvicorn.run`, 2026-07-22): seit
|
- **Uvicorn-Access-Log AUS** (`access_log=False` in `uvicorn.run`, 2026-07-22): seit
|
||||||
dem 4-s-REST-Polling + WS-Push flutete `GET /api/snapshot … 200 OK` die Konsole.
|
dem 4-s-REST-Polling + WS-Push flutete `GET /api/snapshot … 200 OK` die Konsole.
|
||||||
|
|||||||
+32
-3
@@ -8,6 +8,7 @@ loggt Trades in die HistoryLogger-DB.
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import MetaTrader5 as mt5
|
import MetaTrader5 as mt5
|
||||||
|
|
||||||
@@ -22,6 +23,14 @@ from core.mt5_utils import (
|
|||||||
)
|
)
|
||||||
from core.logger import get_logger
|
from core.logger import get_logger
|
||||||
|
|
||||||
|
# ⚠ NUR für die Typ-Annotation `'HistoryLogger | None'` (String, wird zur Laufzeit
|
||||||
|
# nie ausgewertet). Unter TYPE_CHECKING importiert = kein Laufzeit-Import und damit
|
||||||
|
# auch kein Zirkel-Risiko. Vorher meldete pyflakes hier zu Recht einen undefinierten
|
||||||
|
# Namen — und eine Prüfung, die dauerhaft einen bekannten Treffer ausgibt, wird
|
||||||
|
# irgendwann ignoriert.
|
||||||
|
if TYPE_CHECKING: # pragma: no cover
|
||||||
|
from core.history import HistoryLogger
|
||||||
|
|
||||||
log_trade = get_logger("trade")
|
log_trade = get_logger("trade")
|
||||||
log_hist = get_logger("hist")
|
log_hist = get_logger("hist")
|
||||||
|
|
||||||
@@ -673,8 +682,18 @@ class TradeManager:
|
|||||||
stale = (now + self._boff) - tick.time
|
stale = (now + self._boff) - tick.time
|
||||||
if stale <= _BROKER_TICK_FRESH_S and -12 * 3600 <= off <= 14 * 3600:
|
if stale <= _BROKER_TICK_FRESH_S and -12 * 3600 <= off <= 14 * 3600:
|
||||||
if off != self._boff and self._boff_logged != off:
|
if off != self._boff and self._boff_logged != off:
|
||||||
log.info(f"Broker-Zeitzone neu erkannt: "
|
# ⚠ war `log.` — diesen Namen gibt es in dieser Datei NICHT
|
||||||
f"{self._boff / 3600:+.1f} h → {off / 3600:+.1f} h")
|
# (nur `log_trade`/`log_hist`). Gefunden 2026-08-07 mit
|
||||||
|
# pyflakes. Folge war schwerer als ein fehlender Logeintrag:
|
||||||
|
# der NameError lief in das `except Exception: pass` unten,
|
||||||
|
# und die Zuweisung `self._boff = int(off)` DARUNTER wurde
|
||||||
|
# dabei übersprungen → ein echter Broker-Zeitzonen-Wechsel
|
||||||
|
# wäre nie übernommen worden, dauerhaft und lautlos.
|
||||||
|
# Genau der Fall, für den dieser Zweig 2026-08-01 gebaut
|
||||||
|
# wurde (Deployment-Drift Fall 4).
|
||||||
|
log_trade.info(f"Broker-Zeitzone neu erkannt: "
|
||||||
|
f"{self._boff / 3600:+.1f} h → "
|
||||||
|
f"{off / 3600:+.1f} h")
|
||||||
self._boff_logged = off
|
self._boff_logged = off
|
||||||
self._boff = int(off)
|
self._boff = int(off)
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -873,7 +892,17 @@ class TradeManager:
|
|||||||
with self._lock:
|
with self._lock:
|
||||||
self.sl = round(new_sl, si.digits)
|
self.sl = round(new_sl, si.digits)
|
||||||
self.tp = round(new_tp, si.digits)
|
self.tp = round(new_tp, si.digits)
|
||||||
log.info(f"Manuelles SLTP: SL={self.sl} · TP={self.tp} (Ticket {pos.ticket})")
|
# ⚠⚠ war `log.` — den Namen gibt es hier nicht (nur `log_trade`/
|
||||||
|
# `log_hist`). Seit dem Initial-Commit drin, gefunden 2026-08-07 mit
|
||||||
|
# pyflakes. Die Stelle steht NICHT in einem `try`, der NameError lief
|
||||||
|
# also bis in `/api/sltp` durch — mit drei Folgen: (1) der Broker hatte
|
||||||
|
# SL/TP bereits geändert, (2) der User bekam trotzdem eine Fehlermeldung,
|
||||||
|
# (3) `set_sltp` kam nie bis `trail.deactivate()`, das Trailing blieb an
|
||||||
|
# und hätte die Handeingabe beim nächsten Tick zurückgezogen — genau das,
|
||||||
|
# was die User-Vorgabe verhindern soll. (Aufgefangen hat es nur die
|
||||||
|
# separate Extern-Erkennung in `trailing._do_modify`, einen Tick später.)
|
||||||
|
log_trade.info(f"Manuelles SLTP: SL={self.sl} · TP={self.tp} "
|
||||||
|
f"(Ticket {pos.ticket})")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def snapshot(self):
|
def snapshot(self):
|
||||||
|
|||||||
@@ -53,6 +53,60 @@ for p in dateien(".py"):
|
|||||||
print(f" {npy} Dateien geprueft.")
|
print(f" {npy} Dateien geprueft.")
|
||||||
|
|
||||||
|
|
||||||
|
# ── A2) pyflakes: was py_compile NICHT sieht ─────────────────────────────
|
||||||
|
# ⚠⚠ WARUM DIESE STUFE NOETIG IST. `py_compile` prueft nur die SYNTAX. Ein
|
||||||
|
# Tippfehler in einem Namen ist syntaktisch einwandfrei und fliegt erst zur
|
||||||
|
# Laufzeit — und dieses Projekt hat sehr viele breite `except Exception`-Bloecke
|
||||||
|
# (bewusst, als Fail-safe im Trading-Betrieb). Ein `NameError` darin ist damit
|
||||||
|
# UNSICHTBAR: keine Meldung, kein Logeintrag, nur ein Zweig, der nichts tut.
|
||||||
|
# Real gefunden beim ERSTEN Lauf ueberhaupt (2026-08-07):
|
||||||
|
# · `core/trader.py` nutzte zweimal `log.` — den Namen gibt es dort nicht
|
||||||
|
# (nur `log_trade`/`log_hist`). Einer davon seit dem Initial-Commit.
|
||||||
|
# · Folge 1 (`_broker_offset_s`): der NameError lief in ein `except: pass`
|
||||||
|
# und nahm die Zuweisung `self._boff = int(off)` mit → ein echter
|
||||||
|
# Broker-Zeitzonen-Wechsel waere nie uebernommen worden.
|
||||||
|
# · Folge 2 (`modify_sltp`): kein try drumherum → der Fehler lief bis in
|
||||||
|
# `/api/sltp`; der Broker hatte SL/TP schon geaendert, der User bekam
|
||||||
|
# trotzdem einen Fehler, und `trail.deactivate()` wurde uebersprungen.
|
||||||
|
# GETRENNT NACH SCHWEREGRAD: eine Pruefung, die dauerhaft 100 harmlose Zeilen
|
||||||
|
# ausgibt, wird ignoriert — und dann auch die eine wichtige.
|
||||||
|
# ⚠ `redefinition of unused` steht BEWUSST NICHT hier drin. pyflakes meldet damit
|
||||||
|
# auch das voellig legitime Muster `hook = None` gefolgt von einem bedingten
|
||||||
|
# `def hook(...)` — real in `backtest_flipclose2.py` und
|
||||||
|
# `backtest_adverse15_squeeze.py`, beide geprueft und korrekt. Zwei unvermeidbare
|
||||||
|
# Dauer-Treffer sind genau der Weg, auf dem eine Pruefung ignoriert wird, und mit
|
||||||
|
# ihr dann auch die eine echte Meldung. Wer die Klasse doch sehen will, ruft
|
||||||
|
# `python -m pyflakes <datei>` direkt auf.
|
||||||
|
SCHWER = ("undefined name", "undefined local", "syntax error")
|
||||||
|
print()
|
||||||
|
print("=" * 88)
|
||||||
|
print(" A2) PYFLAKES — undefinierte Namen (was py_compile nicht sieht)")
|
||||||
|
print("=" * 88)
|
||||||
|
try:
|
||||||
|
import io
|
||||||
|
|
||||||
|
from pyflakes.api import checkPath
|
||||||
|
from pyflakes.reporter import Reporter
|
||||||
|
|
||||||
|
aus, err = io.StringIO(), io.StringIO()
|
||||||
|
rep = Reporter(aus, err)
|
||||||
|
for p in dateien(".py"):
|
||||||
|
checkPath(str(p), rep)
|
||||||
|
zeilen = [z for z in (aus.getvalue() + err.getvalue()).splitlines() if z.strip()]
|
||||||
|
schwer = [z for z in zeilen if any(s in z for s in SCHWER)]
|
||||||
|
for z in schwer:
|
||||||
|
kurz = z.replace(str(WURZEL) + "\\", "").replace(str(WURZEL) + "/", "")
|
||||||
|
befunde.append(f"PF {kurz}")
|
||||||
|
print(f" FEHLER {kurz}")
|
||||||
|
if not schwer:
|
||||||
|
print(" keine undefinierten Namen.")
|
||||||
|
print(f" ({len(zeilen) - len(schwer)} kosmetische Hinweise unterdrueckt — "
|
||||||
|
f"ungenutzte Importe, f-Strings ohne Platzhalter)")
|
||||||
|
except ImportError:
|
||||||
|
print(" ⚠ pyflakes nicht installiert — Stufe uebersprungen.")
|
||||||
|
print(" Installation: python -m pip install pyflakes")
|
||||||
|
|
||||||
|
|
||||||
# ── B) JavaScript ────────────────────────────────────────────────────────
|
# ── B) JavaScript ────────────────────────────────────────────────────────
|
||||||
def js_offene_strings(text: str):
|
def js_offene_strings(text: str):
|
||||||
"""Zeilen mit angefangenem, nicht geschlossenem ' oder " (Template-Literale
|
"""Zeilen mit angefangenem, nicht geschlossenem ' oder " (Template-Literale
|
||||||
|
|||||||
Reference in New Issue
Block a user