Files
freqtrade-proxmox-deploy/scripts/freqtrade-lxc-install.sh
T

593 lines
24 KiB
Bash
Raw 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.
#!/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() {
# ── Modus wählen ──
echo -e "
${BOLD}${BLUE} Installationsmodus:${NC}"
echo -e " ${CYAN}1)${NC} ${BOLD}Schnellinstallation${NC} Standardwerte, nur Passwörter eingeben"
echo -e " ${CYAN}2)${NC} ${BOLD}Erweitert${NC} alle Einstellungen manuell festlegen"
ask "Modus [1/2]" "1"
local mode="$REPLY"
# Defaults
CT_ID="$NEXT_ID"
CT_HOST="freqtrade"
CT_CORES="2"
CT_RAM="2048"
CT_SWAP="512"
CT_DISK="10"
CT_STORAGE="$DEFAULT_STORAGE"
CT_BRIDGE="$DEFAULT_BRIDGE"
CT_IP="dhcp"
CT_GW=""
FT_PORT="8080"
PT_PORT="9000"
FB_PORT="8081"
FT_EXCHANGE="binance"
FT_STAKE="USDC"
FT_MAX_TRADES="3"
FT_TIMEFRAME="5m"
FT_API_USER="freqtrader"
FT_TELEGRAM=false
TG_TOKEN=""
TG_CHATID=""
if [[ "$mode" == "2" ]]; then
# ── Erweitert: alle Fragen ──
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" "$CT_ID" ; CT_ID="$REPLY"
ask "Hostname" "$CT_HOST" ; CT_HOST="$REPLY"
ask "CPU Kerne" "$CT_CORES" ; CT_CORES="$REPLY"
ask "RAM in MB" "$CT_RAM" ; CT_RAM="$REPLY"
ask "Swap in MB" "$CT_SWAP" ; CT_SWAP="$REPLY"
ask "Disk in GB" "$CT_DISK" ; CT_DISK="$REPLY"
ask "Storage Pool" "$CT_STORAGE" ; CT_STORAGE="$REPLY"
ask "Bridge" "$CT_BRIDGE" ; CT_BRIDGE="$REPLY"
ask "Netzwerk (dhcp oder 192.168.1.100/24)" "$CT_IP" ; CT_IP="$REPLY"
if [[ "$CT_IP" != "dhcp" ]]; then
ask "Gateway"; CT_GW="$REPLY"
fi
step "Freqtrade Konfiguration"
ask "FreqUI Port" "$FT_PORT" ; FT_PORT="$REPLY"
ask "Portainer Port" "$PT_PORT" ; PT_PORT="$REPLY"
ask "Filebrowser Port" "$FB_PORT" ; FB_PORT="$REPLY"
ask "Börse" "$FT_EXCHANGE" ; FT_EXCHANGE="$REPLY"
echo -e "
${CYAN}Stake-Währung:${NC} ${BOLD}1)${NC} USDC (Standard) ${BOLD}2)${NC} USDT"
ask "Auswahl [1/2]" "1"
case "$REPLY" in
2) FT_STAKE="USDT" ;;
*) FT_STAKE="USDC" ;;
esac
ask "Max. offene Trades" "$FT_MAX_TRADES" ; FT_MAX_TRADES="$REPLY"
ask "Timeframe" "$FT_TIMEFRAME" ; FT_TIMEFRAME="$REPLY"
ask "FreqUI Benutzername" "$FT_API_USER" ; FT_API_USER="$REPLY"
else
# ── Schnellinstallation: CT-ID prüfen ──
step "Schnellinstallation"
echo -e " Alle Standardwerte werden verwendet:\n"
echo -e " CT-ID: ${CYAN}${CT_ID}${NC} | Host: ${CYAN}${CT_HOST}${NC} | ${CYAN}${CT_CORES}${NC} CPU | ${CYAN}${CT_RAM}${NC}MB RAM | ${CYAN}${CT_DISK}${NC}GB"
echo -e " Storage: ${CYAN}${CT_STORAGE}${NC} | Bridge: ${CYAN}${CT_BRIDGE}${NC} | IP: ${CYAN}dhcp${NC}"
echo -e " FreqUI: ${CYAN}:${FT_PORT}${NC} | Stake: ${CYAN}${FT_STAKE}${NC} | Börse: ${CYAN}${FT_EXCHANGE}${NC}\n"
# Stake-Währung auch im Schnellmodus wählbar
echo -e " ${CYAN}Stake-Währung:${NC} ${BOLD}1)${NC} USDC (Standard) ${BOLD}2)${NC} USDT"
ask "Auswahl [1/2]" "1"
case "$REPLY" in
2) FT_STAKE="USDT" ;;
*) FT_STAKE="USDC" ;;
esac
echo
fi
# Passwörter immer abfragen
step "Passwörter"
ask_secret "Root-Passwort für den LXC (mind. 8 Zeichen)"
CT_PASS="$REPLY"
[[ ${#CT_PASS} -lt 8 ]] && error "Passwort mind. 8 Zeichen!"
ask_secret "FreqUI Passwort (mind. 8 Zeichen)"
FT_API_PASS="$REPLY"
[[ ${#FT_API_PASS} -lt 8 ]] && error "Passwort mind. 8 Zeichen!"
# Telegram nur im erweiterten Modus
if [[ "$mode" == "2" ]]; then
if confirm "Telegram-Benachrichtigungen?"; then
FT_TELEGRAM=true
ask "Telegram Bot-Token" ; TG_TOKEN="$REPLY"
ask "Telegram Chat-ID" ; TG_CHATID="$REPLY"
fi
fi
# CT-ID validieren
[[ "$CT_ID" =~ ^[0-9]+$ ]] || error "CT-ID muss eine Zahl sein!"
pct status "$CT_ID" &>/dev/null && error "CT-ID $CT_ID existiert bereits!"
# Passwörter base64-kodiert weitergeben
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)
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"
# Container mit Tag "freqtrade" versehen (für proxmox-vpn-install.sh nutzbar)
pct set "$CT_ID" --tags "freqtrade" 2>/dev/null || true
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+1))
echo -n "."
[[ $tries -ge 20 ]] && error "Netzwerk nicht erreichbar Bridge/IP prüfen!"
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} \$*"; }
warn() { echo -e "\${YELLOW}[WARN]\${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})..."
# Verzeichnisse mit korrekten Rechten anlegen (1000:1000 = gleicher User wie Filebrowser+Freqtrade)
mkdir -p "\$INSTALL_DIR/filebrowser/config" "\$INSTALL_DIR/filebrowser/database"
chown -R 1000:1000 "\$INSTALL_DIR/filebrowser"
cat > "\$INSTALL_DIR/filebrowser/docker-compose.yml" << 'FBEOF'
services:
filebrowser:
image: filebrowser/filebrowser:latest
container_name: filebrowser
user: "1000:1000"
ports:
- "FBPORT:8080"
volumes:
- /opt/docker-projekte/freqtrade:/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
# Warten bis Filebrowser das Zufallspasswort geloggt hat (Log-Format ist konstant)
# || true verhindert Abbruch durch set -e wenn grep nichts findet
FB_PASS=""
for i in \$(seq 1 10); do
FB_PASS=\$(docker logs filebrowser 2>&1 | grep -oP "(?<=randomly generated password: )\S+" | tail -1 || true)
[[ -n "\$FB_PASS" ]] && break
sleep 2
done
if [[ -z "\$FB_PASS" ]]; then
warn "Passwort nicht im Log gefunden manuell prüfen: docker logs filebrowser"
FB_PASS="siehe: docker logs filebrowser"
fi
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: proxmox-vpn-install.sh auf dem Proxmox Host 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/proxmox-vpn-install.sh)\"${NC}"
echo
}
# ── Hauptprogramm ──────────────────────────────────────────────────────────────
main() {
print_banner
check_host
detect_resources
prepare_template
collect_config
create_lxc
run_inner_setup
show_result
}
main "$@"