Tobias Zimmermann d081edfbd3 Automatisierten Handel über das Dashboard starten und pausieren
Neuer Abschnitt "Automatisierter Handel" im Dashboard mit Zustandsanzeige und
Start/Pause-Knopf, dazu GET und POST /control/trading. Funktioniert in beiden
Modi, simuliert wie echt.

Pausiert unterbindet ausschließlich neue Einstiege
- Marktdaten, Signalauswertung, Labeling und Training laufen weiter, damit der
  Datenstrom ins Modell nie abreißt. Auf identischen Daten: aktiv 99
  Beobachtungen bei 9 Trades, pausiert 90 Beobachtungen bei 0 Trades - der
  Unterschied sind genau die Trade-Labels, die ohne Handel nicht entstehen.
- Offene Positionen bleiben unter Stop- und Ziel-Überwachung. Ein pausierter Bot
  lässt niemanden ungeschützt im Markt stehen.
- Neu: trading.autostart (Standard true, bisheriges Verhalten) und
  trading.require_confirmation_for_live.

Nebenbei behoben
- Bei aktiver Notbremse (Tagesverlust, Drawdown) wurden bisher gar keine Signale
  mehr ausgewertet. Der Bot hörte damit ausgerechnet in der interessantesten
  Phase auf zu lernen. Jetzt läuft die Auswertung durch, nur der Kauf unterbleibt.
- describe_config meldete "Steuerung möglich", während sie eine Zeile später
  abgeschaltet wurde. Übersicht und Schutzlogik teilen sich jetzt dieselbe
  Prüffunktion (control_effective / control_is_unsafe).

Absicherung für Echtgeld
- Starten im Live-Modus verlangt die Bestätigung START_LIVE_TRADING im Body
  (HTTP 428 ohne sie); das Dashboard fragt sie per Dialog ab. Pausieren geht
  immer ohne Bestätigung - anhalten muss schnell gehen.
- Live-Modus mit offenem Port und ohne control_token wird nicht bedient: Die
  Steuerung wird beim Start abgeschaltet, der Bot läuft normal weiter. Sonst
  könnte jeder im Netz echten Handel starten.

Bewusst nicht enthalten: ein Umschalter zwischen Paper und Live zur Laufzeit.
Der bräuchte einen neuen Broker, andere Zugangsdaten und eine neue Kapitalbasis -
und würde bedeuten, dass ein Web-Klick aus einer Simulation echten Handel macht.

174 Tests (21 neue), ruff sauber. Im gebauten Container geprüft: 401 ohne Token,
200 mit, idempotentes Pausieren, 400 bei kaputtem Payload, und im Live-Start mit
ungültigen Dummy-Schlüsseln greift die Abschaltung der Steuerung.
2026-08-23 12:40:56 +02:00

TradeMind

Ein per Podman deploybarer Krypto-Trading-Bot mit drei Betriebsarten:

Modus Kurse Orders Lernen Zweck
paper live simuliert ja Dauerbetrieb ohne Risiko — Standard
backtest historisch simuliert ja Strategie und Parameter bewerten
live live echt optional Echtgeldhandel

Die Börsenanbindung läuft über ccxt — Binance, Kraken, Coinbase, Bybit, OKX, KuCoin, Bitget, Gate.io, MEXC und rund 100 weitere sind allein über den Konfigurationsschlüssel exchange.id erreichbar (trademind exchanges listet alle auf).

Hinweis zum Risiko. Der Bot ist ein Werkzeug, keine Ertragsgarantie und keine Anlageberatung. Die mitgelieferte Strategie ist eine funktionsfähige Grundlage, kein erprobtes Handelssystem — im Backtest unten liegt sie deutlich hinter Buy & Hold. Vor jedem Live-Einsatz gehören eigene Backtests, ein langer Paper-Lauf und eine bewusste Entscheidung über den Kapitaleinsatz. Der Live-Modus ist doppelt abgesichert und muss ausdrücklich freigeschaltet werden.


Schnellstart

