Skip to content

Instantly share code, notes, and snippets.

@hoangtrung99
Last active August 4, 2025 08:54
Show Gist options
  • Select an option

  • Save hoangtrung99/73593690940ff91015063f2b6f9366a3 to your computer and use it in GitHub Desktop.

Select an option

Save hoangtrung99/73593690940ff91015063f2b6f9366a3 to your computer and use it in GitHub Desktop.
AutoTrader Bot Script - Updated 2025-08-04 08:54:48 UTC
#!/bin/bash
#!/bin/bash
# AutoTrader Bot - Compact Version (Server Deployment Script)
# Focused on core deployment and system operations
# Complex logic moved to Python CLI tools
set -euo pipefail
# Version and basic config
SCRIPT_VERSION="3.3.3"
VERSION="$SCRIPT_VERSION"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DOCKER_CMD="docker"
# ===============================
# Constants and Utilities (inlined - no external dependencies)
# ===============================
# Directory constants
AUTOTRADER_DIR="$HOME/.autotrader"
CREDENTIALS_DIR="$AUTOTRADER_DIR/credentials"
CONFIG_DIR="./configs"
DATA_DIR="./data"
LOGS_DIR="./logs"
# Safe docker inspect with timeout (works on both Linux and macOS)
safe_docker_inspect() {
local image="$1"
local timeout_seconds="${2:-5}"
# Try with timeout if available, otherwise fallback to regular command
if command -v timeout &>/dev/null; then
timeout "$timeout_seconds" docker image inspect "$image" &>/dev/null
elif command -v gtimeout &>/dev/null; then
gtimeout "$timeout_seconds" docker image inspect "$image" &>/dev/null
else
# Fallback without timeout (risk of hanging)
docker image inspect "$image" &>/dev/null
fi
}
# Docker image names - smart detection
detect_and_set_images() {
local silent="${1:-false}"
local registry_telegram="ghcr.io/hoangtrung99/autotrader-telegram:latest"
local registry_trader="ghcr.io/hoangtrung99/autotrader-trader:latest"
local local_telegram="autotrader-telegram:latest"
local local_trader="autotrader-trader:latest"
# Check if LOCAL mode is explicitly enabled
if [[ "${LOCAL:-false}" == "true" ]]; then
TELEGRAM_IMAGE="$local_telegram"
TRADER_IMAGE="$local_trader"
[[ "$silent" != "true" ]] && echo "🔧 LOCAL mode enabled - using local Docker images"
return
fi
# Auto-detect available images (prefer registry, fallback to local)
[[ "$silent" != "true" ]] && echo "🔍 Auto-detecting available Docker images..."
# Check Telegram image with safe timeout
if safe_docker_inspect "$registry_telegram" 5; then
TELEGRAM_IMAGE="$registry_telegram"
[[ "$silent" != "true" ]] && echo "✅ Found registry Telegram image: $registry_telegram"
elif safe_docker_inspect "$local_telegram" 5; then
TELEGRAM_IMAGE="$local_telegram"
[[ "$silent" != "true" ]] && echo "✅ Found local Telegram image: $local_telegram"
else
TELEGRAM_IMAGE="$local_telegram"
[[ "$silent" != "true" ]] && echo "⚠️ No Telegram image found, will build: $local_telegram"
fi
# Check Trader image with safe timeout
if safe_docker_inspect "$registry_trader" 5; then
TRADER_IMAGE="$registry_trader"
[[ "$silent" != "true" ]] && echo "✅ Found registry Trader image: $registry_trader"
elif safe_docker_inspect "$local_trader" 5; then
TRADER_IMAGE="$local_trader"
[[ "$silent" != "true" ]] && echo "✅ Found local Trader image: $local_trader"
else
TRADER_IMAGE="$local_trader"
[[ "$silent" != "true" ]] && echo "⚠️ No Trader image found, will build: $local_trader"
fi
}
# Set images based on detection (silent for system-status)
# Only detect if Docker is available to prevent script exit
if command -v docker &> /dev/null && docker ps &> /dev/null; then
# Use registry images directly to avoid hanging issue
TELEGRAM_IMAGE="ghcr.io/hoangtrung99/autotrader-telegram:latest"
TRADER_IMAGE="ghcr.io/hoangtrung99/autotrader-trader:latest"
else
# Set default images when Docker is not available
TELEGRAM_IMAGE="autotrader-telegram:latest"
TRADER_IMAGE="autotrader-trader:latest"
fi
# Environment variable names
TELEGRAM_BOT_TOKEN_VAR="TELEGRAM_BOT_TOKEN"
BYBIT_API_KEY_VAR="BYBIT_API_KEY"
BYBIT_API_SECRET_VAR="BYBIT_API_SECRET"
BYBIT_SECRET_KEY_VAR="BYBIT_SECRET_KEY"
# Default values
DEFAULT_TRADING_AMOUNT="50"
DEFAULT_SYMBOL_SUFFIX="/USDT:USDT"
DEFAULT_DIRECTION="LONG"
DEFAULT_TEST_MODE="false"
DEFAULT_CREDENTIAL_PROFILE="default"
CREDENTIAL_FILE_PERMISSIONS="600"
# Utility functions (inlined)
ensure_directories() {
mkdir -p "$AUTOTRADER_DIR" "$CREDENTIALS_DIR" "$CONFIG_DIR" "$DATA_DIR" "$LOGS_DIR"
chmod 755 "$AUTOTRADER_DIR" "$CREDENTIALS_DIR" "$CONFIG_DIR" "$DATA_DIR" 2>/dev/null || true
# Set 777 for logs directory to ensure container can write logs
chmod 777 "$LOGS_DIR" 2>/dev/null || true
}
# Standardized messaging functions
print_error() {
echo "❌ $1" >&2
}
print_success() {
echo "✅ $1"
}
print_warning() {
echo "⚠️ $1"
}
print_info() {
echo "ℹ️ $1"
}
print_step() {
echo "🔄 $1"
}
# Standardized error handling
handle_command_error() {
local command="$1"
local error_msg="$2"
local suggestion="${3:-}"
print_error "$error_msg"
if [[ -n "$suggestion" ]]; then
echo ""
echo "💡 Suggestion: $suggestion"
fi
echo ""
echo "📖 For help: ./bot.sh help"
echo "🔍 For system status: ./bot.sh system-status"
return 1
}
normalize_symbol() {
local symbol="$1"
local upper_symbol=$(echo "$symbol" | tr '[:lower:]' '[:upper:]')
if [[ "$upper_symbol" =~ / ]]; then
echo "$upper_symbol"
return
fi
if [[ "$upper_symbol" =~ USDT$ ]]; then
local base_symbol="${upper_symbol%USDT}"
echo "${base_symbol}/USDT:USDT"
else
echo "${upper_symbol}$DEFAULT_SYMBOL_SUFFIX"
fi
}
get_container_name() {
local symbol="$1"
local profile="$2"
local base_symbol=$(echo "$symbol" | cut -d'/' -f1 | tr '[:upper:]' '[:lower:]')
base_symbol="${base_symbol%usdt}"
if [[ -n "$profile" ]]; then
echo "${profile}-${base_symbol}usdt"
else
# Backward compatibility - return old format
echo "${base_symbol}usdt"
fi
}
# Auto-load .env file if exists
if [[ -f ".env" ]]; then
source .env
fi
# Environment variables with defaults
TELEGRAM_BOT_TOKEN="${TELEGRAM_BOT_TOKEN:-}"
# ===============================
# Additional Utility Functions
# ===============================
# (Core utilities are now in src/core/shell_constants.sh)
check_docker() {
if ! command -v docker &> /dev/null; then
print_error "Docker chưa được cài đặt hoặc không có trong PATH"
echo ""
echo "💡 Cách khắc phục:"
echo " - macOS: Tải Docker Desktop từ docker.com"
echo " - Linux: curl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh"
echo " - Hoặc chạy: ./bot.sh setup"
return 1
fi
if ! docker info &> /dev/null; then
print_error "Docker daemon không chạy"
echo ""
echo "💡 Cách khắc phục:"
echo " - macOS: Mở Docker Desktop application"
echo " - Linux: sudo systemctl start docker"
echo " - Hoặc khởi động Docker service trên hệ thống của bạn"
return 1
fi
return 0
}
# Fallback to local runner when Docker is not available
suggest_local_runner() {
echo ""
print_info "Docker không có sẵn. Các phương án thay thế:"
echo ""
echo "🐍 Local Runner (Python):"
echo " ./run_local.sh setup # Setup môi trường local"
echo " ./run_local.sh telegram # Chạy Telegram bot local"
echo " ./run_local.sh start eth # Chạy trading bot local"
echo ""
echo "🐳 Cài đặt Docker (khuyến nghị):"
echo " - macOS: Tải Docker Desktop từ docker.com"
echo " - Linux: curl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh"
echo " - Hoặc chạy: ./bot.sh setup"
echo ""
echo "📖 Để biết thêm thông tin: ./bot.sh help"
}
check_python() {
if ! command -v python3 &> /dev/null; then
print_error "Python3 chưa được cài đặt"
echo ""
echo "💡 Cách khắc phục:"
echo " - macOS: brew install python3"
echo " - Ubuntu/Debian: sudo apt install python3"
echo " - CentOS/RHEL: sudo yum install python3"
echo " - Hoặc tải từ python.org"
return 1
fi
return 0
}
# Docker CLI wrapper - runs Python CLI commands in Docker container
run_docker_cli() {
local cli_script="$1"
shift
# Check if Docker is available
if ! check_docker; then
print_error "Docker cần thiết để chạy lệnh này"
echo ""
echo "💡 Các lựa chọn khác:"
echo " - Cài đặt Docker: ./bot.sh setup"
echo " - Sử dụng local runner: ./run_local.sh (nếu có)"
return 1
fi
# Ensure directories exist for volume mounts
ensure_directories
# Get absolute paths for volume mounts
local ABS_CONFIG_DIR="$(realpath "$CONFIG_DIR")"
local ABS_DATA_DIR="$(realpath "$DATA_DIR")"
local ABS_LOGS_DIR="$(realpath "$LOGS_DIR")"
local ABS_AUTOTRADER_DIR="$(realpath "$AUTOTRADER_DIR")"
# Use TRADER_IMAGE for CLI commands that need full dependencies
local image_to_use="$TRADER_IMAGE"
if [[ "$cli_script" == "assets_cli" ]]; then
echo "🔍 Using trader image for assets command (requires ccxt)..."
image_to_use="$TRADER_IMAGE"
elif [[ "$cli_script" == "python3" && "$1" == "/app/src/core/unified_command_processor.py" ]]; then
echo "🔍 Using local telegram image for unified processor..."
image_to_use="autotrader-telegram:latest"
else
image_to_use="$TELEGRAM_IMAGE"
fi
# Check if this is a unified processor call
if [[ "$cli_script" == "python3" && "$1" == "/app/src/core/unified_command_processor.py" ]]; then
# Unified processor call - use direct path
shift # Remove the path argument
docker run --rm \
--user root \
-v "$ABS_CONFIG_DIR:/app/configs" \
-v "$ABS_DATA_DIR:/app/data" \
-v "$ABS_LOGS_DIR:/app/logs" \
-v "$ABS_AUTOTRADER_DIR:/root/.autotrader" \
-v /var/run/docker.sock:/var/run/docker.sock \
-e BYBIT_API_KEY="${BYBIT_API_KEY:-}" \
-e BYBIT_API_SECRET="${BYBIT_API_SECRET:-}" \
-e BYBIT_SECRET_KEY="${BYBIT_SECRET_KEY:-}" \
"$image_to_use" \
python3 "/app/src/core/unified_command_processor.py" "$@"
else
# Traditional CLI script call
docker run --rm \
--user root \
-v "$ABS_CONFIG_DIR:/app/configs" \
-v "$ABS_DATA_DIR:/app/data" \
-v "$ABS_LOGS_DIR:/app/logs" \
-v "$ABS_AUTOTRADER_DIR:/root/.autotrader" \
-v /var/run/docker.sock:/var/run/docker.sock \
-e BYBIT_API_KEY="${BYBIT_API_KEY:-}" \
-e BYBIT_API_SECRET="${BYBIT_API_SECRET:-}" \
-e BYBIT_SECRET_KEY="${BYBIT_SECRET_KEY:-}" \
"$image_to_use" \
python3 "src/cli/${cli_script}.py" "$@"
fi
}
# ===============================
# System Setup Functions
# ===============================
extract_template_from_image() {
echo "📄 Extracting template config from Docker image..."
# Pull trader image if not exists
if ! docker image inspect "$TRADER_IMAGE" &>/dev/null; then
echo "🐳 Pulling trader image: $TRADER_IMAGE"
if ! docker pull "$TRADER_IMAGE"; then
echo "❌ Failed to pull trader image"
return 1
fi
fi
# Extract template.json from image
local temp_container="temp_extract_$$"
# Create temporary container and copy template
if docker create --name "$temp_container" "$TRADER_IMAGE" >/dev/null 2>&1; then
if docker cp "$temp_container:/app/template.json" "$CONFIG_DIR/template.json" 2>/dev/null; then
echo "✅ Extracted template.json from image"
else
echo "⚠️ Could not extract template.json from image, creating basic template..."
create_basic_template
fi
docker rm "$temp_container" >/dev/null 2>&1
else
echo "⚠️ Could not create temporary container, creating basic template..."
create_basic_template
fi
}
create_basic_template() {
cat > "$CONFIG_DIR/template.json" << 'EOF'
{
"symbol": "SYMBOL_PLACEHOLDER",
"exchange": "bybit",
"direction": "LONG",
"amount": 50.0,
"use_test_mode": false,
"use_sandbox": false,
"order_type": "limit",
"signal_cooldown_minutes": 3.0,
"trading_loop_interval_seconds": 10,
"log_level": "INFO",
"save_trades_to_csv": true,
"enable_notifications": true
}
EOF
}
ensure_docker_images() {
echo "🔍 Ensuring Docker images are available..."
# Re-detect images in case they were pulled/built since startup
detect_and_set_images
local need_build=false
local need_pull=false
# Check if current Telegram image exists
if ! docker image inspect "$TELEGRAM_IMAGE" &>/dev/null; then
echo "⚠️ Telegram image not found: $TELEGRAM_IMAGE"
# If it's a registry image, try to pull it first
if [[ "$TELEGRAM_IMAGE" == ghcr.io/* ]]; then
echo "🐳 Attempting to pull Telegram image from registry..."
if docker pull "$TELEGRAM_IMAGE" 2>/dev/null; then
echo "✅ Successfully pulled Telegram image"
else
echo "⚠️ Failed to pull from registry, will build locally"
TELEGRAM_IMAGE="autotrader-telegram:latest"
need_build=true
fi
else
need_build=true
fi
else
echo "✅ Telegram image found: $TELEGRAM_IMAGE"
fi
# Check if current Trader image exists
if ! docker image inspect "$TRADER_IMAGE" &>/dev/null; then
echo "⚠️ Trader image not found: $TRADER_IMAGE"
# If it's a registry image, try to pull it first
if [[ "$TRADER_IMAGE" == ghcr.io/* ]]; then
echo "🐳 Attempting to pull Trader image from registry..."
if docker pull "$TRADER_IMAGE" 2>/dev/null; then
echo "✅ Successfully pulled Trader image"
else
echo "⚠️ Failed to pull from registry, will build locally"
TRADER_IMAGE="autotrader-trader:latest"
need_build=true
fi
else
need_build=true
fi
else
echo "✅ Trader image found: $TRADER_IMAGE"
fi
# Build images if needed
if [[ "$need_build" == "true" ]]; then
echo "🔨 Building missing Docker images locally..."
build_docker_images
return $?
else
echo "✅ All required Docker images are available"
return 0
fi
}
build_docker_images() {
echo "🔨 Building Docker Images"
echo "========================="
# Check Docker is available
if ! check_docker; then
echo "❌ Docker is required to build images"
return 1
fi
echo "🐳 Building Telegram bot image..."
if docker build -f Dockerfile.telegram -t autotrader-telegram:latest .; then
echo "✅ Telegram image built successfully"
else
echo "❌ Failed to build Telegram image"
return 1
fi
echo "🐳 Building Trader bot image..."
if docker build -f Dockerfile.trader -t autotrader-trader:latest .; then
echo "✅ Trader image built successfully"
else
echo "❌ Failed to build Trader image"
return 1
fi
echo "✅ All Docker images built successfully!"
echo ""
echo "📋 Built images:"
echo " autotrader-telegram:latest"
echo " autotrader-trader:latest"
}
list_docker_images() {
echo "🐳 Available Docker Images"
echo "=========================="
if ! check_docker; then
echo "❌ Docker is not available"
return 1
fi
echo ""
echo "📋 All AutoTrader related images:"
docker images | grep -E "(autotrader|ghcr.io/hoangtrung99)" || echo " No AutoTrader images found"
echo ""
echo "🔍 Current configuration:"
detect_and_set_images
echo " TELEGRAM_IMAGE: $TELEGRAM_IMAGE"
echo " TRADER_IMAGE: $TRADER_IMAGE"
echo ""
echo "✅ Image availability check:"
if docker image inspect "$TELEGRAM_IMAGE" &>/dev/null; then
echo " ✅ Telegram image available: $TELEGRAM_IMAGE"
else
echo " ❌ Telegram image missing: $TELEGRAM_IMAGE"
fi
if docker image inspect "$TRADER_IMAGE" &>/dev/null; then
echo " ✅ Trader image available: $TRADER_IMAGE"
else
echo " ❌ Trader image missing: $TRADER_IMAGE"
fi
}
fix_docker_images() {
echo "🔧 Fixing Docker Images"
echo "======================="
if ! check_docker; then
echo "❌ Docker is not available"
return 1
fi
echo ""
echo "🧹 Cleaning up potentially conflicting images..."
# Remove old conflicting images
local old_images=("autotrader:latest")
for image in "${old_images[@]}"; do
if docker image inspect "$image" &>/dev/null; then
echo "🗑️ Removing old image: $image"
docker rmi "$image" 2>/dev/null || echo " ⚠️ Could not remove (may be in use)"
fi
done
echo ""
echo "🔄 Re-detecting and ensuring images..."
if ensure_docker_images; then
echo ""
echo "✅ Image fix completed successfully!"
echo ""
echo "📋 Final status:"
list_docker_images
else
echo ""
echo "❌ Image fix failed"
return 1
fi
}
check_image_updates() {
echo "🔍 Checking for Docker Image Updates"
echo "===================================="
if ! check_docker; then
echo "❌ Docker is not available"
return 1
fi
local registry_images=(
"ghcr.io/hoangtrung99/autotrader-telegram:latest"
"ghcr.io/hoangtrung99/autotrader-trader:latest"
)
local updates_available=false
echo ""
for image in "${registry_images[@]}"; do
echo "🔍 Checking: $image"
# Get local image digest if exists
local local_digest=""
if docker image inspect "$image" &>/dev/null; then
local_digest=$(docker image inspect "$image" --format '{{.RepoDigests}}' 2>/dev/null | grep -o 'sha256:[a-f0-9]*' | head -1)
echo " 📦 Local digest: ${local_digest:-not found}"
else
echo " ⚠️ Image not found locally"
fi
# Try to get remote digest (this will also pull latest manifest)
echo " 🌐 Checking remote version..."
if timeout 30 docker manifest inspect "$image" &>/dev/null; then
# Pull latest version to compare
if timeout 60 docker pull "$image" &>/dev/null; then
local new_digest=$(docker image inspect "$image" --format '{{.RepoDigests}}' 2>/dev/null | grep -o 'sha256:[a-f0-9]*' | head -1)
echo " 📦 Remote digest: ${new_digest:-not found}"
if [[ -n "$local_digest" && -n "$new_digest" && "$local_digest" != "$new_digest" ]]; then
echo " 🆕 Update available!"
updates_available=true
elif [[ -z "$local_digest" ]]; then
echo " 🆕 New image downloaded!"
updates_available=true
else
echo " ✅ Up to date"
fi
else
echo " ❌ Failed to check remote version"
fi
else
echo " ❌ Cannot access remote registry"
fi
echo ""
done
if [[ "$updates_available" == "true" ]]; then
echo "🎉 Image updates completed!"
echo ""
echo "📋 Updated images:"
list_docker_images
# Auto-restart containers with updated images
echo ""
echo "🔄 Auto-restarting containers with updated images..."
restart_containers_with_updated_images
else
echo "✅ All images are up to date"
fi
}
restart_containers_with_updated_images() {
echo "🔄 Restarting containers with updated images..."
# Get list of running containers
local running_containers=$(docker ps --format "table {{.Names}}\t{{.Image}}" | tail -n +2)
if [[ -z "$running_containers" ]]; then
echo " ℹ️ No running containers found"
return 0
fi
echo " 📋 Checking running containers:"
echo "$running_containers"
echo ""
# Check each running container
while IFS=$'\t' read -r container_name container_image; do
if [[ -z "$container_name" || -z "$container_image" ]]; then
continue
fi
# Check if container is using updated images
if [[ "$container_image" == "ghcr.io/hoangtrung99/autotrader-telegram:latest" ]] ||
[[ "$container_image" == "ghcr.io/hoangtrung99/autotrader-trader:latest" ]] ||
[[ "$container_image" == "autotrader-telegram:latest" ]] ||
[[ "$container_image" == "autotrader-trader:latest" ]]; then
echo " 🔄 Restarting container: $container_name (using $container_image)"
# Restart the container
if docker restart "$container_name" &>/dev/null; then
echo " ✅ Successfully restarted: $container_name"
else
echo " ⚠️ Failed to restart: $container_name"
fi
else
echo " ⏭️ Skipping: $container_name (not using autotrader images)"
fi
done <<< "$running_containers"
echo ""
echo "✅ Container restart process completed"
}
setup_environment() {
echo "🔧 AutoTrader Complete Setup"
echo "============================"
# Check and install Docker (Linux only)
if ! command -v docker &> /dev/null; then
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
echo "🐳 Installing Docker..."
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER
rm get-docker.sh
echo "✅ Docker installed. Please log out and back in."
else
echo "❌ Please install Docker manually for your OS"
return 1
fi
fi
# Verify Docker is running
if ! docker info &> /dev/null; then
echo "❌ Docker daemon is not running. Please start Docker."
return 1
else
echo "✅ Docker is running"
fi
# Build Docker images locally instead of pulling from registry
if check_docker; then
echo "� Building Docker images locally..."
if ! build_docker_images; then
echo "❌ Failed to build Docker images"
return 1
fi
fi
# Create directories and config files
echo "📁 Creating directories and config files..."
ensure_directories
# Extract template config from Docker image
if [[ ! -f "$CONFIG_DIR/template.json" ]]; then
extract_template_from_image
echo "✅ Created template config: $CONFIG_DIR/template.json"
else
echo "✅ Template config already exists"
fi
# Check Telegram setup
echo "📱 Checking Telegram configuration..."
if [[ -z "$TELEGRAM_BOT_TOKEN" ]]; then
echo "⚠️ Telegram not configured. Set environment variable:"
echo " export TELEGRAM_BOT_TOKEN='your_bot_token'"
else
echo "✅ Telegram configured"
fi
echo ""
echo "🎉 Setup finished!"
echo ""
echo "📋 Next steps:"
echo "1. Configure Telegram (if not done):"
echo " export TELEGRAM_BOT_TOKEN='your_token'"
echo "2. Setup Telegram bot authorization:"
echo " ./bot.sh setup-auth"
echo "3. Start Telegram bot:"
echo " ./bot.sh telegram"
echo "4. Use Telegram commands to manage trading bots"
}
upgrade_script() {
echo "🔄 Upgrading bot.sh from GitHub Gist..."
echo "========================================"
# Check if curl is available
if ! command -v curl &> /dev/null; then
echo "❌ curl is required for upgrade. Please install curl first."
return 1
fi
local gist_url="${BOT_GIST_URL:-https://gist.githubusercontent.com/hoangtrung99/73593690940ff91015063f2b6f9366a3/raw/autotrader.sh}"
local script_path="$(realpath "${BASH_SOURCE[0]}")"
local backup_file="${script_path}.backup.$(date +%Y%m%d-%H%M%S)"
echo "📄 Current script: $script_path"
echo "💾 Backup will be saved to: $backup_file"
echo "🌐 Gist URL: $gist_url"
# Create backup
if cp "$script_path" "$backup_file"; then
echo "✅ Backup created: $backup_file"
else
echo "❌ Failed to create backup. Aborting upgrade."
return 1
fi
# Download new version to temporary file
echo ""
echo "📥 Downloading latest version..."
local temp_file=$(mktemp)
if curl -fsSL "$gist_url" -o "$temp_file"; then
echo "✅ Downloaded successfully"
# Show version comparison if possible
local current_version=$(grep "^SCRIPT_VERSION=" "$script_path" | cut -d'"' -f2 2>/dev/null || echo "unknown")
local new_version=$(grep "^SCRIPT_VERSION=" "$temp_file" | cut -d'"' -f2 2>/dev/null || echo "unknown")
echo ""
echo "📊 Version Comparison:"
echo "Current: $current_version"
echo "New: $new_version"
# Confirm upgrade
echo ""
echo "❓ Do you want to proceed with the upgrade? (y/N)"
read -r response
if [[ ! "$response" =~ ^[Yy]$ ]]; then
echo "❌ Upgrade cancelled"
rm -f "$temp_file"
return 0
fi
# Replace current script with new version
echo ""
echo "🔄 Installing new version..."
if mv "$temp_file" "$script_path"; then
chmod +x "$script_path"
echo "✅ Upgrade completed successfully!"
echo ""
echo "🎉 Upgrade completed!"
echo "========================================"
echo "📄 New version installed: $script_path"
echo "💾 Backup available: $backup_file"
echo ""
echo "🔍 To verify the upgrade worked:"
echo " ./bot.sh version"
echo ""
# Check for Docker image updates if Docker is available
if check_docker &>/dev/null; then
echo "🐳 Checking for Docker image updates..."
echo ""
check_image_updates
else
echo "💡 Docker not available - skipping image updates"
echo " Use './bot.sh update-images' later if needed"
fi
else
echo "❌ Failed to install new version"
echo "🔄 Restoring from backup..."
if cp "$backup_file" "$script_path"; then
echo "✅ Restored from backup"
else
echo "❌ Failed to restore backup!"
fi
return 1
fi
else
echo "❌ Failed to download new version"
return 1
fi
}
start_all() {
echo "🚀 Starting Complete AutoTrader System"
echo "======================================"
echo ""
local success=true
# Check Docker availability
if ! check_docker; then
echo "❌ Docker is required for system startup"
return 1
fi
# Ensure Docker images are available
echo "🔍 Ensuring Docker images are available..."
if ! ensure_docker_images; then
echo "❌ Failed to ensure Docker images"
return 1
fi
# Start Telegram bot first
echo "📱 Starting Telegram bot..."
if start_telegram_bot; then
echo "✅ Telegram bot started successfully"
else
echo "❌ Failed to start Telegram bot"
success=false
fi
echo ""
echo "📊 System startup completed!"
echo ""
echo "📋 Next steps:"
echo "1. Check system status: ./bot.sh system-status"
echo "2. Use Telegram bot to create trading bots: /createbot"
echo "3. Monitor with: ./bot.sh list"
echo ""
if [[ "$success" == "true" ]]; then
return 0
else
return 1
fi
}
stop_all() {
echo "🛑 Stopping All AutoTrader Services"
echo "==================================="
echo ""
local stopped_count=0
local failed_count=0
# Check Docker availability
if ! check_docker; then
echo "❌ Docker is not available"
return 1
fi
# Get all AutoTrader containers
echo "🔍 Finding AutoTrader containers..."
local containers=($(docker ps -a --format "{{.Names}}" | grep -E "(telegram-bot|.*usdt$)" || true))
if [[ ${#containers[@]} -eq 0 ]]; then
echo "📭 No AutoTrader containers found"
return 0
fi
echo "📋 Found ${#containers[@]} containers to stop:"
for container in "${containers[@]}"; do
echo " - $container"
done
echo ""
# Stop each container
for container in "${containers[@]}"; do
echo "🛑 Stopping $container..."
if docker stop "$container" &>/dev/null; then
echo "✅ Stopped: $container"
((stopped_count++))
else
echo "❌ Failed to stop: $container"
((failed_count++))
fi
done
echo ""
echo "📊 Stop Summary:"
echo " ✅ Successfully stopped: $stopped_count"
echo " ❌ Failed to stop: $failed_count"
echo ""
if [[ $failed_count -eq 0 ]]; then
echo "🎉 All services stopped successfully!"
return 0
else
echo "⚠️ Some services failed to stop"
return 1
fi
}
system_status() {
echo "📊 AutoTrader System Status"
echo "=========================="
echo ""
local docker_available=false
# Check Docker
echo "🐳 Docker Status:"
if command -v docker &> /dev/null; then
echo " ✅ Docker: Command available"
echo " 📊 Docker version: $(docker --version 2>/dev/null || echo 'Unknown')"
# Check if daemon is running (without hanging)
if docker ps &>/dev/null || false; then
echo " ✅ Docker daemon: Running"
docker_available=true
else
echo " ❌ Docker daemon: Not running"
echo ""
echo "💡 To fix: Start Docker daemon"
fi
else
echo " ❌ Docker: Not installed"
echo ""
echo "💡 To fix: Install Docker with ./bot.sh setup"
fi
echo ""
# Check Docker Images (only if Docker is available)
echo "🖼️ Docker Images Status:"
if [[ "$docker_available" == "true" ]]; then
# Quietly detect images without output
local registry_telegram="ghcr.io/hoangtrung99/autotrader-telegram:latest"
local registry_trader="ghcr.io/hoangtrung99/autotrader-trader:latest"
local local_telegram="autotrader-telegram:latest"
local local_trader="autotrader-trader:latest"
# Check Telegram image
if docker image inspect "$registry_telegram" &>/dev/null; then
echo " ✅ Telegram image: $registry_telegram"
elif docker image inspect "$local_telegram" &>/dev/null; then
echo " ✅ Telegram image: $local_telegram"
else
echo " ❌ Telegram image: Not found"
fi
# Check Trader image
if docker image inspect "$registry_trader" &>/dev/null; then
echo " ✅ Trader image: $registry_trader"
elif docker image inspect "$local_trader" &>/dev/null; then
echo " ✅ Trader image: $local_trader"
else
echo " ❌ Trader image: Not found"
fi
else
echo " ⚠️ Cannot check images (Docker not available)"
fi
echo ""
# Check running containers
echo "📦 Container Status:"
local containers=()
if [[ "$docker_available" == "true" ]]; then
containers=($(docker ps -a --format "{{.Names}}" | grep -E "(telegram-bot|.*usdt$)" || true))
if [[ ${#containers[@]} -eq 0 ]]; then
echo " 📭 No AutoTrader containers found"
else
local running=0
local stopped=0
for container in "${containers[@]}"; do
if docker ps --format "{{.Names}}" | grep -q "^${container}$"; then
echo " 🟢 Running: $container"
((running++))
else
echo " 🔴 Stopped: $container"
((stopped++))
fi
done
echo ""
echo " 📊 Summary: $running running, $stopped stopped"
fi
else
echo " ⚠️ Cannot check containers (Docker not available)"
fi
echo ""
# Check environment
echo "🔧 Environment Status:"
if [[ -n "$TELEGRAM_BOT_TOKEN" ]]; then
echo " ✅ Telegram token: Configured"
else
echo " ❌ Telegram token: Not configured"
fi
if [[ -n "${BYBIT_API_KEY:-}" ]]; then
echo " ✅ Bybit API: Configured"
else
echo " ⚠️ Bybit API: Not configured (use profiles instead)"
fi
echo ""
# Check credentials
echo "🔑 Credentials Status:"
local creds_count=0
if [[ -d "$CREDENTIALS_DIR" ]]; then
creds_count=$(find "$CREDENTIALS_DIR" -name "*.json" 2>/dev/null | wc -l || echo "0")
fi
echo " 📊 Credential profiles: $creds_count"
echo ""
# Check configuration
echo "⚙️ Configuration Status:"
local config_count=0
if [[ -d "$CONFIG_DIR" ]]; then
config_count=$(find "$CONFIG_DIR" -name "*.json" 2>/dev/null | wc -l || echo "0")
fi
echo " 📊 Config files: $config_count"
local data_count=0
if [[ -d "$DATA_DIR" ]]; then
data_count=$(find "$DATA_DIR" -type f 2>/dev/null | wc -l || echo "0")
fi
echo " 📁 Data directory: $data_count files"
local logs_count=0
if [[ -d "$LOGS_DIR" ]]; then
logs_count=$(find "$LOGS_DIR" -type f 2>/dev/null | wc -l || echo "0")
fi
echo " 📝 Log directory: $logs_count files"
echo ""
# Overall health check
echo "🏥 Overall Health:"
local health_score=0
local max_score=5
check_docker && ((health_score++)) || true
docker image inspect "$TELEGRAM_IMAGE" &>/dev/null && ((health_score++)) || true
[[ -n "$TELEGRAM_BOT_TOKEN" ]] && ((health_score++)) || true
[[ $creds_count -gt 0 ]] && ((health_score++)) || true
[[ ${#containers[@]} -gt 0 ]] && ((health_score++)) || true
local health_percent=$((health_score * 100 / max_score))
if [[ $health_percent -ge 80 ]]; then
echo " 🟢 System Health: $health_percent% (Excellent)"
elif [[ $health_percent -ge 60 ]]; then
echo " 🟡 System Health: $health_percent% (Good)"
elif [[ $health_percent -ge 40 ]]; then
echo " 🟠 System Health: $health_percent% (Fair)"
else
echo " 🔴 System Health: $health_percent% (Poor)"
fi
echo ""
echo "💡 Quick Actions:"
echo " - Start system: ./bot.sh start-all"
echo " - Stop system: ./bot.sh stop-all"
echo " - List containers: ./bot.sh list"
echo " - Start Telegram bot: ./bot.sh telegram"
}
bind_global_command() {
echo "🔗 Binding bot.sh as global 'traderbot' command..."
echo "========================================"
# Detect OS for different approaches
local os="unknown"
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
os="linux"
elif [[ "$OSTYPE" == "darwin"* ]]; then
os="macos"
fi
local script_path="$(realpath "${BASH_SOURCE[0]}")"
local installed=false
# Try different installation paths
local install_paths=(
"$HOME/.local/bin/traderbot"
"$HOME/bin/traderbot"
"/usr/local/bin/traderbot"
)
for install_path in "${install_paths[@]}"; do
local install_dir=$(dirname "$install_path")
# Create directory if it doesn't exist
if [[ ! -d "$install_dir" ]]; then
if mkdir -p "$install_dir" 2>/dev/null; then
echo "✅ Created directory: $install_dir"
else
echo "❌ Cannot create directory: $install_dir"
continue
fi
fi
# Try to install
if cp "$script_path" "$install_path" 2>/dev/null; then
chmod +x "$install_path"
echo "✅ Installed to: $install_path"
# Add to PATH if needed
if [[ ":$PATH:" != *":$install_dir:"* ]]; then
echo "💡 Adding $install_dir to PATH..."
# Determine shell profile
local shell_profile=""
if [[ -n "$ZSH_VERSION" ]]; then
shell_profile="$HOME/.zshrc"
elif [[ -n "$BASH_VERSION" ]]; then
shell_profile="$HOME/.bashrc"
fi
if [[ -n "$shell_profile" ]]; then
echo "export PATH=\"$install_dir:\$PATH\"" >> "$shell_profile"
echo "✅ Added to $shell_profile"
echo "💡 Run: source $shell_profile"
fi
fi
installed=true
break
else
echo "❌ Failed to install to: $install_path"
fi
done
if [[ "$installed" == "true" ]]; then
echo ""
echo "🎉 Global 'traderbot' command installed!"
echo "========================================"
echo ""
echo "📋 Usage Examples:"
echo " traderbot setup # Setup environment"
echo " traderbot start eth --amount 100 # Start ETH bot"
echo " traderbot list # List containers"
echo " traderbot telegram # Start Telegram bot"
echo ""
echo "💡 You may need to restart your terminal or run:"
echo " source ~/.bashrc # or ~/.zshrc"
else
echo ""
echo "❌ Failed to install global command"
echo "💡 Manual installation:"
echo " sudo cp $script_path /usr/local/bin/traderbot"
echo " sudo chmod +x /usr/local/bin/traderbot"
fi
}
unbind_global_command() {
echo "🗑️ Removing 'traderbot' global command..."
echo "========================================"
# List of possible installation paths
local remove_paths=(
"/usr/local/bin/traderbot"
"$HOME/.local/bin/traderbot"
"$HOME/bin/traderbot"
)
local removed=false
for remove_path in "${remove_paths[@]}"; do
if [[ -f "$remove_path" ]]; then
if rm "$remove_path" 2>/dev/null; then
echo "✅ Removed: $remove_path"
removed=true
else
echo "❌ Failed to remove: $remove_path (try with sudo)"
fi
fi
done
if [[ "$removed" == "true" ]]; then
echo ""
echo "✅ Global 'traderbot' command removed"
echo "💡 You can still use: ./bot.sh"
else
echo ""
echo "ℹ️ No global 'traderbot' command found"
fi
}
# ===============================
# Docker Operations
# ===============================
start_telegram_bot() {
echo "📱 Starting Telegram Bot"
echo "========================"
# Check Docker
if ! check_docker; then
print_warning "Docker không có sẵn, đang tìm phương án thay thế..."
suggest_local_runner
# Try to run with local runner
if [[ -f "./run_local.sh" ]]; then
print_step "Đang chạy với local runner..."
exec ./run_local.sh telegram
else
print_error "Local runner không tìm thấy"
echo ""
echo "💡 Cách khắc phục:"
echo " - Cài đặt Docker: ./bot.sh setup"
echo " - Hoặc tạo file run_local.sh để chạy local"
return 1
fi
fi
# Ensure Docker images are available
print_step "Đang kiểm tra Docker images..."
if ! ensure_docker_images; then
print_error "Không thể đảm bảo Docker images có sẵn"
echo ""
echo "💡 Cách khắc phục:"
echo " - Build images: ./bot.sh build"
echo " - Pull images: ./bot.sh update-images"
echo " - Fix images: ./bot.sh fix-images"
return 1
fi
# Check authorization setup
echo "🔐 Checking Telegram authorization setup..."
if ! check_telegram_auth_setup; then
echo "❌ Cannot start bot without proper authorization setup"
return 1
fi
echo ""
# Check environment
if [[ -z "$TELEGRAM_BOT_TOKEN" ]]; then
echo "❌ Missing Telegram credentials"
echo ""
echo "💡 Set environment variable:"
echo " export TELEGRAM_BOT_TOKEN='your_bot_token'"
return 1
fi
# Check if container already exists
if docker ps -a --format "{{.Names}}" | grep -q "^telegram-bot$"; then
echo "🔄 Stopping existing Telegram bot container..."
docker stop telegram-bot 2>/dev/null || true
docker rm telegram-bot 2>/dev/null || true
fi
# Start Telegram bot container
echo "🐳 Starting Telegram bot container..."
# Ensure directories exist
ensure_directories
# Convert relative paths to absolute paths to avoid Docker volume mounting issues
local abs_config_dir="$(cd "$CONFIG_DIR" && pwd)"
local abs_data_dir="$(cd "$DATA_DIR" && pwd)"
local abs_logs_dir="$(cd "$LOGS_DIR" && pwd)"
local abs_autotrader_dir="$AUTOTRADER_DIR"
echo "📁 Volume mounts:"
echo " Config: $abs_config_dir -> /app/configs"
echo " Data: $abs_data_dir -> /app/data"
echo " Logs: $abs_logs_dir -> /app/logs"
echo " AutoTrader: $abs_autotrader_dir -> /root/.autotrader"
docker run -d \
--name telegram-bot \
--restart unless-stopped \
-e TELEGRAM_BOT_TOKEN="$TELEGRAM_BOT_TOKEN" \
-e RUNNING_IN_CONTAINER="true" \
-e HOST_CONFIG_DIR="$abs_config_dir" \
-e HOST_DATA_DIR="$abs_data_dir" \
-e HOST_LOGS_DIR="$abs_logs_dir" \
-v "$abs_config_dir:/app/configs" \
-v "$abs_data_dir:/app/data" \
-v "$abs_logs_dir:/app/logs" \
-v "$abs_autotrader_dir:/root/.autotrader" \
-v /var/run/docker.sock:/var/run/docker.sock \
"$TELEGRAM_IMAGE"
if [[ $? -eq 0 ]]; then
echo "✅ Telegram bot started successfully!"
echo "📊 Container: telegram-bot"
echo "🔗 Token: ${TELEGRAM_BOT_TOKEN:0:10}..."
echo ""
echo "📋 Monitor with:"
echo " docker logs telegram-bot"
echo " ./bot.sh simple-logs telegram-bot"
else
echo "❌ Failed to start Telegram bot container"
return 1
fi
}
start_trading_bot() {
local symbol="$1"
shift
echo "🤖 Starting Trading Bot: $symbol"
echo "================================"
# Validate symbol
if [[ -z "$symbol" ]]; then
echo "❌ Symbol is required"
echo "Usage: ./bot.sh start <symbol> [options]"
return 1
fi
# Parse additional options first to get profile
local amount="50"
local dca_amount="25" # Default DCA amount (50% of amount)
local test_mode="false"
local direction="LONG"
local profile=""
local cmd_api_key=""
local cmd_api_secret=""
while [[ $# -gt 0 ]]; do
case $1 in
--amount)
amount="$2"
shift 2
;;
--dca-amount)
dca_amount="$2"
shift 2
;;
--test-mode|--test)
test_mode="true"
shift
;;
--live-mode|--live)
test_mode="false"
shift
;;
--direction)
direction="$2"
shift 2
;;
--profile)
profile="$2"
shift 2
;;
--api-key)
cmd_api_key="$2"
shift 2
;;
--api-secret)
cmd_api_secret="$2"
shift 2
;;
*)
echo "⚠️ Unknown option: $1"
shift
;;
esac
done
# Normalize symbol and generate container name with profile
local full_symbol=$(normalize_symbol "$symbol")
local container_name=$(get_container_name "$symbol" "$profile")
# Check if container already exists and cleanup
if docker ps -a --format "{{.Names}}" | grep -q "^${container_name}$"; then
echo "🔄 Stopping existing container '$container_name'..."
docker stop "$container_name" 2>/dev/null || true
echo "�️ Removing existing container '$container_name'..."
docker rm "$container_name" 2>/dev/null || true
echo "✅ Container '$container_name' cleaned up successfully"
fi
# Create symbol-specific config
local config_file="$CONFIG_DIR/${container_name}.json"
if [[ ! -f "$config_file" ]]; then
echo "📝 Creating config for $symbol..."
# Create config from template (escape special characters for sed)
local escaped_symbol=$(echo "$full_symbol" | sed 's/[\/&]/\\&/g')
sed "s/SYMBOL_PLACEHOLDER/$escaped_symbol/g" "$CONFIG_DIR/template.json" > "$config_file"
echo "✅ Created config: $config_file"
fi
# Update config file with amount and dca_amount
echo "📝 Updating config with amount=$amount, dca_amount=$dca_amount..."
if [[ -f "$config_file" ]]; then
# Use Python to update JSON config safely
python3 -c "
import json
import sys
try:
with open('$config_file', 'r') as f:
config = json.load(f)
# Update amounts
config['amount'] = float('$amount')
config['dca_amount'] = float('$dca_amount')
with open('$config_file', 'w') as f:
json.dump(config, f, indent=2)
print('✅ Config updated successfully')
except Exception as e:
print(f'❌ Failed to update config: {e}')
sys.exit(1)
"
if [[ $? -ne 0 ]]; then
echo "❌ Failed to update config file"
return 1
fi
fi
# Resolve credentials using priority system
if ! resolve_credentials "$profile" "$cmd_api_key" "$cmd_api_secret"; then
echo ""
echo "💡 Available credential sources (in priority order):"
echo " 1. Profile: --profile <name> (highest priority)"
echo " 2. Command line: --api-key <key> --api-secret <secret>"
echo " 3. Environment: BYBIT_API_KEY, BYBIT_API_SECRET/BYBIT_SECRET_KEY"
echo ""
echo "📋 Examples:"
echo " ./bot.sh start btc --profile main --amount 100"
echo " ./bot.sh start btc --api-key 'key' --api-secret 'secret'"
echo " export BYBIT_API_KEY='key' && ./bot.sh start btc"
echo ""
echo "💡 Manage profiles:"
echo " python3 src/cli/credentials_cli.py list"
echo " python3 src/cli/credentials_cli.py store <profile>"
return 1
fi
# Verify credentials are now available
local api_key="${!BYBIT_API_KEY_VAR:-}"
local api_secret="${!BYBIT_API_SECRET_VAR:-}"
if [[ -z "$api_key" ]] || [[ -z "$api_secret" ]]; then
echo "❌ Credential resolution failed"
return 1
fi
# Check Docker is available
if ! check_docker; then
echo "❌ Docker is required to run trading bots"
return 1
fi
# Ensure Docker images are available
echo "🔍 Ensuring Docker images are available..."
if ! ensure_docker_images; then
echo "❌ Failed to ensure Docker images are available"
return 1
fi
# Start Docker container
echo "🐳 Starting Docker container..."
# Handle different execution contexts
if [[ "${RUNNING_IN_CONTAINER:-}" == "true" ]]; then
echo "🐳 Running in container - using host mount paths from environment"
echo "🔍 DEBUG: RUNNING_IN_CONTAINER='${RUNNING_IN_CONTAINER:-}'"
echo "🔍 DEBUG: HOST_CONFIG_DIR='${HOST_CONFIG_DIR:-}'"
echo "🔍 DEBUG: HOST_DATA_DIR='${HOST_DATA_DIR:-}'"
echo "🔍 DEBUG: HOST_LOGS_DIR='${HOST_LOGS_DIR:-}'"
# When running in Telegram bot container, use the host paths that were mounted
# These MUST be the actual host paths, not container paths
local abs_config_dir="${HOST_CONFIG_DIR}"
local abs_data_dir="${HOST_DATA_DIR}"
local abs_logs_dir="${HOST_LOGS_DIR}"
echo "🔍 DEBUG: abs_config_dir='$abs_config_dir'"
echo "🔍 DEBUG: abs_data_dir='$abs_data_dir'"
echo "🔍 DEBUG: abs_logs_dir='$abs_logs_dir'"
# Validate that host paths are available and not empty
if [[ -z "$abs_config_dir" || -z "$abs_data_dir" || -z "$abs_logs_dir" ]]; then
echo "❌ ERROR: Host paths not available in container environment"
echo " HOST_CONFIG_DIR: '$HOST_CONFIG_DIR'"
echo " HOST_DATA_DIR: '$HOST_DATA_DIR'"
echo " HOST_LOGS_DIR: '$HOST_LOGS_DIR'"
echo "💡 This indicates a problem with Telegram bot container setup"
return 1
fi
echo "✅ Using host paths from environment variables"
else
echo "💻 Running on host - using local absolute paths"
# Convert relative paths to absolute paths to avoid Docker volume mounting issues
local abs_config_dir="$(cd "$CONFIG_DIR" && pwd)"
local abs_data_dir="$(cd "$DATA_DIR" && pwd)"
local abs_logs_dir="$(cd "$LOGS_DIR" && pwd)"
fi
echo "📁 Volume mounts:"
echo " Config: $abs_config_dir -> /app/configs"
echo " Data: $abs_data_dir -> /app/data"
echo " Logs: $abs_logs_dir -> /app/logs"
# Prepare Docker command arguments
local docker_args=(
"run" "-d"
"--name" "$container_name"
"--restart" "unless-stopped"
"-e" "$BYBIT_API_KEY_VAR=$api_key"
"-e" "$BYBIT_API_SECRET_VAR=$api_secret"
"-e" "BOT_CONFIG_FILE=configs/${container_name}.json"
"-e" "TRADE_SYMBOL=$full_symbol"
"-e" "TRADE_AMOUNT=$amount"
"-e" "TRADE_DIRECTION=$direction"
"-e" "TEST_MODE=$test_mode"
"-v" "$abs_config_dir:/app/configs"
"-v" "$abs_data_dir:/app/data"
"-v" "$abs_logs_dir:/app/logs"
"$TRADER_IMAGE"
"python" "main.py" "--start"
)
# Add test flag if in test mode
if [[ "$test_mode" == "true" ]]; then
docker_args+=("--test")
fi
# Run Docker container
docker "${docker_args[@]}"
if [[ $? -eq 0 ]]; then
echo "✅ Trading bot started successfully!"
echo "📊 Container: $container_name"
echo "💰 Symbol: $full_symbol"
echo "💵 Amount: $amount USDT"
echo "🧪 Test Mode: $test_mode"
echo ""
echo "📋 Monitor with:"
echo " ./bot.sh logs $container_name"
echo " python3 src/cli/autotrader_cli.py status $container_name"
else
echo "❌ Failed to start trading bot"
return 1
fi
}
simple_logs() {
local container_name="$1"
local lines="${2:-50}"
if [[ -z "$container_name" ]]; then
echo "❌ Container name is required"
return 1
fi
echo "📋 Logs for container: $container_name (last $lines lines)"
echo "=" * 60
if docker logs "$container_name" --tail "$lines" 2>/dev/null; then
echo ""
echo "✅ Logs retrieved successfully"
else
echo "❌ Failed to get logs for container: $container_name"
echo "💡 Available containers:"
docker ps --format "{{.Names}}" | head -5
return 1
fi
}
# ===============================
# Credential Management Functions
# ===============================
check_telegram_profile() {
# Check if Telegram bot has set a profile for this session
local telegram_profile_file="/tmp/traderbot_telegram_profile_$$"
local telegram_profile_global="/tmp/traderbot_telegram_profile"
if [[ -f "$telegram_profile_file" ]]; then
cat "$telegram_profile_file"
return 0
elif [[ -f "$telegram_profile_global" ]]; then
cat "$telegram_profile_global"
return 0
fi
return 1
}
load_profile_credentials() {
local profile="$1"
if [[ -z "$profile" ]]; then
return 1
fi
echo "🔄 Loading credentials from profile: $profile"
# Use centralized credentials directory
local creds_dir="$CREDENTIALS_DIR"
# Try Python CLI first (JSON format)
local json_file="$creds_dir/${profile}.json"
if [[ -f "$json_file" ]]; then
# Load from JSON format (Python CLI) - get export commands
if command -v python3 &> /dev/null; then
local result=$(python3 src/cli/credentials_cli.py load "$profile" 2>/dev/null)
if [[ $? -eq 0 ]]; then
# Parse the export commands
local api_key=$(echo "$result" | grep "export $BYBIT_API_KEY_VAR=" | cut -d"'" -f2)
local api_secret=$(echo "$result" | grep "export $BYBIT_API_SECRET_VAR=" | cut -d"'" -f2)
if [[ -n "$api_key" ]] && [[ -n "$api_secret" ]]; then
export "$BYBIT_API_KEY_VAR"="$api_key"
export "$BYBIT_API_SECRET_VAR"="$api_secret"
echo "✅ Loaded credentials from profile: $profile"
return 0
fi
fi
fi
fi
# Try legacy .env format (for backward compatibility)
local env_file="$creds_dir/${profile}.env"
if [[ -f "$env_file" ]]; then
echo "🔄 Loading from .env format..."
source "$env_file"
if [[ -n "${!BYBIT_API_KEY_VAR}" ]] && [[ -n "${!BYBIT_API_SECRET_VAR}" ]]; then
echo "✅ Loaded credentials from profile: $profile"
return 0
fi
fi
echo "❌ Failed to load credentials from profile: $profile"
return 1
}
resolve_credentials() {
local profile="$1"
local cmd_api_key="$2"
local cmd_api_secret="$3"
echo "🔍 Resolving credentials with priority system..."
# Priority 1: Profile credentials (from Telegram bot selection or command line)
# Check Telegram profile first if no explicit profile provided
if [[ -z "$profile" ]]; then
local telegram_profile=$(check_telegram_profile 2>/dev/null)
if [[ -n "$telegram_profile" ]]; then
echo "🥇 Priority 1a: Found Telegram selected profile '$telegram_profile'"
profile="$telegram_profile"
fi
fi
if [[ -n "$profile" ]]; then
echo "🥇 Priority 1b: Loading from profile '$profile'"
if load_profile_credentials "$profile"; then
return 0
else
echo "⚠️ Failed to load profile '$profile', trying next priority..."
fi
fi
# Priority 2: Command line arguments
if [[ -n "$cmd_api_key" ]] && [[ -n "$cmd_api_secret" ]]; then
echo "🥈 Priority 2: Using command line credentials"
export "$BYBIT_API_KEY_VAR"="$cmd_api_key"
export "$BYBIT_API_SECRET_VAR"="$cmd_api_secret"
echo "✅ Loaded credentials from command line"
return 0
fi
# Priority 3: Environment variables
local env_api_key="${!BYBIT_API_KEY_VAR:-}"
local env_api_secret="${!BYBIT_API_SECRET_VAR:-${!BYBIT_SECRET_KEY_VAR:-}}"
if [[ -n "$env_api_key" ]] && [[ -n "$env_api_secret" ]]; then
echo "🥉 Priority 3: Using environment variables"
export "$BYBIT_API_KEY_VAR"="$env_api_key"
export "$BYBIT_API_SECRET_VAR"="$env_api_secret"
echo "✅ Loaded credentials from environment"
return 0
fi
# No credentials found
echo "❌ No credentials found in any source"
return 1
}
show_help() {
cat << EOF
🤖 AutoTrader Bot - Compact Server Deployment Script
USAGE: $0 <command> [options]
� DEVELOPMENT MODE:
export LOCAL=true && $0 <command> Use local Docker images for development
Examples:
export LOCAL=true && $0 telegram # Use local autotrader-telegram:latest
export LOCAL=true && $0 start btc # Use local autotrader-trader:latest
�📋 DEPLOYMENT COMMANDS:
setup Complete environment setup (creates configs, directories)
build Build Docker images locally
images List available Docker images and check configuration
fix-images Fix Docker image conflicts and issues
update-images Check and update Docker images from registry
upgrade Upgrade script from GitHub Gist
bind Install as global 'traderbot' command
unbind Remove global 'traderbot' command
start-all Start complete system
stop-all Stop all services
system-status Show system status
📱 TELEGRAM BOT:
telegram Start Telegram bot (curl-based, minimal dependencies)
setup-auth Setup Telegram bot authorization (interactive wizard)
config-auth View/manage Telegram bot authorization config
🚀 TRADING BOTS:
start <symbol> [opts] Create trading bot
Options:
--amount <amount> Trading amount (default: 50)
--test Enable test mode
--live Enable live mode (default)
--profile <name> Use credential profile (highest priority)
--api-key <key> API key (medium priority)
--api-secret <secret> API secret (medium priority)
--direction <dir> Trading direction (LONG/SHORT)
📊 BOT MANAGEMENT (Multi-Profile Support):
Docker CLI (Smart Detection):
./bot.sh list # List all bots grouped by profile
./bot.sh status <symbol|container> # Smart detection or specific container
./bot.sh logs <symbol|container> [lines] # Smart detection or specific container
./bot.sh stop <symbol|container> # Smart detection or specific container
./bot.sh restart <symbol|container> # Smart detection or specific container
./bot.sh remove <symbol|container> # Smart detection or specific container
Telegram Commands (Smart Detection):
/list, /status, /logs, /stop, /restart, /remove
Multi-Profile Examples:
./bot.sh logs btc # Shows options if multiple BTC bots
./bot.sh logs main-btc # Specific container logs
./bot.sh stop test-eth # Stop specific profile's ETH bot
🔑 CREDENTIALS (Use Docker CLI):
./bot.sh list-credentials
./bot.sh store-credentials <profile> <api_key> <api_secret> [display_name]
./bot.sh load-credentials <profile>
./bot.sh assets <profile> # View account assets for profile
EXAMPLES:
# First time setup
$0 setup # Complete environment setup (pulls images, extracts template)
$0 setup-auth # Setup Telegram bot authorization
$0 bind # Install as 'traderbot' command (optional)
# Multi-Profile Trading Examples
$0 start eth --profile main --amount 100 # Creates main-ethusdt container
$0 start btc --profile test --amount 50 --test # Creates test-btcusdt container
$0 start eth --profile prod --amount 200 # Creates prod-ethusdt container
# Management Examples (Smart Detection)
$0 logs eth # Shows options if multiple ETH bots exist
$0 logs main-eth # Direct access to main profile's ETH bot
$0 stop test-btc # Stop specific profile's BTC bot
# Credential Priority (highest to lowest)
$0 start btc --profile main --amount 100 # Use 'main' profile (highest)
$0 start btc --api-key 'key' --api-secret 'sec' # Use command line credentials
$0 start btc --amount 50 # Use environment variables (lowest)
# System management
$0 start-all # Start complete system
$0 telegram # Start Telegram bot
$0 system-status # Check system status
# Global command usage (after bind)
traderbot start eth --amount 100
traderbot list
traderbot telegram
EOF
}
# ===============================
# Telegram Auth Setup Functions
# ===============================
check_telegram_auth_setup() {
# Check if Telegram authorization is properly configured
local auth_config="$AUTOTRADER_DIR/telegram_auth.json"
# Check if auth config file exists
if [[ ! -f "$auth_config" ]]; then
echo "❌ Telegram bot authorization chưa được setup!"
echo ""
echo "🔐 Bạn cần setup authorization trước khi khởi động bot:"
echo " ./bot.sh setup-auth"
echo ""
echo "💡 Hoặc chạy lệnh sau để setup ngay:"
echo ""
read -p "Bạn có muốn setup authorization ngay bây giờ? (y/N): " setup_now
if [[ "$setup_now" =~ ^[Yy]$ ]]; then
echo ""
setup_telegram_auth
return $?
else
echo ""
echo "⚠️ Bot không thể khởi động mà không có authorization setup."
echo " Chạy './bot.sh setup-auth' trước khi khởi động bot."
return 1
fi
fi
# Validate auth config structure
if ! python3 -c "
import json
import sys
try:
with open('$auth_config', 'r') as f:
config = json.load(f)
# Check required structure
if 'authorized_users' not in config:
print('❌ Config thiếu authorized_users section')
sys.exit(1)
if 'settings' not in config:
print('❌ Config thiếu settings section')
sys.exit(1)
settings = config['settings']
if 'creator_username' not in settings:
print('❌ Config thiếu creator_username setting')
sys.exit(1)
print('✅ Authorization config hợp lệ')
except json.JSONDecodeError:
print('❌ Config file không phải JSON hợp lệ')
sys.exit(1)
except Exception as e:
print(f'❌ Lỗi đọc config: {e}')
sys.exit(1)
" 2>/dev/null; then
echo "❌ Authorization config file bị lỗi!"
echo ""
echo "💡 Chạy lệnh sau để reset config:"
echo " ./bot.sh setup-auth"
echo ""
read -p "Bạn có muốn reset authorization config? (y/N): " reset_config
if [[ "$reset_config" =~ ^[Yy]$ ]]; then
echo ""
setup_telegram_auth
return $?
else
return 1
fi
fi
echo "✅ Telegram authorization đã được setup"
return 0
}
setup_telegram_auth() {
echo "🔐 Telegram Bot Authorization Setup"
echo "=================================="
echo ""
# Ensure .autotrader directory exists
ensure_directories
local auth_config="$AUTOTRADER_DIR/telegram_auth.json"
echo "This wizard will help you set up Telegram bot authorization."
echo "You'll need to provide a super admin user (yourself)."
echo ""
# Get super admin input
local super_admin_input=""
local super_admin_username=""
local super_admin_id=""
while [[ -z "$super_admin_input" ]]; do
echo "👑 Enter super admin (username or user ID):"
echo " Examples: hoangtrungdev, @hoangtrungdev, or 1631630468"
read -p "Super admin: " super_admin_input
if [[ -z "$super_admin_input" ]]; then
echo "❌ Super admin cannot be empty. Please try again."
echo ""
fi
done
# Parse input - detect if it's username or user ID
if [[ "$super_admin_input" =~ ^[0-9]+$ ]]; then
# It's a user ID
super_admin_id="$super_admin_input"
echo "📝 Enter username for user ID $super_admin_id (optional):"
read -p "Username: " super_admin_username
if [[ -z "$super_admin_username" ]]; then
super_admin_username="user_$super_admin_id"
fi
else
# It's a username - clean it up
super_admin_username="${super_admin_input#@}" # Remove @ if present
echo "📝 Enter user ID for @$super_admin_username (optional, leave empty for auto-detection):"
read -p "User ID: " super_admin_id
fi
# Create auth config
echo ""
echo "🔧 Creating authorization config..."
local config_content='{
"authorized_users": {}'
# Add super admin if we have user ID
if [[ -n "$super_admin_id" ]]; then
config_content='{
"authorized_users": {
"'$super_admin_id'": {
"username": "'$super_admin_username'",
"role": "SUPER_ADMIN",
"added_by": "setup_wizard",
"added_at": "'$(date -u +"%Y-%m-%dT%H:%M:%S")'.000Z"
}
}'
fi
config_content="$config_content,
\"settings\": {
\"default_rejection_message\": \"❌ Bạn không có quyền sử dụng bot này. Liên hệ @$super_admin_username để được cấp quyền.\",
\"super_admin_only_commands\": [
\"/adduser\",
\"/removeuser\",
\"/listusers\"
],
\"creator_username\": \"$super_admin_username\",
\"audit_log\": true,
\"strict_mode\": true,
\"auto_promote_creator\": true
}
}"
# Write config file
echo "$config_content" > "$auth_config"
chmod 600 "$auth_config"
echo "✅ Authorization config created: $auth_config"
echo ""
echo "📋 Configuration Summary:"
echo " Super Admin: @$super_admin_username"
if [[ -n "$super_admin_id" ]]; then
echo " User ID: $super_admin_id"
else
echo " User ID: Auto-detect on first use"
fi
echo " Config File: $auth_config"
echo ""
echo "🎯 Next Steps:"
echo " 1. Start your Telegram bot: ./bot.sh telegram"
echo " 2. Send /start to your bot to auto-promote yourself"
echo " 3. Use /adduser to add other users"
echo ""
}
config_telegram_auth() {
echo "⚙️ Telegram Bot Authorization Config"
echo "===================================="
echo ""
local auth_config="$AUTOTRADER_DIR/telegram_auth.json"
if [[ ! -f "$auth_config" ]]; then
echo "❌ Authorization config not found: $auth_config"
echo "💡 Run './bot.sh setup-auth' first to create the config"
return 1
fi
echo "📋 Current Configuration:"
echo "========================"
# Display current config in a readable format
if command -v python3 &> /dev/null; then
python3 -c "
import json
import sys
try:
with open('$auth_config', 'r') as f:
config = json.load(f)
print('👑 Super Admin Settings:')
settings = config.get('settings', {})
creator = settings.get('creator_username', 'Not set')
print(f' Creator: @{creator}')
print(f' Auto-promote: {settings.get(\"auto_promote_creator\", False)}')
print(f' Strict mode: {settings.get(\"strict_mode\", False)}')
print(f' Audit log: {settings.get(\"audit_log\", False)}')
print()
print('👥 Authorized Users:')
users = config.get('authorized_users', {})
if not users:
print(' No users configured')
else:
for user_id, user_info in users.items():
username = user_info.get('username', 'Unknown')
role = user_info.get('role', 'USER')
added_by = user_info.get('added_by', 'Unknown')
print(f' @{username} ({user_id}) - {role} (added by {added_by})')
print()
print('🚫 Restricted Commands:')
restricted = settings.get('super_admin_only_commands', [])
for cmd in restricted:
print(f' {cmd}')
except Exception as e:
print(f'❌ Error reading config: {e}')
sys.exit(1)
"
else
echo "⚠️ Python3 not available - showing raw config:"
cat "$auth_config"
fi
echo ""
echo "🔧 Available Actions:"
echo " 1. Edit config file manually: $auth_config"
echo " 2. Reset config: ./bot.sh setup-auth"
echo " 3. Manage users via Telegram bot commands"
echo ""
}
# ===============================
# Main Command Router
# ===============================
main() {
case "${1:-help}" in
# Deployment commands
setup) setup_environment ;;
build) build_docker_images ;;
images) list_docker_images ;;
fix-images) fix_docker_images ;;
update-images) check_image_updates ;;
upgrade) upgrade_script ;;
bind) bind_global_command ;;
# Telegram Auth commands
setup-auth) setup_telegram_auth ;;
config-auth) config_telegram_auth ;;
unbind) unbind_global_command ;;
start-all) start_all ;;
stop-all) stop_all ;;
system-status) system_status ;;
# Telegram commands
telegram) start_telegram_bot ;;
telegram-deploy)
echo "⚠️ 'telegram-deploy' is deprecated. Use 'telegram' for simple bot."
echo "💡 For Docker deployment, use: docker run with manual setup"
start_telegram_bot
;;
# Trading commands
start) shift; start_trading_bot "$@" ;;
# Delegated commands (use Unified Processor)
list)
print_step "Running list command..."
run_docker_cli "python3" "/app/src/core/unified_command_processor.py" list
;;
status)
if [[ -z "${2:-}" ]]; then
handle_command_error "status" "Cần tên symbol hoặc container" \
"Cách sử dụng: ./bot.sh status <symbol|container_name>
Ví dụ:
./bot.sh status btc # Smart detection cho BTC
./bot.sh status main-btc # Container cụ thể"
exit 1
fi
local input_arg="$2"
print_step "Đang lấy trạng thái cho: $input_arg"
run_docker_cli "python3" "/app/src/core/unified_command_processor.py" status "$input_arg"
;;
logs)
if [[ -z "${2:-}" ]]; then
handle_command_error "logs" "Cần tên symbol hoặc container" \
"Cách sử dụng: ./bot.sh logs <symbol|container_name> [lines]
Ví dụ:
./bot.sh logs btc 100 # Smart detection cho BTC
./bot.sh logs main-btc 100 # Container cụ thể"
exit 1
fi
local input_arg="$2"
local lines="${3:-50}"
print_step "Đang lấy logs cho: $input_arg ($lines dòng cuối)"
run_docker_cli "python3" "/app/src/core/unified_command_processor.py" logs "$input_arg" "$lines"
;;
simple-logs)
simple_logs "$2" "$3"
;;
stop)
if [[ -z "${2:-}" ]]; then
handle_command_error "stop" "Cần tên symbol hoặc container" \
"Cách sử dụng: ./bot.sh stop <symbol|container_name>
Ví dụ:
./bot.sh stop btc # Smart detection cho BTC
./bot.sh stop main-btc # Container cụ thể"
exit 1
fi
local input_arg="$2"
print_step "Đang dừng: $input_arg"
run_docker_cli "python3" "/app/src/core/unified_command_processor.py" stop "$input_arg"
;;
restart)
if [[ -z "${2:-}" ]]; then
handle_command_error "restart" "Cần tên symbol hoặc container" \
"Cách sử dụng: ./bot.sh restart <symbol|container_name>
Ví dụ:
./bot.sh restart btc # Smart detection cho BTC
./bot.sh restart main-btc # Container cụ thể"
exit 1
fi
local input_arg="$2"
print_step "Đang khởi động lại: $input_arg"
run_docker_cli "python3" "/app/src/core/unified_command_processor.py" restart "$input_arg"
;;
remove)
if [[ -z "${2:-}" ]]; then
handle_command_error "remove" "Cần tên symbol hoặc container" \
"Cách sử dụng: ./bot.sh remove <symbol|container_name> [--force]
Ví dụ:
./bot.sh remove btc # Smart detection cho BTC
./bot.sh remove main-btc # Container cụ thể
./bot.sh remove btc --force # Force remove container đang chạy"
exit 1
fi
local input_arg="$2"
local force_flag="${3:-}"
if [[ "$force_flag" == "--force" ]]; then
print_step "Đang force remove: $input_arg"
run_docker_cli "python3" "/app/src/core/unified_command_processor.py" remove "$input_arg" --force
else
print_step "Đang xóa: $input_arg"
run_docker_cli "python3" "/app/src/core/unified_command_processor.py" remove "$input_arg"
fi
;;
# Credential commands (Docker CLI)
list-credentials)
print_step "Loading credential profiles..."
run_docker_cli "credentials_cli" list
;;
store-credentials)
shift # Remove "store-credentials"
profile="$1"
api_key="$2"
api_secret="$3"
display_name="$4"
if [[ -z "$profile" || -z "$api_key" || -z "$api_secret" ]]; then
handle_command_error "store-credentials" "Missing required parameters" \
"Usage: ./bot.sh store-credentials <profile> <api_key> <api_secret> [display_name]
Example:
./bot.sh store-credentials main 'your_api_key' 'your_api_secret' 'Main Account'"
exit 1
fi
print_step "Storing credentials for profile: $profile"
run_docker_cli "credentials_cli" store "$profile" --api-key "$api_key" --api-secret "$api_secret" --display-name "$display_name" --non-interactive
;;
load-credentials)
if [[ -z "${2:-}" ]]; then
handle_command_error "load-credentials" "Cần tên profile" \
"Cách sử dụng: ./bot.sh load-credentials <profile>
Ví dụ:
./bot.sh load-credentials main"
exit 1
fi
print_step "Đang load credentials cho profile: $2"
run_docker_cli "credentials_cli" load "$2"
;;
# Assets command (Docker CLI)
assets)
if [[ -z "${2:-}" ]]; then
handle_command_error "assets" "Cần tên profile" \
"Cách sử dụng: ./bot.sh assets <profile>
Ví dụ:
./bot.sh assets main
💡 Dùng './bot.sh list-credentials' để xem các profiles có sẵn"
exit 1
fi
print_step "Đang load tài sản tài khoản cho profile: $2"
run_docker_cli "assets_cli" show "$2"
;;
# Utility commands
help|--help|-h) show_help ;;
version|--version|-v)
echo "🤖 AutoTrader Bot v$SCRIPT_VERSION"
echo "📅 Compact & Enhanced Version"
echo "🐳 Docker-based Trading System"
;;
check)
echo "🔍 System Check"
echo "==============="
check_docker && echo "✅ Docker: Available" || echo "❌ Docker: Not available"
check_python && echo "✅ Python: Available" || echo "❌ Python: Not available"
[[ -n "$TELEGRAM_BOT_TOKEN" ]] && echo "✅ Telegram: Configured" || echo "⚠️ Telegram: Not configured"
echo "📁 Configs: $(ls $CONFIG_DIR/*.json 2>/dev/null | wc -l) files"
echo "🐳 Containers: $(docker ps -q | wc -l) running"
;;
*)
handle_command_error "unknown" "Lệnh không hợp lệ: $1" \
"Các lệnh phổ biến:
./bot.sh help # Xem tất cả lệnh
./bot.sh system-status # Kiểm tra trạng thái hệ thống
./bot.sh telegram # Khởi động Telegram bot
./bot.sh list # Liệt kê containers
./bot.sh start btc # Tạo trading bot cho BTC"
exit 1
;;
esac
}
# Run main function
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment