diff --git a/.env b/.env new file mode 100644 index 0000000..8f71b47 --- /dev/null +++ b/.env @@ -0,0 +1,38 @@ +# Database Configuration +POSTGRES_DB=dashboard +POSTGRES_USER=dashboard +POSTGRES_PASSWORD=dashboard123 +POSTGRES_HOST=localhost +POSTGRES_PORT=5432 +DATABASE_URL=postgresql://dashboard:dashboard123@localhost:5432/dashboard + +# Redis Configuration +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_PASSWORD= + +# OKX API Configuration +OKX_API_KEY=your_okx_api_key_here +OKX_SECRET_KEY=your_okx_secret_key_here +OKX_PASSPHRASE=your_okx_passphrase_here +OKX_SANDBOX=true + +# Application Configuration +DEBUG=true +ENVIRONMENT=development +LOG_LEVEL=INFO + +# Dashboard Configuration +DASH_HOST=0.0.0.0 +DASH_PORT=8050 +DASH_DEBUG=true + +# Bot Configuration +MAX_CONCURRENT_BOTS=5 +BOT_UPDATE_INTERVAL=2 # seconds +DEFAULT_VIRTUAL_BALANCE=10000 + +# Data Configuration +MARKET_DATA_SYMBOLS=BTC-USDT,ETH-USDT,LTC-USDT +HISTORICAL_DATA_DAYS=30 +CHART_UPDATE_INTERVAL=2000 # milliseconds \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/README.md b/README.md index 59d50e8..0c278c2 100644 --- a/README.md +++ b/README.md @@ -29,29 +29,27 @@ A simple control dashboard for managing and monitoring multiple cryptocurrency t ### Development Setup -1. **Clone the repository** - ```bash - git clone - cd Dashboard - ``` +*Complete setup workflow* -2. **Install dependencies** - ```bash - uv sync - ``` +``` +python scripts/dev.py setup # Setup environment and dependencies +python scripts/dev.py start # Start Docker services +uv run python tests/test_setup.py # Verify everything works +``` -3. **Start development environment** - ```bash - docker-compose up -d # Start PostgreSQL - ``` +*Development workflow* +``` +python scripts/dev.py dev-server # Start with hot reload (recommended) +python scripts/dev.py run # Start without hot reload +python scripts/dev.py status # Check service status +python scripts/dev.py stop # Stop services +``` -4. **Run the application** - ```bash - uv run python main.py - ``` - -5. **Access the dashboard** - Open your browser to `http://localhost:8050` +*Dependency management* +``` +uv add "new-package>=1.0.0" # Add new dependency +uv sync --dev # Install all dependencies +``` ## Project Structure diff --git a/config/bot_configs/example_bot.json b/config/bot_configs/example_bot.json new file mode 100644 index 0000000..98399b7 --- /dev/null +++ b/config/bot_configs/example_bot.json @@ -0,0 +1,17 @@ +{ + "bot_id": "ema_crossover_01", + "name": "EMA Crossover Strategy", + "strategy": "EMA_Crossover", + "parameters": { + "fast_period": 12, + "slow_period": 26, + "symbol": "BTC-USDT", + "risk_percentage": 0.02, + "stop_loss_percentage": 0.05, + "take_profit_percentage": 0.10 + }, + "virtual_balance": 10000, + "enabled": false, + "created_at": "2024-01-01T00:00:00Z", + "description": "Simple EMA crossover strategy for BTC-USDT trading" +} \ No newline at end of file diff --git a/config/settings.py b/config/settings.py new file mode 100644 index 0000000..7e64d59 --- /dev/null +++ b/config/settings.py @@ -0,0 +1,126 @@ +""" +Configuration settings for the Crypto Trading Bot Dashboard. +""" + +import os +from pathlib import Path +from typing import Optional + +from dotenv import load_dotenv +from pydantic import Field +from pydantic_settings import BaseSettings + + +# Load environment variables from .env file +env_file = Path(__file__).parent.parent / ".env" +if env_file.exists(): + load_dotenv(env_file) + + +class DatabaseSettings(BaseSettings): + """Database configuration settings.""" + + host: str = Field(default="localhost", env="POSTGRES_HOST") + port: int = Field(default=5432, env="POSTGRES_PORT") + database: str = Field(default="dashboard", env="POSTGRES_DB") + user: str = Field(default="dashboard", env="POSTGRES_USER") + password: str = Field(default="dashboard123", env="POSTGRES_PASSWORD") + url: Optional[str] = Field(default=None, env="DATABASE_URL") + + @property + def connection_url(self) -> str: + """Get the database connection URL.""" + if self.url: + return self.url + return f"postgresql://{self.user}:{self.password}@{self.host}:{self.port}/{self.database}" + + +class RedisSettings(BaseSettings): + """Redis configuration settings.""" + + host: str = Field(default="localhost", env="REDIS_HOST") + port: int = Field(default=6379, env="REDIS_PORT") + password: Optional[str] = Field(default=None, env="REDIS_PASSWORD") + + @property + def connection_url(self) -> str: + """Get the Redis connection URL.""" + if self.password: + return f"redis://:{self.password}@{self.host}:{self.port}" + return f"redis://{self.host}:{self.port}" + + +class OKXSettings(BaseSettings): + """OKX API configuration settings.""" + + api_key: str = Field(default="", env="OKX_API_KEY") + secret_key: str = Field(default="", env="OKX_SECRET_KEY") + passphrase: str = Field(default="", env="OKX_PASSPHRASE") + sandbox: bool = Field(default=True, env="OKX_SANDBOX") + + @property + def is_configured(self) -> bool: + """Check if OKX API is properly configured.""" + return bool(self.api_key and self.secret_key and self.passphrase) + + +class DashboardSettings(BaseSettings): + """Dashboard application settings.""" + + host: str = Field(default="0.0.0.0", env="DASH_HOST") + port: int = Field(default=8050, env="DASH_PORT") + debug: bool = Field(default=True, env="DASH_DEBUG") + + +class BotSettings(BaseSettings): + """Bot management settings.""" + + max_concurrent_bots: int = Field(default=5, env="MAX_CONCURRENT_BOTS") + update_interval: int = Field(default=2, env="BOT_UPDATE_INTERVAL") + default_virtual_balance: float = Field(default=10000.0, env="DEFAULT_VIRTUAL_BALANCE") + + +class DataSettings(BaseSettings): + """Data configuration settings.""" + + market_data_symbols: str = Field(default="BTC-USDT,ETH-USDT,LTC-USDT", env="MARKET_DATA_SYMBOLS") + historical_data_days: int = Field(default=30, env="HISTORICAL_DATA_DAYS") + chart_update_interval: int = Field(default=2000, env="CHART_UPDATE_INTERVAL") + + @property + def symbols_list(self) -> list[str]: + """Get the list of trading symbols.""" + return [symbol.strip() for symbol in self.market_data_symbols.split(",")] + + +class AppSettings(BaseSettings): + """Application-wide settings.""" + + debug: bool = Field(default=True, env="DEBUG") + environment: str = Field(default="development", env="ENVIRONMENT") + log_level: str = Field(default="INFO", env="LOG_LEVEL") + + +# Create settings instances +database = DatabaseSettings() +redis = RedisSettings() +okx = OKXSettings() +dashboard = DashboardSettings() +bot = BotSettings() +data = DataSettings() +app = AppSettings() + + +def get_project_root() -> Path: + """Get the project root directory.""" + return Path(__file__).parent.parent + + +def get_config_dir() -> Path: + """Get the configuration directory.""" + return get_project_root() / "config" + + +def get_bot_configs_dir() -> Path: + """Get the bot configurations directory.""" + return get_config_dir() / "bot_configs" \ No newline at end of file diff --git a/database/init/init.sql b/database/init/init.sql new file mode 100644 index 0000000..2934099 --- /dev/null +++ b/database/init/init.sql @@ -0,0 +1,24 @@ +-- Initial database setup for Crypto Trading Bot Dashboard +-- This script runs when PostgreSQL container starts for the first time + +-- Create extensions +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + +-- Set timezone +SET timezone = 'UTC'; + +-- Create initial user with appropriate permissions (if not exists) +DO $$ +BEGIN + IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'dashboard') THEN + CREATE ROLE dashboard WITH LOGIN PASSWORD 'dashboard123'; + END IF; +END +$$; + +-- Grant permissions +GRANT ALL PRIVILEGES ON DATABASE dashboard TO dashboard; +GRANT ALL ON SCHEMA public TO dashboard; + +-- Create initial comment +COMMENT ON DATABASE dashboard IS 'Crypto Trading Bot Dashboard Database'; \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..7f04ab8 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,49 @@ +services: + postgres: + image: postgres:15-alpine + container_name: dashboard_postgres + environment: + POSTGRES_DB: ${POSTGRES_DB:-dashboard} + POSTGRES_USER: ${POSTGRES_USER:-dashboard} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-dashboard123} + POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C" + ports: + - "${POSTGRES_PORT:-5432}:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + - ./database/init:/docker-entrypoint-initdb.d + restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-dashboard}"] + interval: 30s + timeout: 10s + retries: 3 + networks: + - dashboard-network + + redis: + image: redis:7-alpine + container_name: dashboard_redis + command: redis-server --appendonly yes --appendfsync everysec + ports: + - "${REDIS_PORT:-6379}:6379" + volumes: + - redis_data:/data + restart: unless-stopped + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 30s + timeout: 10s + retries: 3 + networks: + - dashboard-network + +volumes: + postgres_data: + driver: local + redis_data: + driver: local + +networks: + dashboard-network: + driver: bridge \ No newline at end of file diff --git a/docs/dependency-management.md b/docs/dependency-management.md new file mode 100644 index 0000000..fe839b6 --- /dev/null +++ b/docs/dependency-management.md @@ -0,0 +1,259 @@ +# Dependency Management Guide + +This guide explains how to manage Python dependencies in the Crypto Trading Bot Dashboard project. + +## Local Development + +### Adding New Dependencies + +#### 1. Core Dependencies (Required for Runtime) + +To add a new core dependency: + +```bash +# Method 1: Add directly to pyproject.toml +# Edit pyproject.toml and add to the dependencies list: +# "new-package>=1.0.0", + +# Method 2: Use UV to add and update pyproject.toml +uv add "new-package>=1.0.0" + +# Sync to install +uv sync +``` + +#### 2. Development Dependencies (Testing, Linting, etc.) + +```bash +# Add development-only dependency +uv add --dev "new-dev-package>=1.0.0" + +# Or edit pyproject.toml under [project.optional-dependencies.dev] +# Then run: +uv sync --dev +``` + +### Installing Dependencies + +```bash +# Install all dependencies +uv sync + +# Install with development dependencies +uv sync --dev + +# Install only production dependencies +uv sync --no-dev +``` + +### Updating Dependencies + +```bash +# Update all dependencies to latest compatible versions +uv sync --upgrade + +# Update specific package +uv sync --upgrade-package "package-name" +``` + +## Docker Environment + +### Current Approach + +The project uses a **volume-based development** approach where: +- Dependencies are installed in the local environment using UV +- Docker containers provide only infrastructure services (PostgreSQL, Redis) +- The Python application runs locally with hot reload + +### Adding Dependencies for Docker-based Development + +If you want to run the entire application in Docker: + +#### 1. Create a Dockerfile + +```dockerfile +FROM python:3.10-slim + +WORKDIR /app + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + postgresql-client \ + curl \ + && rm -rf /var/lib/apt/lists/* + +# Install UV +RUN pip install uv + +# Copy dependency files +COPY pyproject.toml ./ +COPY README.md ./ + +# Install dependencies +RUN uv sync --no-dev + +# Copy application code +COPY . . + +# Expose port +EXPOSE 8050 + +# Run application +CMD ["uv", "run", "python", "main.py"] +``` + +#### 2. Add Application Service to docker-compose.yml + +```yaml +services: + app: + build: . + container_name: dashboard_app + ports: + - "8050:8050" + volumes: + - .:/app + - uv_cache:/root/.cache/uv + environment: + - DATABASE_URL=postgresql://dashboard:dashboard123@postgres:5432/dashboard + - REDIS_URL=redis://redis:6379 + depends_on: + - postgres + - redis + networks: + - dashboard-network + restart: unless-stopped + +volumes: + uv_cache: +``` + +#### 3. Development Workflow with Docker + +```bash +# Build and start all services +docker-compose up --build + +# Add new dependency +# 1. Edit pyproject.toml +# 2. Rebuild container +docker-compose build app +docker-compose up -d app + +# Or use dev dependencies mount +# Mount local UV cache for faster rebuilds +``` + +## Hot Reload Development + +### Method 1: Local Development (Recommended) + +Run services in Docker, application locally with hot reload: + +```bash +# Start infrastructure +python scripts/dev.py start + +# Run app with hot reload +uv run python scripts/dev.py dev-server +``` + +### Method 2: Docker with Volume Mounts + +If using Docker for the app, mount source code: + +```yaml +volumes: + - .:/app # Mount source code + - /app/__pycache__ # Exclude cache +``` + +## Best Practices + +### 1. Version Pinning + +```toml +# Good: Specify minimum version with compatibility +"requests>=2.31.0,<3.0.0" + +# Acceptable: Major version constraint +"pandas>=2.1.0" + +# Avoid: Exact pinning (except for critical deps) +"somepackage==1.2.3" # Only if necessary +``` + +### 2. Dependency Categories + +```toml +[project] +dependencies = [ + # Core web framework + "dash>=2.14.0", + + # Database + "sqlalchemy>=2.0.0", + "psycopg2-binary>=2.9.0", + + # ... group related dependencies +] +``` + +### 3. Security Updates + +```bash +# Check for security vulnerabilities +pip-audit + +# Update specific vulnerable package +uv sync --upgrade-package "vulnerable-package" +``` + +## Troubleshooting + +### Common Issues + +1. **Dependency Conflicts** + ```bash + # Clear UV cache and reinstall + uv cache clean + uv sync --refresh + ``` + +2. **PostgreSQL Connection Issues** + ```bash + # Ensure psycopg2-binary is installed + uv add "psycopg2-binary>=2.9.0" + ``` + +3. **Docker Build Failures** + ```bash + # Clean docker build cache + docker system prune --volumes + docker-compose build --no-cache + ``` + +### Debugging Dependencies + +```bash +# Show installed packages +uv pip list + +# Show dependency tree +uv pip show + +# Check for conflicts +uv pip check +``` + +## Migration from requirements.txt + +If you have an existing `requirements.txt`: + +```bash +# Convert to pyproject.toml +uv add -r requirements.txt + +# Or manually copy dependencies to pyproject.toml +# Then remove requirements.txt +``` \ No newline at end of file diff --git a/docs/setup.md b/docs/setup.md new file mode 100644 index 0000000..af62f39 --- /dev/null +++ b/docs/setup.md @@ -0,0 +1,282 @@ +# Development Environment Setup + +This guide will help you set up the Crypto Trading Bot Dashboard development environment. + +## Prerequisites + +- Python 3.10+ +- Docker Desktop (for Windows/Mac) or Docker Engine (for Linux) +- UV package manager +- Git + +## Quick Start + +### 1. Initial Setup + +```bash +# Install dependencies (including dev tools) +uv sync --dev + +# Set up environment and start services +python scripts/dev.py setup +``` + +### 2. Start Services + +```bash +# Start PostgreSQL and Redis services +python scripts/dev.py start +``` + +### 3. Configure API Keys + +Copy `env.template` to `.env` and update the OKX API credentials: + +```bash +# Copy template (Windows) +copy env.template .env + +# Copy template (Unix) +cp env.template .env + +# Edit .env file with your actual OKX API credentials +# OKX_API_KEY=your_actual_api_key +# OKX_SECRET_KEY=your_actual_secret_key +# OKX_PASSPHRASE=your_actual_passphrase +``` + +### 4. Verify Setup + +```bash +# Run setup verification tests +uv run python tests/test_setup.py +``` + +### 5. Start Dashboard with Hot Reload + +```bash +# Start with hot reload (recommended for development) +python scripts/dev.py dev-server + +# Or start without hot reload +python scripts/dev.py run +``` + +## Development Commands + +### Using the dev.py script: + +```bash +# Show available commands +python scripts/dev.py + +# Set up environment and install dependencies +python scripts/dev.py setup + +# Start all services (Docker) +python scripts/dev.py start + +# Stop all services +python scripts/dev.py stop + +# Restart services +python scripts/dev.py restart + +# Check service status +python scripts/dev.py status + +# Install/update dependencies +python scripts/dev.py install + +# Run development server with hot reload +python scripts/dev.py dev-server + +# Run application without hot reload +python scripts/dev.py run +``` + +### Direct Docker commands: + +```bash +# Start services in background +docker-compose up -d + +# View service logs +docker-compose logs -f + +# Stop services +docker-compose down + +# Rebuild and restart +docker-compose up -d --build +``` + +## Hot Reload Development + +The development server includes hot reload functionality that automatically restarts the application when Python files change. + +### Features: +- ๐Ÿ”ฅ **Auto-restart** on file changes +- ๐Ÿ‘€ **Watches multiple directories** (config, database, components, etc.) +- ๐Ÿš€ **Fast restart** with debouncing (1-second delay) +- ๐Ÿ›‘ **Graceful shutdown** with Ctrl+C + +### Usage: +```bash +# Start hot reload server +python scripts/dev.py dev-server + +# The server will watch these directories: +# - . (root) +# - config/ +# - database/ +# - components/ +# - data/ +# - strategies/ +# - trader/ +``` + +## Data Persistence + +### Database Persistence +โœ… **PostgreSQL data persists** across container restarts +- Volume: `postgres_data` mounted to `/var/lib/postgresql/data` +- Data survives `docker-compose down` and `docker-compose up` + +### Redis Persistence +โœ… **Redis data persists** with AOF (Append-Only File) +- Volume: `redis_data` mounted to `/data` +- AOF sync every second for durability +- Data survives container restarts + +### Removing Persistent Data +```bash +# Stop services and remove volumes (CAUTION: This deletes all data) +docker-compose down -v + +# Or remove specific volumes +docker volume rm dashboard_postgres_data +docker volume rm dashboard_redis_data +``` + +## Dependency Management + +### Adding New Dependencies + +```bash +# Add runtime dependency +uv add "package-name>=1.0.0" + +# Add development dependency +uv add --dev "dev-package>=1.0.0" + +# Install all dependencies +uv sync --dev +``` + +### Key Dependencies Included: +- **Web Framework**: Dash, Plotly +- **Database**: SQLAlchemy, psycopg2-binary, Alembic +- **Data Processing**: pandas, numpy +- **Configuration**: pydantic, python-dotenv +- **Development**: watchdog (hot reload), pytest, black, mypy + +See `docs/dependency-management.md` for detailed dependency management guide. + +## Directory Structure + +``` +Dashboard/ +โ”œโ”€โ”€ config/ # Configuration files +โ”‚ โ”œโ”€โ”€ settings.py # Application settings +โ”‚ โ””โ”€โ”€ bot_configs/ # Bot configuration files +โ”œโ”€โ”€ database/ # Database related files +โ”‚ โ””โ”€โ”€ init/ # Database initialization scripts +โ”œโ”€โ”€ scripts/ # Development scripts +โ”‚ โ”œโ”€โ”€ dev.py # Main development script +โ”‚ โ”œโ”€โ”€ setup.sh # Setup script (Unix) +โ”‚ โ”œโ”€โ”€ start.sh # Start script (Unix) +โ”‚ โ””โ”€โ”€ stop.sh # Stop script (Unix) +โ”œโ”€โ”€ tests/ # Test files +โ”‚ โ””โ”€โ”€ test_setup.py # Setup verification tests +โ”œโ”€โ”€ docs/ # Documentation +โ”‚ โ”œโ”€โ”€ setup.md # This file +โ”‚ โ””โ”€โ”€ dependency-management.md # Dependency guide +โ”œโ”€โ”€ docker-compose.yml # Docker services configuration +โ”œโ”€โ”€ env.template # Environment variables template +โ”œโ”€โ”€ pyproject.toml # Dependencies and project config +โ””โ”€โ”€ main.py # Main application entry point +``` + +## Services + +### PostgreSQL Database +- **Host**: localhost:5432 +- **Database**: dashboard +- **User**: dashboard +- **Password**: dashboard123 (development only) +- **Persistence**: โœ… Data persists across restarts + +### Redis Cache +- **Host**: localhost:6379 +- **No password** (development only) +- **Persistence**: โœ… AOF enabled, data persists across restarts + +## Environment Variables + +Key environment variables (see `env.template` for full list): + +- `DATABASE_URL` - PostgreSQL connection string +- `OKX_API_KEY` - OKX API key +- `OKX_SECRET_KEY` - OKX secret key +- `OKX_PASSPHRASE` - OKX passphrase +- `OKX_SANDBOX` - Use OKX sandbox (true/false) +- `DEBUG` - Enable debug mode +- `LOG_LEVEL` - Logging level (DEBUG, INFO, WARNING, ERROR) + +## Troubleshooting + +### Docker Issues + +1. **Docker not running**: Start Docker Desktop/Engine +2. **Port conflicts**: Check if ports 5432 or 6379 are already in use +3. **Permission issues**: On Linux, add your user to the docker group +4. **Data persistence issues**: Check if volumes are properly mounted + +### Database Connection Issues + +1. **Connection refused**: Ensure PostgreSQL container is running +2. **Authentication failed**: Check credentials in `.env` file +3. **Database doesn't exist**: Run the setup script again +4. **Data loss**: Check if volume is mounted correctly + +### Dependency Issues + +1. **Import errors**: Run `uv sync --dev` to install dependencies +2. **Version conflicts**: Check `pyproject.toml` for compatibility +3. **Hot reload not working**: Ensure `watchdog` is installed + +### Hot Reload Issues + +1. **Changes not detected**: Check if files are in watched directories +2. **Rapid restarts**: Built-in 1-second debouncing should prevent this +3. **Process not stopping**: Use Ctrl+C to gracefully shutdown + +## Performance Tips + +1. **Use SSD**: Store Docker volumes on SSD for better database performance +2. **Increase Docker memory**: Allocate more RAM to Docker Desktop +3. **Hot reload**: Use `dev-server` for faster development cycles +4. **Dependency caching**: UV caches dependencies for faster installs + +## Next Steps + +After successful setup: + +1. **Phase 1.0**: Database Infrastructure Setup +2. **Phase 2.0**: Bot Management System Development +3. **Phase 3.0**: OKX Integration and Data Pipeline +4. **Phase 4.0**: Dashboard UI and Visualization +5. **Phase 5.0**: Backtesting System Implementation + +See `tasks/tasks-prd-crypto-bot-dashboard.md` for detailed task list. \ No newline at end of file diff --git a/env.template b/env.template new file mode 100644 index 0000000..8f71b47 --- /dev/null +++ b/env.template @@ -0,0 +1,38 @@ +# Database Configuration +POSTGRES_DB=dashboard +POSTGRES_USER=dashboard +POSTGRES_PASSWORD=dashboard123 +POSTGRES_HOST=localhost +POSTGRES_PORT=5432 +DATABASE_URL=postgresql://dashboard:dashboard123@localhost:5432/dashboard + +# Redis Configuration +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_PASSWORD= + +# OKX API Configuration +OKX_API_KEY=your_okx_api_key_here +OKX_SECRET_KEY=your_okx_secret_key_here +OKX_PASSPHRASE=your_okx_passphrase_here +OKX_SANDBOX=true + +# Application Configuration +DEBUG=true +ENVIRONMENT=development +LOG_LEVEL=INFO + +# Dashboard Configuration +DASH_HOST=0.0.0.0 +DASH_PORT=8050 +DASH_DEBUG=true + +# Bot Configuration +MAX_CONCURRENT_BOTS=5 +BOT_UPDATE_INTERVAL=2 # seconds +DEFAULT_VIRTUAL_BALANCE=10000 + +# Data Configuration +MARKET_DATA_SYMBOLS=BTC-USDT,ETH-USDT,LTC-USDT +HISTORICAL_DATA_DAYS=30 +CHART_UPDATE_INTERVAL=2000 # milliseconds \ No newline at end of file diff --git a/main.py b/main.py index 29d90cb..33e73c4 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,45 @@ +#!/usr/bin/env python3 +""" +Main entry point for the Crypto Trading Bot Dashboard. +""" + +import sys +from pathlib import Path + +# Add project root to path +project_root = Path(__file__).parent +sys.path.insert(0, str(project_root)) + + def main(): - print("Hello from dashboard!") + """Main application entry point.""" + print("๐Ÿš€ Crypto Trading Bot Dashboard") + print("=" * 40) + + try: + from config.settings import app, dashboard + print(f"Environment: {app.environment}") + print(f"Debug mode: {app.debug}") + + if app.environment == "development": + print("\n๐Ÿ”ง Running in development mode") + print("To start the full application:") + print("1. Run: python scripts/dev.py setup") + print("2. Run: python scripts/dev.py start") + print("3. Update .env with your OKX API credentials") + print("4. Run: uv run python tests/test_setup.py") + + # TODO: Start the Dash application when ready + # from app import create_app + # app = create_app() + # app.run(host=dashboard.host, port=dashboard.port, debug=dashboard.debug) + + print(f"\n๐Ÿ“ Next: Implement Phase 1.0 - Database Infrastructure Setup") + + except ImportError as e: + print(f"โŒ Failed to import modules: {e}") + print("Run: uv sync") + sys.exit(1) if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml index 9d424b5..672a462 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,70 @@ [project] name = "dashboard" version = "0.1.0" -description = "Add your description here" +description = "Crypto Trading Bot Dashboard - A control dashboard for managing and monitoring multiple cryptocurrency trading bots" readme = "README.md" requires-python = ">=3.10" -dependencies = [] +dependencies = [ + # Core web framework + "dash>=2.14.0", + "plotly>=5.17.0", + # Database + "sqlalchemy>=2.0.0", + "psycopg2-binary>=2.9.0", + "alembic>=1.12.0", + # HTTP and WebSocket clients + "requests>=2.31.0", + "websocket-client>=1.6.0", + "aiohttp>=3.8.0", + # Data processing + "pandas>=2.1.0", + "numpy>=1.24.0", + # Configuration and environment + "python-dotenv>=1.0.0", + "pydantic>=2.4.0", + "pydantic-settings>=2.1.0", + # Caching + "redis>=4.6.0", + # Utilities + "python-dateutil>=2.8.0", + "pytz>=2023.3", + # Logging + "structlog>=23.1.0", + # Development tools + "watchdog>=3.0.0", # For file watching and hot reload + "click>=8.0.0", # For CLI commands +] + +[project.optional-dependencies] +dev = [ + "pytest>=7.4.0", + "pytest-asyncio>=0.21.0", + "pytest-cov>=4.1.0", + "black>=23.0.0", + "isort>=5.12.0", + "flake8>=6.0.0", + "mypy>=1.5.0", + "pre-commit>=3.5.0", # For git hooks + "pytest-mock>=3.12.0", # For mocking in tests +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["config", "database", "scripts", "tests"] + +[tool.black] +line-length = 88 +target-version = ['py310'] + +[tool.isort] +profile = "black" +line_length = 88 + +[tool.mypy] +python_version = "3.10" +warn_return_any = true +warn_unused_configs = true +disallow_untyped_defs = true diff --git a/scripts/dev.py b/scripts/dev.py new file mode 100644 index 0000000..c659db1 --- /dev/null +++ b/scripts/dev.py @@ -0,0 +1,282 @@ +#!/usr/bin/env python3 +""" +Development environment management script for the Crypto Trading Bot Dashboard. +""" + +import subprocess +import sys +import time +import os +from pathlib import Path + + +def run_command(command: str, check: bool = True) -> subprocess.CompletedProcess: + """Run a shell command and return the result.""" + print(f"Running: {command}") + return subprocess.run(command, shell=True, check=check) + + +def check_docker(): + """Check if Docker is installed and running.""" + try: + result = subprocess.run( + "docker --version", shell=True, capture_output=True, text=True + ) + if result.returncode != 0: + print("โŒ Docker is not installed or not in PATH") + return False + + result = subprocess.run( + "docker info", shell=True, capture_output=True, text=True + ) + if result.returncode != 0: + print("โŒ Docker daemon is not running") + return False + + print("โœ… Docker is available and running") + return True + except Exception as e: + print(f"โŒ Error checking Docker: {e}") + return False + + +def start_services(): + """Start all development services.""" + print("๐Ÿš€ Starting development environment...") + + if not check_docker(): + sys.exit(1) + + # Start Docker services + run_command("docker-compose up -d") + + # Wait for services to be ready + print("โณ Waiting for services to be ready...") + time.sleep(10) + + # Check service health + check_services() + + +def stop_services(): + """Stop all development services.""" + print("๐Ÿ›‘ Stopping development environment...") + run_command("docker-compose down") + + +def restart_services(): + """Restart all development services.""" + print("๐Ÿ”„ Restarting development environment...") + stop_services() + start_services() + + +def check_services(): + """Check the status of all services.""" + print("๐Ÿ” Checking service status...") + + # Check Docker containers + result = subprocess.run( + "docker-compose ps", shell=True, capture_output=True, text=True + ) + print(result.stdout) + + # Check database connection + check_database() + + +def check_database(): + """Check if the database is accessible.""" + try: + import psycopg2 + from dotenv import load_dotenv + + # Load environment variables + env_file = Path(".env") + if env_file.exists(): + load_dotenv(env_file) + + conn_params = { + "host": os.getenv("POSTGRES_HOST", "localhost"), + "port": os.getenv("POSTGRES_PORT", "5432"), + "database": os.getenv("POSTGRES_DB", "dashboard"), + "user": os.getenv("POSTGRES_USER", "dashboard"), + "password": os.getenv("POSTGRES_PASSWORD", "dashboard123"), + } + + conn = psycopg2.connect(**conn_params) + conn.close() + print("โœ… Database connection successful") + + except ImportError: + print("โš ๏ธ psycopg2 not installed, skipping database check") + except Exception as e: + print(f"โŒ Database connection failed: {e}") + + +def setup_env(): + """Set up environment files.""" + print("๐Ÿ“ Setting up environment...") + + env_file = Path(".env") + template_file = Path("env.template") + + if not env_file.exists() and template_file.exists(): + import shutil + shutil.copy(template_file, env_file) + print(f"โœ… Created .env file from template") + print(f"โš ๏ธ Please update .env with your actual configuration") + elif env_file.exists(): + print("โœ… .env file already exists") + else: + print("โŒ No env.template file found") + + +def install_deps(): + """Install project dependencies using UV.""" + print("๐Ÿ“ฆ Installing dependencies...") + run_command("uv sync --dev") + + +def run_dev_server(): + """Run the development server with hot reload.""" + print("๐Ÿ”ฅ Starting development server with hot reload...") + + try: + from watchdog.observers import Observer + from watchdog.events import FileSystemEventHandler + import threading + import signal + + class ChangeHandler(FileSystemEventHandler): + def __init__(self, restart_callback): + self.restart_callback = restart_callback + self.last_modified = 0 + + def on_modified(self, event): + if event.is_directory: + return + + # Only watch Python files + if not event.src_path.endswith('.py'): + return + + # Avoid rapid restarts + current_time = time.time() + if current_time - self.last_modified < 1: + return + + self.last_modified = current_time + print(f"\n๐Ÿ“ File changed: {event.src_path}") + self.restart_callback() + + # Application process + app_process = None + + def start_app(): + nonlocal app_process + if app_process: + app_process.terminate() + app_process.wait() + + print("๐Ÿš€ Starting application...") + app_process = subprocess.Popen([ + sys.executable, "main.py" + ]) + + def restart_app(): + print("๐Ÿ”„ Restarting application...") + start_app() + + # Set up file watcher + event_handler = ChangeHandler(restart_app) + observer = Observer() + + # Watch main directories + watch_dirs = [".", "config", "database", "components", "data", "strategies", "trader"] + for watch_dir in watch_dirs: + if Path(watch_dir).exists(): + observer.schedule(event_handler, watch_dir, recursive=True) + print(f"๐Ÿ‘€ Watching: {watch_dir}/") + + # Start watching + observer.start() + + # Start initial app + start_app() + + def signal_handler(signum, frame): + print("\n๐Ÿ›‘ Shutting down development server...") + observer.stop() + if app_process: + app_process.terminate() + app_process.wait() + sys.exit(0) + + signal.signal(signal.SIGINT, signal_handler) + + print("๐Ÿ”ฅ Development server running with hot reload") + print("๐Ÿ“ Watching for changes in Python files...") + print("Press Ctrl+C to stop") + + # Keep the main thread alive + observer.join() + + except ImportError: + print("โŒ watchdog not installed, running without hot reload") + print("Install with: uv add watchdog") + # Fallback to regular execution + run_command("uv run python main.py") + except KeyboardInterrupt: + print("\n๐Ÿ›‘ Development server stopped") + except Exception as e: + print(f"โŒ Error running dev server: {e}") + + +def run_app(): + """Run the application without hot reload.""" + print("๐Ÿš€ Starting application...") + run_command("uv run python main.py") + + +def main(): + """Main entry point for the development script.""" + if len(sys.argv) < 2: + print("Usage: python scripts/dev.py [command]") + print("Commands:") + print(" setup - Set up environment and install dependencies") + print(" start - Start development services (Docker)") + print(" stop - Stop development services") + print(" restart - Restart development services") + print(" status - Check service status") + print(" install - Install dependencies") + print(" dev-server - Run development server with hot reload") + print(" run - Run application without hot reload") + sys.exit(1) + + command = sys.argv[1] + + if command == "setup": + setup_env() + install_deps() + elif command == "start": + start_services() + elif command == "stop": + stop_services() + elif command == "restart": + restart_services() + elif command == "status": + check_services() + elif command == "install": + install_deps() + elif command == "dev-server": + run_dev_server() + elif command == "run": + run_app() + else: + print(f"Unknown command: {command}") + sys.exit(1) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/scripts/setup.sh b/scripts/setup.sh new file mode 100644 index 0000000..8d9308c --- /dev/null +++ b/scripts/setup.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# Initial setup for development environment + +echo "Setting up Crypto Trading Bot Dashboard development environment..." +python scripts/dev.py setup + +echo "" +echo "Setup complete! Next steps:" +echo "1. Update .env file with your actual OKX API credentials" +echo "2. Run 'python scripts/dev.py start' to start services" +echo "3. Run 'uv run python main.py' to start the dashboard" \ No newline at end of file diff --git a/scripts/start.sh b/scripts/start.sh new file mode 100644 index 0000000..25f0405 --- /dev/null +++ b/scripts/start.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# Start development environment + +echo "Starting Crypto Trading Bot Dashboard development environment..." +python scripts/dev.py start \ No newline at end of file diff --git a/scripts/stop.sh b/scripts/stop.sh new file mode 100644 index 0000000..d8da043 --- /dev/null +++ b/scripts/stop.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# Stop development environment + +echo "Stopping Crypto Trading Bot Dashboard development environment..." +python scripts/dev.py stop \ No newline at end of file diff --git a/tests/test_setup.py b/tests/test_setup.py new file mode 100644 index 0000000..4bbd03c --- /dev/null +++ b/tests/test_setup.py @@ -0,0 +1,201 @@ +""" +Test script to verify the development environment setup. +""" + +import sys +import time +from pathlib import Path + +# Add the project root to Python path +project_root = Path(__file__).parent.parent +sys.path.insert(0, str(project_root)) + +try: + from config.settings import database, redis, app, okx, dashboard + print("โœ… Configuration module loaded successfully") +except ImportError as e: + print(f"โŒ Failed to load configuration: {e}") + sys.exit(1) + + +def test_database_connection(): + """Test database connection.""" + print("\n๐Ÿ” Testing database connection...") + + try: + import psycopg2 + from psycopg2 import sql + + conn_params = { + "host": database.host, + "port": database.port, + "database": database.database, + "user": database.user, + "password": database.password, + } + + print(f"Connecting to: {database.host}:{database.port}/{database.database}") + + conn = psycopg2.connect(**conn_params) + cursor = conn.cursor() + + # Test basic query + cursor.execute("SELECT version();") + version = cursor.fetchone()[0] + print(f"โœ… Database connected successfully") + print(f" PostgreSQL version: {version}") + + # Test if we can create tables + cursor.execute(""" + CREATE TABLE IF NOT EXISTS test_table ( + id SERIAL PRIMARY KEY, + name VARCHAR(100), + created_at TIMESTAMP DEFAULT NOW() + ); + """) + + cursor.execute("INSERT INTO test_table (name) VALUES ('test_setup');") + conn.commit() + + cursor.execute("SELECT COUNT(*) FROM test_table;") + count = cursor.fetchone()[0] + print(f"โœ… Database operations successful (test records: {count})") + + # Clean up test table + cursor.execute("DROP TABLE IF EXISTS test_table;") + conn.commit() + + cursor.close() + conn.close() + + except ImportError: + print("โŒ psycopg2 not installed, run: uv sync") + return False + except Exception as e: + print(f"โŒ Database connection failed: {e}") + return False + + return True + + +def test_redis_connection(): + """Test Redis connection.""" + print("\n๐Ÿ” Testing Redis connection...") + + try: + import redis as redis_module + + r = redis_module.Redis( + host=redis.host, + port=redis.port, + password=redis.password, + decode_responses=True + ) + + # Test basic operations + r.set("test_key", "test_value") + value = r.get("test_key") + + if value == "test_value": + print("โœ… Redis connected successfully") + print(f" Connected to: {redis.host}:{redis.port}") + + # Clean up + r.delete("test_key") + return True + else: + print("โŒ Redis test failed") + return False + + except ImportError: + print("โŒ redis not installed, run: uv sync") + return False + except Exception as e: + print(f"โŒ Redis connection failed: {e}") + return False + + +def test_configuration(): + """Test configuration loading.""" + print("\n๐Ÿ” Testing configuration...") + + print(f"Database URL: {database.connection_url}") + print(f"Redis URL: {redis.connection_url}") + print(f"Dashboard: {dashboard.host}:{dashboard.port}") + print(f"Environment: {app.environment}") + print(f"OKX configured: {okx.is_configured}") + + if not okx.is_configured: + print("โš ๏ธ OKX API not configured (update .env file)") + + return True + + +def test_directories(): + """Test required directories exist.""" + print("\n๐Ÿ” Testing directory structure...") + + required_dirs = [ + "config", + "config/bot_configs", + "database", + "scripts", + "tests", + ] + + all_exist = True + for dir_name in required_dirs: + dir_path = project_root / dir_name + if dir_path.exists(): + print(f"โœ… {dir_name}/ exists") + else: + print(f"โŒ {dir_name}/ missing") + all_exist = False + + return all_exist + + +def main(): + """Run all tests.""" + print("๐Ÿงช Running setup verification tests...") + print(f"Project root: {project_root}") + + tests = [ + ("Configuration", test_configuration), + ("Directories", test_directories), + ("Database", test_database_connection), + ("Redis", test_redis_connection), + ] + + results = [] + + for test_name, test_func in tests: + try: + result = test_func() + results.append((test_name, result)) + except Exception as e: + print(f"โŒ {test_name} test crashed: {e}") + results.append((test_name, False)) + + print("\n๐Ÿ“Š Test Results:") + print("=" * 40) + + all_passed = True + for test_name, passed in results: + status = "โœ… PASS" if passed else "โŒ FAIL" + print(f"{test_name:15} {status}") + if not passed: + all_passed = False + + print("=" * 40) + + if all_passed: + print("๐ŸŽ‰ All tests passed! Environment is ready.") + return 0 + else: + print("โš ๏ธ Some tests failed. Check the setup.") + return 1 + + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file