git clone <repo> && cd Trademind_Claude
cp config/config.example.yaml config/config.yaml
cp config/trademind.env.example config/trademind.env
podman build --format docker -t trademind:latest -f Containerfile .
podman-compose up -d

--format docker sorgt dafür, dass der eingebaute HEALTHCHECK ins Image übernommen wird — das OCI-Format kennt die Anweisung nicht. Compose und Quadlet bringen ihren eigenen Healthcheck mit, dort spielt das Format keine Rolle.

Dashboard: http://<host>:8080/ · Logs: podman-compose logs -f

Ohne Zugangsdaten läuft der Bot im Paper-Modus auf echten Live-Kursen und beginnt sofort zu lernen. Für den Start werden nur öffentliche Marktdaten gelesen — kein API-Schlüssel nötig.

Ohne Container

python -m venv .venv && . .venv/bin/activate      # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
trademind backtest --config config/config.yaml --bars 6000 --fresh-model
trademind run --config config/config.yaml

Deployment mit Podman

Variante 1 — podman-compose

podman-compose up -d          # starten
podman-compose logs -f        # verfolgen
podman-compose down           # stoppen

Port 8080 wird auf allen Interfaces veröffentlicht — das Dashboard ist damit standardmäßig von anderen Rechnern erreichbar.

Sicherheit. Die lesenden Endpunkte sind nicht authentifiziert — wer den Port erreicht, sieht Kontostand, offene Positionen und die gesamte Trade-Historie. Dazu kommen die Steuerbefehle unter /control/… (Training anstoßen, Lernen umschalten), die sich mit server.control_token schützen lassen und das auch sollten. Gehandelt oder konfiguriert werden kann über HTTP in keinem Fall. In offenen Netzen deshalb einen Reverse Proxy mit Authentifizierung davorsetzen, den Zugriff per Firewall auf bekannte Quell-IPs begrenzen oder auf rein lokalen Zugriff zurückstellen:

Datei lokal statt offen
podman-compose.yml - "127.0.0.1:8080:8080"
deploy/trademind.container PublishPort=127.0.0.1:8080:8080

Ganz abschalten lässt sich der Server über server.enabled: false in der Konfiguration oder trademind run --no-server; der Container-Healthcheck braucht ihn allerdings.

Windows und macOS. Dort läuft Podman in einer VM (podman machine), die veröffentlichte Ports nicht in jeder Konstellation an den Host weiterreicht. Ist das Dashboard nicht erreichbar, mit podman machine ssh curl -s localhost:8080/health gegenprüfen — antwortet das, liegt es an der VM-Weiterleitung, nicht am Bot. Auf Linux — dem Ziel für den Dauerbetrieb — greift die Veröffentlichung unmittelbar.

Variante 2 — einzelner Container

podman volume create trademind-data
podman run -d --name trademind \
  --restart=unless-stopped \
  --env-file config/trademind.env \
  -v ./config:/config:ro,Z \
  -v trademind-data:/data:Z \
  -p 8080:8080 \
  --security-opt no-new-privileges \
  trademind:latest

Variante 3 — systemd über Quadlet (empfohlen für Dauerbetrieb)

mkdir -p ~/.config/containers/systemd ~/.config/trademind
cp deploy/trademind.container deploy/trademind-data.volume ~/.config/containers/systemd/
cp config/config.yaml config/trademind.env ~/.config/trademind/
loginctl enable-linger $USER
systemctl --user daemon-reload
systemctl --user start trademind
journalctl --user -u trademind -f

Der Container läuft als UID 10001 ohne zusätzliche Rechte. podman stop sendet SIGTERM; die Engine beendet den laufenden Durchlauf, sichert Modell und Zustand und fährt sauber herunter.

Persistenz

Alles Wichtige liegt im Volume /data:

Pfad Inhalt
/data/trademind.sqlite3 Trades, Equity-Kurve, Laufzeitzustand
/data/models/adaptive.npz Modellgewichte, Normalisierung, Erfahrungsspeicher

Ohne dieses Volume geht bei jedem Neustart der gesamte Lernfortschritt verloren.


Konfiguration

Alles läuft über config/config.yaml. Geheimnisse kommen über Umgebungsvariablen dazu:

