docker
This commit is contained in:
parent
69462cf3e0
commit
761e595389
10
.gitignore
vendored
10
.gitignore
vendored
@ -68,3 +68,13 @@ Thumbs.db
|
|||||||
tmp/
|
tmp/
|
||||||
temp/
|
temp/
|
||||||
.tmp/
|
.tmp/
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
.dockerignore
|
||||||
|
|
||||||
|
# Backups and deployment
|
||||||
|
backups/
|
||||||
|
backup_*/
|
||||||
|
|
||||||
|
# Health check files
|
||||||
|
/tmp/health
|
||||||
|
|||||||
288
DOCKER_DEPLOYMENT.md
Normal file
288
DOCKER_DEPLOYMENT.md
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
# Docker Deployment Guide
|
||||||
|
|
||||||
|
This guide covers deploying the trading system for 24/7 operation using Docker.
|
||||||
|
|
||||||
|
## 🚀 Quick Start
|
||||||
|
|
||||||
|
### 1. Setup Environment
|
||||||
|
```bash
|
||||||
|
# Copy environment template
|
||||||
|
cp .env.production .env
|
||||||
|
|
||||||
|
# Edit with your API keys
|
||||||
|
nano .env
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Deploy the System
|
||||||
|
```bash
|
||||||
|
# Start the trading system
|
||||||
|
./deploy.sh start
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
./deploy.sh logs live
|
||||||
|
|
||||||
|
# Check status
|
||||||
|
./deploy.sh status
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📋 Deployment Options
|
||||||
|
|
||||||
|
### Paper Trading (Recommended)
|
||||||
|
```bash
|
||||||
|
# Safe paper trading mode
|
||||||
|
./deploy.sh start
|
||||||
|
```
|
||||||
|
|
||||||
|
### Live Trading (Advanced)
|
||||||
|
```bash
|
||||||
|
# Edit .env file first
|
||||||
|
TRADING_MODE=live
|
||||||
|
|
||||||
|
# Deploy with extreme caution
|
||||||
|
./deploy.sh start
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🐳 Docker Architecture
|
||||||
|
|
||||||
|
### Services
|
||||||
|
|
||||||
|
1. **trading-bot**: Main trading application
|
||||||
|
- Runs 24/7 with auto-restart
|
||||||
|
- Health monitoring every 60 seconds
|
||||||
|
- Resource limits for safety
|
||||||
|
|
||||||
|
2. **monitoring**: Optional monitoring service
|
||||||
|
- Displays system status every 5 minutes
|
||||||
|
- Shows recent performance and trades
|
||||||
|
- Lightweight Alpine Linux container
|
||||||
|
|
||||||
|
### Volumes
|
||||||
|
|
||||||
|
- `./logs:/app/logs` - Persistent log storage
|
||||||
|
- `./config:/app/config:ro` - Read-only configuration
|
||||||
|
|
||||||
|
### Security Features
|
||||||
|
|
||||||
|
- Non-root user (trader:1000)
|
||||||
|
- Resource limits (512MB RAM, 0.5 CPU)
|
||||||
|
- Read-only configuration mounting
|
||||||
|
- Environment-based secrets
|
||||||
|
|
||||||
|
## 🛡️ Safety Features
|
||||||
|
|
||||||
|
### Health Monitoring
|
||||||
|
- Container health checks every 60 seconds
|
||||||
|
- Application health checks every 5 minutes
|
||||||
|
- Automatic restart on failure
|
||||||
|
|
||||||
|
### Risk Management
|
||||||
|
- Paper trading by default
|
||||||
|
- Resource constraints
|
||||||
|
- Graceful shutdown handling
|
||||||
|
- Comprehensive error logging
|
||||||
|
|
||||||
|
### Backup & Recovery
|
||||||
|
```bash
|
||||||
|
# Create backup
|
||||||
|
./deploy.sh backup
|
||||||
|
|
||||||
|
# View backups
|
||||||
|
ls backups/
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📊 Monitoring & Logs
|
||||||
|
|
||||||
|
### Real-time Monitoring
|
||||||
|
```bash
|
||||||
|
# Follow live logs
|
||||||
|
./deploy.sh logs live
|
||||||
|
|
||||||
|
# System status
|
||||||
|
./deploy.sh status
|
||||||
|
|
||||||
|
# Container status
|
||||||
|
docker compose ps
|
||||||
|
```
|
||||||
|
|
||||||
|
### Log Files
|
||||||
|
- `logs/trading.log` - General system logs
|
||||||
|
- `logs/trades.log` - Trade execution details
|
||||||
|
- `logs/risk.log` - Risk management events
|
||||||
|
- `logs/performance.log` - Performance metrics
|
||||||
|
|
||||||
|
### Health Endpoints
|
||||||
|
- Health file: `/tmp/health` (inside container)
|
||||||
|
- Status format: `OK:timestamp:price`
|
||||||
|
|
||||||
|
## 🔧 Management Commands
|
||||||
|
|
||||||
|
### Deployment Script (`deploy.sh`)
|
||||||
|
```bash
|
||||||
|
./deploy.sh start # Start system
|
||||||
|
./deploy.sh stop # Stop system
|
||||||
|
./deploy.sh restart # Restart system
|
||||||
|
./deploy.sh logs # Show recent logs
|
||||||
|
./deploy.sh logs live # Follow live logs
|
||||||
|
./deploy.sh status # System status
|
||||||
|
./deploy.sh backup # Create backup
|
||||||
|
./deploy.sh update # Update and restart
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Compose Commands
|
||||||
|
```bash
|
||||||
|
# Manual control
|
||||||
|
docker compose up -d # Start services
|
||||||
|
docker compose down # Stop services
|
||||||
|
docker compose logs -f # Follow logs
|
||||||
|
docker compose ps # Service status
|
||||||
|
|
||||||
|
# Rebuild containers
|
||||||
|
docker compose build --no-cache
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔄 Updates & Maintenance
|
||||||
|
|
||||||
|
### Updating the System
|
||||||
|
```bash
|
||||||
|
# Automated update (with backup)
|
||||||
|
./deploy.sh update
|
||||||
|
|
||||||
|
# Manual update
|
||||||
|
docker compose down
|
||||||
|
git pull # if using git
|
||||||
|
docker compose build --no-cache
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration Changes
|
||||||
|
```bash
|
||||||
|
# Edit configuration
|
||||||
|
nano .env
|
||||||
|
|
||||||
|
# Restart to apply changes
|
||||||
|
./deploy.sh restart
|
||||||
|
```
|
||||||
|
|
||||||
|
### Log Rotation
|
||||||
|
Logs are automatically managed by Docker. To manually clean:
|
||||||
|
```bash
|
||||||
|
# Clean old logs (be careful!)
|
||||||
|
docker system prune -f
|
||||||
|
|
||||||
|
# Or manually rotate logs
|
||||||
|
./deploy.sh backup # Backup first
|
||||||
|
> logs/trading.log
|
||||||
|
> logs/trades.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚨 Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
1. **Container won't start**
|
||||||
|
```bash
|
||||||
|
# Check logs
|
||||||
|
docker compose logs trading-bot
|
||||||
|
|
||||||
|
# Verify environment
|
||||||
|
cat .env
|
||||||
|
|
||||||
|
# Test configuration
|
||||||
|
docker compose config
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **API connection errors**
|
||||||
|
```bash
|
||||||
|
# Verify API keys in .env
|
||||||
|
grep -E "ALPACA_(API|SECRET)_KEY" .env
|
||||||
|
|
||||||
|
# Test connection
|
||||||
|
docker exec trading-system python3 -c "from src.data_handler import DataHandler; print(DataHandler().get_latest_price('AAPL'))"
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Health check failures**
|
||||||
|
```bash
|
||||||
|
# Check health status
|
||||||
|
docker inspect trading-system | grep -A 5 Health
|
||||||
|
|
||||||
|
# Manual health check
|
||||||
|
docker exec trading-system python3 -c "from main import health_check; print(health_check())"
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Performance issues**
|
||||||
|
```bash
|
||||||
|
# Check resource usage
|
||||||
|
docker stats trading-system
|
||||||
|
|
||||||
|
# Increase limits in docker compose.yml
|
||||||
|
nano docker compose.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Emergency Procedures
|
||||||
|
|
||||||
|
1. **Immediate stop**
|
||||||
|
```bash
|
||||||
|
./deploy.sh stop
|
||||||
|
# or
|
||||||
|
docker kill trading-system
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Emergency backup**
|
||||||
|
```bash
|
||||||
|
./deploy.sh backup
|
||||||
|
cp -r logs backups/emergency_backup_$(date +%Y%m%d_%H%M%S)/
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Reset system**
|
||||||
|
```bash
|
||||||
|
./deploy.sh stop
|
||||||
|
docker compose down -v # WARNING: Removes volumes
|
||||||
|
./deploy.sh start
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🌐 Production Considerations
|
||||||
|
|
||||||
|
### Server Requirements
|
||||||
|
- **Minimum**: 1 CPU, 1GB RAM, 10GB storage
|
||||||
|
- **Recommended**: 2 CPU, 2GB RAM, 50GB storage
|
||||||
|
- **OS**: Linux (Ubuntu 20.04+ recommended)
|
||||||
|
|
||||||
|
### Network Requirements
|
||||||
|
- Stable internet connection
|
||||||
|
- HTTPS access to Alpaca API
|
||||||
|
- NTP for accurate timestamps
|
||||||
|
|
||||||
|
### Security Best Practices
|
||||||
|
1. Use paper trading initially
|
||||||
|
2. Limit server access (SSH keys only)
|
||||||
|
3. Regular backups to external storage
|
||||||
|
4. Monitor logs for anomalies
|
||||||
|
5. Keep system updated
|
||||||
|
|
||||||
|
### Scaling Considerations
|
||||||
|
- Multiple symbols: Use separate containers
|
||||||
|
- High frequency: Increase resource limits
|
||||||
|
- Redundancy: Deploy across multiple servers
|
||||||
|
|
||||||
|
## 📞 Support
|
||||||
|
|
||||||
|
### Getting Help
|
||||||
|
1. Check logs: `./deploy.sh logs`
|
||||||
|
2. Verify configuration: `docker compose config`
|
||||||
|
3. Test health: `./deploy.sh status`
|
||||||
|
4. Review documentation
|
||||||
|
|
||||||
|
### Important Notes
|
||||||
|
- Always test in paper trading first
|
||||||
|
- Monitor performance regularly
|
||||||
|
- Keep backups of profitable configurations
|
||||||
|
- Never disable risk management features
|
||||||
|
|
||||||
|
## 🔒 Disclaimer
|
||||||
|
|
||||||
|
This deployment setup is for educational purposes. Trading involves risk of loss. Always:
|
||||||
|
- Use paper trading for testing
|
||||||
|
- Understand the risks involved
|
||||||
|
- Never trade more than you can afford to lose
|
||||||
|
- Monitor the system regularly
|
||||||
|
- Keep security best practices
|
||||||
35
Dockerfile
Normal file
35
Dockerfile
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Trading System Dockerfile
|
||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install system dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
gcc \
|
||||||
|
g++ \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Copy requirements first for better caching
|
||||||
|
COPY requirements.txt .
|
||||||
|
|
||||||
|
# Install Python dependencies
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Copy source code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Create logs directory
|
||||||
|
RUN mkdir -p logs
|
||||||
|
|
||||||
|
# Create non-root user for security
|
||||||
|
RUN useradd -m -u 1000 trader && \
|
||||||
|
chown -R trader:trader /app
|
||||||
|
USER trader
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||||
|
CMD python3 -c "import sys; sys.path.append('src'); from src.data_handler import DataHandler; DataHandler().get_latest_price('AAPL')" || exit 1
|
||||||
|
|
||||||
|
# Default command
|
||||||
|
CMD ["python3", "main.py", "--mode", "paper"]
|
||||||
249
deploy.sh
Executable file
249
deploy.sh
Executable file
@ -0,0 +1,249 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Trading System Deployment Script
|
||||||
|
# Usage: ./deploy.sh [start|stop|restart|logs|status|backup]
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Helper functions
|
||||||
|
log_info() {
|
||||||
|
echo -e "${BLUE}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success() {
|
||||||
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warning() {
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if .env file exists
|
||||||
|
check_env() {
|
||||||
|
if [ ! -f .env ]; then
|
||||||
|
log_warning ".env file not found. Creating from template..."
|
||||||
|
if [ -f .env.production ]; then
|
||||||
|
cp .env.production .env
|
||||||
|
log_info "Please edit .env file with your actual API keys"
|
||||||
|
log_info "nano .env"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
log_error "No .env template found!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Validate environment
|
||||||
|
validate_env() {
|
||||||
|
log_info "Validating environment configuration..."
|
||||||
|
|
||||||
|
# Source the .env file
|
||||||
|
export $(grep -v '^#' .env | xargs)
|
||||||
|
|
||||||
|
# Check required variables
|
||||||
|
if [ -z "$ALPACA_API_KEY" ] || [ "$ALPACA_API_KEY" = "your_paper_api_key_here" ]; then
|
||||||
|
log_error "ALPACA_API_KEY not set in .env file"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$ALPACA_SECRET_KEY" ] || [ "$ALPACA_SECRET_KEY" = "your_paper_secret_key_here" ]; then
|
||||||
|
log_error "ALPACA_SECRET_KEY not set in .env file"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Warn about live trading
|
||||||
|
if [ "$TRADING_MODE" = "live" ]; then
|
||||||
|
log_warning "⚠️ LIVE TRADING MODE ENABLED! ⚠️"
|
||||||
|
log_warning "This will use real money. Are you sure? (y/N)"
|
||||||
|
read -r response
|
||||||
|
if [[ ! "$response" =~ ^[Yy]$ ]]; then
|
||||||
|
log_info "Switching to paper trading mode for safety"
|
||||||
|
sed -i.bak 's/TRADING_MODE=live/TRADING_MODE=paper/' .env
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Environment validation passed"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start the trading system
|
||||||
|
start_system() {
|
||||||
|
log_info "Starting trading system..."
|
||||||
|
|
||||||
|
check_env
|
||||||
|
validate_env
|
||||||
|
|
||||||
|
# Create logs directory
|
||||||
|
mkdir -p logs
|
||||||
|
|
||||||
|
# Build and start containers
|
||||||
|
docker compose build
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
log_success "Trading system started!"
|
||||||
|
log_info "View logs with: ./deploy.sh logs"
|
||||||
|
log_info "Check status with: ./deploy.sh status"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Stop the trading system
|
||||||
|
stop_system() {
|
||||||
|
log_info "Stopping trading system..."
|
||||||
|
docker compose down
|
||||||
|
log_success "Trading system stopped"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Restart the trading system
|
||||||
|
restart_system() {
|
||||||
|
log_info "Restarting trading system..."
|
||||||
|
stop_system
|
||||||
|
sleep 2
|
||||||
|
start_system
|
||||||
|
}
|
||||||
|
|
||||||
|
# Show logs
|
||||||
|
show_logs() {
|
||||||
|
if [ "$2" = "live" ]; then
|
||||||
|
log_info "Following live logs (Ctrl+C to exit)..."
|
||||||
|
docker compose logs -f trading-bot
|
||||||
|
else
|
||||||
|
log_info "Recent logs:"
|
||||||
|
docker compose logs --tail=50 trading-bot
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Show system status
|
||||||
|
show_status() {
|
||||||
|
log_info "Trading system status:"
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Container status
|
||||||
|
docker compose ps
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Recent performance
|
||||||
|
log_info "Recent performance (last 5 entries):"
|
||||||
|
if [ -f logs/performance.log ]; then
|
||||||
|
tail -5 logs/performance.log
|
||||||
|
else
|
||||||
|
log_warning "No performance data yet"
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Recent trades
|
||||||
|
log_info "Recent trades (last 3 entries):"
|
||||||
|
if [ -f logs/trades.log ]; then
|
||||||
|
tail -3 logs/trades.log
|
||||||
|
else
|
||||||
|
log_warning "No trades yet"
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Risk events
|
||||||
|
log_info "Recent risk events:"
|
||||||
|
if [ -f logs/risk.log ]; then
|
||||||
|
tail -3 logs/risk.log
|
||||||
|
else
|
||||||
|
log_success "No risk events"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Backup logs and data
|
||||||
|
backup_data() {
|
||||||
|
log_info "Creating backup..."
|
||||||
|
|
||||||
|
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
|
||||||
|
BACKUP_DIR="backups/backup_$TIMESTAMP"
|
||||||
|
|
||||||
|
mkdir -p "$BACKUP_DIR"
|
||||||
|
|
||||||
|
# Backup logs
|
||||||
|
if [ -d logs ]; then
|
||||||
|
cp -r logs "$BACKUP_DIR/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Backup configuration
|
||||||
|
cp .env "$BACKUP_DIR/.env.backup" 2>/dev/null || true
|
||||||
|
cp docker-compose.yml "$BACKUP_DIR/"
|
||||||
|
|
||||||
|
log_success "Backup created: $BACKUP_DIR"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Update system
|
||||||
|
update_system() {
|
||||||
|
log_info "Updating trading system..."
|
||||||
|
|
||||||
|
# Stop system
|
||||||
|
stop_system
|
||||||
|
|
||||||
|
# Backup current state
|
||||||
|
backup_data
|
||||||
|
|
||||||
|
# Rebuild containers
|
||||||
|
docker compose build --no-cache
|
||||||
|
|
||||||
|
# Start system
|
||||||
|
start_system
|
||||||
|
|
||||||
|
log_success "System updated successfully"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main script logic
|
||||||
|
case "${1:-help}" in
|
||||||
|
start)
|
||||||
|
start_system
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
stop_system
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
restart_system
|
||||||
|
;;
|
||||||
|
logs)
|
||||||
|
show_logs "$@"
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
show_status
|
||||||
|
;;
|
||||||
|
backup)
|
||||||
|
backup_data
|
||||||
|
;;
|
||||||
|
update)
|
||||||
|
update_system
|
||||||
|
;;
|
||||||
|
help|*)
|
||||||
|
echo "Trading System Deployment Script"
|
||||||
|
echo
|
||||||
|
echo "Usage: $0 [command]"
|
||||||
|
echo
|
||||||
|
echo "Commands:"
|
||||||
|
echo " start - Start the trading system"
|
||||||
|
echo " stop - Stop the trading system"
|
||||||
|
echo " restart - Restart the trading system"
|
||||||
|
echo " logs - Show recent logs"
|
||||||
|
echo " logs live - Follow live logs"
|
||||||
|
echo " status - Show system status and performance"
|
||||||
|
echo " backup - Backup logs and configuration"
|
||||||
|
echo " update - Update and restart system"
|
||||||
|
echo " help - Show this help message"
|
||||||
|
echo
|
||||||
|
echo "Examples:"
|
||||||
|
echo " $0 start # Start trading"
|
||||||
|
echo " $0 logs live # Watch live logs"
|
||||||
|
echo " $0 status # Check performance"
|
||||||
|
echo
|
||||||
|
;;
|
||||||
|
esac
|
||||||
89
docker-compose.yml
Normal file
89
docker-compose.yml
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
services:
|
||||||
|
trading-bot:
|
||||||
|
build: .
|
||||||
|
container_name: trading-system
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
# Alpaca API Configuration
|
||||||
|
- ALPACA_API_KEY=${ALPACA_API_KEY}
|
||||||
|
- ALPACA_SECRET_KEY=${ALPACA_SECRET_KEY}
|
||||||
|
- ALPACA_BASE_URL=${ALPACA_BASE_URL:-https://paper-api.alpaca.markets}
|
||||||
|
|
||||||
|
# Trading Configuration
|
||||||
|
- STRATEGY_TYPE=${STRATEGY_TYPE:-enhanced}
|
||||||
|
- SYMBOL=${SYMBOL:-AAPL}
|
||||||
|
- TRADING_MODE=${TRADING_MODE:-paper}
|
||||||
|
|
||||||
|
# Risk Management
|
||||||
|
- MAX_POSITION_SIZE=${MAX_POSITION_SIZE:-0.95}
|
||||||
|
- RISK_PER_TRADE=${RISK_PER_TRADE:-0.02}
|
||||||
|
- MAX_DRAWDOWN_LIMIT=${MAX_DRAWDOWN_LIMIT:-0.15}
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
- LOG_LEVEL=${LOG_LEVEL:-INFO}
|
||||||
|
- TZ=UTC
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
# Persist logs
|
||||||
|
- ./logs:/app/logs
|
||||||
|
# Optional: Mount config for easy updates
|
||||||
|
- ./config:/app/config:ro
|
||||||
|
|
||||||
|
# Resource limits for safety
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 512M
|
||||||
|
cpus: '0.5'
|
||||||
|
reservations:
|
||||||
|
memory: 256M
|
||||||
|
cpus: '0.25'
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "python3", "-c", "import sys; sys.path.append('src'); from src.data_handler import DataHandler; DataHandler().get_latest_price('AAPL')"]
|
||||||
|
interval: 60s
|
||||||
|
timeout: 30s
|
||||||
|
retries: 3
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
|
# Command override based on mode
|
||||||
|
command: >
|
||||||
|
sh -c "
|
||||||
|
if [ '${TRADING_MODE}' = 'live' ]; then
|
||||||
|
echo '⚠️ LIVE TRADING MODE - USE WITH EXTREME CAUTION ⚠️';
|
||||||
|
sleep 10;
|
||||||
|
python3 main.py --mode live --strategy ${STRATEGY_TYPE:-enhanced};
|
||||||
|
else
|
||||||
|
echo '📄 PAPER TRADING MODE (Safe)';
|
||||||
|
python3 main.py --mode paper --strategy ${STRATEGY_TYPE:-enhanced};
|
||||||
|
fi
|
||||||
|
"
|
||||||
|
|
||||||
|
# Optional: Monitoring service
|
||||||
|
monitoring:
|
||||||
|
image: alpine:latest
|
||||||
|
container_name: trading-monitor
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- trading-bot
|
||||||
|
volumes:
|
||||||
|
- ./logs:/logs:ro
|
||||||
|
command: >
|
||||||
|
sh -c "
|
||||||
|
while true; do
|
||||||
|
echo '📊 Trading System Status - $(date)';
|
||||||
|
echo '📈 Latest Performance:';
|
||||||
|
tail -5 /logs/performance.log 2>/dev/null || echo 'No performance data yet';
|
||||||
|
echo '🔄 Latest Trades:';
|
||||||
|
tail -3 /logs/trades.log 2>/dev/null || echo 'No trades yet';
|
||||||
|
echo '⚠️ Latest Warnings:';
|
||||||
|
tail -3 /logs/risk.log 2>/dev/null || echo 'No risk events';
|
||||||
|
echo '----------------------------------------';
|
||||||
|
sleep 300; # Check every 5 minutes
|
||||||
|
done
|
||||||
|
"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
name: trading-network
|
||||||
60
main.py
60
main.py
@ -2,6 +2,7 @@
|
|||||||
"""
|
"""
|
||||||
Main Trading Application
|
Main Trading Application
|
||||||
Entry point for the automated trading system.
|
Entry point for the automated trading system.
|
||||||
|
Supports Docker deployment with health monitoring.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
@ -22,6 +23,7 @@ from config.trading_config import trading_config
|
|||||||
# Global variables for graceful shutdown
|
# Global variables for graceful shutdown
|
||||||
running = True
|
running = True
|
||||||
trading_engine = None
|
trading_engine = None
|
||||||
|
last_health_check = datetime.now()
|
||||||
|
|
||||||
def signal_handler(signum, frame):
|
def signal_handler(signum, frame):
|
||||||
"""Handle shutdown signals gracefully"""
|
"""Handle shutdown signals gracefully"""
|
||||||
@ -30,6 +32,26 @@ def signal_handler(signum, frame):
|
|||||||
logger.info(f"Received signal {signum}, shutting down gracefully...")
|
logger.info(f"Received signal {signum}, shutting down gracefully...")
|
||||||
running = False
|
running = False
|
||||||
|
|
||||||
|
def health_check():
|
||||||
|
"""Perform health check for Docker monitoring"""
|
||||||
|
global last_health_check
|
||||||
|
try:
|
||||||
|
from src.data_handler import DataHandler
|
||||||
|
data_handler = DataHandler()
|
||||||
|
# Quick health check - just verify we can get market data
|
||||||
|
price = data_handler.get_latest_price(trading_config.symbol)
|
||||||
|
last_health_check = datetime.now()
|
||||||
|
|
||||||
|
# Write health status to file for Docker monitoring
|
||||||
|
with open('/tmp/health', 'w') as f:
|
||||||
|
f.write(f"OK:{datetime.now().isoformat()}:{price}")
|
||||||
|
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
logger.error(f"Health check failed: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
def run_backtesting_mode(strategy_type=None):
|
def run_backtesting_mode(strategy_type=None):
|
||||||
"""Run backtesting using historical data"""
|
"""Run backtesting using historical data"""
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -228,20 +250,28 @@ def display_backtest_results(results, strategy_name):
|
|||||||
print("="*60)
|
print("="*60)
|
||||||
|
|
||||||
def run_live_trading_mode():
|
def run_live_trading_mode():
|
||||||
"""Run live trading mode"""
|
"""Run live trading mode with Docker health monitoring"""
|
||||||
global trading_engine, running
|
global trading_engine, running
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
logger.info("Starting live trading mode")
|
logger.info("🚀 Starting live trading mode for 24/7 operation")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Initialize trading engine
|
# Initialize trading engine
|
||||||
trading_engine = TradingEngine(paper_trading=True)
|
trading_engine = TradingEngine(paper_trading=True)
|
||||||
|
|
||||||
|
# Track start time for uptime monitoring
|
||||||
|
start_time = datetime.now()
|
||||||
|
|
||||||
# Main trading loop
|
# Main trading loop
|
||||||
cycle_count = 0
|
cycle_count = 0
|
||||||
last_summary_time = datetime.now()
|
last_summary_time = datetime.now()
|
||||||
|
|
||||||
|
# Initial health check
|
||||||
|
health_check()
|
||||||
|
|
||||||
|
logger.info("📊 Entering main trading loop...")
|
||||||
|
|
||||||
while running:
|
while running:
|
||||||
try:
|
try:
|
||||||
# Run trading cycle
|
# Run trading cycle
|
||||||
@ -250,7 +280,7 @@ def run_live_trading_mode():
|
|||||||
|
|
||||||
# Log cycle results
|
# Log cycle results
|
||||||
if cycle_results['success']:
|
if cycle_results['success']:
|
||||||
logger.info(f"Cycle {cycle_count} completed successfully")
|
logger.info(f"✅ Cycle {cycle_count} completed successfully")
|
||||||
|
|
||||||
if cycle_results['actions_taken']:
|
if cycle_results['actions_taken']:
|
||||||
log_trading_action("trading_cycle", {
|
log_trading_action("trading_cycle", {
|
||||||
@ -260,8 +290,9 @@ def run_live_trading_mode():
|
|||||||
'signals': cycle_results['signals']
|
'signals': cycle_results['signals']
|
||||||
})
|
})
|
||||||
|
|
||||||
# Log performance metrics periodically
|
# Log performance metrics periodically and health check
|
||||||
if datetime.now() - last_summary_time > timedelta(hours=1):
|
if datetime.now() - last_summary_time > timedelta(hours=1):
|
||||||
|
# Performance logging
|
||||||
summary = trading_engine.get_trading_summary()
|
summary = trading_engine.get_trading_summary()
|
||||||
if summary['current_position'] > 0 and summary['entry_price']:
|
if summary['current_position'] > 0 and summary['entry_price']:
|
||||||
current_profit = (cycle_results['current_price'] / summary['entry_price'] - 1) * 100
|
current_profit = (cycle_results['current_price'] / summary['entry_price'] - 1) * 100
|
||||||
@ -270,16 +301,25 @@ def run_live_trading_mode():
|
|||||||
'entry_price': summary['entry_price'],
|
'entry_price': summary['entry_price'],
|
||||||
'current_price': cycle_results['current_price']
|
'current_price': cycle_results['current_price']
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# Health check for Docker monitoring
|
||||||
|
health_check()
|
||||||
last_summary_time = datetime.now()
|
last_summary_time = datetime.now()
|
||||||
|
|
||||||
|
# Docker deployment status
|
||||||
|
logger.info(f"🐳 Docker Status - Cycle: {cycle_count}, Uptime: {datetime.now() - start_time}")
|
||||||
else:
|
else:
|
||||||
logger.error(f"Cycle {cycle_count} failed")
|
logger.error(f"Cycle {cycle_count} failed")
|
||||||
|
|
||||||
# Wait before next cycle (3600 seconds = 1 hour for hourly timeframe)
|
# Wait before next cycle (3600 seconds = 1 hour for hourly timeframe)
|
||||||
sleep_duration = 3600 # 1 hour
|
sleep_duration = 3600 # 1 hour
|
||||||
|
|
||||||
for _ in range(sleep_duration):
|
# Health check every 5 minutes during sleep
|
||||||
|
for i in range(sleep_duration):
|
||||||
if not running:
|
if not running:
|
||||||
break
|
break
|
||||||
|
if i % 300 == 0: # Every 5 minutes
|
||||||
|
health_check()
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
@ -300,12 +340,14 @@ def main():
|
|||||||
|
|
||||||
# Setup command line arguments
|
# Setup command line arguments
|
||||||
parser = argparse.ArgumentParser(description='Automated Trading System')
|
parser = argparse.ArgumentParser(description='Automated Trading System')
|
||||||
parser.add_argument('--mode', choices=['live', 'backtest'], default='backtest',
|
parser.add_argument('--mode', choices=['live', 'paper', 'backtest'], default='backtest',
|
||||||
help='Trading mode: live or backtest (default: backtest)')
|
help='Trading mode: live, paper, or backtest (default: backtest)')
|
||||||
parser.add_argument('--strategy', choices=['conservative', 'enhanced'],
|
parser.add_argument('--strategy', choices=['conservative', 'enhanced'],
|
||||||
default=None, help='Strategy type (default: from config)')
|
default=None, help='Strategy type (default: from config)')
|
||||||
parser.add_argument('--log-level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'],
|
parser.add_argument('--log-level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'],
|
||||||
default='INFO', help='Logging level (default: INFO)')
|
default='INFO', help='Logging level (default: INFO)')
|
||||||
|
parser.add_argument('--days', type=int, default=180,
|
||||||
|
help='Days of historical data for backtesting (default: 180)')
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@ -330,7 +372,9 @@ def main():
|
|||||||
try:
|
try:
|
||||||
if args.mode == 'backtest':
|
if args.mode == 'backtest':
|
||||||
run_backtesting_mode(args.strategy)
|
run_backtesting_mode(args.strategy)
|
||||||
elif args.mode == 'live':
|
elif args.mode in ['live', 'paper']:
|
||||||
|
# Both live and paper use the same function - paper is safer
|
||||||
|
logger.info(f"🛡️ Running in {'PAPER' if args.mode == 'paper' else 'LIVE'} trading mode")
|
||||||
run_live_trading_mode()
|
run_live_trading_mode()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
112
monitor.py
Normal file
112
monitor.py
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Trading System Monitor
|
||||||
|
Real-time monitoring for Docker deployment.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
def monitor_system():
|
||||||
|
"""Monitor the trading system performance"""
|
||||||
|
|
||||||
|
print("🔍 Trading System Monitor - Starting...")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
current_time = datetime.now()
|
||||||
|
print(f"\n📊 System Status - {current_time.strftime('%Y-%m-%d %H:%M:%S')}")
|
||||||
|
print("-" * 40)
|
||||||
|
|
||||||
|
# Check health file
|
||||||
|
health_status = "❌ UNKNOWN"
|
||||||
|
if os.path.exists('/tmp/health'):
|
||||||
|
try:
|
||||||
|
with open('/tmp/health', 'r') as f:
|
||||||
|
health_data = f.read().strip()
|
||||||
|
if health_data.startswith('OK:'):
|
||||||
|
parts = health_data.split(':')
|
||||||
|
health_time = datetime.fromisoformat(parts[1])
|
||||||
|
price = parts[2] if len(parts) > 2 else "N/A"
|
||||||
|
|
||||||
|
# Check if health is recent (within 10 minutes)
|
||||||
|
if current_time - health_time < timedelta(minutes=10):
|
||||||
|
health_status = f"✅ HEALTHY (Price: ${price})"
|
||||||
|
else:
|
||||||
|
health_status = f"⚠️ STALE ({(current_time - health_time).seconds//60}m ago)"
|
||||||
|
else:
|
||||||
|
health_status = "❌ ERROR"
|
||||||
|
except Exception as e:
|
||||||
|
health_status = f"❌ ERROR: {e}"
|
||||||
|
|
||||||
|
print(f"Health Status: {health_status}")
|
||||||
|
|
||||||
|
# Check log files
|
||||||
|
log_files = {
|
||||||
|
'trading.log': 'General',
|
||||||
|
'trades.log': 'Trades',
|
||||||
|
'risk.log': 'Risk Events',
|
||||||
|
'performance.log': 'Performance'
|
||||||
|
}
|
||||||
|
|
||||||
|
for log_file, description in log_files.items():
|
||||||
|
log_path = f'/app/logs/{log_file}'
|
||||||
|
if os.path.exists(log_path):
|
||||||
|
try:
|
||||||
|
stat = os.stat(log_path)
|
||||||
|
size_mb = stat.st_size / (1024 * 1024)
|
||||||
|
mod_time = datetime.fromtimestamp(stat.st_mtime)
|
||||||
|
age_minutes = (current_time - mod_time).seconds // 60
|
||||||
|
|
||||||
|
print(f"{description:12}: {size_mb:.1f}MB, {age_minutes}m ago")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{description:12}: Error - {e}")
|
||||||
|
else:
|
||||||
|
print(f"{description:12}: Not found")
|
||||||
|
|
||||||
|
# Show recent performance if available
|
||||||
|
perf_file = '/app/logs/performance.log'
|
||||||
|
if os.path.exists(perf_file):
|
||||||
|
try:
|
||||||
|
with open(perf_file, 'r') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
if lines:
|
||||||
|
recent_lines = lines[-3:] # Last 3 entries
|
||||||
|
print("\n📈 Recent Performance:")
|
||||||
|
for line in recent_lines:
|
||||||
|
if line.strip():
|
||||||
|
print(f" {line.strip()}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Performance read error: {e}")
|
||||||
|
|
||||||
|
# Show recent trades if available
|
||||||
|
trades_file = '/app/logs/trades.log'
|
||||||
|
if os.path.exists(trades_file):
|
||||||
|
try:
|
||||||
|
with open(trades_file, 'r') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
if lines:
|
||||||
|
recent_trades = [line for line in lines[-5:] if 'BUY' in line or 'SELL' in line]
|
||||||
|
if recent_trades:
|
||||||
|
print("\n💰 Recent Trades:")
|
||||||
|
for trade in recent_trades[-2:]: # Last 2 trades
|
||||||
|
print(f" {trade.strip()}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Trades read error: {e}")
|
||||||
|
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
|
||||||
|
# Wait 5 minutes before next check
|
||||||
|
time.sleep(300)
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n👋 Monitor stopped by user")
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Monitor error: {e}")
|
||||||
|
time.sleep(60) # Wait 1 minute on error
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
monitor_system()
|
||||||
Loading…
x
Reference in New Issue
Block a user