Files
Trademind/README.md
T
Tobias Zimmermann 35d5c64e5b Dashboard und Metriken standardmäßig von außen erreichbar
Port 8080 wird in podman-compose.yml und in der Quadlet-Einheit auf allen
Interfaces veröffentlicht statt nur auf dem Loopback.

Die Endpunkte sind ausschließlich lesend – über HTTP lässt sich weder handeln
noch etwas konfigurieren –, aber sie sind nicht authentifiziert: Kontostand,
offene Positionen und die Trade-Historie sieht jeder, der den Port erreicht.
Das README nennt deshalb jetzt Reverse Proxy und Firewall als Absicherung, den
einzeiligen Weg zurück zu rein lokalem Zugriff sowie server.enabled: false zum
vollständigen Abschalten.

Geprüft im laufenden Container über die Nicht-Loopback-Adresse des Hosts:
/health, / , /status und /metrics antworten.
2026-08-22 11:14:40 +02:00

401 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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://<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
```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.** Alle Endpunkte sind lesend (`GET`, keine Steuerbefehle), aber **nicht
> authentifiziert**. Wer den Port erreicht, sieht Kontostand, offene Positionen und die
> gesamte Trade-Historie. In offenen Netzen deshalb entweder 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
```
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