exchange:
  id: ${TRADEMIND_EXCHANGE:-binance}
  api_key: ${TRADEMIND_API_KEY}
  api_secret: ${TRADEMIND_API_SECRET}
  password: ${TRADEMIND_API_PASSWORD}   # OKX, KuCoin, Coinbase Advanced, Bitget
  sandbox: true                          # Testnet der Börse

Drei Ebenen, in dieser Reihenfolge:

  1. Werte in der YAML-Datei
  2. ${VAR} / ${VAR:-fallback} — aus der Umgebung eingesetzt
  3. TRADEMIND__ABSCHNITT__SCHLUESSEL — überschreibt einzelne Werte punktuell, z. B. TRADEMIND__RISK__MAX_OPEN_POSITIONS=5

Die vollständig kommentierte Vorlage steht in config/config.example.yaml.

Börse wechseln

trademind exchanges --search kraken

Dann exchange.id setzen. Handelspaare in ccxt-Schreibweise (BTC/USDT, ETH/EUR); alle Symbole müssen dieselbe Quote-Währung haben, sonst bricht der Start mit einer verständlichen Meldung ab.

Live-Modus freischalten

Zwei Bedingungen müssen erfüllt sein, sonst startet der Bot nicht:

mode: live
live_confirmation: I_UNDERSTAND_THE_RISK
exchange:
  api_key: ...
  api_secret: ...

API-Schlüssel bitte ausschließlich mit Handelsrecht anlegen — Auszahlungen niemals erlauben — und wenn die Börse es anbietet auf die Server-IP beschränken. Zuerst mit sandbox: true gegen das Testnet fahren.


Wie der Bot lernt

Die Strategie besteht aus zwei Schichten:

1. Regelwerk (strategy.rules) — erzeugt Kandidaten für Einstiege: EMA-Kreuzung nach oben mit RSI- und Trendfilter, dazu Rücksetzer-Einstiege bei überverkauftem RSI im Aufwärtstrend. Ausstiege über EMA-Kreuzung nach unten oder nachlassendes Momentum, dazu immer ATR-basierter Stop und Kursziel.

2. Lernmodell (strategy.learner) — eine online trainierte logistische Regression über 18 skalenfreie Merkmale (EMA-Abstände, RSI und dessen Steigung, MACD-Histogramm, ATR in Prozent, Volatilitätsverhältnis, Momentum, Donchian-Lage, Volumen-z-Score, Kerzenform, Tageszeit). Sie schätzt für jeden Kandidaten die Gewinnwahrscheinlichkeit und lässt nur Signale oberhalb von entry_threshold durch.

Trainiert wird aus drei Quellen:

Quelle Gewicht Wofür
Reale Trade-Ergebnisse 3,0 Das eigentliche Ziel: hat sich der Trade gelohnt?
Signal-Shadow-Labels 1,0 Jeder Kandidat wird nachträglich bewertet — auch abgelehnte
Hintergrund-Stichproben 0,5 Regelmäßige Marktzustände, damit genug Daten zusammenkommen

Gelabelt wird nach der Triple-Barrier-Methode: Ein Signal gilt als Treffer, wenn der Kurs innerhalb von label_horizon_bars das Ziel (label_target_bps) erreicht, ohne vorher um denselben Betrag zu fallen. Läuft das Fenster ohne Berührung ab, entscheidet der Schlusskurs.

Zwei Details sind dabei wichtig:

  • Off-Policy-Lernen — auch abgelehnte Signale werden gelabelt. Der Bot lernt also aus Trades, die er nicht gemacht hat, und kann eine zu strenge Schwelle selbst korrigieren.
  • Explorationexploration_rate (Standard 5 %) handelt gelegentlich bewusst gegen das Modell. Ohne das würde es seine eigenen Vorurteile nie widerlegen.

Kaltstart. Ein frisches Modell würde bei 5-Minuten-Kerzen Tage brauchen, um die Aufwärmphase zu durchlaufen. Deshalb lernt der Bot beim ersten Start automatisch aus bootstrap_bars historischen Kerzen vor — in der Praxis rund 3 Sekunden statt einer Woche:

