Initial commit: TradeMind – Krypto-Trading-Bot mit Lernmodus

Per Podman deploybarer Bot, der Käufe und Verkäufe simuliert ausführt und sich
aus den Ergebnissen weiter antrainiert.

Aufbau
- Einheitliche Bar-Verarbeitung für paper, backtest und live; ausgetauscht werden
  nur Datenquelle und Broker.
- Börsenanbindung über ccxt: rund 100 Börsen allein über exchange.id erreichbar.
  Zugangsdaten kommen über ENV-Platzhalter, der Live-Modus ist doppelt abgesichert.
- Paper-Broker mit Gebühren, Slippage, Börsenpräzision und Volumengrenzen.
- Online trainierte logistische Regression bewertet jedes Einstiegssignal. Sie lernt
  aus realen Trade-Ergebnissen, aus Shadow-Labels aller Kandidaten – auch der
  abgelehnten – und aus Hintergrund-Stichproben; beim Kaltstart wird sie aus der
  Kurshistorie vorgelernt.
- Risikomanagement: Positions- und Exposure-Grenzen, ATR-Stops, Cooldown sowie
  Tagesverlust- und Drawdown-Notbremsen.
- SQLite-Persistenz, HTTP-Status mit Prometheus-Metriken und Dashboard, Webhooks.

Deployment
- Containerfile (zweistufig, non-root UID 10001), podman-compose, systemd-Quadlet.
- Modell und Datenbank liegen im Volume /data und überleben Neustarts.

128 Tests, ruff sauber. Verifiziert gegen echte Marktdaten sowie im gebauten
Container inklusive Healthcheck und Zustandswiederherstellung.
This commit is contained in:
Tobias Zimmermann
2026-08-22 08:53:04 +02:00
commit 65ed73977e
40 changed files with 7033 additions and 0 deletions
+386
View File
@@ -0,0 +1,386 @@
# 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 <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://127.0.0.1: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
```
Der Port ist bewusst auf `127.0.0.1:8080` gebunden; das Dashboard ist damit nicht aus dem
Netz erreichbar. Für Zugriff von außen einen Reverse Proxy mit Authentifizierung davorsetzen.
> **Windows und macOS.** Dort läuft Podman in einer VM (`podman machine`). Auf Loopback
> veröffentlichte Ports bleiben in der VM und erreichen den Host nicht. Zum Testen entweder
> `podman machine ssh curl -s localhost:8080/health` verwenden oder in
> `podman-compose.yml` auf `"8080:8080"` umstellen. Auf Linux — dem Ziel für den
> Dauerbetrieb — funktioniert die Loopback-Bindung 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 127.0.0.1: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
```
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 |
| `/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.
---
## 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