# 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](https://github.com/ccxt/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 ```bash git clone && 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://: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 ```bash 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 ```bash 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 ```bash 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) ```bash 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: ```yaml 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](config/config.example.yaml). ### Börse wechseln ```bash 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: ```yaml 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. - **Exploration** — `exploration_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](#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: ```yaml 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 ```bash 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.: ```bash podman exec -it trademind trademind report --config /config/config.yaml ``` ### Backtest ```bash 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 | ```bash 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: ```bash 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). > > ```bash > 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 ```bash 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