Modell ist untrainiert  lerne aus bis zu 3000 historischen Kerzen vor …
Vorlernen abgeschlossen: 662 neue Beobachtungen (gesamt 662), Modell einsatzbereit

Nachtrainieren lässt sich jederzeit — per Knopf im Dashboard oder über trademind backtest --save-model, siehe Training aus dem Dashboard anstoßen.

Solange das Modell nicht warm ist (warmup_samples), entscheidet allein das Regelwerk. Die Qualität lässt sich im Status unter strategy.learner.online_accuracy verfolgen — das ist eine prequentielle Messung: erst vorhersagen, dann lernen, also keine Selbstbewertung auf bereits gesehenen Daten.

Im Live-Modus lässt sich das Weiterlernen mit freeze_in_live: true einfrieren, wenn ein im Paper-Betrieb gereiftes Modell unverändert bleiben soll.


Risikomanagement

Vor jeder Order greifen mehrere unabhängige Grenzen:

  • max_position_pct — Anteil der Equity je Position
  • max_total_exposure_pct — Summe aller Positionen
  • max_open_positions — parallele Positionen
  • ATR-basierter Stop-Loss und Take-Profit, optional nachziehender Stop
  • cooldown_bars_after_exit — Pause pro Symbol nach einem Ausstieg
  • min_notional sowie die Mindestgrößen und Rundungsschritte der Börse

Dazu zwei Notbremsen:

  • max_daily_loss_pct — Handel pausiert bis zum nächsten UTC-Tag.
  • max_drawdown_pct — dauerhafter Stopp bis zum Neustart; offene Positionen werden geschlossen.

Ausstiege werden nie durch die Liquiditätsgrenze gedrosselt: Das Risikomanagement muss jederzeit vollständig aus einer Position herauskommen.


Simulation

Der Paper-Broker bildet die Kosten nach, an denen Strategien in der Praxis scheitern:

paper:
  fee_rate: 0.001                 # 0,1 % je Seite
  slippage_bps: 5                 # Ausführung 5 bps schlechter als der Referenzkurs
  max_volume_participation: 0.1   # höchstens 10 % des Kerzenvolumens

Zusätzlich werden Mengen auf die Präzision der Börse abgerundet, Käufe auf das verfügbare Guthaben begrenzt und Verkäufe auf den tatsächlichen Bestand. Ein Round-Trip ohne Kursbewegung kostet damit realistisch ~0,3 %.


Kommandos

trademind run       --config config/config.yaml     # Dauerbetrieb (paper oder live)
trademind backtest  --config config/config.yaml     # historischer Durchlauf
trademind validate  --config config/config.yaml     # Konfiguration + Börsenverbindung prüfen
trademind report    --config config/config.yaml     # Ergebnisse aus der Datenbank
trademind exchanges --search kraken                 # verfügbare Börsen

Im Container davor podman exec -it trademind setzen, z. B.:

podman exec -it trademind trademind report --config /config/config.yaml

Backtest

trademind backtest -c config/config.yaml --bars 20000 --fresh-model --save-model --out-dir out/
trademind backtest -c config/config.yaml --start 2025-01-01T00:00:00Z --end 2025-06-30T23:59:59Z
trademind backtest -c config/config.yaml --csv-dir data/csv --json
Option Wirkung
--fresh-model untrainiertes Modell — sonst wird auf dem gespeicherten aufgesetzt
--save-model Ergebnis nach learner.model_path schreiben (Vortraining)
--out-dir trades.csv, equity.csv und report.json ablegen
--csv-dir eigene OHLCV-Daten statt Börsenabruf
--seed reproduzierbare Läufe (Exploration ist zufällig)

CSV-Format: timestamp,open,high,low,close,volume, eine Datei je Symbol (BTC_USDT.csv). Der Zeitstempel darf in Sekunden, Millisekunden oder ISO-8601 stehen.

Der Backtest ist ein Walk-Forward-Lauf: Das Modell trainiert währenddessen ganz normal weiter, es gibt also keine getrennte Trainings- und Testphase.

