Der seit 05.08. dokumentierte Defekt ist behoben. _add_net rechnete
net_pnl = total_pnl - gross_win * 26,375 % und unterstellte, die
Quellensteuer sei endgueltig verloren. Sie wird aber taeglich per
"Tax settlement" erstattet - real zu 99 %.
all: vorher net_pnl -2.449,95 EUR, jetzt +181,00 (total +209,93, echte
Steuerlast +28,93). Verzerrung rund 2.630 EUR - sie machte aus einem
profitablen Konto ein verlustreiches. Die Schaetzung der EINBEHALTENEN
Summe war fast exakt; falsch war allein die Annahme, sie bleibe weg.
trader.steuer_buchungen() liest die WHT-/Tax-Deals direkt
(position_id == 0, sauber von Trade-Deals getrennt), unter mt5_lock,
5 min gecacht. Fail-safe: ohne MT5 Rueckfall auf die Schaetzung, das
Feld wht_quelle sagt welche Zahl drinsteht.
Kurze Zeitraeume bleiben verzerrt, in BEIDE Richtungen - die Erstattung
kommt am Folgetag. Bei "today" ist wht sogar negativ, weil die
Erstattung von gestern heute einging. Ehrlich benannt, nicht kaschiert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
User-Frage "ueberpruefe ob Tag P/L richtig berechnet wird". Die Formel
(realisiert + offen) war bitgenau richtig - die Datenbasis nicht:
Anzeige +14,26 EUR gegen -74,08 EUR realisiert beim Broker.
Ursache, im Log im Sekundentakt (#49926460): 20:04:10 eroeffnet ->
20:04:10 "extern geschlossen" -> "kein OUT-Deal in 1 Deals" ->
Fallback bucht 0,00 -> 20:11:42 der echte Close lief ins Leere.
Drei Defekte: (1) positions_get sieht die frische Position einen Tick
lang nicht, (2) _log_external_close buchte TROTZ "kein OUT-Deal" einen
Close - dabei ist genau das der Beweis, dass sie noch offen ist,
(3) log_trade_close fasste nur exit_time IS NULL an, die Fehlbuchung
blockierte den echten Close dauerhaft. Bei #49852362 kostete das
+5,87 statt -95,38 EUR und den falschen Tag.
Behoben: (1) Abbruch statt Fallback bei "kein OUT-Deal"; (2) eine
erkennbare Fehlbuchung (closed_by='unknown') darf von einem echten
Close korrigiert werden - eng gefasst, gute Zeilen bleiben unberuehrt.
4 Tests inkl. Gegenprobe.
Altlast: tools/repair_closes.py (Trockenlauf Standard, Backup
automatisch). 18 Zeilen ueber 60 Tage korrigiert. Heute von +7,63 auf
-72,67 (Restfehler 1,41). Gesamt-P&L von -282,78 auf +169,98.
Zeitfalle zweimal getroffen: history_deals_get filtert nach
Broker-Wallclock, und fromtimestamp(d.time, BROKER) rendert 3 h zu
spaet. Aufgefallen nur, weil eine Deal-Zeit 23:11 lautete, das Log
aber 20:11:42 sagte.
Statistik-Modul separat geprueft: Arithmetik in allen drei Zeitraeumen
bitgenau korrekt (Abweichung 0,00). Der Netto-Defekt vom 05.08. besteht
weiter und ist groesser als damals: angezeigt -2.449,95 EUR, real
verblieben -17,06 (99 % der Steuer werden erstattet). Nicht gebaut -
die Loesung ist dokumentiert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
Entfernt: Tab-Button, #view-charts, der gesamte app.js-Chart-Block
(Kartenaufbau, renderChart, loadCharts, 20-s-Refresh, Resize-Handler), die
Chart-CSS-Regeln, die vendorte Bibliothek
web/lightweight-charts.standalone.production.js (164 KB) samt script-Tag - und
die Backend-Kette dahinter: /api/bars und engine.get_bars (60 Zeilen). Beide
existierten ausschliesslich fuer dieses Tab; geprueft, dass es keinen anderen
Aufrufer gibt.
Nicht betroffen (sahen nur aehnlich aus): die Chartmuster-Karte (Verdict-Gewicht
0,25, im Tab "SIG Live"), der MQL5-Indikator samt sr_levels.csv-Export (eigener
Pfad ueber _write_levels_file) und die Analyse-Module structure/patterns/cone -
die speisen den Snapshot, nicht das Chart. Der MT5-Chart bleibt voll bedient.
Ein veralteter Kommentar in trader.py, der auf engine.get_bars verwies, wurde
auf core/gaps.py umgehaengt.
Verifiziert am laufenden Server: /api/bars -> 404, Bibliothek -> 404,
data-view="charts" und id="view-charts" nicht mehr im ausgelieferten HTML,
6 Tabs zu 6 Views paarig, keine verwaisten JS-Referenzen, Klammern-Balance ok,
keine Fehler im Log. v=142.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
trader._calc_sl_tp bekommt die ATR-Zeitebene als Parameter (Default SL_TF =
M15); _send/_send_locked/open_long/open_short und place_stop reichen sie durch.
Gesetzt wird sie an genau zwei Stellen: engine._open bei source="auto_squeeze"
und _manage_squeeze_pending bei quelle="squeeze". Manuelle Trades und
Auto-Signal bleiben bei M15.
Grundlage backtest_sl_basis.py: auf Squeeze-Entries ist der Ertrag zwischen
beiden Basen ein Wash (alle Varianten innerhalb eines Standardfehlers), der
Worst-Case sinkt aber von -8,83 auf -2,44 xATR_M5 = Faktor 3. Auf der
Wellen-Population ist der weite M15-SL in BEIDEN Haelften besser - deshalb
bewusst keine globale Umstellung.
Die Multiplikatoren (1,8-2,2 xATR) sind nicht angefasst, nur die Zeitebene.
TP bewusst NICHT betroffen (User: "nur fuer SL, der TP soll mit dem Runner
mitwachsen"). Geprueft statt angenommen: der von _calc_sl_tp zurueckgegebene TP
wird von KEINEM Aufrufer gesendet, beide setzen nur req["sl"]. Das Ziel
verwaltet trailing.py dynamisch (3,5 x ATR der Wellen-Zeitebene). Warnkommentar
im Code, warum der TP dort nicht mit in die Order darf.
4 Szenarien getestet. Dabei eigene Fehlannahme korrigiert: ohne Pivot greift der
1,2-%-Fallback, der beim M15 vom MINDEST-Deckel aufgeweitet wird - die
2,2x-Obergrenze wird dort gar nicht erreicht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
analyze_squeeze_entry_gap.py fuehrt die zwei noetigen Quellen zusammen - das
Ausbruchs-Level steht NUR im Log, der tatsaechliche Fill NUR in trades - und
splittet am Umbau-Stichtag. Die Erfolgsmeldung ist paradox: eine per Pending
gefuellte Order erzeugt KEINE AUTO-SQUEEZE-ENTRY-Logzeile (die entsteht nur im
Market-Fallback), Squeeze-Trades ohne Log-Treffer sind also der Erfolg. Das
Skript weist sie deshalb getrennt aus.
Als Messung squeeze_entry_gap im measurement_reminder.py hinterlegt (>=12
Squeeze-Trades ab dem Umbau). Stichtag exakt 05.08. 08:55 statt Mitternacht -
der 08:33-Trade lief noch ueber die Market-Order und haette die Zaehlung
verfaelscht (0/12 statt faelschlich 1/12).
Zahlen-Korrektur: die zuerst dokumentierten +0,306 / +0,741 xATR stammten aus
einer Scratchpad-Auswertung mit FESTEM Stundenversatz. DST-korrekt ueber
zoneinfo sind es +0,275 / +0,686 (n=34 unveraendert). In CLAUDE.md, engine.py,
trader.py und der ini nachgezogen; die Schlussfolgerung aendert sich nicht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1) STOP-ORDER STATT MARKET (gebaut, live). engine._manage_squeeze_pending haelt
beide Box-Grenzen mit BUY_STOP/SELL_STOP bestueckt, solange der Squeeze armed
ist; trader.place_stop/cancel_pending/pending_orders neu. Groesse und SL kommen
aus denselben Funktionen wie die Market-Order, nur bezogen auf den Trigger.
OCO-Ersatz: faellt eine Seite, wird die andere im selben Tick storniert.
Der kritische Punkt bei einer ruhenden Order sind die Gates - sie waren bisher
Laufzeit-Checks im Market-Pfad. Neu: engine._squeeze_guard() als EINE Quelle
fuer beide Wege, jeden Tick neu bewertet; schlaegt ein Gate zu, werden die
Orders storniert. Market bleibt Fallback mit Nachjagd-Bremse
squeeze_max_chase_atr=0.20 (gemessen der noch positive Bereich).
Behandelt: Broker-Mindestabstand, falsche Marktseite, Toleranz 0,02 xATR gegen
Order-Churn, Log nur bei Fehleraenderung, Startup-Schonfrist. 10 Szenarien
getestet, live am Snapshot verifiziert.
2) META-LABELING (backtest_metalabel.py) - VERWORFEN. Zweitmodell auf 12
kausalen Merkmalen, Fit/Test in BEIDEN Richtungen: AUC 0,508 und 0,479 =
Muenzwurf, Kalibrierung im Top-Bucket 74 % vorhergesagt gegen 39 % real, und
8 von 12 Gewichten kippen das Vorzeichen. Die scheinbar besseren Schwellen
sind reine Handelsvermeidung und reproduzieren gegenlaeufig nicht.
23. verworfener Signal-Eingriff - deckt sich mit dem Caveat der Quelle.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
EINSATZ-FELD (User-Wunsch): Laufzeitwert MANUAL_MARGIN in core/config.py
(set/get_manual_margin, Muster wie set_margin_buffer). 0 = automatisch wie bisher
(margin_buffer_pct % der freien Margin), > 0 = Position wird auf genau diesen
Margin-Einsatz gerechnet.
- calc_lots setzt es um und DECKELT auf freie Margin x Buffer; ein zu hoher
Wunschwert wird begrenzt und geloggt statt an den Broker durchgereicht. Reicht
der Betrag nicht fuers Mindestlot -> 0.0 + Warnung, Trade sauber abgelehnt.
- Vorrang vor dem Risiko-Modus in trader._send_locked: eine ausdrueckliche
Groessenvorgabe schlaegt die Rechenregel.
- Wirkt auf ALLE neuen Positionen, auch die autonomen.
- UI-Feld "Einsatz" in der ORDER-Leiste (nicht in der Trade-Leiste: die ist nur
bei offener Position sichtbar, der Einsatz muss vorher einstellbar sein),
bernstein umrandet solange gesetzt. Enter blurrt nur, change sendet einmal.
- POST /api/manualmargin, Snapshot manual_margin, neustart-fest ueber
runtime_state.json. Ende-zu-Ende getestet (250 -> Snapshot -> 0 -> persistiert).
- Lot-Logik isoliert geprueft: 200 EUR -> 0,58 Lots; 5000 EUR -> gedeckelt.
SPRACHAUSGABE: Piper lokal (tools/speak.py). Windows-TTS funktioniert zwar, aber
es ist KEINE deutsche Stimme installiert - weder SAPI5 noch OneCore (nur
David/Zira/Mark, en-US); deutscher Text kaeme mit englischer Aussprache.
Add-WindowsCapability scheitert ohne Adminrechte.
Piper gewaehlt: laeuft lokal (passt zum Rest - WireGuard-only, Secrets in der
ini, keine Cloud), kostet nichts, aus PowerShell/Python aufrufbar. Stimme
thorsten-medium (de_DE), Real-Time-Faktor 0,08 (4,3 s Audio in 0,36 s).
Das ZIP entpackt eine Ebene tiefer als erwartet (tools/piper/piper/piper.exe) -
speak.py SUCHT Binary und Modell statt Pfade fest zu verdrahten. Der erste
Entwurf scheiterte daran; der Fail-safe meldete die Pfade sauber statt stumm zu
bleiben. Drei Modi getestet (Argument, Pipe, --wav).
tools/piper/ ist gitignored (Binaries + 60-MB-Modell), speak.py versioniert.
v=136.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Frage: kann der Bot aus den manuellen Trades lernen? Vorpruefung: noch NICHT.
1) Die entscheidenden Merkmale wurden nie gespeichert - beim Einstieg nur 5
Felder, davon ai_sentiment in 1,4 % und news_score in 34 % befuellt.
2) Das naive Lernziel ("wann steigt er ein") braucht unbeschriftete Negative;
brauchbar ist nur "welche Einstiege liefen gut" (Labels ueber P&L).
3) Die scheinbaren Muster sind Einzeltage: Stunde 13:00 zeigt +478 EUR ueber
n=69, davon +382 EUR (80 %) aus den fuenf Trades des 04.08.
Gebaut wurde Stufe 1 - reine Telemetrie, kein Verhalten:
- trades um 16 ctx_*-Spalten erweitert (DB-Backup .bak-2026-08-04, 1.148 Zeilen
unveraendert): p_break_target/-stop, dist_res_atr/dist_sup_atr, structure,
squeeze, htf_trend/h1_trend, spread_atr, session, block_reason, conf_pct,
atr_m5, bias, cone_pos, hour
- log_trade_open haengt die Spalten dynamisch an und ignoriert unbekannte
Schluessel -> aeltere DB ohne Migration laeuft weiter (fail-open)
- Kette: engine._run_analysis -> trader.set_open_context(ctx=) -> log_trade_open
Fallstrick beim Bau: wave_snap existiert in _run_analysis NICHT (nur in
snapshot()). Der erste Entwurf haette einen NameError erzeugt, den das umgebende
except still geschluckt haette - der Kontext waere dauerhaft leer geblieben ohne
dass es auffaellt. Jetzt self.wave.snapshot(); vd zusaetzlich per locals()-Guard.
Verifiziert auf einer DB-KOPIE: alle 16 Spalten korrekt geschrieben, unbekannter
Schluessel ignoriert, Basisfelder unberuehrt. Snapshot liefert manual_stats.
Dashboard-Karte "Manuelle Trades" (#card-manual, v=134): heute / 30 Tage /
gesamt / Bot-Vergleich plus Split mit vs ohne Signal-Deckung. Backend
history.manual_stats(30) mit ~120-s-Cache wie _alignment_cached. Bewusst neutral
gefaerbt und deskriptiv; eine Reifegrad-Zeile nennt die ctx-Abdeckung (heute
0 %), damit die Karte nicht ueberschaetzt wird.
Stufe 2 NICHT gebaut: erst bei ausreichender ctx-Abdeckung messen, was Gewinner
von Verlierern unterscheidet (Fit H1, validiert H2, AUC + Kalibrierung). Ergebnis
waere ein Hinweis im Order-Dialog, ausdruecklich KEIN Auto-Entry.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`_broker_offset_s()` leitete die Zeitzone aus `tick.time - now` ab. Das misst
die Zeitzone nur bei FRISCHEM Tick; steht der Markt, misst es die Veraltung.
Real am 01.08. (letzter Tick Fr 23:54): -34210 s -> gerundet -34200 (-9,5 h)
statt +10800. Die Pruefung [-12h,+14h] liess das durch.
Folgen (die Laufzeit-Uhr war nur das Sichtbare):
- open_time 12,5 h in der Zukunft -> Laufzeit 0:00
- Time-Stop-Alter (trailing.py:504) negativ -> 120-min-Stop haette nicht ausgeloest
- deal.time-Umrechnung beim externen Close, alle MQL5-Chart-Anker
Fix: Offset nur noch aus frischem Tick bestimmen, sonst letzter gueltiger Wert
(vorbelegt UTC+3 = dieselbe Konstante wie engine.broker_utc_offset). Frische am
bereits bekannten Offset gemessen -> nicht zirkulaer. Schwelle 900 s < halbes
Rundungsraster. Zeitzonen-Wechsel wird uebernommen und geloggt.
Ausserdem:
- market_closed greift sofort nach Neustart: _last_bid_move_ts wird aus dem
echten Tick-Alter vorbelegt statt bei null zu starten (vorher 180 s blind)
- pollSnapshot: der stille catch umschloss render() -> Render-Fehler wurden
lautlos verschluckt, das Dashboard fror ohne Konsolen-Fehler ein. Jetzt ist
nur noch der fetch im try.
- Header-Labels ausgeschrieben: "PEPPERSTONE KURS" / "HYPERLIQUID KURS"
- v=131, CLAUDE.md (Deployment-Drift Fall 4)
Verifiziert: market_closed sofort True, tick_age gegen die Rohwerte 0 s
Abweichung, open_time -> 31.07. 22:54 (Laufzeit +15,8 h).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- trader: open_time (echte Epoch, Broker-Offset korrigiert) aus position.time,
im Snapshot; nur 1x je Position berechnet, beim Close auf 0.
- _broker_offset_s(sym=None): Symbol explizit uebergebbar -- beim Adoptieren ist
self.symbol noch nicht gesetzt (war Offset 0 -> Zeit 3h in der Zukunft);
zusaetzlich Selbstheilung bei Zukunftswerten.
- app.js/index.html/style.css: Feld #tb-dur + Titel-Uhr #tb-runtime, eigener
1-s-Ticker (flüssig zwischen Snapshots), ab 2h bernstein (Time-Stop-Naehe).
- Verifiziert: Live-Trade 48898382 open_time == Log-Zeile 13:22:48.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Headless FastAPI-Backend (server.py + core/engine.py) mit Mobile-PWA (web/),
Strategie-/Backtest-Suite und Doku. Secrets, DB, Logs und Laufzeit-State sind
via .gitignore ausgeschlossen; Config-Vorlage: oil_widget_config.ini.example.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>