This commit is contained in:
73 files changed
+10609
No files matched your search
+474
@@ -0,0 +1,474 @@
|
||||
# Docker Deployment Guide
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Docker or Podman installed
|
||||
- Gitea instance with Container Registry enabled
|
||||
- `GITEA_TOKEN` secret configured in repository settings
|
||||
|
||||
## Building
|
||||
|
||||
### Local Build
|
||||
|
||||
```bash
|
||||
# Build image
|
||||
docker build -t aitrade:local .
|
||||
|
||||
# Build with specific tag
|
||||
docker build -t aitrade:v1.0.0 .
|
||||
```
|
||||
|
||||
### Automated Build (Gitea Actions)
|
||||
|
||||
The CI/CD pipeline automatically builds and pushes images on:
|
||||
|
||||
- **Push to main/master/develop**: Creates `latest` and branch-specific tags
|
||||
- **Git tags (v*)**: Creates semantic version tags (`v1.0.0`, `1.0`, `1`)
|
||||
- **Pull requests**: Build-only, no push
|
||||
|
||||
**Image Tags:**
|
||||
```
|
||||
gitea.yourdomain.com/username/aitrade:latest
|
||||
gitea.yourdomain.com/username/aitrade:main
|
||||
gitea.yourdomain.com/username/aitrade:main-abc123def
|
||||
gitea.yourdomain.com/username/aitrade:v1.0.0
|
||||
gitea.yourdomain.com/username/aitrade:1.0
|
||||
gitea.yourdomain.com/username/aitrade:1
|
||||
```
|
||||
|
||||
## Running
|
||||
|
||||
### Docker Compose (Recommended)
|
||||
|
||||
```bash
|
||||
# Start application
|
||||
docker-compose up -d
|
||||
|
||||
# View logs
|
||||
docker-compose logs -f aitrade
|
||||
|
||||
# Stop application
|
||||
docker-compose down
|
||||
|
||||
# Restart application
|
||||
docker-compose restart aitrade
|
||||
```
|
||||
|
||||
### With Ollama (LLM Sentiment)
|
||||
|
||||
```bash
|
||||
# Start both services
|
||||
docker-compose --profile llm up -d
|
||||
|
||||
# Pull Mistral model (first time only)
|
||||
docker exec ollama ollama pull mistral
|
||||
|
||||
# Verify Ollama is running
|
||||
curl http://localhost:11434/api/version
|
||||
|
||||
# Enable LLM in aitrade (edit docker-compose.yaml)
|
||||
# Set: LLM_SCORER_ENABLED: "true"
|
||||
|
||||
# Restart aitrade
|
||||
docker-compose restart aitrade
|
||||
```
|
||||
|
||||
### Standalone Container
|
||||
|
||||
```bash
|
||||
# Pull from registry
|
||||
docker pull gitea.yourdomain.com/username/aitrade:latest
|
||||
|
||||
# Run with default settings
|
||||
docker run -d \
|
||||
--name aitrade \
|
||||
-p 8080:8080 \
|
||||
-v $(pwd)/data:/app/data \
|
||||
gitea.yourdomain.com/username/aitrade:latest
|
||||
|
||||
# Run with custom configuration
|
||||
docker run -d \
|
||||
--name aitrade \
|
||||
-p 8080:8080 \
|
||||
-v $(pwd)/data:/app/data \
|
||||
-v $(pwd)/config.yaml:/app/config.yaml:ro \
|
||||
-e CONFIG_FILE=/app/config.yaml \
|
||||
gitea.yourdomain.com/username/aitrade:latest
|
||||
|
||||
# Run with environment variables
|
||||
docker run -d \
|
||||
--name aitrade \
|
||||
-p 8080:8080 \
|
||||
-v $(pwd)/data:/app/data \
|
||||
-e TRADING_STRATEGY=normal \
|
||||
-e DRY_RUN=true \
|
||||
-e TRADING_ENABLED=false \
|
||||
-e LLM_SCORER_ENABLED=false \
|
||||
gitea.yourdomain.com/username/aitrade:latest
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Option 1: Environment Variables
|
||||
|
||||
Pass environment variables via `-e` flag or Docker Compose `environment:` section.
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
-e TRADING_STRATEGY=aggressive \
|
||||
-e MAX_TRADES_PER_HOUR=12 \
|
||||
-e DRY_RUN=true \
|
||||
...
|
||||
```
|
||||
|
||||
### Option 2: Config File (YAML)
|
||||
|
||||
Mount a `config.yaml` file into the container:
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
-v $(pwd)/config.yaml:/app/config.yaml:ro \
|
||||
-e CONFIG_FILE=/app/config.yaml \
|
||||
...
|
||||
```
|
||||
|
||||
### Option 3: Docker Compose
|
||||
|
||||
Edit `docker-compose.yaml` and modify the `environment:` section.
|
||||
|
||||
## Networking
|
||||
|
||||
### IB Gateway on Host
|
||||
|
||||
If IB Gateway runs on the host machine, use `host.docker.internal`:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
IB_GATEWAY_HOST: host.docker.internal
|
||||
IB_GATEWAY_PORT: "4001"
|
||||
```
|
||||
|
||||
### Ollama on Host
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
LLM_SCORER_ENABLED: "true"
|
||||
LLM_SCORER_ENDPOINT: http://host.docker.internal:11434
|
||||
```
|
||||
|
||||
### Custom Network
|
||||
|
||||
```bash
|
||||
# Create network
|
||||
docker network create trading-net
|
||||
|
||||
# Run IB Gateway container
|
||||
docker run -d --name ib-gateway --network trading-net your-ib-image
|
||||
|
||||
# Run aitrade
|
||||
docker run -d \
|
||||
--name aitrade \
|
||||
--network trading-net \
|
||||
-e IB_GATEWAY_HOST=ib-gateway \
|
||||
-e IB_GATEWAY_PORT=4001 \
|
||||
...
|
||||
```
|
||||
|
||||
## Persistence
|
||||
|
||||
### Database
|
||||
|
||||
Mount `/app/data` to persist SQLite database:
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
-v $(pwd)/data:/app/data \
|
||||
...
|
||||
```
|
||||
|
||||
**Important:** Ensure the directory is writable by UID 1000 (trader user).
|
||||
|
||||
```bash
|
||||
mkdir -p data
|
||||
chown -R 1000:1000 data
|
||||
```
|
||||
|
||||
### Config File
|
||||
|
||||
Mount config as read-only:
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
-v $(pwd)/config.yaml:/app/config.yaml:ro \
|
||||
-e CONFIG_FILE=/app/config.yaml \
|
||||
...
|
||||
```
|
||||
|
||||
## Health Checks
|
||||
|
||||
The container includes a built-in health check at `/health`:
|
||||
|
||||
### Docker Health Status
|
||||
|
||||
```bash
|
||||
# Check health status
|
||||
docker inspect --format='{{.State.Health.Status}}' aitrade
|
||||
# Output: healthy, unhealthy, or starting
|
||||
|
||||
# View health check logs
|
||||
docker inspect --format='{{range .State.Health.Log}}{{.Output}}{{end}}' aitrade
|
||||
|
||||
# Manual check
|
||||
curl http://localhost:8080/health
|
||||
# Response: {"status":"healthy"}
|
||||
```
|
||||
|
||||
### Custom Healthcheck Binary
|
||||
|
||||
The image includes a lightweight healthcheck binary (`/app/healthcheck`) for internal health checks:
|
||||
|
||||
```bash
|
||||
# Run healthcheck from inside container
|
||||
docker exec aitrade /app/healthcheck localhost 8080
|
||||
|
||||
# Custom host/port
|
||||
docker exec aitrade /app/healthcheck 127.0.0.1 8080
|
||||
|
||||
# Exit code 0 = healthy, 1 = unhealthy
|
||||
```
|
||||
|
||||
This allows health checks in Distroless without needing curl/wget.
|
||||
|
||||
## Monitoring
|
||||
|
||||
### Logs
|
||||
|
||||
```bash
|
||||
# View logs
|
||||
docker logs aitrade
|
||||
|
||||
# Follow logs
|
||||
docker logs -f aitrade
|
||||
|
||||
# Last 100 lines
|
||||
docker logs --tail 100 aitrade
|
||||
|
||||
# Docker Compose
|
||||
docker-compose logs -f aitrade
|
||||
```
|
||||
|
||||
### Metrics
|
||||
|
||||
Access the web dashboard at `http://localhost:8080`:
|
||||
- Account balance
|
||||
- Active trades
|
||||
- Trade history
|
||||
- News sentiment
|
||||
- Whitelist management
|
||||
|
||||
## Updating
|
||||
|
||||
### Pull Latest Image
|
||||
|
||||
```bash
|
||||
# Stop container
|
||||
docker stop aitrade
|
||||
docker rm aitrade
|
||||
|
||||
# Pull latest
|
||||
docker pull gitea.yourdomain.com/username/aitrade:latest
|
||||
|
||||
# Start with same settings
|
||||
docker run -d \
|
||||
--name aitrade \
|
||||
-p 8080:8080 \
|
||||
-v $(pwd)/data:/app/data \
|
||||
gitea.yourdomain.com/username/aitrade:latest
|
||||
```
|
||||
|
||||
### Docker Compose
|
||||
|
||||
```bash
|
||||
# Pull latest
|
||||
docker-compose pull aitrade
|
||||
|
||||
# Restart
|
||||
docker-compose up -d aitrade
|
||||
```
|
||||
|
||||
### Zero-Downtime Update
|
||||
|
||||
```bash
|
||||
# Pull new image
|
||||
docker pull gitea.yourdomain.com/username/aitrade:latest
|
||||
|
||||
# Start new container with different name
|
||||
docker run -d \
|
||||
--name aitrade-new \
|
||||
-p 8081:8080 \
|
||||
-v $(pwd)/data:/app/data \
|
||||
gitea.yourdomain.com/username/aitrade:latest
|
||||
|
||||
# Verify new container is healthy
|
||||
curl http://localhost:8081/health
|
||||
|
||||
# Switch port mapping (update reverse proxy or load balancer)
|
||||
# Then stop old container
|
||||
docker stop aitrade
|
||||
docker rm aitrade
|
||||
|
||||
# Rename new container
|
||||
docker rename aitrade-new aitrade
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Container Won't Start
|
||||
|
||||
```bash
|
||||
# Check logs
|
||||
docker logs aitrade
|
||||
|
||||
# Check health status
|
||||
docker inspect --format='{{.State.Health.Status}}' aitrade
|
||||
|
||||
# Verify permissions
|
||||
ls -la data/
|
||||
# Should be owned by UID 1000
|
||||
```
|
||||
|
||||
### Database Errors
|
||||
|
||||
```bash
|
||||
# Check database file
|
||||
ls -la data/aitrade.db
|
||||
|
||||
# Reset database (deletes all data!)
|
||||
docker stop aitrade
|
||||
rm -f data/aitrade.db*
|
||||
docker start aitrade
|
||||
```
|
||||
|
||||
### IB Gateway Connection
|
||||
|
||||
```bash
|
||||
# Check IB Gateway is running
|
||||
netstat -an | grep 4001
|
||||
|
||||
# Test from container
|
||||
docker exec aitrade sh -c "nc -zv host.docker.internal 4001"
|
||||
```
|
||||
|
||||
### Ollama Connection
|
||||
|
||||
```bash
|
||||
# Test Ollama from host
|
||||
curl http://localhost:11434/api/version
|
||||
|
||||
# Test from container
|
||||
docker exec aitrade sh -c "wget -qO- http://host.docker.internal:11434/api/version"
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
### Non-Root User
|
||||
|
||||
The container runs as user `trader` (UID 1000) by default.
|
||||
|
||||
### Network Isolation
|
||||
|
||||
Run on a dedicated network:
|
||||
|
||||
```bash
|
||||
docker network create --internal trading-net
|
||||
```
|
||||
|
||||
### Secrets
|
||||
|
||||
Never commit secrets to the repository. Use:
|
||||
- Docker secrets
|
||||
- Environment files (`.env`)
|
||||
- Kubernetes secrets
|
||||
- Vault
|
||||
|
||||
```bash
|
||||
# Using .env file
|
||||
docker run -d \
|
||||
--env-file .env \
|
||||
...
|
||||
```
|
||||
|
||||
## Production Deployment
|
||||
|
||||
### Systemd Service
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=AI Trading Application
|
||||
After=docker.service
|
||||
Requires=docker.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
WorkingDirectory=/opt/aitrade
|
||||
ExecStartPre=-/usr/bin/docker stop aitrade
|
||||
ExecStartPre=-/usr/bin/docker rm aitrade
|
||||
ExecStart=/usr/bin/docker run -d \
|
||||
--name aitrade \
|
||||
--restart unless-stopped \
|
||||
-p 8080:8080 \
|
||||
-v /opt/aitrade/data:/app/data \
|
||||
-v /opt/aitrade/config.yaml:/app/config.yaml:ro \
|
||||
-e CONFIG_FILE=/app/config.yaml \
|
||||
gitea.yourdomain.com/username/aitrade:latest
|
||||
ExecStop=/usr/bin/docker stop aitrade
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Enable and start:
|
||||
|
||||
```bash
|
||||
sudo systemctl enable aitrade
|
||||
sudo systemctl start aitrade
|
||||
sudo systemctl status aitrade
|
||||
```
|
||||
|
||||
### Kubernetes
|
||||
|
||||
See `k8s/` directory for Kubernetes manifests (deployment, service, configmap, secrets).
|
||||
|
||||
## Backup
|
||||
|
||||
### Database Backup
|
||||
|
||||
Since the application uses SQLite in default mode (single file), backups are straightforward:
|
||||
|
||||
```bash
|
||||
# Simple copy (application should be stopped)
|
||||
docker stop aitrade
|
||||
cp data/aitrade.db backups/aitrade-$(date +%Y%m%d).db
|
||||
docker start aitrade
|
||||
|
||||
# Or use SQLite backup command (can run while app is running)
|
||||
docker exec aitrade sqlite3 /app/data/aitrade.db ".backup '/app/data/backup-$(date +%Y%m%d).db'"
|
||||
|
||||
# Copy to host
|
||||
docker cp aitrade:/app/data/backup-20260628.db ./backups/
|
||||
|
||||
# Automated backup (cron) - runs while app is running
|
||||
0 2 * * * docker exec aitrade sqlite3 /app/data/aitrade.db ".backup '/app/data/backup-$(date +\%Y\%m\%d).db'"
|
||||
```
|
||||
|
||||
**Note:** SQLite `.backup` command is safe to run while the application is running. Simple file copy should only be done when the application is stopped.
|
||||
|
||||
### Full Backup
|
||||
|
||||
```bash
|
||||
# Backup entire data directory (stop app first)
|
||||
docker stop aitrade
|
||||
tar -czf aitrade-backup-$(date +%Y%m%d).tar.gz data/
|
||||
docker start aitrade
|
||||
```
|
||||
@@ -0,0 +1,716 @@
|
||||
# Interactive Brokers Gateway Setup
|
||||
|
||||
## Übersicht
|
||||
|
||||
**Keine API Keys nötig!** IB Gateway verwendet direkte TCP-Verbindung, keine OAuth oder Tokens.
|
||||
|
||||
## Authentifizierung
|
||||
|
||||
- ✅ IB Account Login (Username + Password)
|
||||
- ✅ 2FA über IB Key App (Smartphone)
|
||||
- ✅ Socket Connection von aitrade zum Gateway
|
||||
|
||||
---
|
||||
|
||||
## 1. IB Account erstellen
|
||||
|
||||
### Paper Trading Account (Empfohlen für Tests)
|
||||
```
|
||||
https://www.interactivebrokers.com/en/trading/free-trial.php
|
||||
```
|
||||
|
||||
**Vorteile:**
|
||||
- ✅ Kostenlos
|
||||
- ✅ Virtuelles Geld ($1M default)
|
||||
- ✅ Echte Marktdaten
|
||||
- ✅ Alle Features verfügbar
|
||||
|
||||
### Live Trading Account
|
||||
```
|
||||
https://www.interactivebrokers.com/en/trading/open-account.php
|
||||
```
|
||||
|
||||
**Voraussetzungen:**
|
||||
- Mindesteinlage (variiert nach Region)
|
||||
- Identitätsprüfung
|
||||
- W-8BEN/W-9 Formular (US-Steuern)
|
||||
|
||||
---
|
||||
|
||||
## 2. IB Gateway Setup mit Podman + Quadlet
|
||||
|
||||
### Systemd Service mit Quadlet
|
||||
|
||||
Quadlet ist in Podman 4.4+ integriert und generiert automatisch systemd Services aus `.container` Files.
|
||||
|
||||
**Datei:** `~/.config/containers/systemd/ib-gateway.container`
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Interactive Brokers Gateway (Paper Trading)
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Container]
|
||||
Image=ghcr.io/unusualcode/ib-gateway-docker:latest
|
||||
ContainerName=ib-gateway
|
||||
AutoUpdate=registry
|
||||
|
||||
# Environment Variables
|
||||
Environment=TWS_USERID=your_ib_username
|
||||
Environment=TWS_PASSWORD=your_ib_password
|
||||
Environment=TRADING_MODE=paper
|
||||
Environment=VNC_PASSWORD=your_vnc_password
|
||||
Environment=READ_ONLY_API=no
|
||||
Environment=TWOFA_TIMEOUT_ACTION=restart
|
||||
|
||||
# Ports
|
||||
PublishPort=4001:4001
|
||||
PublishPort=5900:5900
|
||||
PublishPort=6080:6080
|
||||
|
||||
# Volumes
|
||||
Volume=ib-gateway-settings.volume:/root/Jts:Z
|
||||
|
||||
# Restart Policy
|
||||
Restart=unless-stopped
|
||||
|
||||
# Health Check
|
||||
HealthCmd=/usr/bin/nc -z localhost 4001
|
||||
HealthInterval=30s
|
||||
HealthTimeout=10s
|
||||
HealthRetries=3
|
||||
|
||||
[Service]
|
||||
# Restart delay after failure
|
||||
RestartSec=30
|
||||
# Kill timeout
|
||||
TimeoutStopSec=70
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
```
|
||||
|
||||
**Wichtig:** Ersetze `your_ib_username` und `your_ib_password` mit deinen IB Credentials!
|
||||
|
||||
### Volume für persistente Settings
|
||||
|
||||
**Datei:** `~/.config/containers/systemd/ib-gateway-settings.volume`
|
||||
|
||||
```ini
|
||||
[Volume]
|
||||
```
|
||||
|
||||
Das war's! Quadlet managed das Volume automatisch.
|
||||
|
||||
### Service aktivieren & starten
|
||||
|
||||
```bash
|
||||
# Systemd User Services neu laden
|
||||
systemctl --user daemon-reload
|
||||
|
||||
# Service aktivieren (auto-start)
|
||||
systemctl --user enable ib-gateway.service
|
||||
|
||||
# Service starten
|
||||
systemctl --user start ib-gateway.service
|
||||
|
||||
# Status prüfen
|
||||
systemctl --user status ib-gateway.service
|
||||
|
||||
# Logs ansehen
|
||||
journalctl --user -u ib-gateway.service -f
|
||||
|
||||
# Service stoppen
|
||||
systemctl --user stop ib-gateway.service
|
||||
```
|
||||
|
||||
### Podman Auto-Update aktivieren
|
||||
|
||||
Quadlet unterstützt automatische Image-Updates:
|
||||
|
||||
```bash
|
||||
# Enable auto-update timer (täglich um 7 Uhr)
|
||||
systemctl --user enable --now podman-auto-update.timer
|
||||
|
||||
# Manueller Update-Check
|
||||
podman auto-update
|
||||
|
||||
# Timer Status
|
||||
systemctl --user status podman-auto-update.timer
|
||||
```
|
||||
|
||||
Mit `AutoUpdate=registry` im `.container` File updated Podman das IB Gateway Image automatisch.
|
||||
|
||||
---
|
||||
|
||||
## 3. VNC Zugriff (GUI)
|
||||
|
||||
IB Gateway ist eine Java GUI - VNC ermöglicht Remote-Zugriff:
|
||||
|
||||
### Option A: VNC Client (Port 5900)
|
||||
|
||||
```bash
|
||||
# Linux
|
||||
vncviewer localhost:5900
|
||||
|
||||
# macOS
|
||||
open vnc://localhost:5900
|
||||
|
||||
# Windows
|
||||
# TightVNC oder RealVNC installieren
|
||||
```
|
||||
|
||||
**VNC Password:** Wie in `VNC_PASSWORD` Environment Variable gesetzt
|
||||
|
||||
### Option B: Browser (Port 6080)
|
||||
|
||||
```bash
|
||||
# noVNC Web Interface
|
||||
http://localhost:6080
|
||||
```
|
||||
|
||||
**Vorteil:** Kein VNC Client nötig, funktioniert überall
|
||||
|
||||
---
|
||||
|
||||
## 4. IB Gateway Konfiguration
|
||||
|
||||
Nach dem ersten Start über VNC/noVNC:
|
||||
|
||||
### API Settings aktivieren
|
||||
|
||||
1. **Login** mit IB Username + Password + 2FA
|
||||
2. **Configure → Settings → API → Settings**
|
||||
- ✅ Enable ActiveX and Socket Clients
|
||||
- ✅ Read-Only API: `No` (für Trading)
|
||||
- ✅ Socket Port: `4001` (Paper) oder `4002` (Paper TWS)
|
||||
- ✅ Create API message log file: Optional für Debugging
|
||||
|
||||
3. **Configure → Settings → API → Precautions**
|
||||
- ❌ Bypass Order Precautions for API orders (für Auto-Trading!)
|
||||
|
||||
4. **Configure → Settings → API → Trusted IPs**
|
||||
- Add: `127.0.0.1`
|
||||
- Optional: Docker Bridge IP (meist `172.17.0.1`)
|
||||
|
||||
5. **Configure → Settings → Lock and Exit**
|
||||
- ✅ Auto restart: `Yes`
|
||||
- ✅ Auto logoff time: `23:50` (vor Market Close)
|
||||
|
||||
### 2FA Setup
|
||||
|
||||
**IB Key App installieren:**
|
||||
- iOS: https://apps.apple.com/app/ibkr-mobile/id00000000
|
||||
- Android: https://play.google.com/store/apps/details?id=atws.app
|
||||
|
||||
**Activation:**
|
||||
1. IB Website → Secure Login System → IB Key
|
||||
2. Scan QR Code mit IB Key App
|
||||
3. Bei Gateway Login: App öffnen + Bestätigen
|
||||
|
||||
**Auto-Restart bei 2FA Timeout:**
|
||||
- `TWOFA_TIMEOUT_ACTION=restart` in `.container` File
|
||||
- Gateway startet neu wenn 2FA länger als 3 Min nicht bestätigt
|
||||
|
||||
---
|
||||
|
||||
## 5. aitrade mit Podman IB Gateway verbinden
|
||||
|
||||
### Docker Compose anpassen
|
||||
|
||||
**Datei:** `/projects/Private/aitrade/docker-compose.yaml`
|
||||
|
||||
```yaml
|
||||
services:
|
||||
aitrade:
|
||||
# ... existing config
|
||||
environment:
|
||||
# IB Gateway Connection
|
||||
IB_GATEWAY_HOST: host.docker.internal
|
||||
IB_GATEWAY_PORT: "4001"
|
||||
IB_CLIENT_ID: "1"
|
||||
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
```
|
||||
|
||||
**Wichtig:** `host.docker.internal` funktioniert mit Docker Desktop und Podman automatisch.
|
||||
|
||||
### Podman Quadlet für aitrade
|
||||
|
||||
**Datei:** `~/.config/containers/systemd/aitrade.container`
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=AI Trading Application
|
||||
After=ib-gateway.service
|
||||
Requires=ib-gateway.service
|
||||
|
||||
[Container]
|
||||
Image=localhost/aitrade:local
|
||||
ContainerName=aitrade
|
||||
AutoUpdate=local
|
||||
|
||||
# Environment
|
||||
Environment=IB_GATEWAY_HOST=10.88.0.1
|
||||
Environment=IB_GATEWAY_PORT=4001
|
||||
Environment=TRADING_STRATEGY=normal
|
||||
Environment=DRY_RUN=true
|
||||
Environment=TRADING_ENABLED=false
|
||||
|
||||
# Ports
|
||||
PublishPort=8080:8080
|
||||
|
||||
# Volumes
|
||||
Volume=aitrade-data.volume:/app/data:Z
|
||||
|
||||
# Network: Share with IB Gateway
|
||||
Network=container:ib-gateway
|
||||
|
||||
# Restart
|
||||
Restart=unless-stopped
|
||||
|
||||
[Service]
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
```
|
||||
|
||||
**Network Trick:** `Network=container:ib-gateway` teilt den Network Stack - aitrade kann `localhost:4001` verwenden!
|
||||
|
||||
**Alternative:** Podman Pod (beide Container im gleichen Pod):
|
||||
|
||||
**Datei:** `~/.config/containers/systemd/trading.pod`
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Trading Pod (IB Gateway + aitrade)
|
||||
|
||||
[Pod]
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
```
|
||||
|
||||
**Datei:** `~/.config/containers/systemd/ib-gateway.container`
|
||||
|
||||
```ini
|
||||
[Container]
|
||||
# ... existing config
|
||||
Pod=trading.pod
|
||||
```
|
||||
|
||||
**Datei:** `~/.config/containers/systemd/aitrade.container`
|
||||
|
||||
```ini
|
||||
[Container]
|
||||
# ... existing config
|
||||
Pod=trading.pod
|
||||
Environment=IB_GATEWAY_HOST=localhost
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Connection Ports
|
||||
|
||||
| Mode | Application | Port |
|
||||
|------|-------------|------|
|
||||
| **Paper Trading** | IB Gateway | `4001` |
|
||||
| **Paper Trading** | TWS | `7497` |
|
||||
| **Live Trading** | IB Gateway | `4001` |
|
||||
| **Live Trading** | TWS | `7496` |
|
||||
|
||||
**Hinweis:** Port `4001` für beides - Unterschied ist der Login (Paper vs Live Account)!
|
||||
|
||||
---
|
||||
|
||||
## 7. Testing & Troubleshooting
|
||||
|
||||
### Gateway läuft?
|
||||
|
||||
```bash
|
||||
# Podman Status
|
||||
podman ps | grep ib-gateway
|
||||
|
||||
# Systemd Status
|
||||
systemctl --user status ib-gateway.service
|
||||
|
||||
# TCP Port Check
|
||||
nc -zv localhost 4001
|
||||
|
||||
# Logs
|
||||
journalctl --user -u ib-gateway.service -n 50
|
||||
```
|
||||
|
||||
### aitrade Connection Test
|
||||
|
||||
```bash
|
||||
# Start aitrade
|
||||
cd /projects/Private/aitrade
|
||||
docker-compose up -d
|
||||
|
||||
# Logs ansehen
|
||||
docker-compose logs -f aitrade
|
||||
|
||||
# Erfolg:
|
||||
# {"level":"INFO","msg":"connected to IB Gateway"}
|
||||
|
||||
# Fehler:
|
||||
# {"level":"ERROR","msg":"failed to connect","error":"connection refused"}
|
||||
```
|
||||
|
||||
### Common Errors
|
||||
|
||||
**Error: "Connection refused"**
|
||||
```bash
|
||||
→ IB Gateway läuft nicht
|
||||
→ Check: systemctl --user status ib-gateway.service
|
||||
→ Check: nc -zv localhost 4001
|
||||
```
|
||||
|
||||
**Error: "Not connected after 30s"**
|
||||
```
|
||||
→ Gateway läuft, aber API nicht enabled
|
||||
→ Login via VNC: http://localhost:6080
|
||||
→ Check: Configure → Settings → API → Enable Socket Clients
|
||||
```
|
||||
|
||||
**Error: "TWS Error 504: Not connected"**
|
||||
```
|
||||
→ Gateway noch nicht eingeloggt
|
||||
→ Check via VNC: http://localhost:6080
|
||||
→ 2FA bestätigen in IB Key App
|
||||
```
|
||||
|
||||
**Error: "TWS Error 502: Couldn't connect to TWS"**
|
||||
```
|
||||
→ Falscher Port
|
||||
→ Paper: 4001, Live: 7496
|
||||
→ Check config: IB_GATEWAY_PORT
|
||||
```
|
||||
|
||||
### VNC zeigt leeren Bildschirm
|
||||
|
||||
```bash
|
||||
# Container neu starten
|
||||
systemctl --user restart ib-gateway.service
|
||||
|
||||
# Logs prüfen
|
||||
journalctl --user -u ib-gateway.service -n 100
|
||||
|
||||
# Java Prozess im Container prüfen
|
||||
podman exec ib-gateway ps aux | grep java
|
||||
```
|
||||
|
||||
### 2FA Timeout
|
||||
|
||||
```bash
|
||||
# IB Key App öffnen und Login bestätigen
|
||||
# Wenn zu spät → Container startet neu (TWOFA_TIMEOUT_ACTION=restart)
|
||||
|
||||
# Manueller Restart
|
||||
systemctl --user restart ib-gateway.service
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Production Setup
|
||||
|
||||
### Secrets Management
|
||||
|
||||
**Niemals Credentials ins Git committen!**
|
||||
|
||||
**Option A: systemd Credentials (Empfohlen)**
|
||||
|
||||
```bash
|
||||
# Credentials verschlüsselt speichern
|
||||
systemd-creds encrypt --name=ib-username - ~/.config/ib-username.cred
|
||||
# Eingabe: your_username
|
||||
|
||||
systemd-creds encrypt --name=ib-password - ~/.config/ib-password.cred
|
||||
# Eingabe: your_password
|
||||
```
|
||||
|
||||
**Datei:** `~/.config/containers/systemd/ib-gateway.container`
|
||||
|
||||
```ini
|
||||
[Container]
|
||||
# ... existing config
|
||||
# Entferne Environment= Zeilen für Credentials
|
||||
|
||||
[Service]
|
||||
# Load encrypted credentials
|
||||
LoadCredentialEncrypted=ib-username:%h/.config/ib-username.cred
|
||||
LoadCredentialEncrypted=ib-password:%h/.config/ib-password.cred
|
||||
|
||||
# Set as environment variables
|
||||
Environment=TWS_USERID=%d/ib-username
|
||||
Environment=TWS_PASSWORD=%d/ib-password
|
||||
```
|
||||
|
||||
**Option B: Podman Secrets**
|
||||
|
||||
```bash
|
||||
# Secrets erstellen
|
||||
echo "your_username" | podman secret create ib_username -
|
||||
echo "your_password" | podman secret create ib_password -
|
||||
|
||||
# Secrets auflisten
|
||||
podman secret ls
|
||||
```
|
||||
|
||||
**Datei:** `~/.config/containers/systemd/ib-gateway.container`
|
||||
|
||||
```ini
|
||||
[Container]
|
||||
# ... existing config
|
||||
Secret=ib_username,type=env,target=TWS_USERID
|
||||
Secret=ib_password,type=env,target=TWS_PASSWORD
|
||||
```
|
||||
|
||||
### Monitoring
|
||||
|
||||
**Health Check Logs:**
|
||||
|
||||
```bash
|
||||
# Health Status
|
||||
podman healthcheck run ib-gateway
|
||||
|
||||
# Health History
|
||||
podman inspect ib-gateway --format='{{json .State.Health}}' | jq
|
||||
```
|
||||
|
||||
**Connection Monitoring Script:**
|
||||
|
||||
**Datei:** `/usr/local/bin/check-ib-gateway.sh`
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Check TCP Port
|
||||
if ! nc -z localhost 4001; then
|
||||
echo "IB Gateway port 4001 not reachable"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check aitrade connection
|
||||
if ! curl -sf http://localhost:8080/health > /dev/null; then
|
||||
echo "aitrade health check failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "OK: IB Gateway and aitrade running"
|
||||
```
|
||||
|
||||
**Systemd Timer:**
|
||||
|
||||
**Datei:** `~/.config/systemd/user/check-ib-gateway.service`
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=IB Gateway Health Check
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/local/bin/check-ib-gateway.sh
|
||||
```
|
||||
|
||||
**Datei:** `~/.config/systemd/user/check-ib-gateway.timer`
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=IB Gateway Health Check Timer
|
||||
|
||||
[Timer]
|
||||
OnBootSec=5min
|
||||
OnUnitActiveSec=5min
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
```
|
||||
|
||||
```bash
|
||||
# Timer aktivieren
|
||||
systemctl --user enable --now check-ib-gateway.timer
|
||||
```
|
||||
|
||||
### Backup der Gateway Settings
|
||||
|
||||
```bash
|
||||
# Backup Volume
|
||||
podman volume export ib-gateway-settings > ib-gateway-backup-$(date +%Y%m%d).tar
|
||||
|
||||
# Restore
|
||||
podman volume import ib-gateway-settings < ib-gateway-backup-20260628.tar
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Sicherheit
|
||||
|
||||
### Firewall Rules
|
||||
|
||||
```bash
|
||||
# Nur localhost darf auf IB Gateway zugreifen
|
||||
sudo firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address="127.0.0.1" port port=4001 protocol=tcp accept'
|
||||
sudo firewall-cmd --reload
|
||||
|
||||
# Oder mit iptables
|
||||
sudo iptables -A INPUT -p tcp --dport 4001 -s 127.0.0.1 -j ACCEPT
|
||||
sudo iptables -A INPUT -p tcp --dport 4001 -j DROP
|
||||
```
|
||||
|
||||
### VNC nur lokal
|
||||
|
||||
```bash
|
||||
# VNC Port nur auf localhost binden
|
||||
# In .container File:
|
||||
PublishPort=127.0.0.1:5900:5900
|
||||
PublishPort=127.0.0.1:6080:6080
|
||||
```
|
||||
|
||||
### Security Best Practices
|
||||
|
||||
1. ✅ **2FA aktiviert** (IB Key App)
|
||||
2. ✅ **Read-Only API für Testing** (dann auf "no" für Trading)
|
||||
3. ✅ **Trusted IPs beschränkt** (nur 127.0.0.1)
|
||||
4. ✅ **VNC Password gesetzt**
|
||||
5. ✅ **Credentials verschlüsselt** (systemd-creds)
|
||||
6. ✅ **Auto-Logout aktiviert** (23:50 vor Market Close)
|
||||
7. ✅ **DRY_RUN=true initial** (Paper Trading)
|
||||
|
||||
---
|
||||
|
||||
## 10. Nützliche Commands
|
||||
|
||||
### Podman Quadlet Management
|
||||
|
||||
```bash
|
||||
# Alle User Services auflisten
|
||||
systemctl --user list-units '*.service' | grep -E 'ib-gateway|aitrade'
|
||||
|
||||
# Service neu laden nach .container Änderungen
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user restart ib-gateway.service
|
||||
|
||||
# Service disable (kein Auto-Start)
|
||||
systemctl --user disable ib-gateway.service
|
||||
|
||||
# Logs seit Boot
|
||||
journalctl --user -u ib-gateway.service -b
|
||||
|
||||
# Logs letzte Stunde
|
||||
journalctl --user -u ib-gateway.service --since "1 hour ago"
|
||||
```
|
||||
|
||||
### Container Debugging
|
||||
|
||||
```bash
|
||||
# Shell im Container
|
||||
podman exec -it ib-gateway bash
|
||||
|
||||
# Java Prozesse
|
||||
podman exec ib-gateway ps aux | grep java
|
||||
|
||||
# Port Bindings prüfen
|
||||
podman port ib-gateway
|
||||
|
||||
# Volume Mountpoints
|
||||
podman volume inspect ib-gateway-settings
|
||||
|
||||
# Resource Usage
|
||||
podman stats ib-gateway
|
||||
```
|
||||
|
||||
### Quick Restart Workflow
|
||||
|
||||
```bash
|
||||
# Alle Trading Services neu starten
|
||||
systemctl --user restart ib-gateway.service aitrade.service
|
||||
|
||||
# Nur aitrade (nach Code-Change)
|
||||
podman build -t localhost/aitrade:local .
|
||||
systemctl --user restart aitrade.service
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. Zusammenfassung: Schnellstart
|
||||
|
||||
```bash
|
||||
# 1. IB Paper Account erstellen
|
||||
# → https://www.interactivebrokers.com/en/trading/free-trial.php
|
||||
|
||||
# 2. IB Key App installieren (Smartphone)
|
||||
# → iOS/Android App Store
|
||||
|
||||
# 3. Quadlet Container File erstellen
|
||||
mkdir -p ~/.config/containers/systemd
|
||||
cat > ~/.config/containers/systemd/ib-gateway.container << 'EOF'
|
||||
[Unit]
|
||||
Description=Interactive Brokers Gateway (Paper Trading)
|
||||
|
||||
[Container]
|
||||
Image=ghcr.io/unusualcode/ib-gateway-docker:latest
|
||||
Environment=TWS_USERID=your_username
|
||||
Environment=TWS_PASSWORD=your_password
|
||||
Environment=TRADING_MODE=paper
|
||||
Environment=VNC_PASSWORD=vnc123
|
||||
PublishPort=4001:4001
|
||||
PublishPort=6080:6080
|
||||
Volume=ib-gateway-settings.volume:/root/Jts:Z
|
||||
Restart=unless-stopped
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
EOF
|
||||
|
||||
# 4. Volume erstellen
|
||||
cat > ~/.config/containers/systemd/ib-gateway-settings.volume << 'EOF'
|
||||
[Volume]
|
||||
EOF
|
||||
|
||||
# 5. Service starten
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user enable --now ib-gateway.service
|
||||
|
||||
# 6. VNC öffnen
|
||||
firefox http://localhost:6080
|
||||
|
||||
# 7. API aktivieren (in VNC)
|
||||
# → Configure → Settings → API → Enable Socket Clients
|
||||
# → Port: 4001
|
||||
|
||||
# 8. aitrade starten
|
||||
cd /projects/Private/aitrade
|
||||
docker-compose up -d
|
||||
|
||||
# 9. Browser öffnen
|
||||
firefox http://localhost:8080
|
||||
|
||||
# Fertig! 🚀
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. Links & Resources
|
||||
|
||||
**IB Gateway Docker Image:**
|
||||
- GitHub: https://github.com/UnusualAlpha/ib-gateway-docker
|
||||
- Registry: ghcr.io/unusualcode/ib-gateway-docker
|
||||
|
||||
**Interactive Brokers:**
|
||||
- Paper Trading: https://www.interactivebrokers.com/en/trading/free-trial.php
|
||||
- IB Key App: https://www.interactivebrokers.com/en/trading/ibkey.php
|
||||
- API Docs: https://interactivebrokers.github.io/tws-api/
|
||||
|
||||
**Podman Quadlet:**
|
||||
- Docs: https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html
|
||||
- Examples: https://github.com/containers/quadlet
|
||||
|
||||
**aitrade:**
|
||||
- README: `/projects/Private/aitrade/README.md`
|
||||
- Docker Guide: `/projects/Private/aitrade/docs/DOCKER.md`
|
||||
Reference in new issue
Block a user