Ein Beispiellauf über 20 Tage BTC/USDT und ETH/USDT (5m, frisches Modell):

  Endkapital         9818.92 USDT        Trades             29 (4/25)
  Gesamtrendite      -1.81 %             Trefferquote       13.8 %
  Max. Drawdown      2.23 %              Profit-Faktor      0.17
  Buy & Hold BTC     +23.81 %            Gebühren           114.84 USDT

Genau dafür ist der Backtest da: Die Standardparameter schlagen in einem starken Aufwärtstrend kein Buy & Hold, und der Bot sagt das offen. Kürzere Zeiträume, andere Timeframes und Parameter gehören ausprobiert, bevor auch nur ein Paper-Euro fließt.


Monitoring

Endpunkt Inhalt
/ Dashboard: Equity, Positionen, Trades, Modellzustand, Training
/health Liveness — nutzt der Container-Healthcheck
/ready Readiness (503, solange der Bot nicht sauber läuft)
/status vollständiger Zustand als JSON
/positions offene Positionen
/trades letzte Trades (?limit=100)
/metrics Prometheus-Textformat
curl -s localhost:8080/status | jq '.portfolio, .strategy.learner'
curl -s localhost:8080/metrics | grep trademind_portfolio

Optionale Benachrichtigungen über notifications.webhook_url — ein Payload bedient sowohl Slack als auch Discord.


Handel und Training aus dem Dashboard steuern

Automatisierter Handel

Der Abschnitt Automatisierter Handel zeigt den Zustand und schaltet ihn um — in beiden Modi, simuliert wie echt. Standardmäßig startet der Bot handelnd (trading.autostart: true); mit autostart: false sammelt er zunächst nur Daten und wartet auf die Freigabe.

Pausiert bedeutet nur: keine neuen Einstiege. Alles andere läuft weiter:

Auch pausiert Pausiert ausgesetzt
Marktdaten werden abgerufen Neue Positionen eröffnen
Signale werden ausgewertet und gelabelt
Das Modell trainiert weiter
Offene Positionen bleiben unter Stop-/Ziel-Überwachung

Der Bot lernt also durchgehend weiter, auch wenn er nicht handelt. Auf identischen Daten:

Handel aktiv      Trades=9  Beobachtungen=99  Shadow=90  Trade-Labels=9  nur-gelernte Signale=0
Handel pausiert   Trades=0  Beobachtungen=90  Shadow=90  Trade-Labels=0  nur-gelernte Signale=9

Der Unterschied sind genau die neun Trade-Labels, die ohne Handel nicht entstehen können — die Shadow-Labels aus dem Marktgeschehen laufen unverändert weiter.

Live-Modus. Das Starten verlangt zusätzlich die Bestätigung START_LIVE_TRADING (das Dashboard fragt sie ab, per HTTP als "confirm" im Body). Pausieren geht immer ohne Bestätigung — im Zweifel muss man schnell anhalten können. Abschalten lässt sich die Rückfrage mit trading.require_confirmation_for_live: false.

Die Kombination Live-Modus + Port nicht nur lokal + kein control_token wird nicht bedient: Der Bot schaltet die Steuerung dann beim Start ab und sagt das im Log. Sonst könnte jeder im Netz echten Handel starten. Mit Token oder auf 127.0.0.1 steht sie wieder zur Verfügung.

Training

Im Abschnitt Training stehen zwei Bedienelemente:

Historisch nachtrainieren. Anzahl Kerzen wählen (500 50 000 je Symbol), Knopf drücken. Der Bot lädt die Historie, läuft sie mit derselben Logik wie ein Backtest durch — ohne zu handeln — und speichert das Modell danach. Der Fortschritt erscheint direkt darunter:

Fertig in 4.3s: +1099 Beobachtungen (gesamt 1753), Modell einsatzbereit, gespeichert

Ein zweiter Start wird abgelehnt, solange einer läuft. Handelsdurchlauf und Nachtraining schließen sich gegenseitig aus, der Live-Betrieb pausiert also für die paar Sekunden. Die Labels des laufenden Betriebs bleiben davon unberührt.

