From a9304d6d61da9e833a8431ae0f52c1e758abc041 Mon Sep 17 00:00:00 2001 From: Oliver Braasch Date: Mon, 20 Apr 2026 16:04:10 +0200 Subject: [PATCH] Dateien nach "scripts" hochladen --- scripts/freqtrade-lxc-install.sh | 519 +++++++++++++++++++++++++++++++ 1 file changed, 519 insertions(+) create mode 100644 scripts/freqtrade-lxc-install.sh diff --git a/scripts/freqtrade-lxc-install.sh b/scripts/freqtrade-lxc-install.sh new file mode 100644 index 0000000..381b8ad --- /dev/null +++ b/scripts/freqtrade-lxc-install.sh @@ -0,0 +1,519 @@ +#!/usr/bin/env bash +# ============================================================================= +# Freqtrade LXC Installer für Proxmox +# Erstellt automatisch einen Docker-LXC und installiert: +# Docker, Portainer, Filebrowser, Freqtrade +# +# Läuft 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:-}" + [[ -n "$default" ]] && echo -ne "${YELLOW} ➜ ${NC}${prompt} [${CYAN}${default}${NC}]: " \ + || echo -ne "${YELLOW} ➜ ${NC}${prompt}: " + 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]$ ]] +} + +# ── Banner ───────────────────────────────────────────────────────────────────── +print_banner() { + clear + echo -e "${BOLD}${BLUE}" + cat << 'BANNER' + ___ _ _ + | __| _ ___ __ _| |_ _ _ __ _ | | __ __ __ + | _| '_/ -_) _` | _| '_/ _` | | |__/ _\ \/ / + |_||_| \___\__, |\__|_| \__,_| |____\__/_/\_/ + |__/ Proxmox LXC Installer +BANNER + echo -e "${NC}" + echo -e " ${CYAN}Installiert: Docker · Portainer · Filebrowser · Freqtrade${NC}" + echo -e " ${CYAN}Quelle: git.obraasch.de/olli1986/freqtrade-proxmox-deploy${NC}\n" + echo -e " ${YELLOW}⚠ Nur für Test-/Lernzwecke. Trading auf eigenes Risiko!${NC}\n" +} + +# ── Proxmox-Checks ───────────────────────────────────────────────────────────── +check_host() { + step "Proxmox Host prüfen" + [[ $EUID -ne 0 ]] && error "Bitte als root ausführen!" + command -v pct &>/dev/null || error "pct nicht gefunden – läuft das auf Proxmox?" + command -v pvesh &>/dev/null || error "pvesh nicht gefunden." + command -v pveam &>/dev/null || error "pveam nicht gefunden." + success "Proxmox erkannt" +} + +# ── Ressourcen ermitteln ─────────────────────────────────────────────────────── +detect_resources() { + NEXT_ID=$(pvesh get /cluster/nextid 2>/dev/null | tr -d '[:space:]' || echo "100") + + # Nur rootdir-fähige Storages (local unterstützt KEINE Container-Rootfs!) + STORAGES=$(pvesm status --content rootdir 2>/dev/null | awk 'NR>1{print $1}' | tr '\n' ' ' || echo "local-lvm") + [[ -z "${STORAGES// }" ]] && STORAGES="local-lvm" + DEFAULT_STORAGE=$(pvesm status --content rootdir 2>/dev/null | awk 'NR==2{print $1}' || echo "local-lvm") + [[ -z "$DEFAULT_STORAGE" ]] && DEFAULT_STORAGE="local-lvm" + + BRIDGES=$(ip link show 2>/dev/null | grep -oP '(?<=: )vmbr\d+' | tr '\n' ' ' || echo "vmbr0") + DEFAULT_BRIDGE=$(ip link show 2>/dev/null | grep -oP '(?<=: )vmbr\d+' | head -1 || echo "vmbr0") +} + +# ── Debian 12 Template ───────────────────────────────────────────────────────── +prepare_template() { + step "Debian 12 Template prüfen" + TEMPLATE_FILE=$(pveam list local 2>/dev/null | grep -oP 'debian-12-standard_\S+' | sort -V | tail -1 || true) + + if [[ -z "$TEMPLATE_FILE" ]]; then + info "Nicht lokal vorhanden – lade herunter..." + pveam update &>/dev/null || warn "pveam update fehlgeschlagen" + TEMPLATE_FILE=$(pveam available --section system 2>/dev/null \ + | grep -oP 'debian-12-standard_\S+' | sort -V | tail -1 || echo "debian-12-standard_12.7-1_amd64.tar.zst") + info "Lade: $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}Storages (rootdir-fähig):${NC} ${BOLD}${STORAGES}${NC}" + echo -e " ${YELLOW} ↑ 'local' reicht nicht – braucht z.B. local-lvm${NC}" + echo -e " ${CYAN}Bridges:${NC} ${BRIDGES}\n" + + ask "LXC Container-ID" "$NEXT_ID" + CT_ID="$REPLY" + [[ "$CT_ID" =~ ^[0-9]+$ ]] || error "Muss eine Zahl sein!" + pct status "$CT_ID" &>/dev/null && error "CT-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 in GB" "10" ; CT_DISK="$REPLY" + ask "Storage Pool" "$DEFAULT_STORAGE" ; CT_STORAGE="$REPLY" + ask "Bridge" "$DEFAULT_BRIDGE" ; CT_BRIDGE="$REPLY" + ask "Netzwerk (dhcp oder 192.168.1.100/24)" "dhcp" ; CT_IP="$REPLY" + + CT_GW="" + if [[ "$CT_IP" != "dhcp" ]]; then + ask "Gateway"; CT_GW="$REPLY" + fi + + ask_secret "Root-Passwort für den LXC" + CT_PASS="$REPLY" + [[ ${#CT_PASS} -lt 8 ]] && error "Passwort mind. 8 Zeichen!" + + step "Freqtrade Konfiguration" + ask "FreqUI Port" "8080" ; FT_PORT="$REPLY" + ask "Portainer Port" "9000" ; PT_PORT="$REPLY" + ask "Filebrowser Port" "8081" ; FB_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 (mind. 8 Zeichen)" + FT_API_PASS="$REPLY" + [[ ${#FT_API_PASS} -lt 8 ]] && error "Passwort mind. 8 Zeichen!" + + FT_TELEGRAM=false; TG_TOKEN=""; TG_CHATID="" + if confirm "Telegram-Benachrichtigungen?"; then + FT_TELEGRAM=true + ask "Telegram Bot-Token" ; TG_TOKEN="$REPLY" + ask "Telegram Chat-ID" ; TG_CHATID="$REPLY" + fi + + # Passwörter base64-kodiert weitergeben (sicher für Heredoc-Transport) + CT_PASS_B64=$(printf '%s' "$CT_PASS" | base64 -w0) + FT_API_PASS_B64=$(printf '%s' "$FT_API_PASS" | base64 -w0) + TG_TOKEN_B64=$(printf '%s' "$TG_TOKEN" | base64 -w0) + + # Zufällige Secrets + JWT_SECRET=$(openssl rand -hex 32) + WS_TOKEN=$(openssl rand -hex 16) + + echo -e "\n${BOLD}${BLUE}══════════════ Zusammenfassung ══════════════${NC}" + echo -e " CT ${CYAN}${CT_ID}${NC} | ${CYAN}${CT_HOST}${NC} | ${CYAN}${CT_CORES}${NC}x CPU | ${CYAN}${CT_RAM}${NC}MB RAM | ${CYAN}${CT_DISK}${NC}GB | ${CYAN}${CT_STORAGE}${NC}" + echo -e " Netz: ${CYAN}${CT_IP}${NC} via ${CYAN}${CT_BRIDGE}${NC}" + echo -e " FreqUI: ${CYAN}:${FT_PORT}${NC} | Portainer: ${CYAN}:${PT_PORT}${NC} | Filebrowser: ${CYAN}:${FB_PORT}${NC}" + echo -e " Börse: ${CYAN}${FT_EXCHANGE}${NC} | Stake: ${CYAN}${FT_STAKE}${NC} | Telegram: ${CYAN}${FT_TELEGRAM}${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="name=eth0,bridge=${CT_BRIDGE},firewall=1" + if [[ "$CT_IP" == "dhcp" ]]; then + NET_CONFIG+=",ip=dhcp" + else + NET_CONFIG+=",ip=${CT_IP},gw=${CT_GW}" + 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 + + success "LXC erstellt" + + 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 nicht erreichbar – Bridge/IP prüfen!" + echo -n "." + done + echo + success "Container läuft und hat Netzwerk" +} + +# ── Inner-Setup-Script generieren und im LXC ausführen ──────────────────────── +run_inner_setup() { + step "Installation im LXC starten" + + # Pairs korrekt aufbauen (BTC/STAKE, ETH/STAKE, BNB/STAKE) + local PAIRS="\"BTC/${FT_STAKE}\", \"ETH/${FT_STAKE}\", \"BNB/${FT_STAKE}\"" + + # Telegram JSON-Block + local TG_ENABLED="false" + [[ "$FT_TELEGRAM" == "true" ]] && TG_ENABLED="true" + + cat > /tmp/ft_lxc_setup.sh << INNEREOF +#!/usr/bin/env bash +set -euo pipefail + +GREEN='\033[0;32m'; CYAN='\033[0;36m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; 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" + +# Konfigurationswerte (vom Installer übergeben) +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=\$(echo "${FT_API_PASS_B64}" | base64 -d) +TG_TOKEN=\$(echo "${TG_TOKEN_B64}" | base64 -d) +TG_CHATID="${TG_CHATID}" +TG_ENABLED="${TG_ENABLED}" +JWT_SECRET="${JWT_SECRET}" +WS_TOKEN="${WS_TOKEN}" +PT_PORT="${PT_PORT}" +FB_PORT="${FB_PORT}" + +# ── System ────────────────────────────────────────────────────────────────────── +inf "System aktualisieren..." +export DEBIAN_FRONTEND=noninteractive +apt-get update -qq +apt-get install -y -qq curl ca-certificates gnupg python3 net-tools +ok "Systempakete installiert" + +# ── Docker ────────────────────────────────────────────────────────────────────── +inf "Docker installieren..." +curl -fsSL https://get.docker.com | sh > /dev/null 2>&1 +systemctl enable docker --now > /dev/null 2>&1 +ok "Docker: \$(docker --version | grep -oP 'Docker version \K[^,]+')" + +# ── Verzeichnisse ──────────────────────────────────────────────────────────────── +mkdir -p "\$FT_DIR" +mkdir -p "\$INSTALL_DIR/portainer" +mkdir -p "\$INSTALL_DIR/filebrowser/config" +mkdir -p "\$INSTALL_DIR/filebrowser/database" +ok "Verzeichnisse angelegt" + +# ── Portainer ─────────────────────────────────────────────────────────────────── +inf "Portainer installieren (Port \${PT_PORT})..." +cat > "\$INSTALL_DIR/portainer/docker-compose.yml" << 'PTEOF' +services: + portainer: + image: portainer/portainer-ce:latest + container_name: portainer + restart: unless-stopped + ports: + - "PTPORT:9000" + - "9443:9443" + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - portainer_data:/data +volumes: + portainer_data: +PTEOF +sed -i "s/PTPORT/\${PT_PORT}/" "\$INSTALL_DIR/portainer/docker-compose.yml" +cd "\$INSTALL_DIR/portainer" +docker compose up -d > /dev/null 2>&1 +ok "Portainer gestartet" + +# ── Filebrowser ────────────────────────────────────────────────────────────────── +inf "Filebrowser installieren (Port \${FB_PORT})..." +FB_PASS=\$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 16) +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 +sed -i "s/FBPORT/\${FB_PORT}/" "\$INSTALL_DIR/filebrowser/docker-compose.yml" +cd "\$INSTALL_DIR/filebrowser" +docker compose up -d > /dev/null 2>&1 +sleep 5 +# Passwort über filebrowser-CLI setzen (zuverlässiger als Log-Parsing) +docker exec filebrowser /filebrowser -d /database/filebrowser.db users update admin --password "\${FB_PASS}" > /dev/null 2>&1 \ + || docker exec filebrowser /filebrowser users update admin --password "\${FB_PASS}" -d /database/filebrowser.db > /dev/null 2>&1 \ + || warn "Filebrowser Passwort konnte nicht automatisch gesetzt werden" +ok "Filebrowser gestartet – Passwort: \${FB_PASS}" +echo "FILEBROWSER_PASS=\${FB_PASS}" >> /root/ft_install_info.txt + +# ── 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 2>/dev/null +# WebUI von außen erreichbar machen +sed -i 's/127\.0\.0\.1/0.0.0.0/g' docker-compose.yml +# Benutzerdefinierten Port setzen falls nicht 8080 +if [[ "\${FT_PORT}" != "8080" ]]; then + sed -i "s/0\.0\.0\.0:8080/0.0.0.0:\${FT_PORT}/" docker-compose.yml +fi +ok "docker-compose.yml bereit" + +# ── user_data erstellen ────────────────────────────────────────────────────────── +inf "user_data Verzeichnis erstellen..." +docker compose run --rm freqtrade create-userdir --userdir user_data > /dev/null 2>&1 +ok "user_data erstellt" + +# ── config.json mit Python3 generieren (sicheres JSON, keine Escaping-Probleme) ── +inf "config.json erstellen..." +export FT_EXCHANGE FT_STAKE FT_MAX_TRADES FT_TIMEFRAME FT_API_USER FT_API_PASS +export TG_TOKEN TG_CHATID TG_ENABLED JWT_SECRET WS_TOKEN FT_DIR FT_PORT + +python3 << 'PYEOF' +import json, os + +stake = os.environ["FT_STAKE"] +exchange = os.environ["FT_EXCHANGE"] + +config = { + "max_open_trades": int(os.environ["FT_MAX_TRADES"]), + "stake_currency": stake, + "stake_amount": "unlimited", + "tradable_balance_ratio": 0.99, + "fiat_display_currency": "EUR", + "timeframe": os.environ["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": exchange, + "key": "", + "secret": "", + "ccxt_config": {}, + "ccxt_async_config": {}, + "pair_whitelist": [ + f"BTC/{stake}", + f"ETH/{stake}", + f"BNB/{stake}" + ], + "pair_blacklist": [ + "BNB/.*" + ] + }, + "pairlists": [{"method": "StaticPairList"}], + "telegram": { + "enabled": os.environ["TG_ENABLED"] == "true", + "token": os.environ["TG_TOKEN"], + "chat_id": os.environ["TG_CHATID"] + }, + "api_server": { + "enabled": True, + "listen_ip_address": "0.0.0.0", + "listen_port": 8080, + "verbosity": "error", + "enable_openapi": False, + "jwt_secret_key": os.environ["JWT_SECRET"], + "ws_token": os.environ["WS_TOKEN"], + "CORS_origins": [], + "username": os.environ["FT_API_USER"], + "password": os.environ["FT_API_PASS"] + }, + "bot_name": "freqtrade", + "initial_state": "running", + "force_entry_enable": False, + "internals": {"process_throttle_secs": 5} +} + +config_path = os.environ["FT_DIR"] + "/user_data/config.json" +with open(config_path, "w") as f: + json.dump(config, f, indent=4) +print(f"config.json geschrieben: {config_path}") +PYEOF +ok "config.json erstellt (via Python3, valides JSON)" + +# ── Freqtrade starten ──────────────────────────────────────────────────────────── +inf "Freqtrade starten..." +cd "\$FT_DIR" +docker compose up -d > /dev/null 2>&1 +sleep 6 +if docker ps --filter "name=freqtrade" --filter "status=running" | grep -q freqtrade; then + ok "Freqtrade läuft" +else + echo -e "\${YELLOW}[WARN]\${NC} Freqtrade nicht gestartet – prüfe: docker logs freqtrade" +fi + +# ── Ergebnis-Datei ──────────────────────────────────────────────────────────────── +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} +PT_PORT=\${PT_PORT} +INFOEOF + +echo "" +echo "=======================================" +echo " Freqtrade Installation abgeschlossen " +echo " FreqUI: http://\$LXC_IP:\${FT_PORT} " +echo "=======================================" +INNEREOF + + pct push "$CT_ID" /tmp/ft_lxc_setup.sh /root/ft_setup.sh + pct exec "$CT_ID" -- chmod +x /root/ft_setup.sh + info "Starte Setup im LXC (ca. 2-3 Minuten)..." + pct exec "$CT_ID" -- bash /root/ft_setup.sh + rm -f /tmp/ft_lxc_setup.sh + success "Setup im LXC abgeschlossen" +} + +# ── Ergebnis anzeigen ────────────────────────────────────────────────────────── +show_result() { + 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=""; FILEBROWSER_PASS="" + if [[ -f "$TMP_INFO" ]]; then + source "$TMP_INFO" 2>/dev/null || true + rm -f "$TMP_INFO" + fi + [[ -z "${LXC_IP:-}" ]] && LXC_IP=$(pct exec "$CT_ID" -- hostname -I 2>/dev/null | awk '{print $1}' || echo "unbekannt") + [[ -z "${FILEBROWSER_PASS:-}" ]] && FILEBROWSER_PASS="${FB_PASS:-unbekannt}" + + echo + echo -e "${BOLD}${GREEN}╔══════════════════════════════════════════════╗${NC}" + echo -e "${BOLD}${GREEN}║ ✅ Freqtrade LXC Installation komplett! ║${NC}" + echo -e "${BOLD}${GREEN}╚══════════════════════════════════════════════╝${NC}" + echo + echo -e " ${BOLD}LXC:${NC} CT ${CYAN}${CT_ID}${NC} | ${CYAN}${CT_HOST}${NC} | IP: ${CYAN}${LXC_IP}${NC}" + echo + echo -e " ${BOLD}${GREEN}Freqtrade WebUI:${NC}" + echo -e " ${CYAN}http://${LXC_IP}:${FT_PORT}${NC}" + echo -e " Benutzer: ${CYAN}${FT_API_USER}${NC} | Passwort: ${CYAN}(dein gewähltes)${NC}" + echo + echo -e " ${BOLD}${GREEN}Portainer (Docker-Verwaltung):${NC}" + echo -e " ${CYAN}http://${LXC_IP}:${PT_PORT}${NC}" + echo -e " ${YELLOW}→ Beim ersten Aufruf Admin-Passwort setzen!${NC}" + echo + echo -e " ${BOLD}${GREEN}Filebrowser (Dateimanager):${NC}" + echo -e " ${CYAN}http://${LXC_IP}:${FB_PORT}${NC}" + echo -e " Benutzer: ${CYAN}admin${NC} | Passwort: ${BOLD}${CYAN}${FILEBROWSER_PASS}${NC}" + echo -e " ${YELLOW}→ Passwort nach dem Login ändern!${NC}" + echo + echo -e " ${BOLD}Wichtige Dateien:${NC}" + echo -e " Config: ${CYAN}/opt/docker-projekte/freqtrade/user_data/config.json${NC}" + echo -e " Logs: ${CYAN}/opt/docker-projekte/freqtrade/user_data/logs/${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. Strategie anpassen${NC}" + echo -e " ${YELLOW}4. NordVPN optional: install-vpn.sh im LXC ausführen${NC}" + echo + echo -e " ${BOLD}LXC-Konsole öffnen:${NC} ${CYAN}pct enter ${CT_ID}${NC}" + echo -e " ${BOLD}NordVPN Addon:${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 +} + +# ── Hauptprogramm ────────────────────────────────────────────────────────────── +main() { + print_banner + check_host + detect_resources + prepare_template + collect_config + create_lxc + run_inner_setup + show_result +} + +main "$@"