588 lines
24 KiB
Bash
588 lines
24 KiB
Bash
#!/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 ──
|
||
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"
|
||
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 immer fragen
|
||
if confirm "Telegram-Benachrichtigungen?"; then
|
||
FT_TELEGRAM=true
|
||
ask "Telegram Bot-Token" ; TG_TOKEN="$REPLY"
|
||
ask "Telegram Chat-ID" ; TG_CHATID="$REPLY"
|
||
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 werden im Python-Block gesetzt
|
||
# Pairs werden im Python-Block gesetzt
|
||
|
||
# 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"SOL/{stake}",
|
||
f"XRP/{stake}",
|
||
f"ADA/{stake}",
|
||
f"DOGE/{stake}",
|
||
f"AVAX/{stake}",
|
||
f"DOT/{stake}",
|
||
f"MATIC/{stake}",
|
||
f"LINK/{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}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 "$@"
|