Kontinuierliches Lernen. Schaltet das Online-Lernen im laufenden Betrieb an und aus. Ausgeschaltet handelt der Bot weiter, verändert das Modell aber nicht mehr — praktisch, um einen erreichten Stand einzufrieren, ohne den Bot anzuhalten.

Dieselben Aktionen per HTTP:

curl -X POST localhost:8080/control/train/history -H 'Content-Type: application/json' -H "X-TradeMind-Token: $TOKEN" -d '{"bars": 5000}'
Endpunkt Methode Wirkung
/control/trading GET Zustand des automatisierten Handels
/control/trading POST {"enabled": true} — Handel starten/pausieren (live zusätzlich "confirm")
/control/training GET Zustand des letzten/laufenden Trainings
/control/train/history POST {"bars": 5000} — Nachtraining anstoßen
/control/train/live POST {"enabled": false} — Lernen ein/aus

Absicherung. Anders als die lesenden Endpunkte verändern diese den Zustand des Bots. Setze deshalb server.control_token (bzw. TRADEMIND_CONTROL_TOKEN), sobald der Port nicht nur lokal erreichbar ist — die Steuerbefehle verlangen ihn dann im Header X-TradeMind-Token, während /status und Co. offen bleiben. Ohne Token warnt der Bot beim Start. Komplett abschalten lässt sich die Steuerung mit server.enable_control: false; die Routen sind dann nicht vorhanden (404).

openssl rand -hex 24

Aufbau

src/trademind/
├── cli.py          Kommandozeile
├── app.py          Zusammenbau aus der Konfiguration
├── config.py       Konfigurationsmodell (pydantic) + ${ENV}-Auflösung
├── engine.py       Handelsschleife und Bar-Verarbeitung
├── backtest.py     Walk-Forward-Durchlauf und Auswertung
├── strategy.py     Regelwerk und lernende Strategie
├── learner.py      Online-Logistikregression, Replay-Buffer, Persistenz
├── features.py     Merkmalsvektoren (vektorisiert)
├── indicators.py   EMA, RSI, ATR, MACD, Bollinger, Donchian, ROC
├── risk.py         Positionsgröße, Stops, Notbremsen
├── portfolio.py    Positionen, Equity, Kennzahlen
├── broker.py       Paper- und Live-Ausführung
├── exchange.py     ccxt-Client und Börsen-Metadaten
├── data.py         Marktdaten: live, CSV, Replay
├── storage.py      SQLite-Persistenz
├── server.py       HTTP-Status, Metriken, Dashboard
└── notify.py       Webhook-Benachrichtigungen

Die Bar-Verarbeitung in engine.process_bar ist für alle drei Modi identisch — ausgetauscht werden nur Datenquelle und Broker. Was im Backtest passiert, passiert live genauso.

Entwicklung

pip install -e ".[dev]"
pytest -q                 # 128 Tests
ruff check .

Bekannte Grenzen

  • Nur Long, nur Spot. Keine Short-Positionen, keine Hebel, keine Futures.
  • Bar-getaktet. Entscheidungen fallen bei Kerzenschluss, nicht tick-genau. Bei 1m ist die Latenz zwischen Signal und Ausführung spürbar.
  • Kein Order-Abgleich im Live-Modus. Der Bot führt seine Positionsbuchhaltung intern; manuelle Trades auf demselben Konto bringen sie durcheinander. Ein eigenes (Sub-)Konto verwenden.
  • Lineares Modell. Bewusst so gewählt: nachvollziehbar (/status zeigt alle Gewichte), robust bei wenig Daten, kein Overfitting-Zoo. Es findet keine nichtlinearen Muster.
  • Marktrisiko bleibt. Backtests sagen wenig über die Zukunft, und ein lernender Filter macht aus einer schwachen Strategie keine starke.

Lizenz

MIT

S
Description
No description provided
Readme
420 KiB
Version 0.1.0
Latest
2026-08-22 11:59:59 +02:00
Languages
Python 99.2%
Dockerfile 0.8%