Dateien nach "scripts" hochladen

This commit is contained in:
2026-04-20 15:26:26 +02:00
parent 34e8692a71
commit d8aff0f817
2 changed files with 818 additions and 0 deletions
+279
View File
@@ -0,0 +1,279 @@
#!/usr/bin/env bash
# =============================================================================
# Freqtrade NordVPN Addon (Gluetun)
# Ergänzt eine bestehende Freqtrade-Installation um NordVPN-Routing.
# Läuft im FREQTRADE LXC (nicht auf dem Proxmox Host).
#
# Quelle: https://git.obraasch.de/olli1986/freqtrade-proxmox-deploy
# =============================================================================
set -euo pipefail
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
info() { echo -e "${CYAN}[INFO]${NC} $*"; }
success() { echo -e "${GREEN}[ OK ]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[FAIL]${NC} $*"; exit 1; }
step() { echo -e "\n${BOLD}${BLUE}$*${NC}"; }
ask() {
local prompt="$1" default="${2:-}"
if [[ -n "$default" ]]; then
echo -ne "${YELLOW}${NC}${prompt} [${CYAN}${default}${NC}]: "
else
echo -ne "${YELLOW}${NC}${prompt}: "
fi
read -r REPLY; REPLY="${REPLY:-$default}"
}
ask_secret() {
echo -ne "${YELLOW}${NC}${1}: "
read -rs REPLY; echo
}
confirm() {
echo -ne "${YELLOW}${NC}${1} [${CYAN}j${NC}/N]: "
read -r ans; [[ "$ans" =~ ^[jJyY]$ ]]
}
print_banner() {
clear
echo -e "${BOLD}${BLUE}"
echo " _ _ _ ___ ___ _ _"
echo " | \| |___ _ _ __ | \ \ \ / / | \| |"
echo " | .\` / _ \ '_/ _\` \ V /\ V /| | .\` |"
echo " |_|\_\___/_| \__,_|\_/ \_/ |_|_|\_|"
echo " Freqtrade + NordVPN Addon (Gluetun)"
echo -e "${NC}"
echo -e " ${CYAN}Ergänzt Freqtrade um NordVPN-Routing via Gluetun${NC}"
echo -e " ${YELLOW}Läuft im Freqtrade LXC nicht auf dem Proxmox Host!${NC}\n"
}
check_requirements() {
step "Voraussetzungen prüfen"
[[ $EUID -ne 0 ]] && error "Bitte als root ausführen!"
command -v docker &>/dev/null || error "Docker nicht gefunden."
docker compose version &>/dev/null || error "Docker Compose Plugin nicht gefunden."
success "Docker + Compose vorhanden"
FT_DIR=""
for candidate in /opt/docker-projekte/freqtrade /opt/freqtrade; do
if [[ -f "$candidate/docker-compose.yml" ]] && [[ -d "$candidate/user_data" ]]; then
FT_DIR="$candidate"; break
fi
done
[[ -z "$FT_DIR" ]] && error "Keine Freqtrade-Installation gefunden in /opt/docker-projekte/freqtrade/"
success "Freqtrade gefunden: $FT_DIR"
if [[ ! -e /dev/net/tun ]]; then
warn "/dev/net/tun fehlt lege an..."
mkdir -p /dev/net
mknod /dev/net/tun c 10 200 2>/dev/null || true
chmod 600 /dev/net/tun 2>/dev/null || true
[[ ! -e /dev/net/tun ]] && error "/dev/net/tun konnte nicht erstellt werden.\nBitte im Proxmox Host prüfen ob in /etc/pve/lxc/<ID>.conf diese Zeilen stehen:\n lxc.cgroup2.devices.allow: c 10:200 rwm\n lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file\n\nHinweis: proxmox-install.sh richtet das automatisch ein."
fi
success "/dev/net/tun vorhanden"
}
collect_config() {
step "NordVPN Konfiguration"
echo -e "\n ${BOLD}Service-Credentials${NC} findest du unter:"
echo -e " ${CYAN}https://my.nordaccount.com → NordVPN → 'Set up NordVPN manually'${NC}"
echo -e " ${RED} Nicht deine E-Mail/Passwort die Service-Credentials!${NC}\n"
ask "NordVPN Service-Username"
NORD_USER="$REPLY"
[[ -z "$NORD_USER" ]] && error "Username darf nicht leer sein!"
ask_secret "NordVPN Service-Passwort"
NORD_PASS="$REPLY"
[[ -z "$NORD_PASS" ]] && error "Passwort darf nicht leer sein!"
echo
echo -e " ${BOLD}VPN-Server Modus:${NC}"
echo -e " ${CYAN}1)${NC} Serverland normaler Server (IP kann wechseln)"
echo -e " ${CYAN}2)${NC} Hostname spezifischen Server/Dedicated-IP pinnen"
ask "Auswahl [1/2]" "1"
VPN_MODE="$REPLY"
if [[ "$VPN_MODE" == "2" ]]; then
ask "Hostname (z.B. de123.nordvpn.com)"
VPN_HOSTNAME="$REPLY"; VPN_COUNTRY=""
else
ask "Serverland" "Germany"
VPN_COUNTRY="$REPLY"; VPN_HOSTNAME=""
fi
CURRENT_PORT=$(grep -oP '"0\.0\.0\.0:\K\d+(?=:\d+")' "$FT_DIR/docker-compose.yml" 2>/dev/null | head -1 || echo "8080")
ask "FreqUI Port" "$CURRENT_PORT"
FT_PORT="$REPLY"
echo -e "\n${BOLD}${BLUE}══════════ Zusammenfassung ══════════${NC}"
echo -e " Freqtrade: ${CYAN}$FT_DIR${NC}"
echo -e " NordVPN User: ${CYAN}$NORD_USER${NC}"
if [[ -n "$VPN_HOSTNAME" ]]; then
echo -e " VPN Server: ${CYAN}$VPN_HOSTNAME${NC}"
else
echo -e " VPN Land: ${CYAN}$VPN_COUNTRY${NC}"
fi
echo -e " FreqUI Port: ${CYAN}$FT_PORT${NC}"
echo -e "${BOLD}${BLUE}=====================================\n${NC}"
confirm "Einrichten?" || { info "Abgebrochen."; exit 0; }
}
backup_and_stop() {
step "Backup & Stack stoppen"
local BAK="$FT_DIR/docker-compose.yml.bak-$(date +%Y%m%d_%H%M%S)"
cp "$FT_DIR/docker-compose.yml" "$BAK"
success "Backup: $BAK"
cd "$FT_DIR"
if docker compose ps --quiet 2>/dev/null | grep -q .; then
docker compose down; success "Stack gestoppt"
else
info "Kein laufender Stack"
fi
}
write_env() {
step ".env schreiben"
[[ -f "$FT_DIR/.env" ]] && cp "$FT_DIR/.env" "$FT_DIR/.env.bak-$(date +%Y%m%d_%H%M%S)"
cat > "$FT_DIR/.env" << ENVEOF
# NordVPN Service-Credentials
# Quelle: https://my.nordaccount.com → NordVPN → Set up NordVPN manually
OPENVPN_USER=${NORD_USER}
OPENVPN_PASSWORD=${NORD_PASS}
ENVEOF
chmod 600 "$FT_DIR/.env"
success ".env gespeichert (chmod 600)"
}
write_compose() {
step "docker-compose.yml erstellen"
local VPN_SERVER_LINE=""
if [[ -n "$VPN_HOSTNAME" ]]; then
VPN_SERVER_LINE=" - SERVER_HOSTNAMES=${VPN_HOSTNAME}"
else
VPN_SERVER_LINE=" - SERVER_COUNTRIES=${VPN_COUNTRY}"
fi
cat > "$FT_DIR/docker-compose.yml" << COMPOSEEOF
# Freqtrade + NordVPN/Gluetun | $(date '+%Y-%m-%d %H:%M:%S')
# Netzwerk: [Freqtrade] → [Gluetun] → NordVPN → Internet
# WebUI: http://LXC-IP:${FT_PORT} (Port liegt bei Gluetun!)
services:
# VPN-Gateway exponiert alle Ports
gluetun:
image: qmcgaw/gluetun:latest
container_name: gluetun
restart: unless-stopped
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
environment:
- VPN_SERVICE_PROVIDER=nordvpn
- VPN_TYPE=openvpn
- OPENVPN_USER=\${OPENVPN_USER}
- OPENVPN_PASSWORD=\${OPENVPN_PASSWORD}
${VPN_SERVER_LINE}
ports:
- "0.0.0.0:${FT_PORT}:8080"
volumes:
- gluetun_data:/gluetun
healthcheck:
test: ["CMD-SHELL", "wget -qO- https://ipinfo.io/ip || exit 1"]
interval: 30s
timeout: 15s
retries: 5
start_period: 45s
# Freqtrade KEIN ports: hier, läuft über Gluetun-Netzwerk
freqtrade:
image: freqtradeorg/freqtrade:stable
restart: unless-stopped
container_name: freqtrade
network_mode: "container:gluetun"
depends_on:
gluetun:
condition: service_healthy
volumes:
- "./user_data:/freqtrade/user_data"
command: >
trade
--logfile /freqtrade/user_data/logs/freqtrade.log
--db-url sqlite:////freqtrade/user_data/tradesv3.sqlite
--config /freqtrade/user_data/config.json
--strategy SampleStrategy
volumes:
gluetun_data:
COMPOSEEOF
success "docker-compose.yml geschrieben"
}
start_and_verify() {
step "Stack starten & VPN prüfen"
cd "$FT_DIR"
docker compose up -d
echo
info "Warte auf VPN-Verbindung (bis zu 90s)..."
local tries=0 vpn_ok=false
while [[ $tries -lt 18 ]]; do
sleep 5; echo -n "."
docker exec gluetun wget -qO- https://ipinfo.io/ip &>/dev/null && { vpn_ok=true; break; }
((tries++))
done
echo
if [[ "$vpn_ok" == "true" ]]; then
VPN_IP=$(docker exec gluetun wget -qO- https://ipinfo.io/ip 2>/dev/null)
success "VPN verbunden! Ausgehende IP: ${BOLD}${VPN_IP}${NC}"
else
warn "VPN-Verbindung Timeout. Prüfe: docker logs gluetun"
VPN_IP="unbekannt prüfe: docker exec gluetun wget -qO- https://ipinfo.io/ip"
fi
sleep 5
docker ps | grep -q "freqtrade" && success "Freqtrade läuft" || warn "Freqtrade prüfen: docker logs freqtrade"
}
show_result() {
local LXC_IP; LXC_IP=$(hostname -I | awk '{print $1}')
echo
echo -e "${BOLD}${GREEN}╔═══════════════════════════════════════╗${NC}"
echo -e "${BOLD}${GREEN}║ ✅ NordVPN Addon eingerichtet! ║${NC}"
echo -e "${BOLD}${GREEN}╚═══════════════════════════════════════╝${NC}"
echo
echo -e " ${BOLD}FreqUI:${NC} ${CYAN}http://${LXC_IP}:${FT_PORT}${NC}"
echo -e " ${BOLD}NordVPN IP:${NC} ${CYAN}${VPN_IP}${NC}"
echo
echo -e " ${BOLD}${YELLOW}⚠ Binance API-Key anpassen:${NC}"
echo -e " ${YELLOW}In Binance → API Management → IP-Beschränkung${NC}"
echo -e " ${YELLOW}Die NordVPN-IP eintragen (nicht mehr die Home-IP)!${NC}"
echo
echo -e " ${BOLD}Befehle:${NC}"
echo -e " ${CYAN}docker exec gluetun wget -qO- https://ipinfo.io/ip${NC} # VPN-IP prüfen"
echo -e " ${CYAN}docker logs gluetun${NC} # VPN-Logs"
echo -e " ${CYAN}cd $FT_DIR && docker compose restart${NC} # Neustart"
echo
}
main() {
print_banner
check_requirements
collect_config
backup_and_stop
write_env
write_compose
start_and_verify
show_result
}
main "$@"
+539
View File
@@ -0,0 +1,539 @@
#!/usr/bin/env bash
# =============================================================================
# Freqtrade Proxmox Installer
# Erstellt automatisch einen Docker-LXC und installiert Freqtrade darin.
# Läuft auf dem PROXMOX HOST.
#
# Quelle: https://git.obraasch.de/olli1986/freqtrade-proxmox-deploy
# =============================================================================
set -euo pipefail
# ── Farben ────────────────────────────────────────────────────────────────────
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
info() { echo -e "${CYAN}[INFO]${NC} $*"; }
success() { echo -e "${GREEN}[ OK ]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[FAIL]${NC} $*"; exit 1; }
step() { echo -e "\n${BOLD}${BLUE}$*${NC}"; }
ask() {
local prompt="$1" default="${2:-}"
if [[ -n "$default" ]]; then
echo -ne "${YELLOW}${NC}${prompt} [${CYAN}${default}${NC}]: "
else
echo -ne "${YELLOW}${NC}${prompt}: "
fi
read -r REPLY
REPLY="${REPLY:-$default}"
}
ask_secret() {
echo -ne "${YELLOW}${NC}${1}: "
read -rs REPLY; echo
}
confirm() {
echo -ne "${YELLOW}${NC}${1} [${CYAN}j${NC}/N]: "
read -r ans; [[ "$ans" =~ ^[jJyY]$ ]]
}
# ── Voraussetzungen ───────────────────────────────────────────────────────────
check_host() {
[[ $EUID -ne 0 ]] && error "Bitte als root ausführen!"
command -v pct &>/dev/null || error "pct nicht gefunden läuft das auf einem Proxmox Host?"
command -v pvesh &>/dev/null || error "pvesh nicht gefunden läuft das auf einem Proxmox Host?"
command -v pveam &>/dev/null || error "pveam nicht gefunden."
success "Proxmox Host erkannt"
}
# ── Banner ────────────────────────────────────────────────────────────────────
print_banner() {
clear
echo -e "${BOLD}${BLUE}"
cat << 'BANNER'
___ _ _
| _ \_ _ _____ _| |_ _ __ _____ __ | |_ ___ ___
| _/ '_/ _ \ \ / | ' \/ _ \ \ / / | _/ -_) _ \
|_| |_| \___/_\_\_|_||_\___/_\_\_| \__\___\___/
+ NordVPN + Proxmox LXC Installer
BANNER
echo -e "${NC}"
echo -e " ${CYAN}Freqtrade vollautomatischer Installer für Proxmox${NC}"
echo -e " ${CYAN}git.obraasch.de/olli1986/freqtrade-proxmox-deploy${NC}\n"
echo -e " ${YELLOW}⚠ Nur für Test- und Lernzwecke. Trading auf eigenes Risiko!${NC}\n"
}
# ── Verfügbare Ressourcen ermitteln ───────────────────────────────────────────
detect_resources() {
# Nächste freie CT-ID
NEXT_ID=$(pvesh get /cluster/nextid 2>/dev/null | tr -d '[:space:]' || echo "100")
# Verfügbare Storages (für LXC-Rootfs)
STORAGES=$(pvesm status 2>/dev/null | awk 'NR>1 && $2!="dir" || $2=="dir" {print $1}' | tr '\n' ' ' || echo "local-lvm local")
# Verfügbare Bridges
BRIDGES=$(ip link show 2>/dev/null | grep -oP '(?<=: )vmbr\d+' | tr '\n' ' ' || echo "vmbr0")
# Erster Storage als Default
DEFAULT_STORAGE=$(pvesm status 2>/dev/null | awk 'NR==2{print $1}' || echo "local-lvm")
# Erste Bridge als Default
DEFAULT_BRIDGE=$(ip link show 2>/dev/null | grep -oP '(?<=: )vmbr\d+' | head -1 || echo "vmbr0")
}
# ── Template holen ────────────────────────────────────────────────────────────
prepare_template() {
step "Debian 12 Template prüfen"
# Suche lokal vorhandenes Debian 12 Template
TEMPLATE_FILE=$(pveam list local 2>/dev/null | grep -oP 'debian-12-standard_\S+' | head -1 || true)
if [[ -z "$TEMPLATE_FILE" ]]; then
info "Template nicht lokal vorhanden lade herunter..."
pveam update &>/dev/null || warn "pveam update fehlgeschlagen, versuche trotzdem..."
# Neuestes Debian 12 Template aus der Liste holen
TEMPLATE_FILE=$(pveam available --section system 2>/dev/null \
| grep -oP 'debian-12-standard_\S+' | sort -V | tail -1 || true)
[[ -z "$TEMPLATE_FILE" ]] && TEMPLATE_FILE="debian-12-standard_12.7-1_amd64.tar.zst"
info "Lade Template: $TEMPLATE_FILE"
pveam download local "$TEMPLATE_FILE" || error "Template-Download fehlgeschlagen!"
fi
TEMPLATE_PATH="local:vztmpl/${TEMPLATE_FILE}"
success "Template bereit: $TEMPLATE_FILE"
}
# ── Konfiguration abfragen ────────────────────────────────────────────────────
collect_config() {
step "LXC Konfiguration"
echo -e " ${CYAN}Verfügbare Storages:${NC} $STORAGES"
echo -e " ${CYAN}Verfügbare Bridges:${NC} $BRIDGES\n"
ask "LXC Container-ID" "$NEXT_ID"
CT_ID="$REPLY"
[[ "$CT_ID" =~ ^[0-9]+$ ]] || error "Container-ID muss eine Zahl sein!"
pct status "$CT_ID" &>/dev/null && error "Container-ID $CT_ID existiert bereits!"
ask "Hostname" "freqtrade"
CT_HOST="$REPLY"
ask "CPU Kerne" "2"
CT_CORES="$REPLY"
ask "RAM in MB" "2048"
CT_RAM="$REPLY"
ask "Swap in MB" "512"
CT_SWAP="$REPLY"
ask "Disk-Größe in GB" "10"
CT_DISK="$REPLY"
ask "Storage Pool" "$DEFAULT_STORAGE"
CT_STORAGE="$REPLY"
ask "Netzwerk-Bridge" "$DEFAULT_BRIDGE"
CT_BRIDGE="$REPLY"
ask "Netzwerk (dhcp oder z.B. 192.168.1.100/24)" "dhcp"
CT_IP="$REPLY"
if [[ "$CT_IP" != "dhcp" ]]; then
ask "Gateway (nur bei statischer IP nötig)"
CT_GW="$REPLY"
else
CT_GW=""
fi
ask_secret "Root-Passwort für den LXC"
CT_PASS="$REPLY"
[[ ${#CT_PASS} -lt 8 ]] && error "Passwort muss mind. 8 Zeichen haben!"
step "Freqtrade Konfiguration"
ask "FreqUI Port" "8080"
FT_PORT="$REPLY"
ask "Börse" "binance"
FT_EXCHANGE="$REPLY"
ask "Stake-Währung" "USDC"
FT_STAKE="$REPLY"
ask "Max. offene Trades" "3"
FT_MAX_TRADES="$REPLY"
ask "Timeframe" "5m"
FT_TIMEFRAME="$REPLY"
ask "FreqUI Benutzername" "freqtrader"
FT_API_USER="$REPLY"
ask_secret "FreqUI Passwort (min. 8 Zeichen)"
FT_API_PASS="$REPLY"
[[ ${#FT_API_PASS} -lt 8 ]] && error "FreqUI Passwort muss mind. 8 Zeichen haben!"
if confirm "Telegram-Benachrichtigungen aktivieren?"; then
FT_TELEGRAM=true
ask "Telegram Bot-Token"
TG_TOKEN="$REPLY"
ask "Telegram Chat-ID"
TG_CHATID="$REPLY"
else
FT_TELEGRAM=false
TG_TOKEN=""
TG_CHATID=""
fi
if confirm "Filebrowser (Web-Dateimanager) installieren?"; then
INSTALL_FB=true
ask "Filebrowser Port" "8081"
FB_PORT="$REPLY"
else
INSTALL_FB=false
FB_PORT="8081"
fi
# ── Zusammenfassung ──
echo -e "\n${BOLD}${BLUE}══════════════ Zusammenfassung ══════════════${NC}"
echo -e " CT-ID: ${CYAN}$CT_ID${NC} Hostname: ${CYAN}$CT_HOST${NC}"
echo -e " CPU/RAM: ${CYAN}$CT_CORES Kerne / ${CT_RAM}MB RAM / ${CT_SWAP}MB Swap${NC}"
echo -e " Disk: ${CYAN}${CT_DISK}GB auf $CT_STORAGE${NC}"
echo -e " Netzwerk: ${CYAN}$CT_IP via $CT_BRIDGE${NC}"
echo -e " FreqUI Port: ${CYAN}$FT_PORT${NC} Börse: ${CYAN}$FT_EXCHANGE${NC}"
echo -e " Filebrowser: $([ "$INSTALL_FB" = true ] && echo "${CYAN}Ja (Port $FB_PORT)${NC}" || echo "${CYAN}Nein${NC}")"
echo -e " Telegram: $([ "$FT_TELEGRAM" = true ] && echo "${CYAN}Ja${NC}" || echo "${CYAN}Nein${NC}")"
echo -e "${BOLD}${BLUE}════════════════════════════════════════════${NC}\n"
confirm "Installation starten?" || { info "Abgebrochen."; exit 0; }
}
# ── LXC erstellen ─────────────────────────────────────────────────────────────
create_lxc() {
step "LXC Container $CT_ID erstellen"
local NET_CONFIG
if [[ "$CT_IP" == "dhcp" ]]; then
NET_CONFIG="name=eth0,bridge=${CT_BRIDGE},ip=dhcp,firewall=1"
else
NET_CONFIG="name=eth0,bridge=${CT_BRIDGE},ip=${CT_IP},gw=${CT_GW},firewall=1"
fi
pct create "$CT_ID" "$TEMPLATE_PATH" \
--hostname "$CT_HOST" \
--password "$CT_PASS" \
--cores "$CT_CORES" \
--memory "$CT_RAM" \
--swap "$CT_SWAP" \
--rootfs "${CT_STORAGE}:${CT_DISK}" \
--net0 "$NET_CONFIG" \
--features "nesting=1,keyctl=1" \
--unprivileged 1 \
--onboot 1 \
--timezone "Europe/Berlin" \
--start 0
# TUN-Device für spätere VPN-Nutzung vorbereiten
cat >> "/etc/pve/lxc/${CT_ID}.conf" << 'TUNCFG'
# TUN-Device für NordVPN/Gluetun (wird von install-vpn.sh genutzt)
lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file
TUNCFG
success "LXC $CT_ID erstellt (mit TUN-Device vorbereitet)"
# Starten
info "Starte Container..."
pct start "$CT_ID"
info "Warte auf Netzwerk..."
local tries=0
until pct exec "$CT_ID" -- bash -c "ip route | grep -q default" 2>/dev/null; do
sleep 3; ((tries++))
[[ $tries -ge 20 ]] && error "Netzwerk im LXC nicht erreichbar. Prüfe Bridge/IP."
echo -n "."
done
echo
success "Container läuft und hat Netzwerk"
}
# ── Inneres Setup-Script generieren und ausführen ─────────────────────────────
run_inner_setup() {
step "Freqtrade innerhalb des LXC installieren"
# Zufällige Secrets für FreqUI generieren
JWT_SECRET=$(openssl rand -hex 32 2>/dev/null || tr -dc 'a-f0-9' < /dev/urandom | head -c 64)
WS_TOKEN=$(openssl rand -hex 16 2>/dev/null || tr -dc 'a-f0-9' < /dev/urandom | head -c 32)
# Telegram-Block für config.json vorbereiten
if [[ "$FT_TELEGRAM" == "true" ]]; then
TG_JSON='"telegram": {"enabled": true, "token": "'"$TG_TOKEN"'", "chat_id": "'"$TG_CHATID"'"},'
else
TG_JSON='"telegram": {"enabled": false, "token": "", "chat_id": ""},'
fi
# Inner-Script in eine Temp-Datei schreiben (Variablen werden jetzt expandiert)
cat > /tmp/ft_inner_setup.sh << INNEREOF
#!/usr/bin/env bash
set -euo pipefail
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
ok() { echo -e "\${GREEN}[ OK ]\${NC} \$*"; }
inf() { echo -e "\${CYAN}[INF]\${NC} \$*"; }
fail() { echo -e "\${RED}[ERR]\${NC} \$*"; exit 1; }
INSTALL_DIR="/opt/docker-projekte"
FT_DIR="\$INSTALL_DIR/freqtrade"
FT_PORT="${FT_PORT}"
FT_EXCHANGE="${FT_EXCHANGE}"
FT_STAKE="${FT_STAKE}"
FT_MAX_TRADES="${FT_MAX_TRADES}"
FT_TIMEFRAME="${FT_TIMEFRAME}"
FT_API_USER="${FT_API_USER}"
FT_API_PASS="${FT_API_PASS}"
JWT_SECRET="${JWT_SECRET}"
WS_TOKEN="${WS_TOKEN}"
INSTALL_FB="${INSTALL_FB}"
FB_PORT="${FB_PORT}"
# ── System-Pakete ─────────────────────────────────────────────────────────────
inf "System aktualisieren..."
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq curl ca-certificates gnupg lsb-release net-tools
ok "Systempakete installiert"
# ── Docker installieren ───────────────────────────────────────────────────────
inf "Docker installieren..."
curl -fsSL https://get.docker.com | sh > /dev/null 2>&1
systemctl enable docker --now > /dev/null 2>&1
ok "Docker installiert: \$(docker --version)"
# ── Verzeichnisse anlegen ─────────────────────────────────────────────────────
mkdir -p "\$FT_DIR"
mkdir -p "\$INSTALL_DIR/filebrowser/config"
mkdir -p "\$INSTALL_DIR/filebrowser/database"
ok "Verzeichnisse angelegt"
# ── Filebrowser (optional) ────────────────────────────────────────────────────
if [[ "\$INSTALL_FB" == "true" ]]; then
inf "Filebrowser installieren..."
cat > "\$INSTALL_DIR/filebrowser/docker-compose.yml" << 'FBEOF'
services:
filebrowser:
image: filebrowser/filebrowser:latest
container_name: filebrowser
user: "0:0"
ports:
- "FBPORT:8080"
volumes:
- /opt/docker-projekte:/srv
- /opt/docker-projekte/filebrowser/database:/database
- /opt/docker-projekte/filebrowser/config:/config
command: ["--address","0.0.0.0","--port","8080",
"--database","/database/filebrowser.db",
"--root","/srv","--noauth=false"]
restart: unless-stopped
FBEOF
# Port einsetzen (war im Heredoc wegen einfacher Anführungszeichen nicht ersetzt)
sed -i "s/FBPORT/\${FB_PORT}/g" "\$INSTALL_DIR/filebrowser/docker-compose.yml"
cd "\$INSTALL_DIR/filebrowser"
docker compose up -d > /dev/null 2>&1
sleep 4
FB_PASS=\$(docker logs filebrowser 2>&1 | grep -oP "(?<=randomly generated password: )\S+" | tail -1 || echo "siehe: docker logs filebrowser")
ok "Filebrowser gestartet Passwort: \$FB_PASS"
echo "FILEBROWSER_PASS=\$FB_PASS" >> /root/ft_install_info.txt
fi
# ── Freqtrade docker-compose.yml ──────────────────────────────────────────────
inf "Freqtrade docker-compose.yml herunterladen..."
cd "\$FT_DIR"
curl -fsSL https://raw.githubusercontent.com/freqtrade/freqtrade/stable/docker-compose.yml \
-o docker-compose.yml
# Nur localhost → 0.0.0.0 für WebUI-Erreichbarkeit
sed -i 's/127\.0\.0\.1/0.0.0.0/g' docker-compose.yml
ok "docker-compose.yml vorbereitet"
# ── user_data Verzeichnis erstellen ───────────────────────────────────────────
inf "user_data erstellen..."
docker compose run --rm freqtrade create-userdir --userdir user_data > /dev/null 2>&1
ok "user_data erstellt"
# ── config.json generieren ────────────────────────────────────────────────────
inf "config.json erstellen..."
cat > "\$FT_DIR/user_data/config.json" << CONFIGEOF
{
"max_open_trades": ${FT_MAX_TRADES},
"stake_currency": "${FT_STAKE}",
"stake_amount": "unlimited",
"tradable_balance_ratio": 0.99,
"fiat_display_currency": "EUR",
"timeframe": "${FT_TIMEFRAME}",
"dry_run": true,
"dry_run_wallet": 1000,
"cancel_open_orders_on_exit": false,
"trading_mode": "spot",
"margin_mode": "",
"unfilledtimeout": {
"entry": 10,
"exit": 10,
"exit_timeout_count": 0,
"unit": "minutes"
},
"entry_pricing": {
"price_side": "same",
"use_order_book": true,
"order_book_top": 1,
"price_last_balance": 0.0,
"check_depth_of_market": {"enabled": false, "bids_to_ask_delta": 1}
},
"exit_pricing": {
"price_side": "same",
"use_order_book": true,
"order_book_top": 1
},
"exchange": {
"name": "${FT_EXCHANGE}",
"key": "",
"secret": "",
"ccxt_config": {},
"ccxt_async_config": {}
},
"pairlists": [{"method": "StaticPairList"}],
"pair_whitelist": [
"${FT_STAKE}/USDT",
"BTC/${FT_STAKE}",
"ETH/${FT_STAKE}"
],
${TG_JSON}
"api_server": {
"enabled": true,
"listen_ip_address": "0.0.0.0",
"listen_port": 8080,
"verbosity": "error",
"enable_openapi": false,
"jwt_secret_key": "${JWT_SECRET}",
"ws_token": "${WS_TOKEN}",
"CORS_origins": [],
"username": "${FT_API_USER}",
"password": "${FT_API_PASS}"
},
"bot_name": "freqtrade",
"initial_state": "running",
"force_entry_enable": false,
"internals": {"process_throttle_secs": 5}
}
CONFIGEOF
ok "config.json erstellt"
# ── Freqtrade starten ─────────────────────────────────────────────────────────
inf "Freqtrade starten..."
cd "\$FT_DIR"
docker compose up -d > /dev/null 2>&1
sleep 5
if docker ps | grep -q freqtrade; then
ok "Freqtrade läuft"
else
fail "Freqtrade gestartet nicht prüfe: docker logs freqtrade"
fi
# ── Info-Datei für Proxmox-Script ─────────────────────────────────────────────
LXC_IP=\$(hostname -I | awk '{print \$1}')
cat > /root/ft_install_info.txt << INFOEOF
LXC_IP=\$LXC_IP
FT_PORT=${FT_PORT}
FT_API_USER=${FT_API_USER}
FB_PORT=${FB_PORT}
INSTALL_FB=${INSTALL_FB}
INFOEOF
echo ""
echo "=== Freqtrade Installation abgeschlossen ==="
echo "WebUI: http://\$LXC_IP:${FT_PORT}"
echo "User: ${FT_API_USER}"
INNEREOF
# Script in den LXC schieben und ausführen
pct push "$CT_ID" /tmp/ft_inner_setup.sh /root/ft_setup.sh
pct exec "$CT_ID" -- chmod +x /root/ft_setup.sh
info "Starte Installation im LXC (kann 2-3 Minuten dauern)..."
pct exec "$CT_ID" -- bash /root/ft_setup.sh
rm -f /tmp/ft_inner_setup.sh
success "Installation im LXC abgeschlossen"
}
# ── Ergebnis anzeigen ─────────────────────────────────────────────────────────
show_result() {
# Info-Datei aus dem LXC lesen
local TMP_INFO="/tmp/ft_info_${CT_ID}.txt"
pct pull "$CT_ID" /root/ft_install_info.txt "$TMP_INFO" 2>/dev/null || true
LXC_IP=""
FB_PASS=""
if [[ -f "$TMP_INFO" ]]; then
# shellcheck disable=SC1090
source "$TMP_INFO"
rm -f "$TMP_INFO"
fi
# Falls IP leer, über pct ermitteln
if [[ -z "$LXC_IP" ]]; then
LXC_IP=$(pct exec "$CT_ID" -- hostname -I 2>/dev/null | awk '{print $1}' || echo "unbekannt")
fi
echo
echo -e "${BOLD}${GREEN}╔══════════════════════════════════════════╗${NC}"
echo -e "${BOLD}${GREEN}║ ✅ Installation erfolgreich! ║${NC}"
echo -e "${BOLD}${GREEN}╚══════════════════════════════════════════╝${NC}"
echo
echo -e " ${BOLD}LXC Container:${NC} ID ${CYAN}$CT_ID${NC} | Host: ${CYAN}$CT_HOST${NC}"
echo -e " ${BOLD}IP-Adresse:${NC} ${CYAN}$LXC_IP${NC}"
echo
echo -e " ${BOLD}${GREEN}FreqUI (Freqtrade WebUI):${NC}"
echo -e " ${CYAN}http://${LXC_IP}:${FT_PORT}${NC}"
echo -e " Benutzer: ${CYAN}$FT_API_USER${NC}"
echo
if [[ "$INSTALL_FB" == "true" ]]; then
echo -e " ${BOLD}${GREEN}Filebrowser (Dateimanager):${NC}"
echo -e " ${CYAN}http://${LXC_IP}:${FB_PORT}${NC}"
echo -e " Benutzer: ${CYAN}admin${NC}"
[[ -n "${FILEBROWSER_PASS:-}" ]] && echo -e " Passwort: ${CYAN}$FILEBROWSER_PASS${NC} ${YELLOW}← sofort ändern!${NC}"
echo
fi
echo -e " ${BOLD}Config-Datei:${NC}"
echo -e " ${CYAN}/opt/docker-projekte/freqtrade/user_data/config.json${NC}"
echo
echo -e " ${BOLD}${YELLOW}Nächste Schritte:${NC}"
echo -e " ${YELLOW}1. Binance API-Keys in config.json eintragen${NC}"
echo -e " ${YELLOW}2. dry_run: false setzen wenn bereit${NC}"
echo -e " ${YELLOW}3. Für NordVPN: install-vpn.sh im LXC ausführen${NC}"
echo
echo -e " ${BOLD}NordVPN Addon (im LXC ausführen):${NC}"
echo -e " ${CYAN}bash -c \"\$(curl -fsSL https://git.obraasch.de/olli1986/freqtrade-proxmox-deploy/raw/branch/main/scripts/install-vpn.sh)\"${NC}"
echo
echo -e " ${BOLD}LXC Konsole öffnen:${NC}"
echo -e " ${CYAN}pct enter $CT_ID${NC}"
echo
}
# ── Hauptprogramm ─────────────────────────────────────────────────────────────
main() {
print_banner
check_host
detect_resources
prepare_template
collect_config
create_lxc
run_inner_setup
show_result
}
main "$@"