# API Reference Source: https://condor.hummingbot.org/api-reference Complete REST API reference for Hummingbot API ## What is Hummingbot API? **Hummingbot API** is a RESTful API server that provides programmatic access to all [Hummingbot](https://hummingbot.org) exchange connectors and trading strategies. It serves as the deterministic execution layer for Condor, handling: * **Data collection**: Standardized access to order books, candles, balances, and positions across 50+ exchanges * **Market access**: Connectors to spot, perpetual, and AMM exchanges, along with Solana and EVM networks * **Trade execution**: Place orders, manage positions, and track fills * **Bot management**: Deploy and manage containerized bots for long-running strategies ## Base URL ``` http://localhost:8000 ``` For remote deployments, replace `localhost` with your server's address. ## Authentication All endpoints require HTTP Basic Authentication: ```bash theme={null} curl -u YOUR_USERNAME:YOUR_PASSWORD http://localhost:8000/accounts/ ``` The username and password are the `USERNAME` and `PASSWORD` you set when you installed Hummingbot API — they are stored in that install's `.env`, and in Condor's `config.yml` under `servers:`. There are **no default credentials**: setup requires you to choose them, and an API left without them answers `401`. ## API Routers The tables below are an oriented tour of the API's routers, with the endpoints you will reach for first. The sidebar has the complete, playground-enabled reference for every endpoint, generated from the API's own OpenAPI spec. ### Accounts Manage trading accounts, exchange credentials, and Gateway wallets. | Endpoint | Method | Description | | ------------------------------------------------------------- | ------ | ----------------------------------------- | | `/accounts/` | GET | List all account names | | `/accounts/{account_name}/credentials` | GET | List configured connectors for an account | | `/accounts/add-account` | POST | Create a new account | | `/accounts/delete-account` | POST | Delete an account | | `/accounts/add-credential/{account_name}/{connector_name}` | POST | Add exchange credentials | | `/accounts/delete-credential/{account_name}/{connector_name}` | POST | Remove exchange credentials | | `/accounts/gateway/add-wallet` | POST | Add a Gateway (DEX) wallet | | `/accounts/gateway/wallets` | GET | List Gateway wallets | ### Portfolio Real-time balances and portfolio analytics. | Endpoint | Method | Description | | ---------------------------------- | ------ | --------------------------------------------------------- | | `/portfolio/state` | POST | Get portfolio balances across all accounts and connectors | | `/portfolio/history` | POST | Get historical portfolio states | | `/portfolio/distribution` | POST | Token distribution across accounts | | `/portfolio/accounts-distribution` | GET | Value distribution by account | ### Trading Execute orders and manage positions. | Endpoint | Method | Description | | -------------------------------------------------------------------------- | --------- | ------------------------------ | | `/trading/orders` | POST | Place a new order | | `/trading/orders/active` | POST | Get active orders | | `/trading/orders/search` | POST | Search order history | | `/trading/trades` | POST | Search trade fills | | `/trading/{account_name}/{connector_name}/orders/{client_order_id}/cancel` | POST | Cancel an order | | `/trading/positions` | POST | Get open positions (perpetual) | | `/trading/{account_name}/{connector_name}/leverage` | POST | Set leverage | | `/trading/{account_name}/{connector_name}/position-mode` | GET, POST | Get or set position mode | | `/trading/funding-payments` | POST | Get funding payment history | ### Connectors Exchange connector information and trading rules. | Endpoint | Method | Description | | -------------------------------------------- | ------ | ------------------------------ | | `/connectors/` | GET | List available connectors | | `/connectors/{connector_name}/config-map` | GET | Get required credential fields | | `/connectors/{connector_name}/trading-rules` | GET | Get trading rules for pairs | | `/connectors/{connector_name}/order-types` | GET | Get supported order types | ### Market Data Prices, candles, order books, tickers, and conversion rates (the former Rate Oracle endpoints live here now). | Endpoint | Method | Description | | ---------------------------------- | ------ | ---------------------------------- | | `/market-data/candles` | POST | Get real-time candles | | `/market-data/historical-candles` | POST | Get historical candles | | `/market-data/order-book` | POST | Get order book snapshot | | `/market-data/prices` | POST | Get current prices | | `/market-data/tickers` | GET | Get 24h ticker stats | | `/market-data/funding-info` | POST | Get perpetual funding info | | `/market-data/rate/{trading_pair}` | GET | Get conversion rate between assets | | `/market-data/rates` | POST | Get conversion rates in bulk | | `/market-data/pool-prices` | GET | Get DEX pool prices | ### Executors Create and manage trading executors — single operations like an order, a TWAP, a grid, or an LP position. | Endpoint | Method | Description | | ------------------------------- | ------ | ---------------------------------- | | `/executors/` | POST | Create a new executor | | `/executors/search` | POST | List and filter executors | | `/executors/{executor_id}` | GET | Get executor details | | `/executors/{executor_id}/stop` | POST | Stop an executor | | `/executors/{executor_id}/logs` | GET | Get executor logs | | `/executors/performance` | GET | Aggregate executor performance | | `/executors/positions/summary` | GET | Summary of executor-held positions | | `/executors/types/available` | GET | List available executor types | ### Bot Orchestration Deploy and manage containerized Hummingbot trading bots. | Endpoint | Method | Description | | ---------------------------------------------------- | ------ | ----------------------------------- | | `/bot-orchestration/status` | GET | Get status of all bots | | `/bot-orchestration/deploy-v2-controllers` | POST | Deploy a bot running V2 controllers | | `/bot-orchestration/deploy-v2-script` | POST | Deploy a bot running a V2 script | | `/bot-orchestration/start-bot` | POST | Start a bot | | `/bot-orchestration/stop-bot` | POST | Stop a bot | | `/bot-orchestration/stop-and-archive-bot/{bot_name}` | POST | Stop a bot and archive its database | | `/bot-orchestration/{bot_name}/status` | GET | Get one bot's status | | `/bot-orchestration/bot-runs` | GET | List bot runs | | `/bot-orchestration/controller-performance-latest` | GET | Latest per-controller performance | ### Controllers V2 strategy controller management. | Endpoint | Method | Description | | ------------------------------------------------------------------ | ----------------- | ---------------------------------- | | `/controllers/` | GET | List available controllers | | `/controllers/{controller_type}/{controller_name}` | GET, POST, DELETE | Read, save, or delete a controller | | `/controllers/configs/` | GET | List controller configs | | `/controllers/configs/{config_name}` | GET, POST, DELETE | Read, save, or delete a config | | `/controllers/{controller_type}/{controller_name}/config/template` | GET | Get a controller's config template | ### Scripts V1 script management. | Endpoint | Method | Description | | ---------------------------------------- | ----------------- | ------------------------------ | | `/scripts/` | GET | List available scripts | | `/scripts/{script_name}` | GET, POST, DELETE | Read, save, or delete a script | | `/scripts/configs/` | GET | List script configs | | `/scripts/{script_name}/config/template` | GET | Get a script's config template | ### Gateway DEX infrastructure via Hummingbot Gateway: lifecycle, connectors, and network configuration (tokens and pools per network). | Endpoint | Method | Description | | ------------------------------------------------------- | --------- | --------------------------------- | | `/gateway/status` | GET | Get Gateway status | | `/gateway/start` / `/gateway/stop` / `/gateway/restart` | POST | Manage the Gateway container | | `/gateway/connectors` | GET | List Gateway connectors | | `/gateway/networks` | GET | List supported networks | | `/gateway/networks/{network_id}` | GET, POST | Read or update a network's config | | `/gateway/networks/{network_id}/tokens` | GET, POST | List or add tokens on a network | | `/gateway/networks/{network_id}/pools` | GET, POST | List or add pools on a network | | `/gateway/logs` | GET | Get Gateway logs | ### Gateway Swap DEX token swaps. | Endpoint | Method | Description | | ------------------------------------------ | ------ | ---------------------------------- | | `/gateway/swap/quote` | POST | Get swap quote | | `/gateway/swap/execute` | POST | Execute a swap | | `/gateway/swap/execute-quote` | POST | Execute a previously fetched quote | | `/gateway/swaps/search` | POST | Search past swaps | | `/gateway/swaps/{transaction_hash}/status` | GET | Get a swap's transaction status | ### Gateway CLMM Concentrated-liquidity positions. | Endpoint | Method | Description | | ------------------------------- | ------ | -------------------------------- | | `/gateway/clmm/pools` | GET | List available pools | | `/gateway/clmm/pool-info` | GET | Get one pool's live state | | `/gateway/clmm/quote-position` | POST | Quote a new position | | `/gateway/clmm/open` | POST | Open a new position | | `/gateway/clmm/add` | POST | Add liquidity to a position | | `/gateway/clmm/remove` | POST | Remove liquidity from a position | | `/gateway/clmm/close` | POST | Close a position | | `/gateway/clmm/collect-fees` | POST | Collect accumulated fees | | `/gateway/clmm/positions_owned` | POST | Get LP positions owned in a pool | ### Gateway AMM Classic (full-range) AMM liquidity. | Endpoint | Method | Description | | ------------------------------- | ------ | ------------------------- | | `/gateway/amm/pool-info` | GET | Get one pool's live state | | `/gateway/amm/quote-liquidity` | POST | Quote adding liquidity | | `/gateway/amm/add-liquidity` | POST | Add liquidity | | `/gateway/amm/remove-liquidity` | POST | Remove liquidity | | `/gateway/amm/positions-owned` | POST | Get AMM positions owned | ### Backtesting Strategy backtesting, synchronous or as background tasks. | Endpoint | Method | Description | | ------------------------------ | ------ | ------------------------------------- | | `/backtesting/run` | POST | Run a backtest synchronously | | `/backtesting/tasks` | POST | Start a backtest as a background task | | `/backtesting/tasks` | GET | List backtest tasks | | `/backtesting/tasks/{task_id}` | GET | Get a task's status and results | | `/backtesting/tasks/{task_id}` | DELETE | Cancel or delete a task | ### Archived Bots Historical bot performance data. | Endpoint | Method | Description | | -------------------------------------- | ------ | --------------------------- | | `/archived-bots/` | GET | List archived bot databases | | `/archived-bots/{db_path}/summary` | GET | Summary of an archived run | | `/archived-bots/{db_path}/performance` | GET | Get historical performance | | `/archived-bots/{db_path}/trades` | GET | Trades from an archived run | | `/archived-bots/{db_path}` | DELETE | Delete an archived database | ### Docker Container lifecycle management. | Endpoint | Method | Description | | --------------------------------- | ------ | -------------------------- | | `/docker/running` | GET | Check if Docker is running | | `/docker/active-containers` | GET | List active containers | | `/docker/available-images/` | GET | List available bot images | | `/docker/pull-image/` | POST | Pull an image | | `/docker/clean-exited-containers` | POST | Remove exited containers | ### Storage and System Disk usage of the API's data, and host resource stats. | Endpoint | Method | Description | | ------------------- | ------ | ------------------------------------------------- | | `/storage/` | GET | Storage overview: databases, archives, disk usage | | `/system/resources` | GET | CPU, memory, and disk for the API host | ## Response Format **Success responses** return the requested data: ```json theme={null} ["master_account"] ``` **Error responses** include a `detail` field: ```json theme={null} { "detail": "Account not found" } ``` ## Interactive Playground Each endpoint page includes an interactive playground where you can test API calls directly. Enter your credentials and server URL to make live requests. ## Resources Condor source code Hummingbot API source code Community support 2-minute survey that shapes the roadmap # Add Account Source: https://condor.hummingbot.org/api-reference/accounts/add-account /api-reference/openapi.json post /accounts/add-account Create a new account with default configuration files. Args: account_name: Name of the new account to create Returns: Success message when account is created Raises: HTTPException: 400 if account already exists or the account name is invalid # Add Credential Source: https://condor.hummingbot.org/api-reference/accounts/add-credential /api-reference/openapi.json post /accounts/add-credential/{account_name}/{connector_name} Add or update connector credentials (API keys) for a specific account and connector. Args: account_name: Name of the account connector_name: Name of the connector credentials: Dictionary containing the connector credentials Returns: Success message when credentials are added Raises: HTTPException: 400 if there's an error adding the credentials # Add Gateway Wallet Source: https://condor.hummingbot.org/api-reference/accounts/add-gateway-wallet /api-reference/openapi.json post /accounts/gateway/add-wallet Add an existing wallet to Gateway using its private key. Gateway handles encryption and storage internally. Args: wallet_credential: Wallet credentials (chain, private_key, and optional set_default) Returns: Wallet information from Gateway including address Raises: HTTPException: 503 if Gateway unavailable, 400 on validation error # Delete Account Source: https://condor.hummingbot.org/api-reference/accounts/delete-account /api-reference/openapi.json post /accounts/delete-account Delete an account and all its associated credentials. Args: account_name: Name of the account to delete Returns: Success message when account is deleted Raises: HTTPException: 400 if trying to delete master account or the account name is invalid, 404 if account not found # Delete Credential Source: https://condor.hummingbot.org/api-reference/accounts/delete-credential /api-reference/openapi.json post /accounts/delete-credential/{account_name}/{connector_name} Delete a specific connector credential for an account. Args: account_name: Name of the account connector_name: Name of the connector to delete credentials for Returns: Success message when credential is deleted Raises: HTTPException: 404 if credential not found # List Account Credentials Source: https://condor.hummingbot.org/api-reference/accounts/list-account-credentials /api-reference/openapi.json get /accounts/{account_name}/credentials Get a list of all connectors that have credentials configured for a specific account. Args: account_name: Name of the account to list credentials for Returns: List of connector names that have credentials configured Raises: HTTPException: 404 if account not found # List Accounts Source: https://condor.hummingbot.org/api-reference/accounts/list-accounts /api-reference/openapi.json get /accounts/ Get a list of all account names in the system. Returns: List of account names # List Gateway Wallets Source: https://condor.hummingbot.org/api-reference/accounts/list-gateway-wallets /api-reference/openapi.json get /accounts/gateway/wallets List all wallets managed by Gateway. Gateway manages its own encrypted wallet storage. Returns: List of wallet information from Gateway Raises: HTTPException: 503 if Gateway unavailable # Remove Gateway Wallet Source: https://condor.hummingbot.org/api-reference/accounts/remove-gateway-wallet /api-reference/openapi.json delete /accounts/gateway/{chain}/{address} Remove a wallet from Gateway. Args: chain: Blockchain chain (e.g., 'solana', 'ethereum') address: Wallet address to remove Returns: Success message Raises: HTTPException: 503 if Gateway unavailable # Set Default Gateway Wallet Source: https://condor.hummingbot.org/api-reference/accounts/set-default-gateway-wallet /api-reference/openapi.json post /accounts/gateway/wallet/set-default Set the default wallet for a chain in Gateway. When multiple wallets are configured for a chain, this endpoint allows switching which wallet is used as the default for operations. Args: request: Contains chain and wallet address to set as default Returns: Dict with success status and updated wallet info. Example: POST /accounts/gateway/wallet/set-default { "chain": "solana", "address": "82SggYRE2Vo4jN4a2pk3aQ4SET4ctafZJGbowmCqyHx5" } # Architecture Source: https://condor.hummingbot.org/api-reference/architecture How Hummingbot API connects to exchanges and AI agents Hummingbot API is a RESTful server that provides programmatic access to the Hummingbot trading framework. It serves as the execution layer for Condor and other AI trading systems. ## System Architecture ```mermaid theme={null} flowchart TB subgraph UI["User Interfaces"] C[Condor] MCP[MCP / AI Agents] end subgraph Server["Server"] API[Hummingbot API] end subgraph Core["Core Components"] HB[Hummingbot Client] GW[Gateway] end UI --> Server Server --> Core HB <--> GW ``` ## Components ### User Interfaces | Component | Description | | ------------------- | -------------------------------------------------------------------- | | **Condor** | Telegram bot for mobile/desktop control of trading operations | | **MCP / AI Agents** | Connect Claude, Gemini, GPT, or other LLMs to trading infrastructure | ### Server Layer | Component | Description | | ------------------ | ----------------------------------------------------------------------------- | | **Hummingbot API** | FastAPI server providing REST endpoints for trading, data, and bot management | | **PostgreSQL** | Stores orders, accounts, positions, and performance metrics | | **EMQX** | Message broker enabling real-time communication with bot instances | ### Core Components | Component | Description | | --------------------- | -------------------------------------------------------------- | | **Hummingbot Client** | Core Python library with CEX connectors and trading strategies | | **Gateway** | DEX middleware for Uniswap, Jupiter, Raydium, and 30+ DEXs | ## How It Works 1. **User Interfaces** (Condor, AI agents) send requests to the Hummingbot API 2. **Hummingbot API** processes requests and routes them to the appropriate component 3. **Hummingbot Client** handles CEX trading via exchange APIs 4. **Gateway** handles DEX trading via blockchain RPCs ## Deployment Options ### Development (Local) Run everything on your local machine: ```bash theme={null} # Clone and start Hummingbot API git clone https://github.com/hummingbot/hummingbot-api cd hummingbot-api make install make run ``` ### Production (Server) Deploy on a cloud server with Docker: * API server runs as a systemd service or Docker container * Bots run as isolated Docker containers * Gateway runs as a separate service for DEX access ## Data Flow ```mermaid theme={null} sequenceDiagram participant User as Condor/AI Agent participant API as Hummingbot API participant HB as Hummingbot Client participant CEX as Exchange User->>API: POST /trading/orders API->>HB: Execute order HB->>CEX: Place order via API CEX-->>HB: Order confirmation HB-->>API: Order result API-->>User: Response ``` ## Learn More The core trading framework DEX middleware for decentralized trading # Delete Archived Bot Source: https://condor.hummingbot.org/api-reference/archived-bots/delete-archived-bot /api-reference/openapi.json delete /archived-bots/{db_path} Delete an archived bot and its entire directory. Also attempts to delete matching BotRun records from PostgreSQL (best-effort). Args: db_path: Path to the database file (as returned by list_databases) Returns: Confirmation message with the deleted bot name and count of cleaned PG records # Get Database Controllers Source: https://condor.hummingbot.org/api-reference/archived-bots/get-database-controllers /api-reference/openapi.json get /archived-bots/{db_path}/controllers Get controller data from a database. Args: db_path: Full path to the database file Returns: List of controllers that were running with their configurations # Get Database Executors Source: https://condor.hummingbot.org/api-reference/archived-bots/get-database-executors /api-reference/openapi.json get /archived-bots/{db_path}/executors Get executor data from a database. Args: db_path: Full path to the database file Returns: List of executors with their configurations and results # Get Database Orders Source: https://condor.hummingbot.org/api-reference/archived-bots/get-database-orders /api-reference/openapi.json get /archived-bots/{db_path}/orders Get order history from a database. Args: db_path: Full path to the database file limit: Maximum number of orders to return offset: Offset for pagination status: Optional status filter Returns: List of orders with pagination info # Get Database Performance Source: https://condor.hummingbot.org/api-reference/archived-bots/get-database-performance /api-reference/openapi.json get /archived-bots/{db_path}/performance Get trade-based performance analysis for a bot database. Args: db_path: Full path to the database file Returns: Trade-based performance metrics with rolling calculations # Get Database Positions Source: https://condor.hummingbot.org/api-reference/archived-bots/get-database-positions /api-reference/openapi.json get /archived-bots/{db_path}/positions Get position data from a database. Args: db_path: Full path to the database file limit: Maximum number of positions to return offset: Offset for pagination Returns: List of positions with pagination info # Get Database Status Source: https://condor.hummingbot.org/api-reference/archived-bots/get-database-status /api-reference/openapi.json get /archived-bots/{db_path}/status Get status information for a specific database. Args: db_path: Path to the database file Returns: Database status including table health # Get Database Summary Source: https://condor.hummingbot.org/api-reference/archived-bots/get-database-summary /api-reference/openapi.json get /archived-bots/{db_path}/summary Get a summary of database contents including basic statistics. Args: db_path: Full path to the database file Returns: Summary statistics of the database contents # Get Database Trades Source: https://condor.hummingbot.org/api-reference/archived-bots/get-database-trades /api-reference/openapi.json get /archived-bots/{db_path}/trades Get trade history from a database. Args: db_path: Full path to the database file limit: Maximum number of trades to return offset: Offset for pagination Returns: List of trades with pagination info # List Databases Source: https://condor.hummingbot.org/api-reference/archived-bots/list-databases /api-reference/openapi.json get /archived-bots/ List all available database files in the system. Returns: List of database file paths # Create Backtest Task Source: https://condor.hummingbot.org/api-reference/backtesting/create-backtest-task /api-reference/openapi.json post /backtesting/tasks Submit a backtest as a background task. Returns task ID for polling. # Delete Backtest Task Source: https://condor.hummingbot.org/api-reference/backtesting/delete-backtest-task /api-reference/openapi.json delete /backtesting/tasks/{task_id} Cancel a running task or remove a completed one. # Get Backtest Task Source: https://condor.hummingbot.org/api-reference/backtesting/get-backtest-task /api-reference/openapi.json get /backtesting/tasks/{task_id} Get a backtest task by ID, including results if completed. # List Backtest Tasks Source: https://condor.hummingbot.org/api-reference/backtesting/list-backtest-tasks /api-reference/openapi.json get /backtesting/tasks List all backtest tasks with their status (results excluded for brevity). # Run Backtesting Source: https://condor.hummingbot.org/api-reference/backtesting/run-backtesting /api-reference/openapi.json post /backtesting/run Run a backtest synchronously. Returns results directly (may timeout for long backtests). # Delete Bot Run Source: https://condor.hummingbot.org/api-reference/bot-orchestration/delete-bot-run /api-reference/openapi.json delete /bot-orchestration/bot-runs/{bot_run_id} Delete a bot run record by ID. Args: bot_run_id: ID of the bot run to delete bots_manager: Bot orchestrator service dependency Returns: Confirmation of deletion Raises: HTTPException: 404 if bot run not found # Deploy V2 Controllers Source: https://condor.hummingbot.org/api-reference/bot-orchestration/deploy-v2-controllers /api-reference/openapi.json post /bot-orchestration/deploy-v2-controllers Deploy a V2 strategy with controllers by generating the script config and creating the instance. This endpoint simplifies the deployment process for V2 controller strategies. Args: deployment: V2ControllerDeployment configuration docker_manager: Docker service dependency Returns: Dictionary with deployment response and generated configuration details Raises: HTTPException: 500 if deployment fails # Deploy V2 Script Source: https://condor.hummingbot.org/api-reference/bot-orchestration/deploy-v2-script /api-reference/openapi.json post /bot-orchestration/deploy-v2-script Deploy a V2 script bot with optional script configuration. This endpoint creates and starts a Hummingbot instance running the specified script. Args: deployment: V2ScriptDeployment configuration containing instance name, credentials, optional script name and configuration docker_manager: Docker service dependency db_manager: Database manager dependency Returns: Dictionary with deployment response including instance details Raises: HTTPException: 500 if deployment fails # Get Active Bots Status Source: https://condor.hummingbot.org/api-reference/bot-orchestration/get-active-bots-status /api-reference/openapi.json get /bot-orchestration/status Get the status of all active bots. Args: bots_manager: Bot orchestrator service dependency Returns: Dictionary with status and data containing all active bot statuses # Get Bot History Source: https://condor.hummingbot.org/api-reference/bot-orchestration/get-bot-history /api-reference/openapi.json get /bot-orchestration/{bot_name}/history Get trading history for a bot with optional parameters. Args: bot_name: Name of the bot to get history for days: Number of days of history to retrieve (0 for all) verbose: Whether to include verbose output precision: Decimal precision for numerical values timeout: Timeout in seconds for the operation bots_manager: Bot orchestrator service dependency Returns: Dictionary with bot trading history # Get Bot Run By Id Source: https://condor.hummingbot.org/api-reference/bot-orchestration/get-bot-run-by-id /api-reference/openapi.json get /bot-orchestration/bot-runs/{bot_run_id} Get a specific bot run by ID. Args: bot_run_id: ID of the bot run bots_manager: Bot orchestrator service dependency Returns: Bot run details Raises: HTTPException: 404 if bot run not found # Get Bot Run Stats Source: https://condor.hummingbot.org/api-reference/bot-orchestration/get-bot-run-stats /api-reference/openapi.json get /bot-orchestration/bot-runs/stats Get statistics about bot runs. Args: bots_manager: Bot orchestrator service dependency Returns: Bot run statistics # Get Bot Runs Source: https://condor.hummingbot.org/api-reference/bot-orchestration/get-bot-runs /api-reference/openapi.json get /bot-orchestration/bot-runs Get bot runs with optional filtering. Args: bot_name: Filter by bot name account_name: Filter by account name strategy_type: Filter by strategy type (script or controller) strategy_name: Filter by strategy name run_status: Filter by run status (CREATED, RUNNING, STOPPED, ERROR) deployment_status: Filter by deployment status (DEPLOYED, FAILED, ARCHIVED) limit: Maximum number of results to return offset: Number of results to skip include_final_status: Include the final status snapshot for each run. Off by default because the blob can be ~89 KB per record (~99% of the payload); use GET /bot-runs/{bot_run_id} to fetch it for a single run. bots_manager: Bot orchestrator service dependency Returns: List of bot runs with their details # Get Bot Status Source: https://condor.hummingbot.org/api-reference/bot-orchestration/get-bot-status /api-reference/openapi.json get /bot-orchestration/{bot_name}/status Get the status of a specific bot. Args: bot_name: Name of the bot to get status for bots_manager: Bot orchestrator service dependency Returns: Dictionary with bot status information Raises: HTTPException: 404 if bot not found # Get Controller Performance History Source: https://condor.hummingbot.org/api-reference/bot-orchestration/get-controller-performance-history /api-reference/openapi.json get /bot-orchestration/controller-performance-history Get historical controller performance snapshots with pagination and interval sampling. # Get Latest Controller Performance Source: https://condor.hummingbot.org/api-reference/bot-orchestration/get-latest-controller-performance /api-reference/openapi.json get /bot-orchestration/controller-performance-latest Get the most recent performance snapshot for each bot/controller. Optionally filter by bot_name. # Get Mqtt Status Source: https://condor.hummingbot.org/api-reference/bot-orchestration/get-mqtt-status /api-reference/openapi.json get /bot-orchestration/mqtt Get MQTT connection status and discovered bots. Args: bots_manager: Bot orchestrator service dependency Returns: Dictionary with MQTT connection status, discovered bots, and broker information # Start Bot Source: https://condor.hummingbot.org/api-reference/bot-orchestration/start-bot /api-reference/openapi.json post /bot-orchestration/start-bot Start a bot with the specified configuration. Args: action: StartBotAction containing bot configuration parameters bots_manager: Bot orchestrator service dependency Returns: Dictionary with status and response from bot start operation # Stop And Archive Bot Source: https://condor.hummingbot.org/api-reference/bot-orchestration/stop-and-archive-bot /api-reference/openapi.json post /bot-orchestration/stop-and-archive-bot/{bot_name} Gracefully stop a bot and archive its data in the background. This initiates a background task that will: 1. Stop the bot trading process via MQTT 2. Wait 15 seconds for graceful shutdown 3. Monitor and stop the Docker container 4. Archive the bot data (locally or to S3) 5. Remove the container Returns immediately with a success message while the process continues in the background. # Stop Bot Source: https://condor.hummingbot.org/api-reference/bot-orchestration/stop-bot /api-reference/openapi.json post /bot-orchestration/stop-bot Stop a bot with the specified configuration. Args: action: StopBotAction containing bot stop parameters bots_manager: Bot orchestrator service dependency Returns: Dictionary with status and response from bot stop operation # Available Connectors Source: https://condor.hummingbot.org/api-reference/connectors/available-connectors /api-reference/openapi.json get /connectors/ Get a list of all available connectors. Returns: List of connector names supported by the system (excludes DEX providers which use Gateway networks) # Get Connector Config Map Source: https://condor.hummingbot.org/api-reference/connectors/get-connector-config-map /api-reference/openapi.json get /connectors/{connector_name}/config-map Get configuration fields required for a specific connector with type information. Args: connector_name: Name of the connector to get config map for Returns: Dictionary mapping field names to their type information. Each field contains: - type: The expected data type (e.g., "str", "SecretStr", "int") - required: Whether the field is required # Get Supported Order Types Source: https://condor.hummingbot.org/api-reference/connectors/get-supported-order-types /api-reference/openapi.json get /connectors/{connector_name}/order-types Get order types supported by a specific connector. This endpoint uses the MarketDataService to access non-trading connector instances, which means no authentication or account setup is required. Args: request: FastAPI request object connector_name: Name of the connector (e.g., 'binance', 'binance_perpetual') Returns: List of supported order types (LIMIT, MARKET, LIMIT_MAKER) Raises: HTTPException: 404 if connector not found, 500 for other errors # Get Trading Rules Source: https://condor.hummingbot.org/api-reference/connectors/get-trading-rules /api-reference/openapi.json get /connectors/{connector_name}/trading-rules Get trading rules for a connector, optionally filtered by trading pairs. This endpoint uses the MarketDataService to access non-trading connector instances, which means no authentication or account setup is required. Args: request: FastAPI request object connector_name: Name of the connector (e.g., 'binance', 'binance_perpetual') trading_pairs: Optional list of trading pairs to filter by (e.g., ['BTC-USDT', 'ETH-USDT']) Returns: Dictionary mapping trading pairs to their trading rules Raises: HTTPException: 404 if connector not found, 500 for other errors # Create Or Update Controller Source: https://condor.hummingbot.org/api-reference/controllers/create-or-update-controller /api-reference/openapi.json post /controllers/{controller_type}/{controller_name} Create or update a controller. If controller exists as a package (folder), updates the file inside. Otherwise creates/updates as a single file. Args: controller_type: Type of controller to create/update controller_name: Name of the controller (from URL path) controller: Controller object with content (and optional type for validation) Returns: Success message when controller is saved Raises: HTTPException: 400 if controller type mismatch or save error # Create Or Update Controller Config Source: https://condor.hummingbot.org/api-reference/controllers/create-or-update-controller-config /api-reference/openapi.json post /controllers/configs/{config_name} Create or update controller configuration. Args: config_name: Name of the configuration file config: Configuration dictionary to save Returns: Success message when configuration is saved Raises: HTTPException: 400 if save error occurs # Delete Controller Source: https://condor.hummingbot.org/api-reference/controllers/delete-controller /api-reference/openapi.json delete /controllers/{controller_type}/{controller_name} Delete a controller. Handles both single-file and package-style controllers. Args: controller_type: Type of the controller controller_name: Name of the controller to delete Returns: Success message when controller is deleted Raises: HTTPException: 404 if controller not found # Delete Controller Config Source: https://condor.hummingbot.org/api-reference/controllers/delete-controller-config /api-reference/openapi.json delete /controllers/configs/{config_name} Delete controller configuration. Args: config_name: Name of the configuration file to delete Returns: Success message when configuration is deleted Raises: HTTPException: 404 if configuration not found # Get Bot Controller Configs Source: https://condor.hummingbot.org/api-reference/controllers/get-bot-controller-configs /api-reference/openapi.json get /controllers/bots/{bot_name}/configs Get all controller configurations for a specific bot. Args: bot_name: Name of the bot to get configurations for Returns: List of controller configurations for the bot Raises: HTTPException: 404 if bot not found # Get Controller Source: https://condor.hummingbot.org/api-reference/controllers/get-controller /api-reference/openapi.json get /controllers/{controller_type}/{controller_name} Get controller content by type and name. Supports both single-file controllers (controller.py) and package-style controllers (controller/controller.py). Args: controller_type: Type of the controller controller_name: Name of the controller Returns: Dictionary with controller name, type, and content Raises: HTTPException: 404 if controller not found # Get Controller Config Source: https://condor.hummingbot.org/api-reference/controllers/get-controller-config /api-reference/openapi.json get /controllers/configs/{config_name} Get controller configuration by config name. Args: config_name: Name of the configuration file to retrieve Returns: Dictionary with controller configuration Raises: HTTPException: 404 if configuration not found # Get Controller Config Template Source: https://condor.hummingbot.org/api-reference/controllers/get-controller-config-template /api-reference/openapi.json get /controllers/{controller_type}/{controller_name}/config/template Get controller configuration template with default values. Args: controller_type: Type of the controller controller_name: Name of the controller Returns: Dictionary with configuration template and default values Raises: HTTPException: 404 if controller configuration class not found # List Controller Configs Source: https://condor.hummingbot.org/api-reference/controllers/list-controller-configs /api-reference/openapi.json get /controllers/configs/ List all controller configurations with metadata. Returns: List of controller configuration objects with name, controller_name, controller_type, and other metadata # List Controllers Source: https://condor.hummingbot.org/api-reference/controllers/list-controllers /api-reference/openapi.json get /controllers/ List all controllers organized by type. Detects both single-file controllers (controller.py) and package-style controllers (controller/controller.py). Returns: Dictionary mapping controller types to lists of controller names # Update Bot Controller Config Source: https://condor.hummingbot.org/api-reference/controllers/update-bot-controller-config /api-reference/openapi.json post /controllers/bots/{bot_name}/{controller_name}/config Update controller configuration for a specific bot. Args: bot_name: Name of the bot controller_name: Name of the controller to update config: Configuration dictionary to update with Returns: Success message when configuration is updated Raises: HTTPException: 404 if bot or controller not found, 400 if update error # Validate Controller Config Source: https://condor.hummingbot.org/api-reference/controllers/validate-controller-config /api-reference/openapi.json post /controllers/{controller_type}/{controller_name}/config/validate Validate controller configuration against the controller's config class. Args: controller_type: Type of the controller controller_name: Name of the controller config: Configuration dictionary to validate Returns: Success message if configuration is valid Raises: HTTPException: 400 if validation fails # Active Containers Source: https://condor.hummingbot.org/api-reference/docker/active-containers /api-reference/openapi.json get /docker/active-containers Get all currently active (running) Docker containers. Args: name_filter: Optional filter to match container names (case-insensitive) docker_service: Docker service dependency Returns: List of active container information # Available Images Source: https://condor.hummingbot.org/api-reference/docker/available-images /api-reference/openapi.json get /docker/available-images/ Get available Docker images matching the specified name. Args: image_name: Name pattern to search for in image tags docker_service: Docker service dependency Returns: Dictionary with list of available image tags # Clean Exited Containers Source: https://condor.hummingbot.org/api-reference/docker/clean-exited-containers /api-reference/openapi.json post /docker/clean-exited-containers Remove all exited Docker containers to free up space. Args: docker_service: Docker service dependency Returns: Response from cleanup operation # Exited Containers Source: https://condor.hummingbot.org/api-reference/docker/exited-containers /api-reference/openapi.json get /docker/exited-containers Get all exited (stopped) Docker containers. Args: name_filter: Optional filter to match container names (case-insensitive) docker_service: Docker service dependency Returns: List of exited container information # Get Pull Status Source: https://condor.hummingbot.org/api-reference/docker/get-pull-status /api-reference/openapi.json get /docker/pull-status/ Get status of all pull operations. Args: docker_service: Docker service dependency Returns: Dictionary with all pull operations and their statuses # Is Docker Running Source: https://condor.hummingbot.org/api-reference/docker/is-docker-running /api-reference/openapi.json get /docker/running Check if Docker daemon is running. Args: docker_service: Docker service dependency Returns: Dictionary indicating if Docker is running # Pull Image Source: https://condor.hummingbot.org/api-reference/docker/pull-image /api-reference/openapi.json post /docker/pull-image/ Initiate Docker image pull as background task. Returns immediately with task status for monitoring. Args: image: DockerImage object containing the image name to pull docker_service: Docker service dependency Returns: Status of the pull operation initiation # Remove Container Source: https://condor.hummingbot.org/api-reference/docker/remove-container /api-reference/openapi.json post /docker/remove-container/{container_name} Remove a Hummingbot container and optionally archive its bot data. NOTE: This endpoint only works with Hummingbot containers (names starting with 'hummingbot-') as it archives bot-specific data from the bots/instances directory. Args: container_name: Name of the Hummingbot container to remove archive_locally: Whether to archive data locally (default: True) s3_bucket: S3 bucket name for cloud archiving (optional) docker_service: Docker service dependency bot_archiver: Bot archiver service dependency Returns: Response from container removal operation Raises: HTTPException: 400 if container is not a Hummingbot container HTTPException: 500 if archiving fails # Start Container Source: https://condor.hummingbot.org/api-reference/docker/start-container /api-reference/openapi.json post /docker/start-container/{container_name} Start a stopped Docker container. Args: container_name: Name of the container to start docker_service: Docker service dependency Returns: Response from container start operation # Stop Container Source: https://condor.hummingbot.org/api-reference/docker/stop-container /api-reference/openapi.json post /docker/stop-container/{container_name} Stop a running Docker container. Args: container_name: Name of the container to stop docker_service: Docker service dependency Returns: Response from container stop operation # Clear Position Held Source: https://condor.hummingbot.org/api-reference/executors/clear-position-held /api-reference/openapi.json delete /executors/positions/{connector_name}/{trading_pair} Clear a held position (after manual close or full exit). This removes the position from tracking but preserves historical data in completed executors. Query parameters: - **controller_id**: Controller ID (default "main") # Create Executor Source: https://condor.hummingbot.org/api-reference/executors/create-executor /api-reference/openapi.json post /executors/ Create and start a new executor. Supported executor types: - **position_executor**: Single position with triple barrier (stop loss, take profit, time limit) - **grid_executor**: Grid trading with multiple levels - **dca_executor**: Dollar-cost averaging with multiple entry points - **twap_executor**: Time-weighted average price execution - **arbitrage_executor**: Cross-exchange arbitrage - **xemm_executor**: Cross-exchange market making - **order_executor**: Simple order execution - **lp_executor**: Liquidity provider position on CLMM DEXs (Meteora, Raydium, etc.) The `executor_config` must include: - `type`: One of the executor types above - `connector_name`: Exchange connector (e.g., "binance", "binance_perpetual") - `trading_pair`: Trading pair (e.g., "BTC-USDT") - Additional type-specific configuration (see /executors/types/{type}/config for details) Returns the created executor ID and initial status. # Get Available Executor Types Source: https://condor.hummingbot.org/api-reference/executors/get-available-executor-types /api-reference/openapi.json get /executors/types/available Get list of available executor types with descriptions. Returns information about each supported executor type. # Get Executor Source: https://condor.hummingbot.org/api-reference/executors/get-executor /api-reference/openapi.json get /executors/{executor_id} Get detailed information about a specific executor. Checks active executors in memory first, then falls back to database for completed executors. Returns full executor information including: - Current status and PnL - Full configuration - Executor-specific custom information # Get Executor Config Schema Source: https://condor.hummingbot.org/api-reference/executors/get-executor-config-schema /api-reference/openapi.json get /executors/types/{executor_type}/config Get configuration schema for a specific executor type. Returns detailed information about each configuration field including: - **name**: Field name - **type**: Data type (str, int, Decimal, enum, etc.) - **description**: Field description - **required**: Whether the field is required - **default**: Default value if any - **constraints**: Validation constraints (min, max, pattern, etc.) - **enum_values**: Possible values for enum types Also returns nested type definitions for complex fields. # Get Executor Logs Source: https://condor.hummingbot.org/api-reference/executors/get-executor-logs /api-reference/openapi.json get /executors/{executor_id}/logs Get captured log entries for a specific executor. Returns log entries from the in-memory ring buffer. Only available for active executors - logs are cleared when the executor completes. Query parameters: - **level**: Filter by log level (ERROR, WARNING, INFO, DEBUG) - **limit**: Maximum entries to return (default 50) # Get Executors Summary Source: https://condor.hummingbot.org/api-reference/executors/get-executors-summary /api-reference/openapi.json get /executors/summary Get summary statistics for all executors. Returns aggregate information including: - Total active/completed executor counts - Total PnL and volume - Breakdown by executor type, connector, and status # Get Orphaned Positions Source: https://condor.hummingbot.org/api-reference/executors/get-orphaned-positions /api-reference/openapi.json get /executors/positions/orphaned List terminated executors that may still own an on-chain position. Covers three orphan classes: - Involuntary holds: close_type POSITION_HOLD with hold_reason set (an LP close that exhausted its retries): the position is live on-chain with no automated owner. - Legacy FAILED records whose final state still reported a position_address (force-stop stragglers, records persisted by older executors). - Executors terminated by SYSTEM_CLEANUP after an API restart: their on-chain state was never persisted, so they need external reconciliation. This is a DB-side listing. Before recovering, cross-check candidates against on-chain reality via the gateway positions-owned endpoints (/trading/clmm/positions-owned, /trading/amm/positions-owned). # Get Performance Report Source: https://condor.hummingbot.org/api-reference/executors/get-performance-report /api-reference/openapi.json get /executors/performance Get a performance report for executors. Aggregates metrics from all completed executors (optionally filtered by controller_id): - Realized PnL (from completed executors, excluding POSITION_HOLD close type) - Unrealized PnL (from active executors + position holds) - Global PnL (realized + unrealized) - Fees and volume totals - Win rate and Sharpe ratio - Breakdown by executor type - Active position count Query parameters: - **controller_id**: Filter by controller ID (omit for all controllers) # Get Position Held Source: https://condor.hummingbot.org/api-reference/executors/get-position-held /api-reference/openapi.json get /executors/positions/{connector_name}/{trading_pair} Get held position for a specific connector/trading pair. Returns the aggregated position from executors stopped with keep_position=True, including breakeven prices, matched/unmatched volume, realized PnL, and unrealized PnL. Query parameters: - **controller_id**: Controller ID (default "main") # Get Positions Summary Source: https://condor.hummingbot.org/api-reference/executors/get-positions-summary /api-reference/openapi.json get /executors/positions/summary Get summary of all held positions from executors stopped with keep_position=True. Returns aggregate information including: - Total number of active position holds - Total realized PnL across all positions - Total unrealized PnL (when market rates are available) - List of all positions with breakeven prices and PnL Query parameters: - **controller_id**: Filter positions by controller ID # List Executors Source: https://condor.hummingbot.org/api-reference/executors/list-executors /api-reference/openapi.json post /executors/search Get list of executors with optional filtering. Returns active executors from memory combined with completed executors from database. Filters: - `account_names`: Filter by specific accounts - `connector_names`: Filter by connectors - `trading_pairs`: Filter by trading pairs - `executor_types`: Filter by executor types - `status`: Filter by status (RUNNING, TERMINATED, etc.) Returns paginated list of executor summaries. # Resolve Orphaned Position Source: https://condor.hummingbot.org/api-reference/executors/resolve-orphaned-position /api-reference/openapi.json post /executors/{executor_id}/resolve-orphan Mark an orphaned position as recovered. Call after the stranded on-chain position has been closed (or adopted) externally. Removes the executor from /executors/positions/orphaned and from agent-facing orphan warnings. Only valid for terminated executors that are orphan candidates: an involuntary hold (POSITION_HOLD with hold_reason or the orphaned_position flag), FAILED, or SYSTEM_CLEANUP. # Stop Executor Source: https://condor.hummingbot.org/api-reference/executors/stop-executor /api-reference/openapi.json post /executors/{executor_id}/stop Stop an active executor. Options: - `keep_position`: If true, keeps any open position (for position executors). If false, the executor will attempt to close all positions before stopping. Returns confirmation of the stop action. # Gateway Source: https://condor.hummingbot.org/api-reference/gateway DEX middleware for decentralized exchange trading **Gateway** is DEX middleware that enables Hummingbot API to trade on decentralized exchanges. It provides a standardized interface to swap tokens, provide liquidity, and interact with DeFi protocols across multiple blockchains. ## What is Gateway? Gateway is a separate service that: * Connects to blockchain RPCs (Ethereum, Solana, etc.) * Manages wallet keys and transaction signing * Provides unified API for different DEX protocols * Handles gas estimation and transaction submission ```mermaid theme={null} flowchart LR API[Hummingbot API] --> GW[Gateway] GW --> ETH[Ethereum RPC] GW --> SOL[Solana RPC] GW --> ARB[Arbitrum RPC] ETH --> UNI[Uniswap] SOL --> JUP[Jupiter] SOL --> RAY[Raydium] ARB --> UNI2[Uniswap] ``` ## Supported Protocols ### AMM Swaps | Protocol | Chains | | --------------- | ------------------------------------------- | | **Uniswap** | Ethereum, Arbitrum, Base, Polygon, Optimism | | **Jupiter** | Solana | | **Raydium** | Solana | | **PancakeSwap** | BSC, Ethereum | | **SushiSwap** | Ethereum, Arbitrum | | **Orca** | Solana | | **Curve** | Ethereum | | **Balancer** | Ethereum, Arbitrum | ### CLMM (Concentrated Liquidity) | Protocol | Chains | | ------------------- | --------------------------------- | | **Uniswap V3** | Ethereum, Arbitrum, Base, Polygon | | **Raydium CLMM** | Solana | | **Orca Whirlpools** | Solana | | **PancakeSwap V3** | BSC, Ethereum | ### Supported Chains | Chain | Type | RPC Required | | -------- | ---- | ------------ | | Ethereum | EVM | Yes | | Arbitrum | EVM | Yes | | Base | EVM | Yes | | Polygon | EVM | Yes | | Optimism | EVM | Yes | | BSC | EVM | Yes | | Solana | SVM | Yes | ## How Hummingbot API Uses Gateway ### Swaps Execute token swaps via the `/gateway/swap` endpoints: ```bash theme={null} # Get a swap quote curl -u YOUR_USERNAME:YOUR_PASSWORD -X POST http://localhost:8000/gateway/swap/quote \ -H "Content-Type: application/json" \ -d '{ "connector": "jupiter/router", "network": "solana-mainnet-beta", "trading_pair": "SOL-USDC", "side": "SELL", "amount": 1.0 }' # Execute the swap (same fields; wallet_address is optional — the default wallet is used) curl -u YOUR_USERNAME:YOUR_PASSWORD -X POST http://localhost:8000/gateway/swap/execute \ -H "Content-Type: application/json" \ -d '{ "connector": "jupiter/router", "network": "solana-mainnet-beta", "trading_pair": "SOL-USDC", "side": "SELL", "amount": 1.0, "slippage_pct": 1.0 }' ``` Past swaps are searchable via `POST /gateway/swaps/search`, and a transaction's status via `GET /gateway/swaps/{transaction_hash}/status`. ### Liquidity Provision Manage CLMM positions via `/gateway/clmm` endpoints: ```bash theme={null} # List pools for a connector curl -u YOUR_USERNAME:YOUR_PASSWORD \ "http://localhost:8000/gateway/clmm/pools?connector=raydium/clmm&search_term=SOL-USDC" # Get one pool's live state curl -u YOUR_USERNAME:YOUR_PASSWORD \ "http://localhost:8000/gateway/clmm/pool-info?connector=raydium/clmm&network=solana-mainnet-beta&pool_address=POOL_ADDRESS" # Add liquidity to an existing position (open a new one with /gateway/clmm/open) curl -u YOUR_USERNAME:YOUR_PASSWORD -X POST http://localhost:8000/gateway/clmm/add \ -H "Content-Type: application/json" \ -d '{ "connector": "raydium/clmm", "network": "solana-mainnet-beta", "position_address": "position-address", "base_token_amount": 1.0, "quote_token_amount": 150.0 }' ``` Full-range AMM liquidity has its own parallel router under `/gateway/amm` (pool-info, quote-liquidity, add/remove-liquidity). ## Setup ### 1. Configure Gateway Gateway's per-network configuration (RPC endpoints, tokens, pools) is readable and writable through the API: ```bash theme={null} # List networks Gateway knows about curl -u YOUR_USERNAME:YOUR_PASSWORD http://localhost:8000/gateway/networks # Read one network's config (e.g. its RPC endpoint) curl -u YOUR_USERNAME:YOUR_PASSWORD http://localhost:8000/gateway/networks/solana-mainnet-beta ``` Updating a network's config is a `POST` to the same path; tokens and pools have their own subpaths (`/gateway/networks/{network_id}/tokens`, `/gateway/networks/{network_id}/pools`). ### 2. Add Wallets Add wallet credentials via Condor or API: **Telegram:** ``` /gateway → Add Wallet → Solana → Enter private key ``` **API:** ```bash theme={null} curl -u YOUR_USERNAME:YOUR_PASSWORD -X POST http://localhost:8000/accounts/gateway/add-wallet \ -H "Content-Type: application/json" \ -d '{ "chain": "solana", "private_key": "your-private-key" }' ``` ### 3. Verify Connection Check Gateway status: ```bash theme={null} curl -u YOUR_USERNAME:YOUR_PASSWORD http://localhost:8000/gateway/status ``` ## Gateway vs CEX Connectors | Aspect | CEX Connectors | Gateway | | ---------------- | -------------------- | ------------------- | | **Custody** | Exchange holds funds | You hold keys | | **Speed** | Fast (centralized) | Slower (blockchain) | | **Fees** | Trading fees | Gas + trading fees | | **Availability** | Exchange uptime | Blockchain uptime | | **Privacy** | KYC required | Permissionless | ## Resources Gateway source code Full Gateway documentation # Add Amm Liquidity Source: https://condor.hummingbot.org/api-reference/gateway-amm/add-amm-liquidity /api-reference/openapi.json post /gateway/amm/add-liquidity Add two-sided liquidity to an AMM pool. Meteora DAMM v2: pass position_address to add to that NFT position; omit it to open a new one. Fungible-LP AMMs ignore position_address. # Create Amm Pool Source: https://condor.hummingbot.org/api-reference/gateway-amm/create-amm-pool /api-reference/openapi.json post /gateway/amm/create-pool Create and seed a new AMM pool. Seed price priority: initial_price → quote_token_amount ratio → live market price (anti-snipe). Connector-specific params ride extra_params under Gateway's own names (configAddress for meteora — required there, ammConfigIndex for raydium) — the same contract as clmm open's extra_params. Seeding slippage for uniswap/pancakeswap is the standard slippage_pct field. # Get Amm Pool Info Source: https://condor.hummingbot.org/api-reference/gateway-amm/get-amm-pool-info /api-reference/openapi.json get /gateway/amm/pool-info Get AMM pool information (reserves, price, base fee) by pool address. # Get Amm Position Info Source: https://condor.hummingbot.org/api-reference/gateway-amm/get-amm-position-info /api-reference/openapi.json get /gateway/amm/position-info Get a wallet's aggregate liquidity in an AMM pool plus a per-position breakdown (DAMM v2). # Get Amm Positions Owned Source: https://condor.hummingbot.org/api-reference/gateway-amm/get-amm-positions-owned /api-reference/openapi.json post /gateway/amm/positions-owned List all of a wallet's AMM positions across pools (Meteora DAMM v2 only). Fungible-LP AMMs (raydium, uniswap, pancakeswap) have no enumerable positions; Gateway rejects them with a 400, surfaced here unchanged. Use position-info with a specific pool address instead. # Quote Amm Liquidity Source: https://condor.hummingbot.org/api-reference/gateway-amm/quote-amm-liquidity /api-reference/openapi.json post /gateway/amm/quote-liquidity Quote a two-sided liquidity deposit. # Remove Amm Liquidity Source: https://condor.hummingbot.org/api-reference/gateway-amm/remove-amm-liquidity /api-reference/openapi.json post /gateway/amm/remove-liquidity Remove liquidity from an AMM pool. Meteora DAMM v2 requires position_address (positions are NFTs); Gateway rejects a missing one with a 400, surfaced here unchanged, so "remove 100%" is a true exit of the named position. # Search Amm Events Source: https://condor.hummingbot.org/api-reference/gateway-amm/search-amm-events /api-reference/openapi.json post /gateway/amm/events/search Search recorded AMM liquidity writes, newest first. This is the AMM history: ADD_LIQUIDITY, REMOVE_LIQUIDITY and CREATE_POOL with their on-chain amounts and gas. Current holdings are not here — read those live from /gateway/amm/position-info, which is the only authority on them. # Search Amm Positions Source: https://condor.hummingbot.org/api-reference/gateway-amm/search-amm-positions /api-reference/openapi.json post /gateway/amm/positions/search Search tracked AMM positions (Meteora DAMM v2 NFTs), newest first. Fungible-LP AMMs never appear here — they have no position identity. Their holdings come from /gateway/amm/position-info and their history from /gateway/amm/events/search. # Add Liquidity To Clmm Position Source: https://condor.hummingbot.org/api-reference/gateway-clmm/add-liquidity-to-clmm-position /api-reference/openapi.json post /gateway/clmm/add Add MORE liquidity to an EXISTING CLMM position. Example: connector: 'meteora' network: 'solana-mainnet-beta' position_address: '...' base_token_amount: 0.5 quote_token_amount: 50.0 slippage_pct: 1 (optional; omit to use the connector's configured slippagePct) wallet_address: (optional) extra_params: {"strategyType": 0} # Meteora-specific Returns: Transaction hash # Close Clmm Position Source: https://condor.hummingbot.org/api-reference/gateway-clmm/close-clmm-position /api-reference/openapi.json post /gateway/clmm/close CLOSE a CLMM position completely (removes all liquidity and collects pending fees). Example: connector: 'meteora' network: 'solana-mainnet-beta' position_address: '...' wallet_address: (optional) Returns: Transaction hash and collected fee amounts # Collect Fees From Clmm Position Source: https://condor.hummingbot.org/api-reference/gateway-clmm/collect-fees-from-clmm-position /api-reference/openapi.json post /gateway/clmm/collect-fees Collect accumulated fees from a CLMM liquidity position. Example: connector: 'meteora' network: 'solana-mainnet-beta' position_address: '...' wallet_address: (optional) Returns: Transaction hash and collected fee amounts # Create Clmm Pool Source: https://condor.hummingbot.org/api-reference/gateway-clmm/create-clmm-pool /api-reference/openapi.json post /gateway/clmm/create-pool Create a new (empty) CLMM pool — liquidity is added afterwards by opening positions. Mirrors Gateway's POST /trading/clmm/create-pool (which shares the AMM create-pool response shape). Connector-specific params ride extra_params under Gateway's own names — the same contract as open's extra_params. # Get Clmm Pool Info Source: https://condor.hummingbot.org/api-reference/gateway-clmm/get-clmm-pool-info /api-reference/openapi.json get /gateway/clmm/pool-info Get detailed information about a CLMM pool by pool address. Args: connector: CLMM connector (e.g., 'meteora', 'raydium') network: Network ID in 'chain-network' format (e.g., 'solana-mainnet-beta') pool_address: Pool contract address bin_count: If > 0, include the per-tick liquidity distribution (`bins`) around the active tick. Meteora always returns its bins and ignores this; orca, raydium, uniswap and pancakeswap honour it. Example: GET /gateway/clmm/pool-info?connector=meteora&network=solana-mainnet-beta &pool_address=2sf5NYcY4zUPXUSmG6f66mskb24t5F8S11pC1Nz5nQT3 Returns: Pool information including liquidity, price, bins (for Meteora), etc. All field names are returned in snake_case format. # Get Clmm Pools Source: https://condor.hummingbot.org/api-reference/gateway-clmm/get-clmm-pools /api-reference/openapi.json get /gateway/clmm/pools Get list of available CLMM pools for a connector via Gateway. Supports: meteora, orca Args: connector: CLMM connector (meteora, orca) network: Solana network name (bare, default 'mainnet-beta') page: Page number (default: 0) limit: Results per page (default: 50, max: 100) search_term: Search query to filter pools (optional) sort_key: Sort by field. Defaults to tvl — see the parameter description for why, and for the keys each connector accepts. order_by: Sort order (asc, desc) include_unknown: Include pools with unverified tokens Example: GET /gateway/clmm/pools?connector=meteora&search_term=SOL&limit=20 Returns: List of available pools with trading pairs, addresses, liquidity, volume, APR, etc. # Get Clmm Position Events Source: https://condor.hummingbot.org/api-reference/gateway-clmm/get-clmm-position-events /api-reference/openapi.json get /gateway/clmm/positions/{position_address}/events Get event history for a CLMM position. Args: position_address: Position NFT address event_type: Filter by event type (OPEN, ADD_LIQUIDITY, REMOVE_LIQUIDITY, COLLECT_FEES, CLOSE, DISCOVERED — written by the poller for positions it found on-chain, with a synthetic discovered_ transaction hash) limit: Max events to return Returns: List of position events # Get Clmm Position Info Source: https://condor.hummingbot.org/api-reference/gateway-clmm/get-clmm-position-info /api-reference/openapi.json get /gateway/clmm/position-info Get a single CLMM position by its address. Mirrors Gateway's GET /trading/clmm/position-info. Gateway reports a missing or closed position as an error (500/404), surfaced here as 404. # Get Clmm Positions Owned Source: https://condor.hummingbot.org/api-reference/gateway-clmm/get-clmm-positions-owned /api-reference/openapi.json post /gateway/clmm/positions_owned Get all CLMM liquidity positions owned by a wallet. Mirrors Gateway's /trading/clmm/positions-owned, which takes no pool filter: every CLMM position the wallet owns on the connector is returned, each row carrying its own pool_address. (The old pool_address request field was a silent no-op — Gateway never read it and the response was never filtered.) Example: connector: 'meteora' network: 'solana-mainnet-beta' wallet_address: (optional, uses default if not provided) Returns: List of CLMM position information # Open Clmm Position Source: https://condor.hummingbot.org/api-reference/gateway-clmm/open-clmm-position /api-reference/openapi.json post /gateway/clmm/open Open a NEW CLMM position with initial liquidity. Example: connector: 'meteora' network: 'solana-mainnet-beta' pool_address: '2sf5NYcY4zUPXUSmG6f66mskb24t5F8S11pC1Nz5nQT3' lower_price: 150 upper_price: 250 base_token_amount: 0.01 quote_token_amount: 2 slippage_pct: 1 (optional; omit to use the connector's configured slippagePct) wallet_address: (optional) extra_params: {"strategyType": 0} # Meteora-specific Returns: Transaction hash and position address. position_address is None when the transaction was submitted but not yet confirmed — poll the transaction; the poller's discovery sweep records the position once it lands on-chain. # Quote Clmm Position Source: https://condor.hummingbot.org/api-reference/gateway-clmm/quote-clmm-position /api-reference/openapi.json post /gateway/clmm/quote-position Quote a candidate CLMM position before opening or adding liquidity. Mirrors Gateway's GET /trading/clmm/quote-position: returns the base/quote split the pool would actually take for the given range and deposit amounts (and which side limits it), without signing or submitting anything. # Remove Liquidity From Clmm Position Source: https://condor.hummingbot.org/api-reference/gateway-clmm/remove-liquidity-from-clmm-position /api-reference/openapi.json post /gateway/clmm/remove Remove SOME liquidity from a CLMM position (partial removal). Example: connector: 'meteora' network: 'solana-mainnet-beta' position_address: '...' percentage_to_remove: 50 slippage_pct: 1 (optional; Orca only — other connectors ignore it) wallet_address: (optional) Returns: Transaction hash # Search Clmm Positions Source: https://condor.hummingbot.org/api-reference/gateway-clmm/search-clmm-positions /api-reference/openapi.json post /gateway/clmm/positions/search Search CLMM positions with filters. Args: network: Filter by network (e.g., 'solana-mainnet-beta') connector: Filter by connector (e.g., 'meteora') wallet_address: Filter by wallet address trading_pair: Filter by trading pair (e.g., 'SOL-USDC'; a token outside Gateway's token list is stored under its full mint address instead of a symbol) status: Filter by status (OPEN, CLOSED) position_addresses: Filter by specific position addresses (list of addresses) limit: Max results (default 50, max 1000) offset: Pagination offset refresh: If True, refresh position data from Gateway before returning (default False) Returns: Paginated list of positions # Execute Swap Source: https://condor.hummingbot.org/api-reference/gateway-swaps/execute-swap /api-reference/openapi.json post /gateway/swap/execute Execute a swap transaction via router (Jupiter, 0x). Example: connector: 'jupiter' network: 'solana-mainnet-beta' trading_pair: 'SOL-USDC' side: 'BUY' amount: 1 slippage_pct: 1 (optional; omit to use the connector's configured slippagePct) wallet_address: (optional, uses default if not provided) extra_params: {"approximateIfNoExactOut": false} # Solana routers Returns: Transaction hash and swap details # Execute Swap Quote Source: https://condor.hummingbot.org/api-reference/gateway-swaps/execute-swap-quote /api-reference/openapi.json post /gateway/swap/execute-quote Execute a quote returned by /swap/quote, by its quote_id. The two-step flow: quote, decide, then commit to THAT quote. /swap/execute prices again at execution, which discards the price the caller saw — the whole reason dflow, titan and 0x return a held quote. Router connectors only; a pool-scoped connector has no cached quote to execute and is rejected rather than quietly re-priced. Example: connector: 'jupiter' network: 'solana-mainnet-beta' quote_id: '' trading_pair: 'SOL-USDC' side: 'SELL' amount: 0.01 Returns: Transaction hash and what the swap actually moved # Get Swap Quote Source: https://condor.hummingbot.org/api-reference/gateway-swaps/get-swap-quote /api-reference/openapi.json post /gateway/swap/quote Get a price quote for a swap. Example: connector: 'jupiter' (or typed: 'jupiter/router', 'meteora/clmm', 'raydium/amm') network: 'solana-mainnet-beta' trading_pair: 'SOL-USDC' side: 'BUY' amount: 1 slippage_pct: 1 (optional; omit to use the connector's configured slippagePct) extra_params: {"approximateIfNoExactOut": false} # Solana routers Returns: Quote with price, expected output amount, and execution-safety fields # Get Swap Status Source: https://condor.hummingbot.org/api-reference/gateway-swaps/get-swap-status /api-reference/openapi.json get /gateway/swaps/{transaction_hash}/status Get status of a specific swap by transaction hash. Args: transaction_hash: Transaction hash of the swap Returns: Swap details including current status # Get Swaps Summary Source: https://condor.hummingbot.org/api-reference/gateway-swaps/get-swaps-summary /api-reference/openapi.json get /gateway/swaps/summary Get swap summary statistics. Args: network: Filter by network wallet_address: Filter by wallet address start_time: Start timestamp (unix seconds) end_time: End timestamp (unix seconds) Returns: Summary statistics including volume, fees, success rate # Search Swaps Source: https://condor.hummingbot.org/api-reference/gateway-swaps/search-swaps /api-reference/openapi.json post /gateway/swaps/search Search swap history with filters. Args: network: Filter by network (e.g., 'solana-mainnet-beta') connector: Filter by connector (e.g., 'jupiter') wallet_address: Filter by wallet address trading_pair: Filter by trading pair (e.g., 'SOL-USDC') status: Filter by status (SUBMITTED, CONFIRMED, FAILED) start_time: Start timestamp (unix seconds) end_time: End timestamp (unix seconds) limit: Max results (default 50, max 1000) offset: Pagination offset Returns: Paginated list of swaps # Add Network Pool Source: https://condor.hummingbot.org/api-reference/gateway/add-network-pool /api-reference/openapi.json post /gateway/networks/{network_id}/pools Add a custom pool to Gateway's pool list for a specific network. Args: network_id: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta', 'ethereum-mainnet') pool_request: Pool details (connector, type, base, quote, address, etc.) Example: POST /gateway/networks/solana-mainnet-beta/pools { "connector_name": "raydium", "type": "clmm", "base": "SOL", "quote": "USDC", "address": "58oQChx4yWmvKdwLLZzBi4ChoCc2fqCUWBkwMihLYQo2", "base_address": "So11111111111111111111111111111111111111112", "quote_address": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "fee_pct": 0.25 } No Gateway restart is needed: the pool list is read off disk per request, so the pool is listed and priced as soon as this returns. # Add Network Token Source: https://condor.hummingbot.org/api-reference/gateway/add-network-token /api-reference/openapi.json post /gateway/networks/{network_id}/tokens Add a custom token to Gateway's token list for a specific network. Args: network_id: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta', 'ethereum-mainnet') token_request: Token details (address, symbol, name, decimals) Example: POST /gateway/networks/ethereum-mainnet/tokens { "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "symbol": "USDC", "name": "USD Coin", "decimals": 6 } No Gateway restart is needed: the token list is read off disk per request, so the token is live as soon as this returns. # Delete Network Pool Source: https://condor.hummingbot.org/api-reference/gateway/delete-network-pool /api-reference/openapi.json delete /gateway/networks/{network_id}/pools/{pool_address} Delete a pool from Gateway's pool list for a specific network. Args: network_id: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta', 'ethereum-mainnet') pool_address: Pool contract address to delete Example: DELETE /gateway/networks/solana-mainnet-beta/pools/58oQChx4yWmvKdwLLZzBi4ChoCc2fqCUWBkwMihLYQo2 No Gateway restart is needed: the pool list is read off disk per request, so the deletion is live as soon as this returns. # Delete Network Token Source: https://condor.hummingbot.org/api-reference/gateway/delete-network-token /api-reference/openapi.json delete /gateway/networks/{network_id}/tokens/{token_address} Delete a custom token from Gateway's token list for a specific network. Args: network_id: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta', 'ethereum-mainnet') token_address: Token contract address to delete Example: DELETE /gateway/networks/solana-mainnet-beta/tokens/9QFfgxdSqH5zT7j6rZb1y6SZhw2aFtcQu2r6BuYpump No Gateway restart is needed: the token list is read off disk per request, so the deletion is live as soon as this returns. # Get Api Keys Source: https://condor.hummingbot.org/api-reference/gateway/get-api-keys /api-reference/openapi.json get /gateway/apiKeys Get all configured API keys from Gateway. Returns a dict mapping provider name to API key value. Example response: { "helius": "46951ec2-16af-4fc0-a5df-970b0eb925b7", "infura": "920646320ec3463fa1b5235be9fa48d3", "coingecko": "CG-Rw786jTpNmV1MvRrqpDAHR6r", "etherscan": "" } # Get Connector Config Source: https://condor.hummingbot.org/api-reference/gateway/get-connector-config /api-reference/openapi.json get /gateway/connectors/{connector_name} Get configuration for a specific DEX connector. Args: connector_name: Connector name (e.g., 'meteora', 'raydium') # Get Gateway Logs Source: https://condor.hummingbot.org/api-reference/gateway/get-gateway-logs /api-reference/openapi.json get /gateway/logs Get Gateway container logs. # Get Gateway Status Source: https://condor.hummingbot.org/api-reference/gateway/get-gateway-status /api-reference/openapi.json get /gateway/status Get Gateway container status. # Get Network Config Source: https://condor.hummingbot.org/api-reference/gateway/get-network-config /api-reference/openapi.json get /gateway/networks/{network_id} Get configuration for a specific network. Args: network_id: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta', 'ethereum-mainnet') Example: GET /gateway/networks/solana-mainnet-beta # Get Network Pools Source: https://condor.hummingbot.org/api-reference/gateway/get-network-pools /api-reference/openapi.json get /gateway/networks/{network_id}/pools Get available pools for a network. Args: network_id: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta') connector: Optional filter by connector (e.g., 'raydium', 'meteora', 'uniswap') pool_type: Optional filter by type ('amm' or 'clmm') search: Optional search by trading pair (e.g., 'SOL-USDC') or pool address Example: GET /gateway/networks/solana-mainnet-beta/pools?connector=raydium&type=clmm # Get Network Tokens Source: https://condor.hummingbot.org/api-reference/gateway/get-network-tokens /api-reference/openapi.json get /gateway/networks/{network_id}/tokens Get available tokens for a network. Args: network_id: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta') search: Filter tokens by symbol, name, or address (case-insensitive substring) Example: GET /gateway/networks/solana-mainnet-beta/tokens?search=USDC # List Chains Source: https://condor.hummingbot.org/api-reference/gateway/list-chains /api-reference/openapi.json get /gateway/chains List all available blockchain chains and their networks. This also serves as the networks list endpoint. # List Connectors Source: https://condor.hummingbot.org/api-reference/gateway/list-connectors /api-reference/openapi.json get /gateway/connectors List all available DEX connectors with their configurations. Returns connector details including name, trading types, chain, and networks. All fields normalized to snake_case. # List Networks Source: https://condor.hummingbot.org/api-reference/gateway/list-networks /api-reference/openapi.json get /gateway/networks List all available networks across all chains. Returns a flattened list of network IDs in the format 'chain-network'. This is the primary interface for network discovery. # List Pools Legacy Source: https://condor.hummingbot.org/api-reference/gateway/list-pools-legacy /api-reference/openapi.json get /gateway/pools [DEPRECATED] Use GET /gateway/networks/{network_id}/pools instead. List all liquidity pools for a connector and network. # Restart Gateway Source: https://condor.hummingbot.org/api-reference/gateway/restart-gateway /api-reference/openapi.json post /gateway/restart Restart Gateway container. If config is provided, the container will be removed and recreated with new configuration. If no config is provided, the container will be stopped and started with existing configuration. # Save Network Pool Source: https://condor.hummingbot.org/api-reference/gateway/save-network-pool /api-reference/openapi.json post /gateway/networks/{network_id}/pools/save/{pool_address} Save a pool by address, auto-adding any missing tokens. Gateway only needs GeckoTerminal to answer one question: which DEX does this address belong to, and is it amm or clmm. The pool's base, quote and fee always come from the connector. Pass connector and type to answer that directly and skip the lookup — which is what a caller holding an LP provider config like 'meteora/clmm' can always do, and what makes this work for a token or pool GeckoTerminal has not indexed. Args: network_id: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta') pool_address: Pool contract address connector: DEX connector ('meteora', 'raydium', 'orca', 'uniswap'). With type. type: Pool type, 'amm' or 'clmm'. With connector. Example: POST /gateway/networks/solana-mainnet-beta/pools/save/2sf5NYcY...?connector=meteora&type=clmm # Save Network Token Source: https://condor.hummingbot.org/api-reference/gateway/save-network-token /api-reference/openapi.json post /gateway/networks/{network_id}/tokens/save/{token_address} Save a token by address - auto-fetches token info from GeckoTerminal. This is the simplest way to add a token. Just provide the address and the API will fetch symbol, name, and decimals automatically. Args: network_id: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta', 'ethereum-mainnet') token_address: Token contract address Example: POST /gateway/networks/solana-mainnet-beta/tokens/save/9QFfgxdSqH5zT7j6rZb1y6SZhw2aFtcQu2r6BuYpump # Start Gateway Source: https://condor.hummingbot.org/api-reference/gateway/start-gateway /api-reference/openapi.json post /gateway/start Start Gateway container. # Stop Gateway Source: https://condor.hummingbot.org/api-reference/gateway/stop-gateway /api-reference/openapi.json post /gateway/stop Stop Gateway container. # Update Api Keys Source: https://condor.hummingbot.org/api-reference/gateway/update-api-keys /api-reference/openapi.json post /gateway/apiKeys Update API keys in Gateway configuration. Args: request: Contains api_keys dict mapping provider name to API key value Example request: { "api_keys": { "helius": "new-api-key-value", "infura": "another-api-key" } } Note: After updating API keys, restart Gateway for changes to take effect. # Update Connector Config Source: https://condor.hummingbot.org/api-reference/gateway/update-connector-config /api-reference/openapi.json post /gateway/connectors/{connector_name} Update configuration for a DEX connector. Args: connector_name: Connector name (e.g., 'meteora', 'raydium') config_updates: Dict with path-value pairs to update. Keys can be in snake_case (e.g., {"slippage_pct": 0.5}) or camelCase (e.g., {"slippagePct": 0.5}) # Update Network Config Source: https://condor.hummingbot.org/api-reference/gateway/update-network-config /api-reference/openapi.json post /gateway/networks/{network_id} Update configuration for a specific network. Args: network_id: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta') config_updates: Dict with path-value pairs to update. Keys can be in snake_case (e.g., {"node_url": "https://..."}) or camelCase (e.g., {"nodeURL": "https://..."}) Example: POST /gateway/networks/solana-mainnet-beta # Hummingbot Source: https://condor.hummingbot.org/api-reference/hummingbot The open source, institutional-grade crypto trading framework **Hummingbot** is an open source trading framework used by thousands of individual and institutional traders worldwide. Hummingbot API uses the Hummingbot Python library for exchange connectivity, and bots are Docker containers running Hummingbot instances. ## Why Trust Hummingbot? ### Security * **Battle-tested**: Running in production since 2019 * **Open source**: All code is auditable on GitHub * **Self-hosted**: Your keys never leave your infrastructure * **No custody**: Direct exchange API connections, no intermediaries ### Reliability * **50+ exchange connectors** actively maintained * **Standardized interfaces** across all exchanges * **Automatic reconnection** on network failures * **Order tracking** with fill reconciliation ### Scalability * **Multi-instance**: Run hundreds of bots on one server * **Docker isolation**: Each bot in its own container * **Shared infrastructure**: Bots share market data connections * **Enterprise-ready**: Used by trading firms and funds ### Open Source * **Apache 2.0 license**: Use commercially without restrictions * **Active community**: 10,000+ Discord members * **Regular updates**: Monthly releases with new features * **Extensible**: Add custom connectors and strategies ## How Hummingbot API Uses It Hummingbot API wraps the Hummingbot Python library to provide: ### Exchange Connectors The Hummingbot library provides standardized connectors to exchanges: ```python theme={null} # Hummingbot API uses these connectors internally from hummingbot.connector.exchange.binance import BinanceExchange from hummingbot.connector.exchange.hyperliquid import HyperliquidExchange ``` **Supported exchange types:** * **Spot**: Binance, Coinbase, Kraken, KuCoin, Gate.io, etc. * **Perpetual**: Binance Futures, Bybit, OKX, Hyperliquid, dYdX * **AMM/DEX**: Via Gateway (Uniswap, Jupiter, Raydium) ### Bot Containers Bots are Docker containers running Hummingbot instances: ```bash theme={null} # Hummingbot API deploys bots as containers docker run -d \ --name hummingbot-mm-bot \ hummingbot/hummingbot:latest \ --config-file mm_strategy.yml ``` Each bot: * Runs in isolated container * Has its own configuration * Streams logs to API * Can be started/stopped remotely ### Executors Executors use Hummingbot's order management: ```python theme={null} # Position Executor uses Hummingbot's order tracking executor = PositionExecutor( connector=binance_connector, trading_pair="SOL-USDT", side=TradeType.BUY, entry_price=150.0, amount=10 ) ``` ## Connector Coverage ### CEX Spot | Exchange | Status | | -------- | ------ | | Binance | Active | | Coinbase | Active | | Kraken | Active | | KuCoin | Active | | Gate.io | Active | | OKX | Active | | Bybit | Active | | MEXC | Active | ### CEX Perpetual | Exchange | Status | | ----------------- | ------ | | Binance Futures | Active | | Bybit Perpetual | Active | | OKX Perpetual | Active | | Hyperliquid | Active | | dYdX | Active | | Gate.io Perpetual | Active | ### DEX (via Gateway) | Protocol | Chains | | ----------- | --------------------------------- | | Uniswap | Ethereum, Arbitrum, Base, Polygon | | Jupiter | Solana | | Raydium | Solana | | PancakeSwap | BSC | | Orca | Solana | ## Resources Full Hummingbot documentation Hummingbot source code # Add Trading Pair Source: https://condor.hummingbot.org/api-reference/market-data/add-trading-pair /api-reference/openapi.json post /market-data/trading-pair/add Initialize order book for a trading pair. This endpoint dynamically adds a trading pair to a connector's order book tracker. It uses the best available connector (trading connectors are preferred over data connectors). Args: request: Request with connector name, trading pair, optional account name, and timeout Returns: TradingPairResponse with success status and message Raises: HTTPException: 500 if initialization fails # Get Active Feeds Source: https://condor.hummingbot.org/api-reference/market-data/get-active-feeds /api-reference/openapi.json get /market-data/active-feeds Get information about currently active market data feeds. Args: request: FastAPI request object to access application state Returns: Dictionary with active feeds information including last access times and expiration # Get Available Candle Connectors Source: https://condor.hummingbot.org/api-reference/market-data/get-available-candle-connectors /api-reference/openapi.json get /market-data/available-candle-connectors Get list of available connectors that support candle data feeds. Returns: List of connector names that can be used for fetching candle data # Get Candles Source: https://condor.hummingbot.org/api-reference/market-data/get-candles /api-reference/openapi.json post /market-data/candles Get real-time candles data for a specific trading pair. This endpoint uses the MarketDataProvider to get or create a candles feed that will automatically start and maintain real-time updates. Subsequent requests with the same configuration will reuse the existing feed for up-to-date data. Args: request: FastAPI request object candles_config: Configuration for the candles including connector, trading_pair, interval, and max_records Returns: Real-time candles data or error message # Get Funding Info Source: https://condor.hummingbot.org/api-reference/market-data/get-funding-info /api-reference/openapi.json post /market-data/funding-info Get funding information for a perpetual trading pair. Args: request: Funding info request with connector name and trading pair market_data_manager: Injected market data feed manager Returns: Funding information including rates, timestamps, and prices Raises: HTTPException: 400 for non-perpetual connectors, 500 for other errors # Get Historical Candles Source: https://condor.hummingbot.org/api-reference/market-data/get-historical-candles /api-reference/openapi.json post /market-data/historical-candles Get historical candles data for a specific trading pair. Args: config: Configuration for historical candles including connector, trading pair, interval, start and end time Returns: Historical candles data or error message # Get Market Data Settings Source: https://condor.hummingbot.org/api-reference/market-data/get-market-data-settings /api-reference/openapi.json get /market-data/settings Get current market data settings for debugging. Returns: Dictionary with current market data configuration including cleanup and timeout settings # Get Order Book Source: https://condor.hummingbot.org/api-reference/market-data/get-order-book /api-reference/openapi.json post /market-data/order-book Get order book snapshot with specified depth. Args: request: Order book request with connector, trading pair, and depth market_data_manager: Injected market data feed manager Returns: Order book snapshot with bids and asks Raises: HTTPException: 500 if there's an error fetching order book # Get Order Book Diagnostics Source: https://condor.hummingbot.org/api-reference/market-data/get-order-book-diagnostics /api-reference/openapi.json get /market-data/order-book/diagnostics/{connector_name} Get diagnostics for a connector's order book tracker. Returns detailed information about the order book tracker status including: - Task status (running/crashed) - WebSocket connection status - Metrics (messages processed, latency, etc.) - Current order book state Args: connector_name: The connector to diagnose (e.g., "binance") account_name: Optional account name for trading connectors Returns: Diagnostic information dictionary # Get Pool Prices Source: https://condor.hummingbot.org/api-reference/market-data/get-pool-prices /api-reference/openapi.json get /market-data/pool-prices Get a snapshot of the merged price pool used for cross-rate resolution. # Get Price For Quote Volume Source: https://condor.hummingbot.org/api-reference/market-data/get-price-for-quote-volume /api-reference/openapi.json post /market-data/order-book/price-for-quote-volume Get the price required to fill a specific quote volume on the order book. Args: request: Request with connector, trading pair, quote volume, and side market_data_manager: Injected market data feed manager Returns: Order book query result with price and volume information # Get Price For Volume Source: https://condor.hummingbot.org/api-reference/market-data/get-price-for-volume /api-reference/openapi.json post /market-data/order-book/price-for-volume Get the price required to fill a specific volume on the order book. Args: request: Request with connector, trading pair, volume, and side market_data_manager: Injected market data feed manager Returns: Order book query result with price and volume information # Get Prices Source: https://condor.hummingbot.org/api-reference/market-data/get-prices /api-reference/openapi.json post /market-data/prices Get current prices for specified trading pairs from a connector. Args: request: Price request with connector name and trading pairs market_data_manager: Injected market data feed manager Returns: Current prices for the specified trading pairs Raises: HTTPException: 500 if there's an error fetching prices # Get Quote Volume For Price Source: https://condor.hummingbot.org/api-reference/market-data/get-quote-volume-for-price /api-reference/openapi.json post /market-data/order-book/quote-volume-for-price Get the quote volume available at a specific price level on the order book. Args: request: Request with connector, trading pair, price, and side market_data_manager: Injected market data feed manager Returns: Order book query result with quote volume information # Get Rates Source: https://condor.hummingbot.org/api-reference/market-data/get-rates /api-reference/openapi.json post /market-data/rates Resolve cross-rates for trading pairs from the collected ticker pool. Rates are resolved via direct, reverse or bridged paths. When ``connector`` is set, only that exchange's tickers are used; otherwise the merged multi-exchange pool is used. # Get Single Rate Source: https://condor.hummingbot.org/api-reference/market-data/get-single-rate /api-reference/openapi.json get /market-data/rate/{trading_pair} Resolve a cross-rate for a single ``BASE-QUOTE`` trading pair from the ticker pool. Pass ``?connector=`` to restrict resolution to a single exchange's tickers. # Get Tickers Source: https://condor.hummingbot.org/api-reference/market-data/get-tickers /api-reference/openapi.json get /market-data/tickers Get tickers grouped by connector, with 24h base and quote volume where available. Without ``connectors`` this returns the collected pool as-is. Naming connectors fetches them on demand (concurrently) through keyless public data connectors when the cache is missing or stale, so it works for exchanges no API keys are configured for; those connectors then join the background refresh cycle. A connector that fails does not remove the others from the response: it is reported under ``errors`` alongside the successful results. An error status is returned only when nothing could be served at all. # Get Volume For Price Source: https://condor.hummingbot.org/api-reference/market-data/get-volume-for-price /api-reference/openapi.json post /market-data/order-book/volume-for-price Get the volume available at a specific price level on the order book. Args: request: Request with connector, trading pair, price, and side market_data_manager: Injected market data feed manager Returns: Order book query result with volume information # Get Vwap For Volume Source: https://condor.hummingbot.org/api-reference/market-data/get-vwap-for-volume /api-reference/openapi.json post /market-data/order-book/vwap-for-volume Get the VWAP (Volume Weighted Average Price) for a specific volume on the order book. Args: request: Request with connector, trading pair, volume, and side market_data_manager: Injected market data feed manager Returns: Order book query result with VWAP information # Remove Trading Pair Source: https://condor.hummingbot.org/api-reference/market-data/remove-trading-pair /api-reference/openapi.json post /market-data/trading-pair/remove Remove a trading pair from order book tracking. This endpoint removes a trading pair from a connector's order book tracker, cleaning up resources for pairs that are no longer needed. Args: request: Request with connector name, trading pair, and optional account name Returns: TradingPairResponse with success status and message Raises: HTTPException: 500 if removal fails # Restart Order Book Tracker Source: https://condor.hummingbot.org/api-reference/market-data/restart-order-book-tracker /api-reference/openapi.json post /market-data/order-book/restart/{connector_name} Restart the order book tracker for a connector. Use this endpoint when the order book is stale (WebSocket disconnected). This will: 1. Stop the existing order book tracker 2. Restart it with the same trading pairs 3. Wait for the WebSocket to reconnect Args: connector_name: The connector to restart (e.g., "binance") account_name: Optional account name for trading connectors Returns: Restart status with success/failure and trading pairs # Get Accounts Distribution Source: https://condor.hummingbot.org/api-reference/portfolio/get-accounts-distribution /api-reference/openapi.json get /portfolio/accounts-distribution Get portfolio distribution by accounts with percentages. Returns: Dictionary with account distribution including percentages, values, and breakdown by connectors # Get Portfolio Distribution Source: https://condor.hummingbot.org/api-reference/portfolio/get-portfolio-distribution /api-reference/openapi.json post /portfolio/distribution Get portfolio distribution by tokens with percentages across all or filtered accounts. Args: filter_request: JSON payload with filtering criteria Returns: Dictionary with token distribution including percentages, values, and breakdown by accounts/connectors # Get Portfolio History Source: https://condor.hummingbot.org/api-reference/portfolio/get-portfolio-history /api-reference/openapi.json post /portfolio/history Get the historical state of all or filtered accounts portfolio with pagination and interval sampling. The interval parameter allows you to control data granularity: - 5m: Raw data (default, collected every 5 minutes) - 15m: One data point every 15 minutes - 30m: One data point every 30 minutes - 1h: One data point every hour - 4h: One data point every 4 hours - 12h: One data point every 12 hours - 1d: One data point every day Using larger intervals significantly reduces response size and improves performance. Args: filter_request: JSON payload with filtering criteria (account_names, connector_names, start_time, end_time, limit, cursor, interval) Returns: Paginated response with historical portfolio data sampled at the requested interval # Get Portfolio State Source: https://condor.hummingbot.org/api-reference/portfolio/get-portfolio-state /api-reference/openapi.json post /portfolio/state Get the current state of all or filtered accounts portfolio. Args: filter_request: JSON payload with filtering criteria including: - account_names: Optional list of account names to filter by - connector_names: Optional list of connector names to filter by - skip_gateway: If True, skip Gateway wallet balance updates for faster CEX-only queries - refresh: If True, refresh balances from exchanges. If False, return cached state. Returns: Dict containing account states with connector balances and token information # Root Source: https://condor.hummingbot.org/api-reference/root /api-reference/openapi.json get / API root endpoint returning basic information. # Create Or Update Script Source: https://condor.hummingbot.org/api-reference/scripts/create-or-update-script /api-reference/openapi.json post /scripts/{script_name} Create or update a script. Args: script_name: Name of the script (from URL path) script: Script object with content Returns: Success message when script is saved Raises: HTTPException: 400 if save error occurs # Create Or Update Script Config Source: https://condor.hummingbot.org/api-reference/scripts/create-or-update-script-config /api-reference/openapi.json post /scripts/configs/{config_name} Create or update script configuration. Args: config_name: Name of the configuration file config: Configuration dictionary to save Returns: Success message when configuration is saved Raises: HTTPException: 400 if save error occurs # Delete Script Source: https://condor.hummingbot.org/api-reference/scripts/delete-script /api-reference/openapi.json delete /scripts/{script_name} Delete a script. Args: script_name: Name of the script to delete Returns: Success message when script is deleted Raises: HTTPException: 404 if script not found # Delete Script Config Source: https://condor.hummingbot.org/api-reference/scripts/delete-script-config /api-reference/openapi.json delete /scripts/configs/{config_name} Delete script configuration. Args: config_name: Name of the configuration file to delete Returns: Success message when configuration is deleted Raises: HTTPException: 404 if configuration not found # Get Script Source: https://condor.hummingbot.org/api-reference/scripts/get-script /api-reference/openapi.json get /scripts/{script_name} Get script content by name. Args: script_name: Name of the script to retrieve Returns: Dictionary with script name and content Raises: HTTPException: 404 if script not found # Get Script Config Source: https://condor.hummingbot.org/api-reference/scripts/get-script-config /api-reference/openapi.json get /scripts/configs/{config_name} Get script configuration by config name. Args: config_name: Name of the configuration file to retrieve Returns: Dictionary with script configuration Raises: HTTPException: 404 if configuration not found # Get Script Config Template Source: https://condor.hummingbot.org/api-reference/scripts/get-script-config-template /api-reference/openapi.json get /scripts/{script_name}/config/template Get script configuration template with default values. Args: script_name: Name of the script to get template for Returns: Dictionary with configuration template and default values Raises: HTTPException: 404 if script configuration class not found # List Script Configs Source: https://condor.hummingbot.org/api-reference/scripts/list-script-configs /api-reference/openapi.json get /scripts/configs/ List all script configurations with metadata. Returns: List of script configuration objects with name, script_file_name, and other metadata # List Scripts Source: https://condor.hummingbot.org/api-reference/scripts/list-scripts /api-reference/openapi.json get /scripts/ List all available scripts. Returns: List of script names (without .py extension) # Get Storage Overview Source: https://condor.hummingbot.org/api-reference/storage/get-storage-overview /api-reference/openapi.json get /storage/ Get disk usage overview for bots directories (archived, instances, conf). # Get System Resources Source: https://condor.hummingbot.org/api-reference/system/get-system-resources /api-reference/openapi.json get /system/resources Get host machine CPU, RAM, and disk usage. Returns: Dictionary with current CPU, memory, and disk utilization for the host. # Cancel Order Source: https://condor.hummingbot.org/api-reference/trading/cancel-order /api-reference/openapi.json post /trading/{account_name}/{connector_name}/orders/{client_order_id}/cancel Cancel a specific order by its client order ID. Args: account_name: Name of the account connector_name: Name of the connector client_order_id: Client order ID to cancel trading_pair: Trading pair for the order accounts_service: Injected accounts service Returns: Success message with cancelled order ID Raises: HTTPException: 404 if account/connector not found, 500 for cancellation errors # Get Active Orders Source: https://condor.hummingbot.org/api-reference/trading/get-active-orders /api-reference/openapi.json post /trading/orders/active Get active (in-flight) orders across all or filtered accounts and connectors. This endpoint fetches real-time active orders directly from the connectors' in_flight_orders property, providing current order status, fill amounts, and other live order data. Args: filter_request: JSON payload with filtering criteria Returns: Paginated response with active order data and pagination metadata Raises: HTTPException: 500 if there's an error fetching orders # Get Funding Payments Source: https://condor.hummingbot.org/api-reference/trading/get-funding-payments /api-reference/openapi.json post /trading/funding-payments Get funding payment history across all or filtered perpetual connectors. This endpoint retrieves historical funding payment records including funding rates, payment amounts, and position data at time of payment. Args: filter_request: JSON payload with filtering criteria Returns: Paginated response with funding payment data and pagination metadata Raises: HTTPException: 500 if there's an error fetching funding payments # Get Orders Source: https://condor.hummingbot.org/api-reference/trading/get-orders /api-reference/openapi.json post /trading/orders/search Get historical order data across all or filtered accounts from the database/registry. Args: filter_request: JSON payload with filtering criteria Returns: Paginated response with historical order data and pagination metadata # Get Position Mode Source: https://condor.hummingbot.org/api-reference/trading/get-position-mode /api-reference/openapi.json get /trading/{account_name}/{connector_name}/position-mode Get current position mode for a perpetual connector. Args: account_name: Name of the account connector_name: Name of the perpetual connector Returns: Dictionary with current position mode, connector name, and account name Raises: HTTPException: 400 if not a perpetual connector # Get Positions Source: https://condor.hummingbot.org/api-reference/trading/get-positions /api-reference/openapi.json post /trading/positions Get current positions across all or filtered perpetual connectors. This endpoint fetches real-time position data directly from the connectors, including unrealized PnL, leverage, funding fees, and margin information. Args: filter_request: JSON payload with filtering criteria Returns: Paginated response with position data and pagination metadata Raises: HTTPException: 500 if there's an error fetching positions # Get Trades Source: https://condor.hummingbot.org/api-reference/trading/get-trades /api-reference/openapi.json post /trading/trades Get trade history across all or filtered accounts with complex filtering. Args: filter_request: JSON payload with filtering criteria Returns: Paginated response with trade data and pagination metadata # Place Trade Source: https://condor.hummingbot.org/api-reference/trading/place-trade /api-reference/openapi.json post /trading/orders Place a buy or sell order using a specific account and connector. Args: trade_request: Trading request with account, connector, trading pair, type, amount, etc. accounts_service: Injected accounts service Returns: TradeResponse with order ID and trading details Raises: HTTPException: 400 for invalid parameters, 404 for account/connector not found, 500 for trade execution errors # Set Leverage Source: https://condor.hummingbot.org/api-reference/trading/set-leverage /api-reference/openapi.json post /trading/{account_name}/{connector_name}/leverage Set leverage for a specific trading pair on a perpetual connector. Args: account_name: Name of the account connector_name: Name of the perpetual connector request: Leverage request with trading pair and leverage value accounts_service: Injected accounts service Returns: Dictionary with success status and message Raises: HTTPException: 400 for invalid parameters or non-perpetual connector, 404 for account/connector not found, 500 for execution errors # Set Position Mode Source: https://condor.hummingbot.org/api-reference/trading/set-position-mode /api-reference/openapi.json post /trading/{account_name}/{connector_name}/position-mode Set position mode for a perpetual connector. Args: account_name: Name of the account connector_name: Name of the perpetual connector position_mode: Position mode to set (HEDGE or ONEWAY) Returns: Success message with status Raises: HTTPException: 400 if not a perpetual connector or invalid position mode # Backtesting Source: https://condor.hummingbot.org/bots/backtesting Use historical data to research strategy parameters and validate behavior Backtesting simulates your strategy against historical exchange data. Use it to understand parameter sensitivity and validate that your strategy behaves as expected—not to predict exact live results. ## Philosophy Backtesting market making strategies cannot perfectly simulate reality: 1. **Queue Position** — Order books are FIFO (First In, First Out). You never know your position in the queue because exchanges don't reveal participant order positions. 2. **Path Dependency** — If one fill differs between backtest and live, all subsequent behavior diverges. The correlation breaks with a single mismatched order. 3. **Order Book Opacity** — Thick order books (like USDT/BRL with \$600K at the first level) mean your orders may never fill even if price touches your level. Don't trust absolute P\&L numbers from backtests. Use backtesting as a **parameter research tool**, not a prediction engine. ## What Backtesting Is Good For | Use Case | Example | | --------------------- | ----------------------------------------------------------------------- | | Parameter sensitivity | "What happens if I change portfolio allocation from 2% to 10%?" | | Behavior validation | "Is my strategy placing sell orders only after reaching 30% inventory?" | | Relative comparison | "Does configuration A generate more volume than B?" | | Strategy debugging | "Why did the bot stop trading in this scenario?" | ## Supported Executors The backtesting engine works with controllers using these executors: | Executor | Supported | Notes | | ------------------ | --------- | ------------------------------------ | | Position Executor | Yes | Full support including position hold | | DCA Executor | Yes | Dollar-cost averaging strategies | | Grid Executor | Yes | Grid trading strategies | | Order Executor | Coming | Not yet supported | | Arbitrage Executor | No | Requires order book data | | XEMM Executor | No | Requires cross-exchange order books | ## Candle Resolution **One-second candles are crucial for market making backtests.** With one-minute candles, the engine can only simulate one fill per minute. Real market making often has 30+ fills per minute. One-second resolution captures the granularity needed. Currently, only Binance Spot provides one-second candles. You may need a server in a region where Binance is accessible, or use Tailscale to route requests through an allowed region. ## Running Backtests ### Via Hummingbot Scripts Hummingbot includes backtesting scripts in the `scripts/` folder: ```bash theme={null} cd ~/hummingbot python scripts/backtest_pmm_mister.py 0.5 # Backtest 0.5 days ``` The script outputs: * Processing time (e.g., "34 seconds for 0.5 days") * Interactive Plotly chart in browser ### Via Condor Web Dashboard 1. Navigate to **Bots** → **Backtest** 2. Select a controller config 3. Set the time range 4. Run and view results ## Interpreting Results ### Chart Panes The Plotly output has three panes: | Pane | Content | | ---------- | ----------------------------- | | **Top** | Candles with executor markers | | **Middle** | P\&L lines and volume | | **Bottom** | Position size over time | ### P\&L Lines | Line | Meaning | | --------------------------------- | ---------------------------------- | | **Yellow** | Total P\&L (realized + unrealized) | | **Purple** | Position unrealized P\&L | | **Gap between yellow and purple** | Executor realized P\&L | ### Executor Colors | Color | Meaning | | ---------- | -------------------------------------------------------------- | | **White** | Orders placed but not filled (early stop/refresh) | | **Green** | Filled orders that hit take profit | | **Blue** | Filled orders moved to position hold (market went against you) | | **Purple** | Executors reducing position (selling accumulated inventory) | ## Example Analysis ### Portfolio Allocation: 2% vs 10% **2% allocation:** * Inventory builds gradually * Trading continues through drawdowns * More consistent activity **10% allocation:** * Inventory builds quickly * May hit max position during drawdowns * Trading stops if position is underwater and at max (profit protection enabled) ``` # With profit_protection=true and 10% allocation: # If market drops while at max position → bot stops trading # Bot resumes only when position becomes profitable again ``` ## PMM Mister Parameters Key parameters that affect backtesting behavior: | Parameter | Description | Example | | ------------------------------- | --------------------------------------- | ---------- | | `portfolio_allocation` | % of capital placed around mid price | 0.02 (2%) | | `min_base_percentage` | Minimum inventory before selling | 0.30 (30%) | | `max_base_percentage` | Maximum inventory before buying stops | 0.70 (70%) | | `max_active_executors_by_level` | How many times to replace a level | 20 | | `cooldown_time` | Seconds between level replacements | 30 | | `price_distance_tolerance` | Minimum price move before replacement | 0.0002 | | `activization_time` | Seconds before inventory is "permanent" | 1660 | | `profit_protection` | Only sell if position is profitable | true | ## Trading Bots vs Trading Agents | Aspect | Trading Bots | Trading Agents | | ----------- | -------------------- | ----------------------------- | | Logic | Deterministic, coded | LLM-driven decisions | | Tick speed | 1 second or faster | 60+ seconds | | Backtesting | Fully supported | Not applicable | | Use case | HFT market making | Oversight, strategy selection | Agents can oversee and modify bot parameters, but they operate at a higher level (every minute+) rather than every tick. This separation keeps HFT performance while adding intelligent oversight. # Controllers Source: https://condor.hummingbot.org/bots/controllers V2 strategy components for algorithmic trading **Controllers** are V2 strategy components that implement algorithmic trading logic. They run inside bot containers and manage complex, multi-step trading strategies by creating and supervising executors. ## What Are Controllers? Controllers are Python classes that: * Define trading strategy logic * Process market data into a signal * Create and manage executors * Expose status and custom metrics ```mermaid theme={null} flowchart TB subgraph Bot["Bot Container"] C[Controller] C --> E1[Executor 1] C --> E2[Executor 2] C --> E3[Executor 3] end M[Market Data] --> C C --> O[Orders] ``` A single bot can run **multiple controllers** at once — each targeting a different market — within one container. ## Built-in Controllers Controllers ship under three `controller_type` categories. A selection of the built-ins: | `controller_type` | Controller (`controller_name`) | Description | | --------------------- | ------------------------------ | ------------------------------------------------------ | | `market_making` | `pmm_simple` | Two-sided market making with fixed spreads | | `market_making` | `pmm_dynamic` | Market making with spreads driven by volatility | | `directional_trading` | `bollinger_v1` | Bollinger Bands trend signal | | `directional_trading` | `dman_v3` | Multi-level directional strategy | | `directional_trading` | `macd_bb_v1` | MACD + Bollinger Bands signal | | `generic` | `grid_strike` | Grid of orders across a price range | | `generic` | `arbitrage_controller` | Cross-exchange arbitrage | | `generic` | `xemm_multiple_levels` | Cross-exchange market making | | `generic` | `pmm_mister` | Market making with position hold and profit protection | `directional_trading`, `market_making`, and `generic` are controller **types**, not controller names. The exact set of built-ins tracks the [Hummingbot repository](https://github.com/hummingbot/hummingbot/tree/development/controllers) — browse `controllers/` for the current list. ## Controller Structure A controller overrides two core methods from `ControllerBase`: * **`update_processed_data()`** — pull market data and compute a signal into `self.processed_data` * **`determine_executor_actions()`** — decide which executors to create or stop The specialized base classes `DirectionalTradingControllerBase` and `MarketMakingControllerBase` already implement `determine_executor_actions()` for you, so trend and market-making controllers typically only implement `update_processed_data()` and `get_candles_config()`. This is the actual `bollinger_v1` directional controller: ```python theme={null} from typing import List from hummingbot.data_feed.candles_feed.data_types import CandlesConfig from hummingbot.strategy_v2.controllers.directional_trading_controller_base import ( DirectionalTradingControllerBase, DirectionalTradingControllerConfigBase, ) class BollingerV1ControllerConfig(DirectionalTradingControllerConfigBase): controller_name: str = "bollinger_v1" interval: str = "3m" bb_length: int = 100 bb_std: float = 2.0 bb_long_threshold: float = 0.0 bb_short_threshold: float = 1.0 class BollingerV1Controller(DirectionalTradingControllerBase): def __init__(self, config: BollingerV1ControllerConfig, *args, **kwargs): self.config = config self.max_records = config.bb_length super().__init__(config, *args, **kwargs) def get_candles_config(self) -> List[CandlesConfig]: return [CandlesConfig( connector=self.config.candles_connector, trading_pair=self.config.candles_trading_pair, interval=self.config.interval, max_records=self.max_records, )] async def update_processed_data(self): df = self.market_data_provider.get_candles_df( connector_name=self.config.candles_connector, trading_pair=self.config.candles_trading_pair, interval=self.config.interval, max_records=self.max_records, ) df.ta.bbands(length=self.config.bb_length, std=self.config.bb_std, append=True) bbp = df[f"BBP_{self.config.bb_length}_{self.config.bb_std}"] df["signal"] = 0 df.loc[bbp < self.config.bb_long_threshold, "signal"] = 1 df.loc[bbp > self.config.bb_short_threshold, "signal"] = -1 self.processed_data["signal"] = df["signal"].iloc[-1] self.processed_data["features"] = df ``` Market data comes from `self.market_data_provider` (e.g. `get_candles_df(...)`, `get_price_by_type(...)`), not from ad-hoc fetches. ## Controller Configuration Each controller instance is configured by a YAML file. This is a real `pmm_simple` config: ```yaml theme={null} id: pmm_simple_hype controller_name: pmm_simple controller_type: market_making total_amount_quote: '100' connector_name: hyperliquid trading_pair: HYPE-USDC buy_spreads: - 0.001 sell_spreads: - 0.001 buy_amounts_pct: - '1' sell_amounts_pct: - '1' executor_refresh_time: 60 cooldown_time: 15 leverage: 1 position_mode: HEDGE stop_loss: '0.005' take_profit: '0.002' time_limit: 2700 ``` ## Deploying a Controller The usual flow is: create one or more controller configs (in the dashboard **Editor**, in Telegram, or via the API), then deploy a bot that runs them. ### Via Telegram ``` /bots → Create New Bot → Select one or more controller configs → Deploy ``` ### Via API Deploy a bot from existing controller config files (without the `.yml` extension). You can pass several configs to run multiple controllers in one bot: ```bash theme={null} curl -u YOUR_USERNAME:YOUR_PASSWORD -X POST http://localhost:8000/bot-orchestration/deploy-v2-controllers \ -H "Content-Type: application/json" \ -d '{ "instance_name": "hype-mm", "credentials_profile": "master_account", "controllers_config": ["conf_market_making.pmm_simple_hype"], "max_global_drawdown_quote": 50, "max_controller_drawdown_quote": 25 }' ``` ## Updating a Running Controller Controllers can be reconfigured while running — no restart required. Only fields a controller marks as updatable are applied; everything else (for example, the connector name) is ignored on update. A config field is made updatable with `json_schema_extra`: ```python theme={null} from pydantic import Field total_amount_quote: Decimal = Field( default=Decimal("100"), json_schema_extra={"is_updatable": True}, ) ``` At runtime, `ControllerBase.update_config(new_config)` copies over only the fields flagged `is_updatable`. In Condor, push a live update from the dashboard **Bots → Active** tab by editing the config and saving. ## Custom Controller Metrics Override `get_custom_info()` to publish controller-specific fields alongside the standard performance report. The payload is sent over MQTT to the Hummingbot API, so keep it small (recommended \< 1 KB): ```python theme={null} def get_custom_info(self) -> dict: return {"inventory_pct": float(self.current_inventory_pct)} ``` These fields are stored with each controller snapshot and surfaced per controller in the dashboard. ## Creating Custom Controllers 1. Create a new Python file under `controllers/` (in the matching `controller_type` subfolder) 2. Subclass the appropriate base — `ControllerBase`, `DirectionalTradingControllerBase`, or `MarketMakingControllerBase` 3. Implement `update_processed_data()` (and `determine_executor_actions()` if you subclass `ControllerBase` directly) 4. Add a config file and deploy it Custom controllers run in stock Hummingbot too, and can be uploaded directly from the dashboard Editor. ## Monitoring Controllers The Hummingbot API records a snapshot of every running controller every 5 minutes, giving you a time series of P\&L and volume rather than a single point in time. ```bash theme={null} # Latest snapshot per controller curl -u YOUR_USERNAME:YOUR_PASSWORD "http://localhost:8000/bot-orchestration/controller-performance-latest?bot_name=hype-mm" # Time-series history (interval one of 5m, 15m, 30m, 1h, 4h, 12h, 1d) curl -u YOUR_USERNAME:YOUR_PASSWORD "http://localhost:8000/bot-orchestration/controller-performance-history?bot_name=hype-mm&controller_id=pmm_simple_hype&interval=5m" ``` In Condor, view this history under **Bots → Runs**, and the combined P\&L across a bot's controllers under **Bots → Active**. # How Bots Work Source: https://condor.hummingbot.org/bots/overview Docker containers for long-running trading automation **Bots** are Docker containers running Hummingbot instances for long-running automation tasks. They execute [Scripts](/bots/scripts) for simpler tasks or [Controllers](/bots/controllers) for algorithmic trading strategies. ## Bots vs Executors | Aspect | Executors | Bots | | ------------- | ------------------------------ | ---------------------------- | | **Lifecycle** | Short-lived (minutes to hours) | Long-running (days to weeks) | | **Scope** | Single operation | Complex strategies | | **Control** | Agent-controlled | Autonomous or supervised | | **Use Case** | Individual trades | Continuous market making | ```mermaid theme={null} flowchart TB subgraph Agent["Trading Agent"] A[Agent Logic] end subgraph Bots["Bot Containers"] B1[Bot: MM Strategy] B2[Bot: Grid Trading] end subgraph Executors["Executors"] E1[Position Executor] E2[Order Executor] end A --> B1 & B2 A --> E1 & E2 B1 & B2 --> E1 & E2 ``` ## When to Use Bots | Scenario | Use | Reason | | --------------------------- | -------- | ---------------------------- | | Single directional trade | Executor | Short-lived, defined outcome | | Continuous market making | Bot | Long-running, complex logic | | One-time swap | Executor | Simple, immediate | | Multi-leg arbitrage | Bot | Requires coordination | | LP position with time limit | Executor | Self-contained lifecycle | | 24/7 grid trading | Bot | Persistent, adaptive | ## Bot Lifecycle ### Creation Create a bot via Telegram or API: **Telegram**: ``` /bots → Create New Bot → Select one or more controller configs ``` **API**: Deploy a bot from existing controller config files (without `.yml`). Pass several configs to run multiple controllers in one bot: ```bash theme={null} curl -u YOUR_USERNAME:YOUR_PASSWORD -X POST http://localhost:8000/bot-orchestration/deploy-v2-controllers \ -H "Content-Type: application/json" \ -d '{ "instance_name": "my-market-maker", "credentials_profile": "master_account", "controllers_config": ["conf_market_making.pmm_simple_1"] }' ``` ### Starting and Stopping **Telegram**: ``` /bots → Select bot → Start/Stop ``` **API**: Start and stop take the bot name in the request body. ```bash theme={null} # Start curl -u YOUR_USERNAME:YOUR_PASSWORD -X POST http://localhost:8000/bot-orchestration/start-bot \ -H "Content-Type: application/json" -d '{"bot_name": "my-market-maker"}' # Stop curl -u YOUR_USERNAME:YOUR_PASSWORD -X POST http://localhost:8000/bot-orchestration/stop-bot \ -H "Content-Type: application/json" -d '{"bot_name": "my-market-maker"}' # Stop and archive curl -u YOUR_USERNAME:YOUR_PASSWORD -X POST http://localhost:8000/bot-orchestration/stop-and-archive-bot/my-market-maker ``` ### Monitoring **Telegram**: `/bots` shows: * Bot status (running/stopped) * Uptime and resource usage * Recent P\&L * Active orders and positions **API**: ```bash theme={null} # Status of all bots curl -u YOUR_USERNAME:YOUR_PASSWORD http://localhost:8000/bot-orchestration/status # Status of a single bot curl -u YOUR_USERNAME:YOUR_PASSWORD http://localhost:8000/bot-orchestration/my-market-maker/status ``` The Hummingbot API also records a snapshot of every running controller every 5 minutes, so you get a time series of P\&L and volume rather than a single point. Query it with `/bot-orchestration/controller-performance-history` (see [Monitoring Controllers](/bots/controllers#monitoring-controllers)), or view it in Condor under **Bots → Runs**. When a bot runs multiple controllers, **Bots → Active** charts their combined P\&L with toggles to isolate each one. ### Logs **Telegram**: ``` /bots → Select bot → View Logs ``` Each bot runs in its own Docker container, so you can also read logs directly: ```bash theme={null} docker logs hummingbot-my-market-maker ``` ## Container Isolation Each bot runs in an isolated Docker container: * Separate filesystem * Independent network * Own log streams * Can be started/stopped individually ```bash theme={null} # List bot containers docker ps --filter "name=hummingbot" # View container logs docker logs hummingbot-my-market-maker ``` ## Integration with Agents Agents can deploy and manage bots programmatically: The `manage_bots` MCP tool handles the full lifecycle through a single `action` parameter (`deploy`, `status`, `logs`, `stop_bot`, `start_controllers`, `stop_controllers`, `get_config`, `update_config`): ```python theme={null} # Agent deploys a bot from one or more controller configs result = await mcp_tools.manage_bots( action="deploy", bot_name="eth-mm", controllers_config=["conf_market_making.pmm_simple_1"], ) # Agent checks bot status status = await mcp_tools.manage_bots(action="status", bot_name="eth-mm") ``` # Scripts Source: https://condor.hummingbot.org/bots/scripts V1 scripts for simpler trading automation **Scripts** are V1 Python scripts that run inside bot containers for simpler automation tasks. They're ideal for straightforward strategies that don't require the full controller framework. ## Scripts vs Controllers | Aspect | Scripts | Controllers | | ----------------------- | ------------------- | ------------------------ | | **Complexity** | Simple, single-file | Complex, multi-component | | **Framework** | V1 (legacy) | V2 (current) | | **Best For** | Quick automation | Full strategies | | **Executor Management** | Manual | Automatic | ## Built-in Scripts | Script | Description | | -------------------------- | ------------------------------------- | | `simple_pmm` | Basic pure market making | | `simple_xemm` | Cross-exchange market making | | `spot_perpetual_arbitrage` | Spot-perp basis trading | | `twap` | Time-weighted average price execution | ## Script Structure ```python theme={null} # scripts/simple_strategy.py from hummingbot.strategy.script_strategy_base import ScriptStrategyBase class SimpleStrategy(ScriptStrategyBase): """ A simple trading script. """ # Configuration trading_pair = "SOL-USDT" exchange = "binance" order_amount = 10 def on_tick(self): """Called every tick.""" # Get current price price = self.connectors[self.exchange].get_mid_price(self.trading_pair) # Simple logic if self.should_buy(price): self.buy( connector_name=self.exchange, trading_pair=self.trading_pair, amount=self.order_amount, order_type=OrderType.MARKET ) def should_buy(self, price): """Determine if we should buy.""" # Your logic here return False ``` ## Running Scripts ### Via Telegram ``` /bots → Create New Bot → Script → Select script → Configure → Start ``` ### Via API ```bash theme={null} curl -u YOUR_USERNAME:YOUR_PASSWORD -X POST http://localhost:8000/bot-orchestration/deploy-v2-script \ -H "Content-Type: application/json" \ -d '{ "instance_name": "my-script-bot", "credentials_profile": "master_account", "script": "simple_pmm", "script_config": "simple_pmm" }' ``` ## Script Configuration Scripts use a simpler configuration format: ```yaml theme={null} # config/simple_pmm.yml script_name: simple_pmm # Exchange settings exchange: binance trading_pair: BTC-USDT # Strategy parameters bid_spread: 0.001 ask_spread: 0.001 order_amount: 0.001 order_refresh_time: 15 ``` ## Creating Custom Scripts 1. Create a Python file in `scripts/` 2. Inherit from `ScriptStrategyBase` 3. Implement `on_tick()` method ```python theme={null} # scripts/my_script.py from hummingbot.strategy.script_strategy_base import ScriptStrategyBase from hummingbot.core.data_type.common import OrderType class MyScript(ScriptStrategyBase): """ My custom trading script. """ # Default configuration markets = {"binance": {"SOL-USDT"}} def on_tick(self): """Main strategy logic.""" for connector_name, trading_pairs in self.markets.items(): for trading_pair in trading_pairs: self.process_pair(connector_name, trading_pair) def process_pair(self, connector_name, trading_pair): """Process a single trading pair.""" connector = self.connectors[connector_name] mid_price = connector.get_mid_price(trading_pair) # Your logic here self.logger().info(f"{trading_pair} mid price: {mid_price}") ``` ## Script Lifecycle Scripts have a simpler lifecycle than controllers: ```python theme={null} def __init__(self): """Initialize the script.""" super().__init__() # Setup code def on_tick(self): """Called every tick interval.""" # Main logic def on_stop(self): """Called when script stops.""" # Cleanup code ``` ## When to Use Scripts **Use Scripts for:** * Simple market making * Basic arbitrage * One-off automation tasks * Learning/prototyping **Use Controllers for:** * Complex multi-executor strategies * Advanced risk management * Production trading systems * Strategies requiring state management # Skill: Agent Builder Source: https://condor.hummingbot.org/condor-agent/agent-builder Create and configure Trading Agents via Telegram or manually Trading Agents can be created using the `/agent` command in Telegram or by manually creating the required files. ## Why Use Agent Builder? The Agent Builder guides you through a structured process that: 1. **Defines your strategy** in a structured way so the agent has context 2. **Creates market data routines** so analysis is deterministic and reproducible 3. **Builds decision logic** in `agent.md` with rules and constraints 4. **Tests reasoning first** before risking real money 5. **Deploys with confidence** after verifying behavior This flow exists because agents that work well require iteration. You want to verify the agent's reasoning before letting it trade. ## 5-Phase Development Flow | Phase | Description | | -------------------------- | -------------------------------------------------------- | | **1. Strategy Design** | Define your edge, timeframe, and instruments | | **2. Market Data Routine** | Create a Python routine to fetch and process market data | | **3. Strategy Logic** | Write `agent.md` with decision rules and constraints | | **4. Dry Run** | Test reasoning without trading capability | | **5. Run Once** | Test with real trading—one tick only | | **6. Loop** | Deploy live with frequency and risk limits | ### Run Modes These modes exist for debugging—you don't want to put an agent to trade without understanding its reasoning first. | Mode | Behavior | | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `dry_run` | One tick, no trading capability. The agent gets told in its prompt that executor creation is blocked. You see how it gathers data, reasons about it, and what decision it *would* take. | | `run_once` | One tick with real trading. Before looping, try it once to see actual outputs. | | `loop` | Standard mode. Ticks every `frequency_sec` until stopped or `max_ticks` reached. | **Recommended flow**: Start with dry\_run to verify reasoning, then run\_once to test with real money, then loop when confident. ## Via Telegram Use `/agent` → **Switch Mode** → **Agent Builder** to start the guided flow: ### Phase 1: Strategy Design The Agent Builder asks questions to understand your goals: ``` Agent Builder: I need to understand your goals. What kind of strategy? 1. Grid strategy 2. DCA 3. Trend following 4. Mean reversion 5. Other (describe) ``` You'll answer questions about: * Strategy type (grid, DCA, momentum, etc.) * Direction (long only, short only, or both) * Budget allocation * Timeframe (scalping, intraday, swing) * Risk tolerance ### Phase 2: Market Data Routine The Agent Builder creates a Python routine to fetch and analyze market data: ``` You: Create a local routine for the agent to analyze where to place grids Agent Builder: Creating market data routine... ``` The routine fetches candles, order book data, and calculates indicators. This routine runs **deterministically**—same input always produces same output. The agent doesn't spend tokens computing indicators at runtime. ### Phase 3: Strategy Logic The Agent Builder generates `agent.md` with your decision rules. You can review and refine: ``` You: I'm seeing that the agent created two grids below the current price and they were closed by take profit immediately. Can you fix that? Agent Builder: I see the issue. Let me update the agent.md to ensure grid prices straddle the current price... ``` ### Phase 4: Dry Run Test reasoning without real trading: ``` /agent → Select Agent → Dry Run ``` The dry run shows: * What data the agent received * How it reasoned about the data * What decision it *would* make Review the dry run in the web dashboard under **Agents → \[Agent] → Sessions**. ### Phase 5: Deploy When confident, start a live session: ``` /agent → Select Agent → Start Session ``` ## Manual Creation Create a new agent directory with the required files: ```bash theme={null} mkdir -p ~/condor/trading_agents/my-strategy/{sessions,dry_runs,routines} ``` ### Directory Structure ``` trading_agents/my-strategy/ ├── agent.md # Strategy definition ├── learnings.md # Agent-populated insights ├── routines/ # Agent-specific routines ├── sessions/ # Live trading session data └── dry_runs/ # Dry run session data ``` ### agent.md The strategy definition with YAML frontmatter and Markdown instructions: ```yaml theme={null} --- id: abc123def456 name: Grid Scalper SOL-USDT description: Grid scalping on SOL-USDT perpetual agent_key: claude-code skills: [] default_config: connector_name: binance_perpetual trading_pair: SOL-USDT total_amount_quote: 300 leverage: 5 frequency_sec: 60 risk_limits: max_loss_per_tick_quote: 30 max_total_drawdown_quote: 90 default_trading_context: '' created_by: 123456789 created_at: '2026-04-10T17:59:33.147107+00:00' --- # Grid Scalper — SOL-USDT ## Objective Grid scalping on SOL-USDT (binance_perpetual). Deploy ONE grid at a time. Total budget: $300. ## Step 1 — Run Analysis Call the `spread_analyzer` routine with default config. It returns: - mid_price, best_bid, best_ask - volatility_1h, avg_candle_range_pct - recommended_spread_pct, grid_upper, grid_lower - signal (bullish_bias / bearish_bias / neutral) ## Step 2 — Decision Logic Based on the spread_analyzer output: ### Choose Direction by Signal - bullish_bias → deploy LONG grid (side=1) - bearish_bias → deploy SHORT grid (side=2) - neutral → deploy LONG grid (default) ### Skip Tick Conditions - If recommended_spread_pct < 0.08 (market too calm) - If there is already a running grid executor for this pair ### Replace Grid If mid_price has moved more than 1.0x the original spread from grid center, stop the existing grid and deploy a new one. ## Step 3 — Execute Deploy grid executor with appropriate config... ## Risk Rules - Max $300 total deployed at any time - Only ONE grid executor running at a time - If unrealized PnL drops below -$30 (10%), stop grid immediately - Log every decision with reasoning in the journal ``` ### learnings.md Start with an empty learnings file—the agent populates it as it learns: ```markdown theme={null} # Learnings ## Active Insights (Agent will add observations here) ``` When the agent discovers something valuable—like "price below EMA 7 while EMA 7 > EMA 25 indicates weakness"—it writes it to this file. Learnings persist across sessions. ## Inspecting Agent Sessions View agent sessions in the web dashboard: ``` /web → Agents → [Agent Name] → Sessions ``` For each session you can see: * **Snapshots**: Every tick's system prompt, agent response, and actions taken * **Dry Runs**: Test sessions without real trading * **Executors**: All executors created by the agent with P\&L * **Learnings**: Insights the agent has accumulated ### Analyzing Snapshots Each snapshot shows: * **System Prompt**: Everything the agent received (strategy, configs, market data) * **Agent Response**: The agent's reasoning and decision * **Actions**: Executors created, routines called This lets you understand exactly why the agent made each decision. ## Common Patterns ### Grid Scalping Deploy grids that straddle current price with tight take-profits: ``` Signal: bullish_bias → Deploy LONG grid from (mid_price - spread) to (mid_price + spread) → Take profit: 0.05% → Limit price: start_price * 0.998 (safety stop) ``` ### Dynamic Grid Replacement Replace grids when price moves significantly: ``` If mid_price moved > 1x original spread from grid center: → Stop existing grid → Deploy new grid centered on current price ``` ### One Position Constraint Many exchanges (like Hyperliquid) only allow one position per pair: ``` ## CRITICAL: One Position Only Before deploying, check for existing running executors. If one exists, do NOT deploy another. ``` ## Best Practices Always test reasoning before real trading. The dry run shows exactly what the agent would do without risking money. Move indicator calculations into routines. The agent shouldn't spend tokens computing EMAs—that should be deterministic code. Structure agent.md with explicit steps: 1) Run analysis, 2) Make decision, 3) Execute. This makes the agent's reasoning predictable. The agent writes observations to learnings.md. Review these to understand what the agent is learning and refine accordingly. # How Condor Works Source: https://condor.hummingbot.org/condor-agent/overview The assistant you chat with is itself an agent — a folder of files you can read: a brain, a skill library, and a memory store The assistant you talk to on the [Agents page](/webui/agents) is not a hard-coded chatbot. **Condor is itself an agent**, built from the same parts as the agents you create — and every one of those parts is a plain file on disk you can open: ``` agents/condor/ ├── AGENT.md # its brain: identity, tools, and operating rules ├── skills/ # how it operates — playbooks it reads before acting │ ├── agent_builder/SKILL.md │ ├── log_analyzer/SKILL.md │ └── verify_connector_support/SKILL.md └── store/ # what it remembers — per user └── user_1/ ├── MEMORY.md # one-line index, injected into every session ├── memories/ # one file per fact └── audit.log # every write and delete, timestamped ``` Agents you build with [Agent Builder](/condor-agent/agent-builder) get the same layout under `agents//`. There is only one kind of thing in Condor — an agent — and this page dissects the one you talk to. ## AGENT.md — the brain `AGENT.md` is the agent's system prompt with a small frontmatter header: ```markdown theme={null} --- name: Condor description: General trading assistant agent_key: claude-acp:sonnet --- # Condor — Trading Assistant You are Condor, a trading assistant. ... ``` `agent_key` names the LLM harness it thinks with — change it (or pick a model in the chat's model picker) and the same brain runs on a different model. The body then does three jobs: 1. **Names its tools.** Two MCP servers: `mcp-hummingbot` (market data, portfolio, executors, orders, bots, DEX discovery — the trading surface) and `condor` (notifications, routines, trading agents, servers, memory, skills, and `consult`/`delegate`). One deliberate absence: connecting or removing exchange keys is *not* available to the assistant — keys are managed by you, in [Settings → Keys and Wallets](/webui/settings#keys-and-wallets). 2. **Sets the routing discipline** (next section). 3. **States the rules**: lead with the answer; confirm dangerous actions (orders, swaps, LP changes) before acting; keep tool chains short; never explore source code. ## A coordinator, not a tool-caller The most important instruction in `AGENT.md` is a routing check that runs *before any tool call*: 1. **Does a skill match?** Its skill index is injected into context as `[SKILLS]` — if a playbook covers the request, Condor reads it and follows its steps instead of improvising. 2. **Does a domain agent match?** The `[AGENTS]` index lists your expert agents. Domain work goes to the specialist, which holds the domain's tools and memory. 3. **Only then** does it reach for raw tools. When it routes to a specialist, it picks one of two calls: **`consult`** blocks and relays the answer inline — for quick lookups and small changes you are waiting on; **`delegate`** runs the task in a detached background session that pings you when done — for multi-step work like a full deployment or anything that waits on a backtest. One rule is absolute: Condor never hand-writes routine code in chat. Authoring or fixing a routine is always delegated — to *itself*, in a background session that reads the `routine_cookbook` playbook, writes the routine into the library, and tests it before reporting back. ## Skills — how it operates A skill is a playbook: *when* to apply it, and the steps. Each is a folder holding a `SKILL.md`: ```markdown theme={null} --- name: verify_connector_support description: Check what a connector actually supports before answering capability questions when_to_use: User asks "can I use connector X?" or "does Y support Z?" created: '2026-08-12T11:51:59Z' source: chat --- ``` The `source` field is worth a pause: `builtin` skills ship with Condor, but `source: chat` means **Condor wrote this one itself** — it discovered a reusable procedure during a conversation and saved it with `manage_skill`. The library is self-editing: skills get created, refined, and deleted as the agent works, and you can open any of them in a text editor. Here is the library on a real install: | Skill | When it fires | Source | | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | | **`agent_builder`** | You ask to create, edit, dry-run, launch, or delete an autonomous trading agent. Builds minimally — role and purpose first, routines and loop strategy layered on only when wanted. The full playbook: [Skill: Agent Builder](/condor-agent/agent-builder) | `builtin` | | **`log_analyzer`** | Any question about bot, executor, or gateway health — "any errors in the logs?", "why is my bot failing?" (its triggers include Spanish phrasings). Runs the `logs_summary` routine rather than hand-rolling log calls | `builtin`, `references_routine: logs_summary` | | **`verify_connector_support`** | Capability questions — "can I use connector X?", "does Y support Z?" — check what the connector actually supports before answering | `chat` — Condor wrote it | Two more mechanics: * **A skill can carry a routine.** `references_routine: logs_summary` in `log_analyzer` means the executable part is a routine, not prose — the skill says *when*, the routine does *what*. If the routine disappears, reading the skill flags it (`routine_ok: false`) rather than silently invoking nothing. * **There is one shared library, and Condor is its only publisher.** `agents/_shared/skills/` (playbooks like `routine_cookbook`, `strategy_builder`, `backtest_flow`, `self_improve`) is read by *every* agent, but only the Condor assistant may publish into it — a shared playbook lands in every agent's context, so it must read correctly from every seat. A skill that belongs to one domain agent is written to that agent instead. Skills are advisory, not authority: executing what a playbook describes still passes the normal confirmation for dangerous actions. ## Memory — what it knows about you Skills are how the agent operates, shared across users. **Memory is what it knows about *you*** — per user, under `store/user_/`, and shared across your sessions *and* with your trading agents. `MEMORY.md` is the index — one line per memory — and it is injected into every session as `[USER MEMORY]`, so the agent starts each conversation already knowing the headline of everything it has learned. The detail lives in `memories/.md`, read on demand: ```markdown theme={null} --- name: consult_lp_agent_before_config description: Always consult solana_dex_lp_expert before setting lp_rebalancer or lp_executor parameters type: feedback created: '2026-08-21T04:35:43Z' source: chat --- Before creating any lp_rebalancer or lp_executor config on Solana, consult the solana_dex_lp_expert agent for position_width_pct, rebalance_threshold_pct, ... Skipping this caused two failed deployments (20% width → 102 bins, Meteora cap is 69). **Why:** Agent knows pool-specific constraints (bin caps, fee structures, volatility sizing) that aren't obvious from the pool listing alone. **How to apply:** Any time the user asks to LP on Solana ... consult solana_dex_lp_expert first, then build the config from its answer. ``` That is a real memory, and it shows the shape well: one file, one fact, typed (`preference | fact | feedback | reference`), with a **Why** and a **How to apply** — this one written after two deployments failed, so the third wouldn't. The agent's writing rules are strict: save only what is *new and stable* about the user, never ephemeral conversation detail. Memory is also accountable. Every write and delete is appended to `audit.log` as a timestamped JSON line, and you can review or delete any memory yourself — `/memory` in Telegram, or ask the agent to run `manage_memory(action="audit")`. ## Memory vs. skills, in one table | | **Memory** | **Skills** | | ------------------ | -------------------------------------------------------------- | --------------------------------------- | | What it holds | Facts about *you* | Procedures for *itself* | | Scope | Per user | Per agent, shared across users | | Injected as | `[USER MEMORY]` index | `[SKILLS]` index | | Written when | You state a preference, correct it, or it learns a stable fact | It discovers a reusable procedure | | You control it via | `/memory`, audit log | The files under `skills/`, or asking it | ## Learn more * [Skill: Agent Builder](/condor-agent/agent-builder) — the playbook Condor follows to build *new* agents with this same anatomy * [Agent Architecture](/trading-agents/architecture) — how agents execute against the Hummingbot API * [Managing Sessions](/getting-started/sessions) — watching an agent work # Arbitrage Executor Source: https://condor.hummingbot.org/executors/arbitrage-executor Simultaneous execution across markets for arbitrage profits The **Arbitrage Executor** captures price discrepancies between markets by simultaneously executing buy and sell orders, particularly useful for CEX-DEX arbitrage. ## Overview | Property | Value | | -------------- | --------------------------------------- | | Position Type | Spot | | keep\_position | Configurable | | Use Cases | CEX-DEX arbitrage, cross-market spreads | ## Configuration ```python theme={null} from hummingbot.strategy_v2.executors.arbitrage_executor.data_types import ( ArbitrageExecutorConfig, ExchangePair, ) config = ArbitrageExecutorConfig( controller_id="my-agent", buying_market=ExchangePair( exchange="uniswap_polygon_mainnet", trading_pair="WMATIC-USDT", ), selling_market=ExchangePair( exchange="binance", trading_pair="MATIC-USDT", ), order_amount=Decimal("500"), # In base asset min_profitability=Decimal("0.004"), # 0.4% minimum ) ``` ## Parameters | Parameter | Description | | ------------------- | ------------------------------- | | `buying_market` | Exchange and pair for buy side | | `selling_market` | Exchange and pair for sell side | | `order_amount` | Amount in base asset | | `min_profitability` | Minimum profit threshold | ## How It Works 1. **Validation**: Confirms trading pairs are interchangeable 2. **Price Comparison**: Monitors prices on both markets 3. **Profitability Check**: Calculates profit after all fees 4. **Simultaneous Execution**: Places both orders when profitable 5. **Tracking**: Records fill prices and actual profit ## CEX-DEX Arbitrage Common use case is arbitraging between centralized and decentralized exchanges: ```python theme={null} cex_dex_arb = ArbitrageExecutorConfig( controller_id="cex-dex-arb", buying_market=ExchangePair( exchange="jupiter", # DEX trading_pair="SOL-USDC", ), selling_market=ExchangePair( exchange="binance", # CEX trading_pair="SOL-USDT", ), order_amount=Decimal("10"), min_profitability=Decimal("0.005"), ) ``` ## Token Interchangeability The executor validates tokens are equivalent: | Check | Example | | ---------------------- | -------------------------- | | Same token | MATIC = MATIC | | Wrapped equivalents | ETH = WETH | | Stablecoin equivalents | USDT ≈ USDC (configurable) | ## Profitability Formula ``` Buy Cost = Buy Amount × Buy Price + Buy Fees + Gas Sell Revenue = Sell Amount × Sell Price - Sell Fees Net Profit = Sell Revenue - Buy Cost Profitability = Net Profit / Buy Cost Execute if: Profitability > min_profitability ``` ## Example: Polygon MATIC Arbitrage ```python theme={null} matic_arb = ArbitrageExecutorConfig( controller_id="matic-arb", buying_market=ExchangePair( exchange="uniswap_polygon_mainnet", trading_pair="WMATIC-USDT", ), selling_market=ExchangePair( exchange="binance", trading_pair="MATIC-USDT", ), order_amount=Decimal("1000"), min_profitability=Decimal("0.004"), ) ``` ## Considerations | Factor | Impact | | --------------- | ---------------------------------- | | **Gas Costs** | DEX trades have variable gas costs | | **Slippage** | Large orders may move price | | **Bridge Time** | Cross-chain requires bridging | | **Latency** | Speed matters for volatile spreads | # DCA Executor Source: https://condor.hummingbot.org/executors/dca-executor Dollar cost averaging across multiple price levels The **DCA Executor** implements Dollar Cost Averaging by spreading investment across multiple orders at different price levels, reducing the impact of volatility. ## Overview | Property | Value | | -------------- | ---------------------------------------------------- | | Position Type | Spot or Perp | | keep\_position | Configurable | | Use Cases | Building positions, averaging down, systematic entry | ## Configuration ```python theme={null} from hummingbot.strategy_v2.executors.dca_executor.data_types import ( DCAExecutorConfig, DCAMode, TrailingStop, ) config = DCAExecutorConfig( controller_id="my-agent", connector_name="binance", trading_pair="BTC-USDT", side=TradeType.BUY, leverage=1, amounts_quote=[100, 150, 200, 300], # Increasing amounts prices=[65000, 63000, 61000, 59000], # Entry prices take_profit=Decimal("0.05"), # 5% TP from avg stop_loss=Decimal("0.10"), # 10% SL from avg time_limit=86400, # 24 hours mode=DCAMode.MAKER, ) ``` ## Parameters | Parameter | Description | | ------------------- | --------------------------------------- | | `amounts_quote` | List of order amounts in quote currency | | `prices` | List of entry prices for each level | | `take_profit` | Exit target as % from average entry | | `stop_loss` | Stop loss as % from average entry | | `time_limit` | Maximum duration in seconds | | `mode` | `MAKER` (limit) or `TAKER` (market) | | `activation_bounds` | Optional price bounds for activation | ## How It Works 1. **Level Setup**: Creates orders at each price/amount pair 2. **Order Management**: Places limit orders at specified prices 3. **Fill Tracking**: Tracks fills and calculates average entry 4. **Exit Management**: Applies TP/SL relative to average entry ## DCA Modes | Mode | Behavior | | ------- | ------------------------------------------- | | `MAKER` | Places limit orders at each price level | | `TAKER` | Uses market orders when price reaches level | ## Example: Systematic BTC Entry ```python theme={null} # Build BTC position with increasing size at lower prices btc_dca = DCAExecutorConfig( controller_id="btc-dca", connector_name="binance", trading_pair="BTC-USDT", side=TradeType.BUY, amounts_quote=[ Decimal("200"), # First entry Decimal("300"), # Avg down Decimal("500"), # Larger at support ], prices=[ Decimal("67000"), Decimal("64000"), Decimal("61000"), ], take_profit=Decimal("0.08"), # 8% from avg stop_loss=Decimal("0.15"), # 15% from avg mode=DCAMode.MAKER, ) ``` ## Example: Quick DCA with Trailing Stop ```python theme={null} # Tighter DCA with trailing stop quick_dca = DCAExecutorConfig( controller_id="quick-dca", connector_name="binance_perpetual", trading_pair="ETH-USDT", side=TradeType.BUY, leverage=3, amounts_quote=[ Decimal("100"), Decimal("100"), Decimal("100"), ], prices=[ Decimal("3200"), Decimal("3100"), Decimal("3000"), ], take_profit=Decimal("0.05"), stop_loss=Decimal("0.08"), trailing_stop=TrailingStop( activation_price=Decimal("0.03"), trailing_delta=Decimal("0.015"), ), time_limit=7200, # 2 hours ) ``` ## Spot vs Perpetual | Market | Behavior | | ------------- | --------------------------------------- | | **Spot** | Accumulates base asset across levels | | **Perpetual** | Builds leveraged position across levels | On perpetual markets, leverage is applied to all orders uniformly. # Grid Executor Source: https://condor.hummingbot.org/executors/grid-executor Like Position Executor but with multiple order levels across a price range The **Grid Executor** is like a Position Executor with multiple levels. It places orders at evenly spaced price intervals between a start and end price, managing each level independently with take profit targets. ## Overview | Property | Value | | -------------- | ------------------------------------------------ | | Position Type | Spot or Perp | | keep\_position | Configurable | | Use Cases | Range-bound markets, accumulation, market making | ## How It Works ```mermaid theme={null} flowchart TB subgraph Grid["Grid Levels"] L1[Level 1: $3000] L2[Level 2: $3100] L3[Level 3: $3200] L4[Level 4: $3300] L5[Level 5: $3400] end L1 --> F1{Filled?} L2 --> F2{Filled?} L3 --> F3{Filled?} F1 -->|Yes| TP1[Take Profit Order] F2 -->|Yes| TP2[Take Profit Order] TP1 --> C1[Level Complete] TP2 --> C2[Level Complete] ``` 1. **Grid Creation**: Divides price range into evenly spaced levels 2. **Order Placement**: Places entry orders at each level 3. **Fill Management**: When a level fills, places take profit order 4. **Level Completion**: Each level manages its own entry/exit cycle 5. **Risk Management**: Overall position managed by triple barrier ## Configuration ```python theme={null} from hummingbot.strategy_v2.executors.grid_executor.data_types import GridExecutorConfig from hummingbot.strategy_v2.executors.position_executor.data_types import TripleBarrierConfig config = GridExecutorConfig( controller_id="my-agent", connector_name="binance", trading_pair="ETH-USDT", side=TradeType.BUY, # Grid boundaries start_price=Decimal("3000"), # Lower bound end_price=Decimal("3500"), # Upper bound limit_price=Decimal("2900"), # Stop loss price # Sizing total_amount_quote=Decimal("1000"), # Total capital to deploy min_spread_between_orders=Decimal("0.01"), # 1% between levels min_order_amount_quote=Decimal("5"), # Min order size # Execution max_open_orders=5, # Max simultaneous orders activation_bounds=Decimal("0.02"), # Only active within 2% of price # Risk management triple_barrier_config=TripleBarrierConfig( stop_loss=Decimal("0.05"), # 5% total stop loss time_limit=86400, # 24 hour limit ), leverage=1, keep_position=False, ) ``` ## Parameters ### Grid Boundaries | Parameter | Description | | ------------- | ---------------------------------------------------- | | `start_price` | Lower price bound of the grid | | `end_price` | Upper price bound of the grid | | `limit_price` | Stop loss price - exits all positions if breached | | `side` | `BUY` for accumulation grid, `SELL` for distribution | ### Sizing | Parameter | Default | Description | | --------------------------- | ------- | ----------------------------------------- | | `total_amount_quote` | - | Total capital to deploy across all levels | | `min_spread_between_orders` | 0.05% | Minimum % spread between grid levels | | `min_order_amount_quote` | 5 | Minimum order size in quote | ### Execution | Parameter | Default | Description | | ---------------------- | ------- | ----------------------------------------------- | | `max_open_orders` | 5 | Maximum simultaneous open orders | | `max_orders_per_batch` | None | Orders to place per batch | | `order_frequency` | 0 | Seconds between order batches | | `activation_bounds` | None | Only keep orders within this % of current price | | `safe_extra_spread` | 0.01% | Extra spread for safety | ### Risk Management | Parameter | Description | | ----------------------- | ------------------------------------------------------- | | `triple_barrier_config` | Stop loss, take profit, time limit for overall position | | `leverage` | Leverage for perpetual markets | | `keep_position` | Whether to keep net position on termination | ## Grid Level States Each level tracks its own state independently: | State | Description | | -------------------- | -------------------------------------------- | | `NOT_ACTIVE` | No orders placed at this level | | `OPEN_ORDER_PLACED` | Entry order active, waiting for fill | | `OPEN_ORDER_FILLED` | Entry filled, take profit order being placed | | `CLOSE_ORDER_PLACED` | Take profit order active | | `COMPLETE` | Both entry and take profit filled | ## Example: Accumulation Grid Buy ETH between $3000-$3500, taking profit at each level: ```python theme={null} accumulation = GridExecutorConfig( controller_id="eth-accumulator", connector_name="binance", trading_pair="ETH-USDT", side=TradeType.BUY, start_price=Decimal("3000"), end_price=Decimal("3500"), limit_price=Decimal("2800"), # Stop if price crashes total_amount_quote=Decimal("5000"), min_spread_between_orders=Decimal("0.02"), # 2% between levels max_open_orders=10, triple_barrier_config=TripleBarrierConfig( stop_loss=Decimal("0.10"), # 10% overall stop time_limit=604800, # 1 week ), keep_position=True, # Keep accumulated ETH ) ``` ## Example: Range Trading Grid Trade BTC in a range, closing each level for profit: ```python theme={null} range_trade = GridExecutorConfig( controller_id="btc-range", connector_name="binance_perpetual", trading_pair="BTC-USDT", side=TradeType.BUY, start_price=Decimal("60000"), end_price=Decimal("65000"), limit_price=Decimal("58000"), total_amount_quote=Decimal("10000"), min_spread_between_orders=Decimal("0.01"), max_open_orders=5, activation_bounds=Decimal("0.03"), # Orders within 3% of price triple_barrier_config=TripleBarrierConfig( stop_loss=Decimal("0.05"), time_limit=86400, ), leverage=3, keep_position=False, # Close positions, take P&L ) ``` ## Grid vs Position Executor | Feature | Position Executor | Grid Executor | | ------------ | ----------------- | -------------------------- | | Order Levels | Single | Multiple (auto-calculated) | | Entry | One price | Range of prices | | Take Profit | Single target | Per-level targets | | Use Case | Directional bet | Range trading | | Complexity | Simple | More complex | Think of Grid Executor as running multiple Position Executors simultaneously across a price range, with coordinated risk management. ## Activation Bounds The `activation_bounds` parameter keeps orders active only near the current price: ```python theme={null} activation_bounds=Decimal("0.02") # 2% ``` * Orders more than 2% from current price are cancelled * As price moves, new orders are placed within bounds * Reduces open order count and exchange rate limits ## Position Handover When `keep_position=True`: * Net inventory from all levels stays in account * Added to Position Hold for agent management * Useful for accumulation strategies When `keep_position=False`: * All positions closed on termination * Realized P\&L from each level reported * Clean exit with no leftover inventory # LP Executor Source: https://condor.hummingbot.org/executors/lp-executor Concentrated liquidity provision on CLMM DEXs The **LP Executor** automates liquidity provision on Concentrated Liquidity Market Maker (CLMM) DEXs like Meteora, Raydium, Orca, and Uniswap V3. ## Overview | Property | Value | | ---------------- | ------------------------------------------------------ | | Position Type | LP | | P\&L Calculation | Fees earned - impermanent loss - tx fees | | keep\_position | Configurable | | Use Cases | Earning LP fees, concentrated liquidity, range trading | ## Configuration ```python theme={null} from hummingbot.strategy_v2.executors.lp_executor.data_types import LPExecutorConfig config = LPExecutorConfig( controller_id="my-agent", connector_name="meteora", pool_address="5Q544fK...", trading_pair="SOL-USDC", lower_price=Decimal("140.0"), upper_price=Decimal("160.0"), base_amount=Decimal("1.0"), quote_amount=Decimal("150.0"), side=0, # 0=both, 1=buy only, 2=sell only auto_close_above_range_seconds=3600, auto_close_below_range_seconds=3600, keep_position=True, ) ``` ## Parameters | Parameter | Description | | -------------------------------- | ----------------------------------------------- | | `connector_name` | DEX connector (meteora, raydium, orca, uniswap) | | `pool_address` | On-chain pool address | | `trading_pair` | Token pair (e.g., SOL-USDC) | | `lower_price` | Price range lower bound | | `upper_price` | Price range upper bound | | `base_amount` | Base token to deposit | | `quote_amount` | Quote token to deposit | | `side` | 0=both sides, 1=buy only, 2=sell only | | `auto_close_above_range_seconds` | Close if above range for N seconds | | `auto_close_below_range_seconds` | Close if below range for N seconds | ## Lifecycle States | State | Description | | -------------- | ----------------------------------- | | `NOT_ACTIVE` | Initial state | | `OPENING` | Adding liquidity | | `IN_RANGE` | Position active, price within range | | `OUT_OF_RANGE` | Price moved outside range | | `CLOSING` | Removing liquidity | | `COMPLETE` | Position closed | | `FAILED` | Failed after retries | ## How It Works 1. **Open**: Deploys liquidity at configured price range 2. **Monitor**: Tracks if current price is within range 3. **Fees**: Accumulates trading fees while in range 4. **Close**: Removes liquidity when conditions met ## P\&L Tracking The executor tracks: | Metric | Description | | ------------------ | ------------------------------ | | `base_fee` | Fees earned in base token | | `quote_fee` | Fees earned in quote token | | `position_rent` | Solana rent for position NFT | | `tx_fee` | Transaction fees | | `impermanent_loss` | Value loss from price movement | ## Example: SOL-USDC LP ```python theme={null} sol_lp = LPExecutorConfig( controller_id="sol-lp", connector_name="meteora", pool_address="5Q544fK...", trading_pair="SOL-USDC", lower_price=Decimal("130"), upper_price=Decimal("170"), base_amount=Decimal("5.0"), quote_amount=Decimal("750.0"), side=0, auto_close_above_range_seconds=7200, # 2 hours auto_close_below_range_seconds=3600, # 1 hour ) ``` ## Example: Single-Sided LP ```python theme={null} # Only provide sell-side liquidity (sell SOL as price rises) sell_side_lp = LPExecutorConfig( controller_id="sell-lp", connector_name="raydium", pool_address="...", trading_pair="SOL-USDC", lower_price=Decimal("150"), upper_price=Decimal("200"), base_amount=Decimal("10.0"), quote_amount=Decimal("0"), side=2, # Sell only ) ``` ## Via API ```bash theme={null} POST /executors/create { "type": "lp_executor", "connector_name": "meteora", "trading_pair": "SOL-USDC", "total_amount_quote": 0.30, "side": 2, "width_percent": 0.4 } ``` ## Position Handover When `keep_position=true` and executor closes: 1. LP position is **always closed on-chain** (liquidity withdrawn) 2. Net token change tracked in Position Hold 3. ADD events → SELL (tokens deposited) 4. REMOVE events → BUY (tokens + fees returned) This allows agents to track LP performance as standard trades. # Order Executor Source: https://condor.hummingbot.org/executors/order-executor The simplest executor - places and executes a single order The **Order Executor** is the simplest executor type. It places a single order using one of four execution strategies and terminates when the order is filled or cancelled. ## Overview | Property | Value | | -------------- | ------------------------------------------------- | | Position Type | Spot or Perp | | keep\_position | `true` (always) | | Use Cases | Single entries, building positions, simple trades | ## Configuration ```python theme={null} from hummingbot.strategy_v2.executors.order_executor.data_types import ( OrderExecutorConfig, ExecutionStrategy, LimitChaserConfig, ) config = OrderExecutorConfig( controller_id="my-agent", connector_name="binance", trading_pair="BTC-USDT", side=TradeType.BUY, amount=Decimal("0.01"), execution_strategy=ExecutionStrategy.LIMIT, price=Decimal("65000.0"), leverage=1, ) ``` ## Execution Strategies The Order Executor supports four execution strategies: ### LIMIT Standard limit order at a specified price. Only fills at your price or better. ```python theme={null} OrderExecutorConfig( connector_name="binance", trading_pair="ETH-USDT", side=TradeType.BUY, amount=Decimal("0.5"), execution_strategy=ExecutionStrategy.LIMIT, price=Decimal("3200.0"), # Required for LIMIT ) ``` ### LIMIT\_MAKER Post-only limit order that must be a maker order. Rejected if it would immediately fill as taker. ```python theme={null} OrderExecutorConfig( connector_name="binance", trading_pair="ETH-USDT", side=TradeType.BUY, amount=Decimal("0.5"), execution_strategy=ExecutionStrategy.LIMIT_MAKER, price=Decimal("3200.0"), # Required for LIMIT_MAKER ) ``` Use this to ensure you pay maker fees (typically lower) and avoid crossing the spread. ### MARKET Market order that fills immediately at the best available price. ```python theme={null} OrderExecutorConfig( connector_name="binance", trading_pair="SOL-USDT", side=TradeType.SELL, amount=Decimal("10.0"), execution_strategy=ExecutionStrategy.MARKET, # No price needed for MARKET ) ``` ### LIMIT\_CHASER A limit order that chases the market price, refreshing when price moves away. ```python theme={null} OrderExecutorConfig( connector_name="binance", trading_pair="BTC-USDT", side=TradeType.BUY, amount=Decimal("0.01"), execution_strategy=ExecutionStrategy.LIMIT_CHASER, chaser_config=LimitChaserConfig( distance=Decimal("0.001"), # Place order 0.1% from mid refresh_threshold=Decimal("0.002"), # Refresh if price moves 0.2% ), ) ``` | Parameter | Description | | ------------------- | ------------------------------------------------------- | | `distance` | How far from current price to place the order | | `refresh_threshold` | How far price must move before cancelling and replacing | The chaser keeps adjusting your limit order to stay near the market price, giving you a better chance of filling while still using limit orders. ## Parameters | Parameter | Type | Description | | -------------------- | ----------------- | ------------------------------------------- | | `connector_name` | string | Exchange connector | | `trading_pair` | string | Market (e.g., "BTC-USDT") | | `side` | TradeType | `BUY` or `SELL` | | `amount` | Decimal | Order size in base asset | | `execution_strategy` | ExecutionStrategy | One of the four strategies above | | `price` | Decimal | Required for LIMIT and LIMIT\_MAKER | | `chaser_config` | LimitChaserConfig | Required for LIMIT\_CHASER | | `leverage` | int | Leverage for perpetual markets (default: 1) | | `position_action` | PositionAction | `OPEN` or `CLOSE` (for perps) | ## Lifecycle ```mermaid theme={null} flowchart LR C[CREATED] --> P[Order Placed] P --> F[FILLED] --> T[TERMINATED] P --> X[CANCELLED] --> T ``` The Order Executor terminates when: * Order is filled * Order is cancelled * Early stop requested ## Position Handover Order Executor **always** uses `keep_position=true`: * Filled orders add to the agent's Position Hold * Tokens remain in the account * No P\&L attributed until position is closed by another executor This makes it ideal for building positions that will be managed by Position Executor or Grid Executor. ## When to Use | Scenario | Strategy | | -------------------------- | ------------- | | Passive entry at support | LIMIT | | Guaranteed maker fees | LIMIT\_MAKER | | Immediate execution | MARKET | | Better fills with patience | LIMIT\_CHASER | # Types of Executors Source: https://condor.hummingbot.org/executors/overview Self-contained trading operations with standardized lifecycle and P&L reporting **Executors** are self-contained trading operations that manage their complete lifecycle—from entry to exit—with standardized P\&L and fee reporting. Each executor is tagged with a `controller_id` linking it to the agent that created it. ## Why Executors? Executors are the heart of the Trading Agent design. Agents **only act through executors**, which provides: | Benefit | Description | | ------------------------ | ------------------------------------------------------------------- | | **Standardization** | Same interface across 50+ exchanges | | **Error Handling** | Clear errors instead of cryptic API responses | | **Isolation** | Each agent only sees its own executors via `controller_id` | | **Frequency Separation** | Agent reasons at mid-frequency; executor operates at high frequency | | **Position Handover** | `keep_position=true` retains inventory for the next tick | ## Executor Types From simplest to most complex: | Executor | Description | Builds On | | --------------------------------------------------- | ------------------------------------------------- | --------- | | [Order Executor](/executors/order-executor) | Places and executes a single order | - | | [Position Executor](/executors/position-executor) | Order + TP/SL/trailing stop/time limit management | Order | | [Grid Executor](/executors/grid-executor) | Multiple Position Executors across a price range | Position | | [DCA Executor](/executors/dca-executor) | Multiple orders at different price levels | Order | | [TWAP Executor](/executors/twap-executor) | Orders spread over time | Order | | [XEMM Executor](/executors/xemm-executor) | Cross-exchange market making | Order | | [Arbitrage Executor](/executors/arbitrage-executor) | Cross-market arbitrage | Order | | [LP Executor](/executors/lp-executor) | Concentrated liquidity provision | - | ### The Core Three **Order Executor** is the simplest—it places an order using one of four execution strategies (LIMIT, LIMIT\_MAKER, MARKET, LIMIT\_CHASER) and terminates when filled. **Position Executor** builds on Order Executor by adding position management: after the entry order fills, it monitors the position and exits at take profit, stop loss, trailing stop, or time limit. **Grid Executor** is like running multiple Position Executors simultaneously across a price range, with each level having its own entry and take profit orders. ## Lifecycle All executors follow a standard lifecycle: ```mermaid theme={null} flowchart LR C[CREATED] --> A[ACTIVE] A --> TP[Take Profit] --> T[TERMINATED] A --> SL[Stop Loss] --> T A --> TL[Time Limit] --> T A --> TS[Trailing Stop] --> T A --> ES[Early Stop] --> T A --> CP[Completed] --> T ``` | Close Type | Description | | --------------- | -------------------------------- | | `TAKE_PROFIT` | Price reached profit target | | `STOP_LOSS` | Price reached loss limit | | `TIME_LIMIT` | Maximum duration exceeded | | `TRAILING_STOP` | Trailing stop triggered | | `EARLY_STOP` | Manually stopped | | `COMPLETED` | Finished normally (order filled) | | `FAILED` | Failed after retries | ## Position Handover When an executor terminates with `keep_position=true`: 1. Inventory stays in the account, tagged with `controller_id` 2. Agent sees it on the next tick via the positions provider 3. Agent can manage it with a new executor (scale out, hedge, exit) 4. P\&L is not attributed until position is fully closed **Example**: Grid hits stop-loss → keeps 0.005 BTC → agent waits for recovery → spawns Order Executor to exit at better price. ## Creating Executors ### Via MCP Tools ```python theme={null} result = await mcp_tools.manage_executors( action="create", executor_type="position_executor", config={ "connector_name": "binance_perpetual", "trading_pair": "SOL-USDT", "side": "BUY", "amount": 10.0, "triple_barrier_config": { "take_profit": 0.02, "stop_loss": 0.01, } } ) ``` ### Via API ```bash theme={null} # List executors curl -u YOUR_USERNAME:YOUR_PASSWORD http://localhost:8000/executors # Filter by agent curl -u YOUR_USERNAME:YOUR_PASSWORD "http://localhost:8000/executors?controller_id=my-agent" # Stop executor curl -u YOUR_USERNAME:YOUR_PASSWORD -X DELETE http://localhost:8000/executors/{id} ``` ## Standardized Metrics All executors report: | Metric | Description | | ------------------ | --------------------------------- | | `net_pnl_quote` | Realized P\&L in quote currency | | `fees_paid_quote` | Trading fees, gas costs | | `volume_quote` | Total trading volume | | `close_type` | How executor terminated | | `duration_seconds` | Time from creation to termination | # Position Executor Source: https://condor.hummingbot.org/executors/position-executor Places an entry order and manages the position with TP, SL, trailing stop, and time limit The **Position Executor** places an entry order and, once filled, manages the position using the Triple Barrier Method—automatically closing at take profit, stop loss, trailing stop activation, or time limit. ## Overview | Property | Value | | -------------- | ------------------------------------------- | | Position Type | Spot or Perp | | keep\_position | Configurable | | Use Cases | Directional trades, scalping, swing trading | ## How It Works ```mermaid theme={null} flowchart LR E[Entry Order] --> F{Filled?} F -->|Yes| M[Monitor Position] F -->|No| W[Wait/Retry] W --> F M --> TP[Take Profit Hit] --> C[Close Position] M --> SL[Stop Loss Hit] --> C M --> TS[Trailing Stop Hit] --> C M --> TL[Time Limit Hit] --> C C --> T[TERMINATED] ``` 1. **Entry**: Places order at `entry_price` (or market if not specified) 2. **Monitor**: Once filled, continuously monitors price against barriers 3. **Exit**: First barrier hit triggers position close 4. **Report**: Reports P\&L and terminates ## Configuration ```python theme={null} from hummingbot.strategy_v2.executors.position_executor.data_types import ( PositionExecutorConfig, TripleBarrierConfig, TrailingStop, ) config = PositionExecutorConfig( controller_id="my-agent", connector_name="binance_perpetual", trading_pair="SOL-USDT", side=TradeType.BUY, amount=Decimal("10.0"), entry_price=Decimal("150.0"), # Optional: None for market entry leverage=5, triple_barrier_config=TripleBarrierConfig( take_profit=Decimal("0.02"), # 2% profit target stop_loss=Decimal("0.01"), # 1% stop loss time_limit=3600, # 1 hour max trailing_stop=TrailingStop( activation_price=Decimal("0.01"), # Activate at 1% profit trailing_delta=Decimal("0.005"), # Trail by 0.5% ), ), ) ``` ## Triple Barrier Config The `TripleBarrierConfig` defines all exit conditions: | Parameter | Type | Description | | ------------------------ | ------------ | ------------------------------------------------- | | `take_profit` | Decimal | Exit when profit reaches this % (e.g., 0.02 = 2%) | | `stop_loss` | Decimal | Exit when loss reaches this % (e.g., 0.01 = 1%) | | `time_limit` | int | Exit after this many seconds | | `trailing_stop` | TrailingStop | Dynamic stop that follows price | | `open_order_type` | OrderType | Entry order type (default: LIMIT) | | `take_profit_order_type` | OrderType | TP exit order type (default: MARKET) | | `stop_loss_order_type` | OrderType | SL exit order type (default: MARKET) | | `time_limit_order_type` | OrderType | Time exit order type (default: MARKET) | ### Take Profit Closes position when price moves in your favor by the specified percentage. ```python theme={null} TripleBarrierConfig( take_profit=Decimal("0.02"), # Close at 2% profit ) ``` For a long position entered at $100, take profit triggers at $102. ### Stop Loss Closes position when price moves against you by the specified percentage. ```python theme={null} TripleBarrierConfig( stop_loss=Decimal("0.01"), # Close at 1% loss ) ``` For a long position entered at $100, stop loss triggers at $99. ### Time Limit Closes position after a maximum duration, regardless of P\&L. ```python theme={null} TripleBarrierConfig( time_limit=3600, # Close after 1 hour ) ``` ### Trailing Stop A dynamic stop loss that follows the price as it moves in your favor. ```python theme={null} TripleBarrierConfig( trailing_stop=TrailingStop( activation_price=Decimal("0.01"), # Start trailing at 1% profit trailing_delta=Decimal("0.005"), # Keep stop 0.5% behind ), ) ``` **How it works**: 1. Position enters at \$100 2. Price rises to \$101 (1% profit) → trailing stop activates 3. Stop is placed at \$100.50 (0.5% below current price) 4. Price rises to $103 → stop moves to $102.49 5. Price drops to \$102.49 → trailing stop triggers, position closes The trailing stop locks in gains while letting winners run. ## Parameters | Parameter | Type | Description | | ----------------------- | ------------------- | ----------------------------------- | | `connector_name` | string | Exchange connector | | `trading_pair` | string | Market (e.g., "SOL-USDT") | | `side` | TradeType | `BUY` (long) or `SELL` (short) | | `amount` | Decimal | Position size in base asset | | `entry_price` | Decimal | Entry price (None for market order) | | `leverage` | int | Leverage for perpetual markets | | `triple_barrier_config` | TripleBarrierConfig | Exit conditions | | `activation_bounds` | List\[Decimal] | Optional price bounds to activate | ## Order Types You can configure which order type to use for each action: ```python theme={null} TripleBarrierConfig( open_order_type=OrderType.LIMIT, # Entry: limit order take_profit_order_type=OrderType.LIMIT, # TP: limit order stop_loss_order_type=OrderType.MARKET, # SL: market order (fast exit) time_limit_order_type=OrderType.MARKET, # Time: market order ) ``` Use LIMIT for entries and take profits to get better fills. Use MARKET for stop losses to ensure execution. ## Example: Scalp Trade ```python theme={null} scalp = PositionExecutorConfig( controller_id="scalper", connector_name="binance_perpetual", trading_pair="BTC-USDT", side=TradeType.BUY, amount=Decimal("0.01"), leverage=10, triple_barrier_config=TripleBarrierConfig( take_profit=Decimal("0.003"), # 0.3% profit stop_loss=Decimal("0.002"), # 0.2% stop time_limit=300, # 5 min max ), ) ``` ## Example: Swing Trade with Trailing Stop ```python theme={null} swing = PositionExecutorConfig( controller_id="swing-trader", connector_name="binance", trading_pair="ETH-USDT", side=TradeType.BUY, amount=Decimal("0.5"), entry_price=Decimal("3200.0"), triple_barrier_config=TripleBarrierConfig( take_profit=Decimal("0.10"), # 10% target stop_loss=Decimal("0.03"), # 3% stop time_limit=86400, # 24 hours trailing_stop=TrailingStop( activation_price=Decimal("0.05"), # Trail after 5% profit trailing_delta=Decimal("0.02"), # 2% trail distance ), ), ) ``` ## Close Types When the executor terminates, it reports which barrier was hit: | Close Type | Description | | --------------- | --------------------------- | | `TAKE_PROFIT` | Price reached profit target | | `STOP_LOSS` | Price reached loss limit | | `TIME_LIMIT` | Maximum duration exceeded | | `TRAILING_STOP` | Trailing stop triggered | | `EARLY_STOP` | Manually stopped | ## Position Handover If `keep_position=true` (set via early stop): * Position remains open in the account * Added to agent's Position Hold for later management If `keep_position=false` (default for barrier exits): * Position fully closed * Realized P\&L calculated and reported # Position Handover Source: https://condor.hummingbot.org/executors/position-handover How executors transfer positions to the agent's inventory **Position Handover** is the mechanism by which executors transfer their positions to the agent's inventory (Position Hold) when they terminate with `keep_position=true`. ## How It Works When an executor terminates, it can either: * **Close the position** and report realized P\&L * **Hand over the position** to the agent's inventory for later management ```mermaid theme={null} flowchart TB E[Executor Terminates] --> KP{keep_position?} KP -->|true| Hold[Add to Inventory] KP -->|false| Close[Close & Report P&L] Hold --> Next[Agent manages on next tick] Close --> Done[P&L attributed] ``` ## keep\_position Behavior | Setting | Behavior | | ------- | ------------------------------------------------------ | | `true` | Position added to inventory, no P\&L attributed yet | | `false` | Position closed, realized P\&L calculated and reported | ## Executor Defaults | Executor | keep\_position | Rationale | | ----------------- | --------------- | ------------------------------------------------ | | Order Executor | `true` (always) | Builds positions for other executors | | Position Executor | Configurable | Usually closes on barrier hit | | Grid Executor | Configurable | Can accumulate or close per-level | | DCA Executor | Configurable | Often accumulates across levels | | LP Executor | Configurable | Always closes on-chain, optionally tracks tokens | ## The Handover Flow ### 1. Executor Creates Trades ```python theme={null} # Position Executor entry entry_order = { "connector_name": "binance", "trading_pair": "SOL-USDT", "side": "BUY", "amount": 10.0, "price": 150.0, } ``` ### 2. Executor Terminates When the executor terminates (e.g., stop loss hit with `keep_position=true`): ```python theme={null} # Executor reports close_type = "STOP_LOSS" held_position = { "connector_name": "binance", "trading_pair": "SOL-USDT", "side": "BUY", "amount": 10.0, "entry_price": 150.0, } ``` ### 3. Position Added to Inventory The Position Hold aggregates with any existing position: ```python theme={null} # Agent's inventory for (binance, SOL-USDT) { "buy_amount_base": 10.0, "buy_amount_quote": 1500.0, "breakeven_price": 150.0, "unrealized_pnl_quote": -50.0, # At current price $145 } ``` ### 4. Agent Manages on Next Tick On the next tick, the agent sees the position: ``` Current Positions: - binance SOL-USDT: Long 10 SOL @ $150 (unrealized: -$50) ``` The agent can then: * Wait for recovery * Spawn a new executor to exit * Hedge with another position * Add to the position ## Example: Grid Stop-Loss Recovery ```python theme={null} # Grid Executor hits stop loss grid_config = GridExecutorConfig( keep_position=True, # Keep accumulated inventory triple_barrier_config=TripleBarrierConfig( stop_loss=Decimal("0.05"), ), ) # After stop loss: # - Grid executor terminates # - Net inventory (e.g., 0.5 BTC) stays in account # - Position added to agent's inventory # On next tick, agent sees: # "Held position: Long 0.5 BTC @ $62,500" # Agent decides to wait for recovery # Later, spawns Order Executor to exit at better price: exit_config = OrderExecutorConfig( side=TradeType.SELL, amount=Decimal("0.5"), execution_strategy=ExecutionStrategy.LIMIT, price=Decimal("64000"), # Better exit ) ``` ## LP Position Handover LP positions work differently: 1. **On-chain position always closes** when executor terminates 2. If `keep_position=true`, the **net token change** is tracked 3. ADD events → SELL (tokens left wallet) 4. REMOVE events → BUY (tokens + fees returned) ```python theme={null} # LP Executor closes lp_result = { "initial_base": 10.0, "initial_quote": 1500.0, "final_base": 9.5, # Less base (sold some) "final_quote": 1600.0, # More quote (fees earned) "fees_earned_base": 0.1, "fees_earned_quote": 15.0, } # If keep_position=True: # Net change tracked in inventory as spot position ``` ## Position Accumulation Multiple executors can add to the same position: ``` Trade 1 (Order Executor): Buy 10 SOL @ $150 Trade 2 (Order Executor): Buy 5 SOL @ $145 Trade 3 (Grid level): Buy 5 SOL @ $140 Inventory: Long 20 SOL @ $146.25 (weighted average) ``` Each executor adds to the Position Hold, creating a single aggregated position. ## When to Use keep\_position | Scenario | Setting | Why | | ----------------------------- | ------- | ------------------------------------ | | Building a position over time | `true` | Accumulate across multiple executors | | Quick scalp trade | `false` | Clean exit, immediate P\&L | | Grid accumulation | `true` | Keep inventory for later exit | | Range trading grid | `false` | Lock in profits per level | | Recovery from stop loss | `true` | Allow agent to manage exit | ## Related * [Inventory](/trading-agents/inventory) - The virtual portfolio concept * [Executors Overview](/executors/overview) - All executor types # TWAP Executor Source: https://condor.hummingbot.org/executors/twap-executor Time-weighted average price execution over a duration The **TWAP Executor** executes trades over a specified time horizon by splitting a large order into smaller orders at regular intervals, minimizing market impact. ## Overview | Property | Value | | -------------- | ---------------------------------------------------------- | | Position Type | Spot or Perp | | keep\_position | Configurable | | Use Cases | Large order execution, reducing slippage, systematic entry | ## Configuration ```python theme={null} from hummingbot.strategy_v2.executors.twap_executor.data_types import TWAPExecutorConfig config = TWAPExecutorConfig( controller_id="my-agent", connector_name="binance", trading_pair="BTC-USDT", side=TradeType.BUY, leverage=1, total_amount_quote=Decimal("10000"), total_duration=3600, # 1 hour total order_interval=60, # Order every minute mode="MAKER", # MAKER or TAKER ) ``` ## Parameters | Parameter | Description | | -------------------- | ------------------------------------------------- | | `total_amount_quote` | Total amount to execute in quote currency | | `total_duration` | Total time to spread execution (seconds) | | `order_interval` | Time between orders (seconds) | | `mode` | `MAKER` (limit orders) or `TAKER` (market orders) | ## How It Works 1. **Order Plan**: Creates schedule of orders based on duration and interval 2. **Execution**: Places orders at scheduled times 3. **Monitoring**: Tracks fill rates and adjusts if needed 4. **Completion**: Reports average price and total filled ## Calculated Values The executor automatically calculates: * **Number of orders**: `total_duration / order_interval` * **Amount per order**: `total_amount_quote / number_of_orders` * **Average executed price**: Volume-weighted average of all fills ## Example: Execute \$50k Over 2 Hours ```python theme={null} large_order = TWAPExecutorConfig( controller_id="whale-entry", connector_name="binance", trading_pair="ETH-USDT", side=TradeType.BUY, total_amount_quote=Decimal("50000"), total_duration=7200, # 2 hours order_interval=120, # Every 2 minutes mode="MAKER", # Use limit orders ) # Creates 60 orders of ~$833 each ``` ## Example: Quick TWAP with Market Orders ```python theme={null} quick_twap = TWAPExecutorConfig( controller_id="quick-fill", connector_name="binance_perpetual", trading_pair="SOL-USDT", side=TradeType.BUY, leverage=5, total_amount_quote=Decimal("5000"), total_duration=300, # 5 minutes order_interval=30, # Every 30 seconds mode="TAKER", # Market orders for guaranteed fills ) # Creates 10 orders of $500 each ``` ## Performance Metrics The executor reports: | Metric | Description | | ----------------- | ---------------------------------- | | `filled_amount` | Total amount executed | | `average_price` | Volume-weighted average price | | `cumulative_fees` | Total fees paid | | `trade_pnl` | P\&L vs if executed at start price | | `net_pnl` | P\&L minus fees | ## When to Use TWAP * Executing orders larger than 1% of daily volume * Avoiding front-running or detection * Systematic rebalancing * Reducing timing risk # XEMM Executor Source: https://condor.hummingbot.org/executors/xemm-executor Cross-exchange market making with arbitrage capture The **XEMM Executor** (Cross-Exchange Market Making) captures arbitrage by simultaneously placing orders on different exchanges, exploiting price discrepancies. ## Overview | Property | Value | | -------------- | ------------------------------------------------------- | | Position Type | Spot | | keep\_position | Configurable | | Use Cases | Cross-exchange arbitrage, market making, spread capture | ## Configuration ```python theme={null} from hummingbot.strategy_v2.executors.xemm_executor.data_types import XEMMExecutorConfig config = XEMMExecutorConfig( controller_id="my-agent", buying_market=ExchangePair( exchange="binance", trading_pair="ETH-USDT", ), selling_market=ExchangePair( exchange="kucoin", trading_pair="ETH-USDT", ), maker_side="buy", # Which side to make order_amount=Decimal("0.5"), # Amount in base asset min_profitability=Decimal("0.002"), # 0.2% minimum ) ``` ## Parameters | Parameter | Description | | ------------------- | ------------------------------------------------ | | `buying_market` | Exchange and pair for buy side | | `selling_market` | Exchange and pair for sell side | | `maker_side` | Which side places maker orders (`buy` or `sell`) | | `order_amount` | Order size in base asset | | `min_profitability` | Minimum profit threshold after fees | ## How It Works 1. **Price Monitoring**: Watches prices on both exchanges 2. **Spread Calculation**: Computes potential profit including fees 3. **Maker Order**: Places limit order on maker side 4. **Taker Order**: When maker fills, immediately takes on other side 5. **Profit Capture**: Locks in spread as profit ## Arbitrage Validation The executor validates that: * Trading pairs are interchangeable (same tokens) * Sufficient balance on both exchanges * Spread exceeds minimum profitability threshold * Transaction costs are accounted for ## Example: ETH Arbitrage ```python theme={null} eth_xemm = XEMMExecutorConfig( controller_id="eth-arb", buying_market=ExchangePair( exchange="binance", trading_pair="ETH-USDT", ), selling_market=ExchangePair( exchange="okx", trading_pair="ETH-USDT", ), maker_side="buy", order_amount=Decimal("1.0"), min_profitability=Decimal("0.003"), # 0.3% min profit ) ``` ## Example: Stablecoin Spread ```python theme={null} stable_xemm = XEMMExecutorConfig( controller_id="stable-spread", buying_market=ExchangePair( exchange="kraken", trading_pair="USDC-USD", ), selling_market=ExchangePair( exchange="coinbase", trading_pair="USDC-USD", ), maker_side="sell", order_amount=Decimal("10000"), min_profitability=Decimal("0.0005"), # 0.05% min ) ``` ## Profitability Calculation ``` Gross Spread = Sell Price - Buy Price Net Profit = Gross Spread - Buy Fee - Sell Fee - Transfer Costs Execute if: Net Profit / Trade Value > min_profitability ``` ## Inventory Management The executor creates positions on both exchanges: | Exchange | Position | | ---------------- | -------------------------- | | Buying Exchange | Long base asset | | Selling Exchange | Short base asset (or sold) | Total inventory risk depends on `keep_position` setting: * `true`: Positions remain for future management * `false`: Inventory rebalanced or hedged # Give Feedback Source: https://condor.hummingbot.org/feedback A 2-minute survey that helps decide what we build next in Condor If you use Condor to monitor and trade with Hummingbot, we want your honest take — what's working, what's frustrating, and what you wish you could do that you can't today. The survey takes about **two minutes**. There are no wrong answers. Even a few sentences about a rough edge or a missing feature helps. Every response goes to the Hummingbot Foundation team and **directly shapes the Condor roadmap**. What's working? What's confusing? What do you wish Condor could do? Your answers go straight to the Hummingbot Foundation team and directly shape what we build next. You'll be asked: * How valuable Condor is in your trading workflow today * The most frustrating or confusing part of using it * What you wish Condor could do that it can't today * The most painful or time-consuming part of running your trading bots (beyond Condor) Prefer to talk it through live? Join **[#condor-feedback](https://discord.gg/hummingbot)** on Discord. # Configuration Reference Source: https://condor.hummingbot.org/getting-started/configuration Every environment variable Condor reads, what it defaults to, and when you would change it Condor is configured in two places. **`.env`** holds the settings below, and **`config.yml`** holds servers, users, permissions and chat defaults — written by `make setup` and by `/servers`, not by hand. Almost nothing here needs setting. The install wizard writes what a working install requires, and the rest exist for operators with a specific reason. After editing `.env`, restart Condor for it to take effect — `make restart`. `make doctor` re-reads it and reports what the running install would see. ## Identity and mode | Variable | Default | What it does | | ---------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `CONDOR_MODE` | `telegram` | `telegram` or `local`. Never inferred from whether a token is present — an install whose token went missing fails at boot rather than quietly becoming an unauthenticated dashboard. See [Choosing Telegram or Local mode](/getting-started/installing#choose-your-install) | | `TELEGRAM_TOKEN` | — | Bot token from [@BotFather](https://t.me/botfather). Required unless `CONDOR_MODE=local` | | `ADMIN_USER_ID` | — | Your numeric Telegram user id, and the id Local mode logs in as. Required. Everything — servers, preferences, memory, chat defaults — is keyed to it | ## Web dashboard | Variable | Default | What it does | | ---------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `WEB_PORT` | `8088` | Port the dashboard listens on | | `WEB_URL` | `http://localhost:` | The full URL `/web` hands out. Set it to the address a browser can actually reach — a VPS's public address, or a tailnet name | | `WEB_HOST` | `0.0.0.0` in Telegram mode; `127.0.0.1` in Local mode or with Tailscale | Which interface the dashboard binds. The explicit opt-out from the loopback default — see [What binds where by default](/getting-started/installing#what-binds-where-by-default) before changing it | | `WEB_JWT_SECRET` | generated, stored in `config.yml` | Signs dashboard login tokens. Set it explicitly to keep sessions valid across instances, or to hold the secret outside `config.yml` | | `USE_TAILSCALE` | unset | `true` binds the dashboard to loopback and publishes it through `tailscale serve` — tailnet only, never a public interface | ## Privacy Both are the operator-level kill switches. They override anything chosen in the dashboard, and neither needs to be set on a fresh install — see [Privacy: telemetry and sharing](/getting-started/installing#privacy-telemetry-and-sharing). | Variable | Default | What it does | | ------------------ | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `CONDOR_TELEMETRY` | unset | `off` disables telemetry entirely and suppresses the consent prompt. `ping` or `usage` sets the level and overrides the stored answer | | `CONDOR_SHARING` | unset | `off` stops any conversation leaving, and destroys anything already queued. A queued *unshare* is still delivered — a revocation is a deletion the install already promised | ## Updates | Variable | Default | What it does | | ----------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `UPDATE_CHECK_INTERVAL` | `3600` | Seconds between background update checks. `0` disables them. The first check runs 30 seconds after startup — see [Keeping Condor updated](/getting-started/installing#keeping-condor-updated) | ## AI models Provider keys are covered in [Integrating your LLM](/llm-integration). One variable belongs here: | Variable | Default | What it does | | ---------------------- | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | | `CONDOR_DEFAULT_AGENT` | set by `make pick-model` | Which model Condor thinks with. Normally written by the wizard rather than edited — `make pick-model` also verifies the model is actually reachable | ## Retention and limits Every one of these has a working default. They exist for installs where disk, history depth or context size needs a different answer. | Variable | Default | What it does | | ----------------------------------- | ----------------- | ---------------------------------------------- | | `CONDOR_BACKTEST_RETENTION_DAYS` | `30` | How long backtest payloads are kept on disk | | `CONDOR_RUN_HISTORY_RETENTION_DAYS` | `60` | How long run-history payloads are kept | | `CONDOR_MAX_REPORTS` | `100` | Reports retained before the oldest are dropped | | `CONDOR_MAX_DELEGATION_RECORDS` | `500` | Background delegation records retained | | `CONDOR_MAX_CONSULT_RECORDS` | `300` | Domain-agent consult records retained | | `CONDOR_TRANSCRIPT_MAX_BYTES` | `2097152` (2 MiB) | Transcript size that triggers a trim | | `CONDOR_TRANSCRIPT_KEEP_BYTES` | `1048576` (1 MiB) | How much is kept when one is trimmed | | `CONDOR_REPLAY_MAX_CHARS` | `6000` | Characters of replay context handed to a model | A retention value is a floor on what you can look back at later. Lowering one deletes history at the next sweep, and there is no undo — an over-small value is history that is gone for good. ## Advanced Rarely set, and listed so a value you find in someone's `.env` is not a mystery. | Variable | Default | What it does | | ------------------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `CONDOR_PERSISTENCE_FILE` | `condor_bot_data.pickle` | Where preferences and conversation state are persisted | | `CONDOR_RUNTIME_MODE` | `local` | Session-runtime transport. `http` is reserved for a future process split and is not implemented | | `GATEWAY_URL` | unset | Condor does **not** reach Gateway through this — Gateway is configured in `hummingbot-api`'s own `.env` and driven via `/gateway`. Setting it here only flips a capability flag in telemetry | ## Not in `.env` Servers, their credentials, users, roles and per-chat defaults live in `config.yml`, written by `make setup` and by `/servers`. The Hummingbot API's own settings — `API_BIND`, `DB_BIND`, broker credentials, `GATEWAY_PASSPHRASE` — live in `hummingbot-api/.env`, not here. # Adding Credentials Source: https://condor.hummingbot.org/getting-started/credentials Connect your exchange accounts via API keys Before trading, you need to connect your exchange accounts by adding API credentials. ## Add Exchange Credentials 1. In Telegram, send `/keys` 2. Select **Perpetual** or **Spot** 3. Choose an exchange (e.g., Binance, Hyperliquid) 4. Enter your API key and secret For security, only enable **read + trade** permissions on your API keys. Never enable withdraw or transfer permissions. ## Supported Exchanges | Exchange | Spot | Perpetual | | --------------- | ---- | --------- | | **Binance** | ✓ | ✓ | | **Bybit** | ✓ | ✓ | | **OKX** | ✓ | ✓ | | **Hyperliquid** | - | ✓ | | **Kucoin** | ✓ | ✓ | | **Gate.io** | ✓ | ✓ | | **Kraken** | ✓ | - | | **Coinbase** | ✓ | - | ## Getting API Keys ### Binance 1. Go to [Binance API Management](https://www.binance.com/en/my/settings/api-management) 2. Create a new API key 3. Enable **Spot & Margin Trading** and/or **Futures** 4. Restrict to your IP address (recommended) ### Hyperliquid 1. Go to [Hyperliquid](https://app.hyperliquid.xyz/) 2. Connect your wallet 3. Go to **API** in settings 4. Generate API credentials ### Other Exchanges Each exchange has its own API management page. Look for: * API Management * API Keys * Developer Settings ## Verify Connection After adding credentials, verify the connection: ``` You: What's my balance on Binance? Condor: Here's your Binance balance: - USDT: 1,234.56 - BTC: 0.025 - ETH: 0.5 ``` Or use `/portfolio` to see all connected exchanges. ## Multiple Exchanges You can add credentials for multiple exchanges. Condor aggregates them into a single portfolio view. ``` /portfolio 📊 Portfolio Overview Total Value: $15,234.56 Binance: $10,000 Hyperliquid: $5,234.56 ``` ## Troubleshooting * Double-check the key and secret * Ensure the API key is active (not expired) * Verify permissions include trading * Adding a credential runs a live balance check before saving it, so a rejected key fails with the **exchange's own error text** rather than a generic message — read it for the actual cause (bad key, wrong permissions, expired key, clock skew, and so on) * Add your server's IP to the exchange's allowed list * Or temporarily disable IP restriction for testing ## Next Step Access the browser-based interface for managing trading # Build Your First Agent Source: https://condor.hummingbot.org/getting-started/first-agent Create and deploy an autonomous Trading Agent Now let's create your first Trading Agent—an autonomous system that observes markets, reasons about strategy, and executes trades. ## Start Agent Builder 1. Send `/agent` in Telegram 2. Select **Agent Builder** mode 3. Follow the guided flow ## Phase 1: Strategy Design Tell the Agent Builder what you want to trade: ``` You: I want to create a scalping strategy for SOL-USDT on Binance perpetual. Use support/resistance levels with EMAs for trend confirmation. Take profit at 2%, stop loss at 1%. Agent Builder: Great! I'll create a SOL perp scalper with: - Market: Binance Perpetual SOL-USDT - Entry: Support levels + EMA alignment - Exit: 2% TP / 1% SL - Position size: $25 per trade Shall I proceed with strategy design? ``` ## Phase 2: Market Data The builder selects or creates routines for market analysis: ``` Agent Builder: I'll create a routine to analyze SOL-USDT with: - 1-minute candles - EMA(7), EMA(25), EMA(99) for trend - Support/resistance detection Running analysis now... Analysis Result: - Price: $156.20 - Trend: Bullish (EMAs aligned) - Support: $154.50, $152.00 - Resistance: $158.00, $160.50 Does this look correct? ``` ## Phase 3: Strategy Logic The builder creates your `agent.md` file: ```yaml theme={null} --- name: SOL Scalper tick_interval: 60 connectors: - binance_perpetual configs: trading_pair: SOL-USDT position_size: 25 limits: max_position_size_quote: 100 max_single_order_quote: 25 max_open_executors: 3 max_drawdown_pct: 5 --- ## Goal Scalp SOL-USDT on Binance perpetual using support/resistance with EMA confirmation. ## Entry Rules 1. Run technical analysis routine 2. If trend is bullish AND price near support → Long 3. If trend is bearish AND price near resistance → Short ## Exit Rules - Take profit: 2% - Stop loss: 1% - Keep position on stop (to recover) ## Constraints - Max 3 concurrent positions - Never exceed $100 total exposure ``` ## Phase 4: Dry Run Test the agent's reasoning without real trades: ``` Agent Builder: Starting dry run... Dry Run Result: - Analyzed SOL-USDT - Trend: Bullish - Price near support ($154.50) - Decision: Would open LONG - Reasoning: "EMAs aligned bullish, price 1.1% from S1" No trades executed (dry run mode). Ready for live test? ``` ## Phase 5: Run Once Test with a single real trade: ``` Agent Builder: Running once with live trading... Run Once Result: - Created Position Executor - Side: LONG - Entry: $155.80 - Take Profit: $158.92 (2%) - Stop Loss: $154.24 (1%) Position is now active. Monitor with /agent. ``` ## Phase 6: Deploy Live Start the agent in loop mode: ``` You: Deploy live with 60 second frequency Agent Builder: Agent deployed! 🚀 SOL Scalper is now running: - Frequency: Every 60 seconds - Mode: Loop - Risk limits active Monitor with /agent → SOL Scalper → View Status ``` ## Monitor Your Agent Once deployed, monitor via Telegram: ``` /agent → SOL Scalper → View Status 🤖 SOL Scalper Status: Running Session: session_1 Tick: 47 Active Executors: - Position #1: LONG 10 SOL @ $155.80 (+$4.20) Recent Decisions: - Tick 47: Monitoring, no action - Tick 46: Opened long position - Tick 45: Waiting for setup ``` ## Stop or Pause ``` /agent → SOL Scalper → Stop Agent ``` The agent stops cleanly, keeping any open positions for you to manage manually or resume later. ## Next Step Learn to monitor, debug, and control agents # Installing Condor and Hummingbot API Source: https://condor.hummingbot.org/getting-started/installing Deploy the two-server architecture Condor uses a [two-server architecture](/motivation): the **Condor Server** (agentic layer) and the **Hummingbot API Server** (execution layer). This guide installs both. **Secure your API before going live.** Hummingbot API can place orders, read balances, and manage bots. AI assistants (MCP, Condor agents, and similar tools) make that surface easier to reach—and easier to misuse if the API is open on the public internet. **We recommend [Tailscale](#secure-remote-access-with-tailscale) for production**, especially when Condor and the API run on different machines. Tailscale puts the API on a private encrypted network so only your devices can connect—without publishing port **8000** on a public IP. Use strong API passwords too; Tailscale is network isolation on top of auth, not a replacement for it. Full walkthrough: [Securing Condor and Hummingbot API with Tailscale](https://hummingbot.org/blog/securing-condor-and-hummingbot-api-with-tailscale/) · [Hummingbot API Tailscale guide](https://hummingbot.org/hummingbot-api/tailscale/) ## Prerequisites | Component | Minimum | Recommended | | --------- | --------------------------------------- | ---------------- | | OS | Linux x64 or ARM (Ubuntu 20.04+), macOS | Ubuntu 22.04 LTS | | Memory | 4 GB RAM | 8 GB RAM | | Storage | 10 GB SSD | 20 GB SSD | | CPU | 2 vCPUs | 4 vCPUs | Any cloud provider works. Popular choices: * **AWS EC2**: t3.medium or larger * **Google Cloud**: e2-medium or larger * **Digital Ocean**: Basic Droplet with 4GB RAM * **Hetzner**: CX21 or larger Allow **SSH (port 22)** for administration. **Do not expose port 8000 publicly** for production—use [Tailscale](#secure-remote-access-with-tailscale) so Condor and other clients reach the API at `http://hummingbot-api:8000` on your private tailnet. Create a free account at [tailscale.com](https://tailscale.com), then: 1. Generate a **reusable** auth key at [Settings → Keys](https://login.tailscale.com/admin/settings/keys) (starts with `tskey-auth-`) 2. Enable **[MagicDNS](https://login.tailscale.com/admin/dns)** so `hummingbot-api` resolves by name 3. Install Tailscale on any device that should reach the API (your laptop, Condor host, etc.) and sign in to the same account During Hummingbot API setup, answer **`y`** when asked to enable Tailscale. ## Do I Need Tailscale? | Situation | Tailscale needed? | | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | | Testing locally—Condor and Hummingbot API on one machine | No | | Same VPS, both services together | **No** — the API binds `127.0.0.1` by default; still worth it if anything ever needs off-box access | | Different machines (for example, laptop + VPS) | **Yes** | | A team or multiple devices need access | **Yes** | | You want the **web dashboard** from another device | **Yes** — it is the supported way; see [The dashboard under Tailscale](#the-dashboard-under-tailscale) | ### What binds where by default Every port in the Hummingbot API stack is on loopback out of the box. The one port that is not is Condor's own dashboard, and only in Telegram mode — which is the mode that authenticates. | Service | Port | Default bind | Knob | | -------------------- | ------- | ----------------------------------------------------------------------- | ----------------------------------- | | Hummingbot API | `8000` | `127.0.0.1` | `API_BIND` in `hummingbot-api/.env` | | PostgreSQL | `5432` | `127.0.0.1` | `DB_BIND` in `hummingbot-api/.env` | | EMQX broker (MQTT) | `1883` | `127.0.0.1` | not configurable | | EMQX dashboard | `18083` | `127.0.0.1` | not configurable | | Condor web dashboard | `8088` | `0.0.0.0` in Telegram mode, `127.0.0.1` in Local mode or with Tailscale | `WEB_HOST` in `condor/.env` | EMQX's other listeners — `8883`, `8083`, `8084`, `8081`, `61613` — are not published at all, since nothing in this stack speaks them. The Condor dashboard is the one thing that binds all interfaces by default, and only in **Telegram mode**, which authenticates. Local mode has no login, so it binds loopback and stays there unless you set `WEB_HOST` yourself; turning Tailscale on binds loopback too, because `tailscale serve` is then what exposes it — to your tailnet only. None of this is a substitute for the credentials. A loopback bind protects the port; it does nothing about anyone already on that machine. **Installed before the API's port lockdown? Check it.** Hummingbot API used to publish port **8000 on every interface**, including a VPS's public IP. It now binds `127.0.0.1` by default (`${API_BIND:-127.0.0.1}` in `docker-compose.yml`), and so do Postgres and the EMQX broker — but an existing stack keeps its old bindings until the containers are recreated. Run `make deploy` in `hummingbot-api`, then `make doctor`, which flags any of those ports still sitting on a public interface. If you *do* need the API reachable off-box, widen it deliberately with `API_BIND` in `hummingbot-api/.env` — prefer a specific interface over `0.0.0.0`, and with the Tailscale overlay `API_BIND=` keeps MagicDNS working without publishing anything to the internet. Note that Docker's published ports are *not* blocked by `ufw`: its rules are evaluated before ufw's, so `ufw deny 8000` leaves a widened port reachable unless you write `DOCKER-USER` rules yourself. Close the port in your cloud provider's firewall instead — that is enforced off-host. If you're just testing locally, skip ahead—the Quick Install below works without Tailscale. See [Learning Path](/learning-path#do-i-need-tailscale) for more use-case-based routing. ## Quick Install From an empty directory: ```bash theme={null} curl -fsSL https://raw.githubusercontent.com/hummingbot/deploy/main/setup.sh | bash ``` Use this **only** if you still need the API and database stack **by itself**—for example the Quick Start script **did not** install Hummingbot API (you skipped it or something failed), or you are deploying the API on a **separate** machine (VPS, etc.). **Docker** must be running on that host before you run the command. ```bash theme={null} curl -fsSL https://raw.githubusercontent.com/hummingbot/deploy/main/setup.sh | bash -s -- --hummingbot-api ``` On a VPS or remote server, **enable Tailscale when prompted** (answer **`y`**) so Condor and other clients can reach the API privately. **Note:** Prefer **Quick Start** above. Use manual install **only** if the deploy script fails or you are intentionally setting up from source (for example development). Clone the Condor repo and use the Makefile. You still need a running **Hummingbot API** (for example [Docker with hummingbot-api](https://github.com/hummingbot/hummingbot-api)) unless you already have one. ```bash theme={null} git clone https://github.com/hummingbot/condor.git cd condor make install # Interactive setup + dependencies + AI CLI tools make run # Start the bot ``` Interactive setup will prompt for, in order: 1. **How you will use Condor** — **Telegram** or **Local**. See [Choose your install](#choose-your-install) below. 2. **Telegram Bot Token** and **Telegram User ID** (Telegram mode only): create the bot via [@BotFather](https://t.me/botfather), get your id via [@userinfobot](https://t.me/userinfobot). 3. **Tailscale, yes or no** — whether to secure the connection to Hummingbot API (and the dashboard) with Tailscale. Answer **`y`** for production or any remote setup. You paste the `tskey-auth-...` key later, at the point it is actually needed. 4. **Public IP or hostname** (Telegram mode without Tailscale only) — press Enter if Condor runs on your own machine, or give the server's public address so the `/web` dashboard link works from your phone. 5. **AI model** — the wizard offers to pick the model your Trading Agents think with, and installs its CLI bridge for you. It asks **before** it downloads anything and tells you the cost (\~250MB on a first run, 1–3 minutes), so declining is instant. You can skip it and run `make pick-model` later. See [Integrating your LLM](/llm-integration). 6. Whether to **configure and launch Hummingbot API** with Docker — answer **yes** if you are deploying **Condor and the API on the same machine**. Have **[Docker](https://docs.docker.com/get-docker/)** installed and running first. If the API runs on another server, skip this and enter that API's URL instead. If an API is already running on port 8000, the wizard offers to keep it and asks for its credentials rather than replacing it. 7. **Tailscale auth key** (if you said yes at step 3) — paste your `tskey-auth-...` key from the [Tailscale admin console](https://login.tailscale.com/admin/settings/keys). When Condor is deploying Hummingbot API on this same machine, it also asks whether that API should get its **own** tailnet node. Answer **no** unless another device needs to reach the API directly — Condor itself reaches it over `localhost` either way. 8. **Hummingbot API credentials** — an admin **username**, **password**, and a **config password**. These are required and have no defaults: whatever you enter is written to the API's `.env` and to Condor's `config.yml` so the two sides match. Passwords are not echoed as you type. **`make install` finishes by running `make doctor` for you**, so the first report arrives without asking. It cannot fail the install—a warning there never blocks setup—so read it rather than assuming a clean exit meant a clean report. Run it again any time. It re-checks everything the wizard only confirms once: dependencies (`uv`, `tmux`, `node`, `npm`, `tsc`), `.env`/`config.yml` sanity, the AI model, the dashboard's network binding, Tailscale, and whether each Hummingbot API server in `config.yml` is reachable **and** authenticating. It is read-only and safe to run at any time, and exits non-zero only on real failures—warnings do not. ## Choose your install Three paths. All three run the same agents, bots, routines and web dashboard — what differs is who can reach them and where alerts go. Your own machine, no Telegram account. Dashboard only, no login. Your own machine, plus a bot that pushes alerts and approvals to your phone. A VPS running 24/7. Reachable from anywhere over Tailscale. | | **Local** | **Local + Telegram alerts** | **Server** | | ----------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------- | ----------------------------------------------------------------- | | **Runs on** | Your machine | Your machine | A VPS, 24/7 | | **You need** | Nothing extra | A bot token and your Telegram user id | The above, plus [Tailscale](#secure-remote-access-with-tailscale) | | **How you drive it** | Dashboard at `localhost:8088` | Telegram commands, plus `/web` for the dashboard | Telegram from anywhere; dashboard over the tailnet | | **Login** | **None at all** — whoever reaches the port has full trading control | Telegram authenticates you; `/web` is a time-limited link | Same, and the tailnet is the outer boundary | | **Alerts and approvals** | Only while the dashboard is open | Pushed to your phone | Pushed to your phone | | **Survives you closing the laptop** | No | No | Yes | | **Best for** | Trying it out, or a machine you would rather not attach a bot to | Most people | Anything holding real capital | ### Local No bot, no token, no Telegram account. The dashboard runs on the machine you installed on and you are already logged in when you open it: ``` http://localhost:8088 ``` **Local mode has no login.** Anyone who can reach the port has full trading control. * It binds **`127.0.0.1`** only: reachable from that machine, nowhere else. * Exposing it takes an explicit **`WEB_HOST=0.0.0.0`** in `.env`. Only do that behind something that authenticates — [Tailscale](#secure-remote-access-with-tailscale), an SSH tunnel, or an authenticating reverse proxy. * Alerts and approval requests are only visible while the dashboard is open; there is nothing to push them to. Local mode logs in as `ADMIN_USER_ID` — the same id your servers, chat defaults, preferences and agent memory are already keyed to. An existing Telegram install that switches to local keeps all of it. ### Local + Telegram alerts The same install, with a bot attached. You control Condor with commands (`/portfolio`, `/trade`, `/agent`), agents push alerts and approval requests to your phone, and `/web` hands you a time-limited link to the browser dashboard. This is the difference that matters: an agent asking permission to place a trade reaches you when you are not at the machine. In Local mode that request waits until you next open the dashboard. Needs a bot token from [@BotFather](https://t.me/botfather) and your numeric Telegram user id from [@userinfobot](https://t.me/userinfobot) — both covered in [Create a Telegram Bot](#create-a-telegram-bot) below. ### Server Condor and Hummingbot API on a VPS, running whether or not your laptop is on. Everything from the path above, plus: * **[Tailscale](#secure-remote-access-with-tailscale) is not optional here.** Hummingbot API binds `127.0.0.1` by default, so on a VPS your laptop cannot reach it at all — and the alternative, widening `API_BIND` to a public interface, is the thing you do not want: Docker's published ports are not blocked by `ufw`, whose rules are evaluated after Docker's own. Tailscale gives your devices a private path without opening anything. See [Do I Need Tailscale?](#do-i-need-tailscale) for why. * The dashboard binds `127.0.0.1` and is published to your tailnet through `tailscale serve` — see [The dashboard under Tailscale](#the-dashboard-under-tailscale). * Either mode works. Telegram is the usual choice, since it reaches you off the tailnet; Local mode on a server is only reachable through the tailnet. * Run `make doctor` on the VPS after setup. It is the fastest way to catch a public bind before it matters. The Telegram/Local distinction is recorded as `CONDOR_MODE` in `.env` (`telegram` or `local`) and is never inferred from whether a token happens to be present. To switch later, re-run `make setup` and pick the other mode — your `TELEGRAM_TOKEN` is left in place, so switching back costs nothing. "Server" is not a mode; it is where you run one of the two. For the usual sequence — Local first, Telegram later, a server after that — see [Start local, grow later](#start-local-grow-later). ## Start local, grow later The three paths are not a one-time choice. The common route is to start with **Local**, attach **Telegram** once you want alerts reaching your phone, and involve a **server** once real capital is on the line. Each hop keeps what you have built — here is what each one takes. ### Add Telegram to a Local install 1. Create a bot and get your numeric user id — [Create a Telegram Bot](#create-a-telegram-bot) and [Get Your User ID](#get-your-user-id) below. 2. Run `make setup`. It shows the current mode and asks **Keep local mode?** — answer `n` and pick **Telegram**, then enter the token and your id. Everything else is remembered. 3. `make restart`. Send `/start` to your bot. A Local install that never had Telegram runs as user id **1**. Adding a bot switches your login to your Telegram id: the API **servers** you configured are shared and carry over, but dashboard **preferences and agent conversations** are keyed per user and stay behind under id 1. Make this hop early, before there is much history to leave. (The reverse switch, Telegram → Local, keeps your id — and with it everything.) ### Keep Condor local, put the API on a server You do not have to move Condor to get 24/7 execution. Bots and executors run on the **Hummingbot API server** — a laptop Condor driving a VPS API keeps strategies running while the laptop sleeps. Only agents, routines and alerts live in Condor itself. 1. On the VPS, run the **Hummingbot API only** installer and answer **`y`** to Tailscale — see [On the API server](#on-the-api-server). 2. On your machine, install [Tailscale](https://tailscale.com/download) and sign in to the same tailnet. 3. Add the server — **Settings → Servers** in the dashboard (Local mode) or **`/servers`** in Telegram: host `hummingbot-api`, port `8000`, the credentials you set on the VPS. 4. `make doctor` — the new server should report **reachable, authenticated**. ### Move Condor itself to the server When you want agents and routines running around the clock too: 1. Install Condor on the VPS ([Quick Install](#quick-install)), in the same mode you ran locally, and answer **`y`** to Tailscale — on a VPS it [is not optional](#server). 2. Stop the new install (`make stop`) and copy your state over. From the old Condor directory: ```bash theme={null} scp .env config.yml you@vps:condor/ # mode, servers, users, credentials scp -r data you@vps:condor/ # preferences, notifications scp -r condor/.runtime you@vps:condor/condor/ # agent conversations ``` Custom agents in `agents/` and routines in `routines/` are plain files — copy those too if you wrote any. 3. On the VPS: `make doctor`, then `make run`. If the API now lives on this same VPS, point the server entry at `localhost` in **Settings → Servers** (or **`/servers`**). 4. Retire the old install (`make stop` on the laptop) so two Condors are not driving the same accounts. ## Create a Telegram Bot Telegram mode only—skip this and the next section if you chose Local mode. 1. Open [@BotFather](https://t.me/botfather) in Telegram 2. Send `/newbot` and follow the prompts to name your bot 3. Copy the bot token (format: `123456789:ABCdefGHIjklMNOpqrsTUVwxyz`) ## Get Your User ID 1. Open [@userinfobot](https://t.me/userinfobot) in Telegram 2. Send `/start` to receive your user ID ## Secure Remote Access with Tailscale Use Tailscale when Condor and Hummingbot API run on **different machines**—for example Condor on your laptop and the API on a cloud VPS. ### On the API server 1. Run the **Hummingbot API only** installer (or Quick Start with API enabled) 2. When asked **Use Tailscale for secure private networking?**, answer **`y`** 3. Paste your auth key and deploy: ```bash theme={null} cd hummingbot-api make deploy make tailscale-status # confirm hummingbot-api appears on your tailnet ``` ### On the Condor machine 1. Install [Tailscale](https://tailscale.com/download) and sign in to the **same account** 2. In Telegram, open **`/servers`** — or in Local mode, **Settings → Servers** in the dashboard — and add the API with: * **Host**: `hummingbot-api` (MagicDNS name, not a public IP) * **Port**: `8000` * **Username / Password**: same as the API `.env` Test from the Condor host: ```bash theme={null} curl -u YOUR_USERNAME:YOUR_PASSWORD http://hummingbot-api:8000/ ``` ### The dashboard under Tailscale Turning Tailscale on changes where the web dashboard listens. With `USE_TAILSCALE=true` in `.env`, Condor binds the dashboard to **`127.0.0.1`** and publishes it to your tailnet through `tailscale serve`, rather than binding `0.0.0.0` and trusting the network. The port is never on a public interface, and only devices signed in to your tailnet can reach it. If the `tailscale serve` proxy cannot be confirmed, Condor **logs an error and leaves the dashboard on loopback**. It does not fall back to a public bind — a silent fallback is how a trading dashboard ends up on the open internet. So a dashboard you cannot reach from another device means the proxy is not up, not that the bind is wrong: ```bash theme={null} tailscale serve status # what is actually published to the tailnet make doctor # names the same problem, with the fix ``` Do not set `WEB_HOST=0.0.0.0` to "fix" an unreachable dashboard while Tailscale is on. That publishes a login-free interface in Local mode, and a login-link one in Telegram mode, to every network the machine is attached to. `make doctor` reports it as a failure for exactly this reason. Tailscale also works when Condor and the API run on the **same machine**—you still get a stable hostname and avoid publishing port 8000 publicly. ## Verify Installation Run `make doctor` first—it reports dependencies, config, the AI model, the dashboard binding, Tailscale, and Hummingbot API connectivity in one pass, and exits non-zero if anything is actually broken. **Telegram mode** 1. Open your Telegram bot and send `/start` 2. You should see the main menu with commands like `/portfolio`, `/trade`, `/agent` **Local mode** 1. Open `http://localhost:8088` in a browser on the machine running Condor 2. You are logged in already—no token, no login screen > **Note:** After Condor starts successfully, **admins** should receive a Telegram message: **"Condor is online and ready."** If that does not appear within a minute or two, see [Troubleshooting](/getting-started/troubleshooting) (for example attach to the `condor` tmux session after Quick Start). ``` Welcome to Condor! 🦅 Commands: /portfolio - View balances /trade - Place orders /agent - Trading Agents /keys - Manage credentials /servers - API servers ``` ## Access Points | Service | Local URL | Tailnet URL (when Tailscale enabled) | Description | | ----------------------------- | --------------------------------- | ------------------------------------ | ---------------------------------------------------------------------------------- | | Telegram | Your bot | — | Primary interface (Telegram mode) | | API | `http://localhost:8000` | `http://hummingbot-api:8000` | REST API | | Swagger | `http://localhost:8000/docs` | `http://hummingbot-api:8000/docs` | API documentation | | Web Dashboard (Telegram mode) | Run `/web` in Telegram for a link | `https://..ts.net` | Browser interface, time-limited login link | | Web Dashboard (Local mode) | `http://localhost:8088` | `https://..ts.net` | Browser interface, no login — loopback only, or tailnet-only via `tailscale serve` | ## Managing Services How you manage Condor depends on how you installed it. **After Quick Start (deploy installer):** Condor runs in a **tmux** session named `condor`. ```bash theme={null} tmux attach -t condor # live logs and tracebacks (detach: Ctrl+B, then D) tmux kill-session -t condor # stop Condor completely ``` From the directory that contains the `condor` folder, you can also upgrade with: ```bash theme={null} curl -fsSL https://raw.githubusercontent.com/hummingbot/deploy/main/setup.sh | bash -s -- --upgrade ``` **After Manual install:** `make run` also starts Condor in a **tmux** session named `condor`, and the Makefile wraps the same operations: ```bash theme={null} make run # start (fails fast and prints the error if startup dies) make logs # attach to the session (detach: Ctrl+B, then D) make status # is Condor running? make restart # stop and start again make stop # stop Condor make run-fg # run in the foreground instead, for debugging a startup error make doctor # re-check dependencies, config, and API access ``` **Hummingbot API (Docker):** From the `hummingbot-api` directory (sibling of `condor` when installed by the deploy script): ```bash theme={null} make doctor # containers, port exposure, credentials, Tailscale, API auth docker compose ps docker compose logs -f # or your compose plugin equivalent make tailscale-status # when Tailscale is enabled make emqx-audit # broker listeners, auth, ACL and any rule-engine rules ``` ## Keeping Condor updated Updating is admin-only and available from **both** surfaces, over one shared engine: `/update` in Telegram and **Settings → Updates** in the dashboard. They are two views of the same run, so an update started in Telegram can be watched to completion in the browser and vice versa — starting a second one hands you back the one already in flight rather than queueing it. This is what makes a **Local mode** install self-sufficient: the dashboard panel is the surface it has, so an install with no Telegram bot does not need one to update. ### What can be updated Two components, versioned by what actually governs them: | Component | How it is versioned | | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Condor** | A git checkout — `HEAD` against `origin/` | | **Hummingbot API** | A git checkout **and** a container image. The repo governs the compose file and the bind-mounted `bots/`; the image governs the API's own code, so both are reported | Whether the API is rebuilt from source or pulled as a published image is not a setting — `docker compose config` already knows, from whether the service has a `build:` key. ### Preflight: what is in the way Before anything runs you get the blockers, the warnings, and the ordered plan. **Blockers** stop the update and say why in terms you can act on — the checkout is not a git repo, it has commits the remote does not (so it cannot be fast-forwarded), local changes would be overwritten, or the registry could not be reached. Where a blocker has a safe way out it offers one: a dirty checkout can be resolved with **discard** or **stash** without leaving the panel. **Warnings** never refuse — they tell you a consequence worth knowing: restarting the API container stops its executors, and Condor keeps running the code it booted with until you relaunch it. ### The relaunch step An update deliberately stops one step short of running the new code. It lands the code, syncs dependencies, rebuilds the dashboard, records that a relaunch is owed, and asks you to do it. That is not an omission. Condor is almost never the top of its own process tree — started through `make run`, a shell wrapper, or a supervisor — so re-execing itself races the parent into bringing a **second** Condor up on the same port, against the same config and the same Telegram token. ```bash theme={null} make restart ``` Between the update and the relaunch, every seat sees a **relaunch to apply** banner, because the browser is running the new bundle against the old API and would otherwise just look broken. The run is journalled to `data/update_run.json` at every step, so the panel picks the result back up across the restart and the next boot tells you whether the update worked. ### Update notifications Condor checks in the background and tells the admin when a component falls behind — **once per update, not once per check**, so deferring one does not bury the rest of your notifications. Each surface gets a next step it can actually take: a Telegram message, and a bell entry that opens the update panel for a browser-only admin. Tune it with `UPDATE_CHECK_INTERVAL` in `.env` — seconds, default `3600`, and `0` disables the checks. The first runs 30 seconds after startup. Both paths are **git-based**, so they work on a git checkout — which is what the Quick Start and manual installs both produce. An install unpacked some other way is reported as a blocker rather than silently skipped, and updates by whatever means it was installed. ## Privacy: telemetry and sharing Two separate things, with separate switches. Neither sends anything about your trading, your keys, or your balances. ### Telemetry — anonymous counts An admin answers once, for the whole install. Three levels: | Level | What it sends | | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ping` | **The default and the floor.** Only that this install exists: `install`, `heartbeat`, `version_change`, `shutdown`. | | `usage` | The above plus feature, reliability and agent events — which command ran, which connector a bot deployed on, an error's *type* and a hash of its message. Opt-in only. | | `off` | Nothing, ever. No install id is created, nothing is buffered, nothing is written. | The identifier is a random UUID generated once — not derived from your hostname, username, MAC, or any token. Server names, URLs and keys are never sent; a routine you wrote yourself is reported as `custom` rather than by name. The consent prompt offers `ping` or `usage` and has no "off" button, so ignoring it is not read as a refusal. Turning it off is a deliberate act, in any of three places — highest precedence first: 1. **`.env`** — `CONDOR_TELEMETRY=off`. The full kill switch: it overrides everything and suppresses the prompt. 2. **Settings → Privacy** in the dashboard — readable by every seat, changeable only by the admin. This is also where an install running **without Telegram** is asked in the first place, since it has no bot to be asked through. Choosing `off` here is the durable form: it is stored, not read from one process's environment. 3. **`config.yml`** — the `telemetry` section directly: `consent: granted|denied` plus `level`. To see what your install is actually doing: ```bash theme={null} uv run python -c "from condor.telemetry import consent; print(consent.level())" ``` Downgrading from `usage` to `ping`, or turning telemetry off, is a **withdrawal, not a pause** — the in-memory buffer and the outbox file are deleted, so nothing already recorded can be sent afterwards. ### Sharing — whole conversations Separate from telemetry, and off until you turn it on. Telemetry is anonymous counts an admin consents to once; a share is a **transcript**, and only the person who held the conversation can hand it over. Secrets are redacted before a transcript is built, and again before it leaves. In Settings → Privacy: * **Off** — nothing is shared. The share button still works if you want it. **This is where every install starts.** * **Ask me** — the button, one conversation at a time, with the redacted transcript in front of you before you confirm. * **Always** — conversations are shared on their own, redacted the same way, **without showing you first**. Always is the only path where nobody reads the payload first, so it is deliberately narrower than the button — any one of these stops a conversation going: * **Idle** — only after 30 minutes of silence in that chat. * **Forward-only** — only conversations *started after* you chose Always. Your archive is never swept, and turning it off and on again starts a fresh window rather than reaching back. * **Single-author only** — a Telegram group is never taken automatically. You can consent for yourself, not for the other people in the room. * **Excluded chats, forever** — every covered conversation carries an undismissable marker with one click to leave it out, and that exclusion is permanent. * **Rate limited** — at most 3 conversations per 15-minute sweep. The operator-level kill switch: ```bash theme={null} CONDOR_SHARING=off # in .env — destroys queued transcripts too ``` `CONDOR_SHARING=off` reaches transcripts already queued and destroys them — but a queued **unshare** is still delivered, because a revocation is a deletion the install already promised. ## Troubleshooting Hit a snag—Condor can't reach the API, Telegram stays quiet, Tailscale won't connect—or want to check whether your broker needs securing? See the dedicated [Troubleshooting](/getting-started/troubleshooting) page, which also covers the [message broker security fix](/getting-started/troubleshooting#secure-the-message-broker). ## Next Step Connect your exchange accounts Got Condor running? Tell us how install went, what's confusing, and what you need next. What's working? What's confusing? What do you wish Condor could do? Your answers go straight to the Hummingbot Foundation team and directly shape what we build next. # Introduction Source: https://condor.hummingbot.org/getting-started/overview Step-by-step guide to setting up Condor and building your first Trading Agent This guide walks you through setting up Condor from scratch and building your first autonomous Trading Agent. ## What You'll Learn Deploy the two-server architecture on your machine Control your trading infrastructure via mobile-friendly commands — or skip it entirely with [Local](/getting-started/installing#local) and drive Condor from the browser Access the browser-based interface for managing bots and agents Connect Claude, Gemini, Codex, OpenRouter, or a local model for Trading Agents Connect your exchange accounts via API keys View balances and positions across all exchanges Create and deploy an autonomous Trading Agent Monitor, inspect, and control running agents ## Prerequisites Before starting, ensure you have: * A Linux server (Ubuntu 20.04+) or macOS machine * 4 GB RAM minimum (8 GB recommended) * Exchange API keys with trade permissions * A **Telegram** account and bot token — unless you install in [Local](/getting-started/installing#local), which needs neither * Access to an LLM provider (Claude, Gemini, Copilot, Codex, OpenRouter, or a local model) for agent reasoning * **[Tailscale](https://tailscale.com) account** (free tier is enough) — recommended when Condor and Hummingbot API run on different machines or any production VPS setup ## Time to Complete Most users complete this guide in under 30 minutes. Begin with installation Jump to building your first agent Already using Condor? Tell us what's working and what you need next. What's working? What's confusing? What do you wish Condor could do? Your answers go straight to the Hummingbot Foundation team and directly shape what we build next. # Seeing Your Portfolio Source: https://condor.hummingbot.org/getting-started/portfolio View balances and positions across all exchanges Once you've added credentials, you can view your unified portfolio across all connected exchanges. ## View Portfolio Send `/portfolio` in Telegram to see: ``` 📊 Portfolio Overview Total Value: $15,234.56 24h Change: +$234.12 (+1.56%) Holdings: • USDT: $10,000.00 • BTC: $3,250.00 (0.05 @ $65,000) • ETH: $1,750.00 (0.5 @ $3,500) • SOL: $234.56 (1.5 @ $156.37) By Exchange: • Binance: $12,000.00 • Hyperliquid: $3,234.56 ``` ## Conversational Queries Ask Condor about your portfolio in natural language: ``` You: What's my total balance? Condor: Your total portfolio value is $15,234.56 across 2 exchanges. You: How much BTC do I have? Condor: You have 0.05 BTC worth $3,250.00 on Binance. You: What are my open positions? Condor: You have 2 open perpetual positions: - SOL-USDT Long: 10 SOL @ $155.20 (P&L: +$11.70) - ETH-USDT Short: 0.5 ETH @ $3,520 (P&L: -$10.00) ``` ## Web Dashboard For a visual view, open the web dashboard: 1. Send `/web` in Telegram 2. Click the login link 3. View the Portfolio tab The dashboard shows: * Portfolio value over time * Asset distribution charts * Position details with P\&L * Historical performance ## Filtering ### By Exchange ``` You: What's my Binance balance? Condor: Your Binance balance: - USDT: 5,000.00 - BTC: 0.05 ``` ### By Asset Type ``` You: Show my perpetual positions Condor: Perpetual positions on Hyperliquid: - SOL-USDT Long: 10 @ $155.20 - ETH-USDT Short: 0.5 @ $3,520 ``` ## Refresh Portfolio data is fetched in real-time from exchanges. The data refreshes automatically when you query. Some exchanges have rate limits. If you see stale data, wait a few seconds and query again. ## Next Step Create an autonomous Trading Agent # Managing Agent Sessions Source: https://condor.hummingbot.org/getting-started/sessions Monitor, inspect, and control running agents Each time you start a Trading Agent, it creates a **session** that tracks all activity, decisions, and state changes. Here's how to manage them. ## View Active Sessions ``` /agent → Select agent → View Sessions 📂 SOL Scalper Sessions Active: • session_3 (running) - Started 2h ago, 120 ticks Completed: • session_2 - 6h, 360 ticks, +$45.20 • session_1 - 4h, 240 ticks, +$12.50 ``` ## Monitor a Running Session ``` /agent → SOL Scalper → View Status 🤖 SOL Scalper - Session 3 Status: Running Tick: 121 Uptime: 2h 1m Portfolio: - Exposure: $75.00 / $100.00 limit - Session P&L: +$23.40 - Active Executors: 3 Last Decision (Tick 121): "Price consolidating at $156.20. EMAs still bullish. Holding current positions, waiting for breakout." ``` ## View Session Journal The journal contains the agent's complete decision history: ``` /agent → SOL Scalper → Journal 📓 Session 3 Journal ## Summary - Started: 2024-03-27 14:00 UTC - Current Tick: 121 - Session P&L: +$23.40 ## Recent Decisions ### Tick 121 - 16:01 UTC Decision: Hold Reasoning: Price consolidating, waiting for breakout ### Tick 120 - 16:00 UTC Decision: Take partial profit Reasoning: Position #2 hit 1.5% gain, scaling out 50% Action: Closed 5 SOL @ $157.80 ### Tick 115 - 15:55 UTC Decision: Open long Reasoning: Price bounced off S1 ($154.50), EMAs aligned Action: Created Position Executor - LONG 10 SOL @ $155.20 ``` ## View Snapshots Snapshots capture the complete state of each tick for debugging: ``` /agent → SOL Scalper → Snapshots → Tick 115 📸 Snapshot - Tick 115 Prompt: [Full system prompt sent to LLM] Response: "Analyzing SOL-USDT... Price at $155.20, bounced from support at $154.50. EMAs: 7 > 25 > 99 (bullish). Opening long position with 1% stop, 2% take profit." Tool Calls: 1. run_routine("technical_analysis") → {trend: "bullish"...} 2. manage_executors("create", "position_executor") → {id: "exec_115"} Risk State: - Exposure: $50.00 - Daily P&L: +$18.20 - Is Blocked: false ``` ## Inject Directives Send real-time instructions to a running agent: ``` /agent → SOL Scalper → Inject Directive Enter directive: "Reduce exposure - high volatility expected in 10 minutes" ✓ Directive injected. Will appear in next tick. ``` The agent sees your directive and responds: ``` Tick 122 Decision: "Received user directive about high volatility. Closing position #3 to reduce exposure from $75 to $50." ``` ## Pause and Resume **Pause** an agent without losing state: ``` /agent → SOL Scalper → Pause ⏸️ Agent paused. Executors remain active. Resume anytime with /agent → Resume. ``` **Resume** continues from where it left off: ``` /agent → SOL Scalper → Resume ▶️ Agent resumed. Next tick in 45 seconds. ``` ## View Learnings Learnings persist across sessions: ``` /agent → SOL Scalper → Learnings 📚 SOL Scalper Learnings ## Active Insights 1. SOL tends to respect round numbers ($150, $155, $160) 2. Avoid entries during funding rate settlement (every 8h) 3. Wider stops (1.5%) work better in high volatility 4. EMAs lag during sharp moves - confirm with volume ## Session Notes - Session 3: Consolidation day, fewer setups - Session 2: Strong trend, 4/5 winners ``` ## Stop and Archive **Stop** ends the session: ``` /agent → SOL Scalper → Stop 🛑 Agent stopped. Session 3 complete: 121 ticks, +$23.40 Open positions remain active. Manage manually or start new session. ``` Sessions are automatically archived and can be reviewed anytime. ## Next Step Extend agents with deterministic Python code You've got an agent running. Tell us what's working and what Condor should do next. What's working? What's confusing? What do you wish Condor could do? Your answers go straight to the Hummingbot Foundation team and directly shape what we build next. # Telegram Interface Source: https://condor.hummingbot.org/getting-started/telegram Mobile-friendly interface for controlling Condor via Telegram bot Condor provides a Telegram bot interface for mobile-friendly control of your trading infrastructure. You can interact with it conversationally or use structured commands. ## Conversational Mode Before creating Trading Agents, you can talk to Condor directly using natural language: ``` You: What's my balance on Binance? Condor: Here's your Binance balance: - USDT: 1,234.56 - BTC: 0.025 - ETH: 0.5 ``` ``` You: What routines do I have for market analysis? Condor: You have these analysis routines available: - technical_analysis: Analyze trend, volatility, support/resistance - support_resistance_ema: Identify S/R levels with EMA alignment ``` ``` You: Can you analyze BTC-USDT and tell me what you think? Condor: Running technical analysis routine... - Trend: EMA aligned (fast > mid > slow) - bullish - Key support: $62,500 - Key resistance: $65,200 - Suggestion: Consider grid with long bias ``` ## Commands | Command | Description | | -------------- | --------------------------------------------------- | | `/start` | Welcome message and setup | | `/portfolio` | View balances across exchanges | | `/agent` | Trading Agent management and LLM connection | | `/executors` | Deploy and manage trading executors | | `/bots` | Deploy and manage trading bots | | `/new_bot` | Create bot configurations | | `/routines` | Run configurable Python routines | | `/trade` | Place CEX and DEX orders | | `/swap` | DEX token swaps via Gateway | | `/lp` | Liquidity pool management | | `/servers` | Manage Hummingbot API servers | | `/keys` | Configure exchange API credentials | | `/gateway` | Gateway for DEX trading | | `/web` | Open the web dashboard | | `/memory` | What each assistant remembers about you, grouped | | `/delegations` | Background agent tasks and their results | | `/stop` | Interrupt the answer in flight, keeping the session | | `/cancel` | Back out of any in-progress input flow | | `/admin` | User approvals and roles (admin) | | `/update` | Check for updates and restart (admin) | ## /start - Main Menu The welcome screen providing quick access to all features. Returns to the main menu at any time. ## /portfolio - Portfolio Dashboard Comprehensive portfolio tracking across all connected accounts and exchanges. **Features:** * **P\&L Tracking**: 24-hour, 7-day, and 30-day performance metrics * **Token Holdings**: Current balances with price changes * **Position Monitoring**: Perpetual futures and CLMM positions * **Visual Analytics**: Portfolio value charts and distribution graphs ## /bots - Bot Management Monitor and control all Hummingbot trading bot instances. **Features:** * **Status Overview**: Health indicators for all bots * **System Metrics**: CPU usage, memory, uptime * **Controller Performance**: Track strategy metrics * **Start/Stop**: Control bot lifecycle * **Log Viewing**: Access bot logs for debugging ## /agent - Trading Agents Create and manage autonomous Trading Agents. **Features:** * **Create Agent**: Guided setup for new Trading Agents * **Select LLM**: Connect Claude, Gemini, Copilot, Codex, OpenRouter, or a local model — see [Integrating your LLM](/llm-integration) * **Run Modes**: dry\_run, run\_once, or loop * **Monitor**: View active sessions and tick status * **Inject Directives**: Send real-time instructions to running agents ## /trade - CEX Trading Unified trading interface for centralized exchanges. **Supported Exchanges:** Binance, Bybit, OKX, Kucoin, Kraken, Coinbase, Hyperliquid **Features:** * Market and limit orders * Position management (perpetual) * Leverage configuration * Order tracking and cancellation ## /swap - DEX Token Swaps Execute token swaps on decentralized exchanges via Gateway. **Supported DEXs:** Jupiter (Solana), Uniswap (Ethereum, Base) **Features:** * Get quotes from multiple protocols * Slippage protection * Transaction status tracking ## /lp - Liquidity Pool Management Manage Concentrated Liquidity Market Maker (CLMM) positions. **Supported Protocols:** Orca, Raydium, Meteora, Uniswap V3 **Features:** * View active LP positions * Add/remove liquidity * Collect accumulated fees * Position range monitoring ## /servers, /keys, /gateway - System Configuration Configuration is three separate commands rather than one menu: | Command | What it covers | | ---------- | ------------------------------------------------------------------------------------------------------- | | `/servers` | Add, edit, or remove Hummingbot API server connections, set the default, and see live status per server | | `/keys` | Which exchanges are connected. **Read-only in Telegram** — see below | | `/gateway` | Gateway for DEX trading: status, connectors, wallets, networks and RPC endpoints, pools and tokens | ### Exchange credentials are dashboard-only `/keys` lists the exchanges currently connected and nothing more. Connecting a new one, or removing an existing one, happens in the web dashboard under **Settings → Keys** — the command hands you a login link, valid for 5 minutes, to get there. An API secret is never typed into a Telegram chat, where it would sit in message history on Telegram's servers. When creating the key at your exchange, enable **read + trade** only. Do not enable withdrawal or transfer permissions. Older material refers to a single `/config` command. It no longer exists — use the three above, or the dashboard's [Settings page](/webui/settings), which covers the same ground in one place. ## /update - Updates and Restart Admin only. Checks **Condor** and the **Hummingbot API** beside it, showing what each is running and what is available — for the API, both its git checkout and its container image, since they answer different questions. Before anything runs, a preflight reports **blockers** (a diverged checkout, local changes that would be overwritten, an unreachable registry) with a way out where one exists — a dirty checkout offers **discard** or **stash** — and **warnings** that never refuse, such as restarting the API reaping its executors. An update lands the code, syncs dependencies, rebuilds the dashboard, and then **asks you to relaunch** rather than restarting itself: Condor is rarely the top of its own process tree, so re-execing races the parent into a second Condor on the same port and token. Run `make restart` when it asks. The dashboard has the same panel at **Settings → Updates**, over the same engine — an update started here can be watched to completion there, and a **Local mode** install uses that panel instead of this command. See [Keeping Condor updated](/getting-started/installing#keeping-condor-updated). ## /cancel - Back Out of a Flow Several flows ask for the next message as their answer — a server name, a price, an amount. `/cancel` abandons that flow and returns you to normal command handling. It is what the prompts themselves tell you to send, and it works from inside any of them. ## Notifications Condor sends notifications for: * Executor state changes (created, filled, stopped) * Risk limit violations * Bot status changes * Price alerts from routines ## Voice Commands Send voice messages to Condor for hands-free trading. Condor uses Whisper for local transcription—no audio leaves your server. ### How It Works 1. Hold the microphone button in Telegram 2. Speak your command 3. Release to send 4. Condor transcribes and processes your request ### Examples **Check prices:** ``` "Hey Condor, what's the price of ORCA USDT on Binance Perpetual?" → ORCA $1.96 ``` **Create executors:** ``` "Create a grid executor on ORCA to go long between 1.9 and 2.02, invest $300 with take profit of 0.15%" → Grid executor created: ORCA-USDT long grid, 1.90-2.02, $300 ``` **Check status:** ``` "Can you tell me the state of that grid?" → Your grid is cooking. 3 levels filled, $45 invested, +$0.82 unrealized ``` The first voice message may take longer as Condor downloads the Whisper model (\~1GB). Subsequent messages are faster. ## Security * **User Whitelist**: Only authorized Telegram user IDs can interact with the bot * **No Credential Storage**: API keys are stored encrypted on the server, not in Telegram * **Session Isolation**: Each user's session is isolated Using Condor in Telegram every day? Tell us what's working and what you wish it could do. What's working? What's confusing? What do you wish Condor could do? Your answers go straight to the Hummingbot Foundation team and directly shape what we build next. # Troubleshooting Source: https://condor.hummingbot.org/getting-started/troubleshooting Diagnose Condor and Hummingbot API connection problems, and lock down the message broker Before working through the cases below, run **`make doctor`** in the Condor directory — it covers most of what follows and names the fix directly. On the API host, `hummingbot-api` ships its own **`make doctor`**: it checks the containers, which ports are on a public interface, the broker's credentials, Tailscale's tailnet *and* serve status, and whether the API answers an authenticated request. ## Connection issues This is where most problems land: Condor showing **Offline**, *Cannot reach server*, *Connection timeout*, or **Auth Error** somewhere in the UI. Work through this section first. Condor talks to the **Hummingbot API** over HTTP using the **host**, **port**, **username**, and **password** stored for each server (see **`/servers`** in Telegram or `condor/config.yml`). Status checks call `http://{host}:{port}` and list accounts — a failure to connect surfaces as **Offline**, a failure to authenticate as **Auth Error**. 1. **Is the API stack up?** On the machine where Docker runs the API: `docker compose ps` (from the `hummingbot-api` repo directory). You want the API container healthy and port **8000** (default) listening. 2. **Smoke test from the Condor host:** ```bash theme={null} curl -sS -o /dev/null -w "%{http_code}" http://127.0.0.1:8000/docs # same machine curl -u USER:PASS http://hummingbot-api:8000/ # over Tailscale ``` You should get **200**. If this fails, Condor will show offline until the API is reachable — everything below is about getting this curl to succeed. 3. **Wrong host for your layout** * API and Condor on the **same** machine: `host` is usually `localhost` or `127.0.0.1`. * API on a **different** host or VPS: use **`hummingbot-api`** (MagicDNS) when Tailscale is enabled — not a public IP and not `localhost` (localhost would point at the Condor machine itself). * **Docker Desktop / WSL:** If Condor runs in one environment and the API in another, `localhost` may not cross namespaces; use the host gateway IP (`host.docker.internal` on Mac/Windows) or publish ports and use the reachable address from Condor's network namespace. 4. **Credentials:** **Auth Error** means HTTP **401** — username/password in **`/servers`** must match the API's HTTP basic auth (the deploy flow syncs `config.yml` with `hummingbot-api/.env` when both are installed together; if you changed one side, align the other or re-save in **`/servers`**). The setup wizard requires real credentials and no longer seeds `config.yml` with a default pair, so an entry with a blank or placeholder password means it was never filled in — `make doctor` flags that case by name. Just fixed the API and the server still shows **Offline**? Condor caches an unreachable server for a short cooldown window and won't retry on every request — give it a few seconds, or open **`/servers`** and check the entry again to force a fresh check. ### Tailscale-specific problems Prefer Tailscale over opening port **8000** on a public firewall — see [Secure Remote Access with Tailscale](/getting-started/installing#secure-remote-access-with-tailscale). If you must use a public IP instead, allow inbound **8000** only from the Condor host. | Problem | Try this | | ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Name `hummingbot-api` does not resolve | Enable **MagicDNS** in [Tailscale DNS settings](https://login.tailscale.com/admin/dns) | | `curl: (6) Could not resolve host` | MagicDNS hasn't been accepted on this device yet — run `sudo tailscale up --accept-dns=true` | | Could not connect / connection timeout | Tailscale isn't actually up on this machine. Run `tailscale status` — if it errors or the peer is missing, run `sudo tailscale up` (macOS: open the Tailscale app and log in), then retry the curl test above | | `curl` succeeds but Condor still shows Offline | The host in **`/servers`** doesn't match what you just tested, or the credentials weren't re-saved after an `.env` change — re-check both against `config.yml` | | Two machines signed into different accounts | Both devices must be on the **same tailnet** — check the [admin console](https://login.tailscale.com/admin/machines) lists both, not just one | | `tailscale up` warns something already owns `tailscale0` | Hummingbot API's own Tailscale sidecar is already holding that device on this machine. Set `TAILSCALE_MODE=host` or `TAILSCALE_MODE=sidecar` in `hummingbot-api/.env` so the two don't contend for it | | Two `hummingbot-api` entries in your tailnet (`hummingbot-api`, `hummingbot-api-1`) | A second API deployment registered under the same base name — they are different machines, and connecting to the wrong one looks exactly like a bad password. Check the [admin console](https://login.tailscale.com/admin/machines) for both and point `/servers` at the right one | | Auth key rejected | Key must start with `tskey-auth-`; generate a new one if it expired | | Asked for a `tskey-auth-...` key unexpectedly | The installer only skips this question when Tailscale is **already connected** before you run it. Ctrl+C, finish `tailscale up` first, then re-run `make setup` | | Connection refused | On the API server: `make tailscale-status` and `make deploy` again | | Login fails (401) | Use the same username/password as in the API `.env` — a mistyped password is the usual cause, since it isn't echoed as you type | | Still reachable on public IP | Remove port **8000** from your cloud provider's firewall / security group | See also: [Hummingbot API Tailscale guide](https://hummingbot.org/hummingbot-api/tailscale/) ## Other issues ### Won't start Telegram mode requires a token, and Condor refuses to start without one rather than quietly falling back to the unauthenticated local dashboard. Run `make setup` and either re-enter the bot token, or choose **Local** mode if you do not want a bot at all. `make doctor` reports the same thing before you try to start. Both come from the same check: `ADMIN_USER_ID` in `.env` must be a positive integer, and in **Local** mode that id must also exist as an approved user in `config.yml`. * **Not a valid user id:** something non-numeric ended up in `ADMIN_USER_ID` — fix it in `.env` or run `make setup` again. * **"Local mode logs in as user N ... but that user is not an approved user in config.yml":** the id in `.env` and the id `config.yml` expects have drifted apart, usually after editing one file by hand. Re-run `make setup` so both are written together, rather than editing either file in isolation. `CONFIG_PASSWORD` encrypts every exchange credential the API holds, and it's fixed the first time the API ever starts — a verification file is written then, and every later boot checks the current `.env` value against it. Editing `CONFIG_PASSWORD` in `.env` after that first run doesn't rotate anything; it just fails this check. Restore the original password, or if it's genuinely lost, delete `bots/credentials/` and re-add every exchange API key — there's no way to recover the old encrypted credentials without the original password. Something else is already listening on the port Condor or the API is trying to bind — a previous instance that didn't fully stop, or an unrelated process. Neither side currently checks for this before binding, so the error is a raw one from the process crashing, not a friendly message. Find and stop whatever holds the port (`lsof -i :8088` for the dashboard, `:8000` for the API), or change the port — `WEB_HOST`/dashboard port in Condor's `.env`, `API_BIND` in `hummingbot-api/.env`. ### Telegram and servers 1. **Confirm the process is running** * *Quick Start / deploy:* `tmux attach -t condor` and look for tracebacks or exit messages. * *Manual install:* Check the terminal where `make run` is running. 2. **Check `condor/.env`** on the machine that runs Condor: `TELEGRAM_TOKEN` must match [@BotFather](https://t.me/botfather), and `ADMIN_USER_ID` must be your numeric Telegram user id (from [@userinfobot](https://t.me/userinfobot)). 3. **Access still pending:** New users must be approved. An admin should use **`/admin`** (or the admin flow from **`/start`**) to approve you. 4. **Deploy installer hint:** If admins never see *"Condor is online and ready."*, attach to tmux (above) and fix errors shown there; the installer also reminds you to verify `.env` when that message is missing. * **`/servers`** shows *No servers configured* until at least one entry exists in **`condor/config.yml`** under `servers:` (and users need **access** to a server — admins own new entries; others must be **shared**). * **Server not found** in the UI usually means the configured server name does not exist anymore (typo after edit, or deleted entry) — open **`/servers`**, pick a valid server, or add one again. * **No servers available** (errors when running commands) often means your user has no server shared with them — ask an **owner** admin to share a server with your Telegram user id. * After **Manual install**, you must add the real API URL (and auth) via **`/servers`** if `config.yml` was not pre-filled. That is intended. Local mode has **no login**, so it binds `127.0.0.1` and is reachable only from the machine running Condor. If you need it from elsewhere, put something that authenticates in front of it rather than setting `WEB_HOST=0.0.0.0` on its own: * **Tailscale** — re-run `make setup` and enable it. Condor then binds loopback and lets `tailscale serve` expose the dashboard to your tailnet only. * **SSH tunnel** — `ssh -L 8088:localhost:8088 you@host`, then open `http://localhost:8088` locally. * **Switch to Telegram mode** — re-run `make setup`; `/web` then issues time-limited login links that work from any device. ### Docker, updates, and data 1. **Docker daemon:** The deploy script checks that Docker is running before bringing up the API. On Linux: `docker info`. On Mac/Windows: open **Docker Desktop** and wait until it is fully started. 2. **Compose:** You need either `docker compose` (plugin) or legacy `docker-compose` (the installer checks this). 3. **API-only recovery:** If Quick Start skipped or failed the API step, use the **Hummingbot API only** tab command, or from `hummingbot-api`: `make setup`, `docker compose pull`, `make deploy` (see [hummingbot-api](https://github.com/hummingbot/hummingbot-api) README). 4. **Sibling layout:** Condor's setup wizard expects **`hummingbot-api`** next to **`condor`** (`../hummingbot-api` from the Condor directory) when both are installed by the same flow — keep that layout unless you know how to point `config.yml` at a custom URL. 5. **API errors mentioning `DockerService` / `'DockerService' object has no attribute 'client'`:** Docker (or its daemon) wasn't running when the `hummingbot-api` container itself started, so it never connected. Start Docker, then restart or redeploy the `hummingbot-api` container so it reconnects — restarting Docker alone isn't enough once the API is already up. `make run` rebuilds the dashboard before starting, but it only installs frontend packages when `frontend/node_modules` is missing **entirely**. After a `git pull` or `/update` that added a new frontend dependency, the stale `node_modules` fails the build with an error naming the missing package. ```bash theme={null} cd frontend && npm install cd .. && make run ``` `make doctor` warns when `node_modules` is absent, but cannot tell a stale one from a fresh one — a build error naming a package you never heard of is this case. Condor's dashboard preferences, chat state, and agent conversations persist to a pickle file (`data/condor_bot_data.pickle`) with a `.bak` backup. If the main file is corrupted, Condor recovers from `.bak` automatically; if both are corrupted (for example after a hard crash or a full disk), it silently starts with empty state instead of failing to boot — the only trace is a warning in the logs, not an error you'd notice at the time. There's no way to recover state once both copies are gone. If this happens repeatedly, check for disk space issues or anything killing Condor mid-write. ## Secure the message broker Condor and Hummingbot API reach bot instances through an **EMQX** broker. Older Hummingbot API installs ran this broker with no authentication and published its port on every interface — if you installed before the fix, or haven't redeployed since, treat it as exposed until `make emqx-audit` says otherwise. See [Broker Security](https://hummingbot.org/hummingbot-api/broker-security/) in the Hummingbot API docs for the full issue and the fix (`make deploy`, `make emqx-auth-reset`, then `make doctor` / `make emqx-audit` to verify). A few things worth knowing beyond that fix: * **Rotating `BROKER_PASSWORD` later** needs `make emqx-auth-reset` too, not just `make deploy` — EMQX only imports the bootstrap file for accounts that don't already exist, so a broker that's already running keeps the old password until its data volume is reset. * **Restarting a bot instance doesn't refresh its broker credentials** — only (re)creating one writes the current password into its `conf_client.yml`. After any broker credential change, recreate existing instances rather than just restarting them. * **Bring the stack up with `make deploy`, not a bare `docker compose up`** — the plain compose command skips the step that creates the broker's credential file, so the broker comes up "healthy" with zero accounts and rejects everyone, including the API itself. * **Default credentials aren't a hard failure** — `hummingbot-api` boots fine on out-of-the-box `USERNAME`/`PASSWORD`/`CONFIG_PASSWORD` values and only logs a warning. Change them before this instance touches real funds. ## Recovery toolkit The commands worth reaching for before reading anything above. All are safe to run at any time; none of them trade. | Command | Where | What it tells you | | ------------------------------------------------------------ | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `make doctor` | Condor dir | Dependencies, `.env`/`config.yml`, AI model, dashboard bind, Tailscale, and whether each API server is reachable **and** authenticating | | `curl -s -o /dev/null -w "%{http_code}" localhost:8000/docs` | API host | Whether the API answers at all — **200** means up | | `make status` | Condor dir | Whether Condor is running, and under which tmux session | | `make logs` | Condor dir | Attach to the running session (detach with Ctrl+B then D) | | `make restart` | Condor dir | Rebuild the frontend and restart — the fix for a stale dashboard | | `tailscale status` | Either host | Whether this machine is on the tailnet at all | | `tailscale serve status` | Condor host | What is actually published to the tailnet, including the dashboard | | `docker compose ps` | `hummingbot-api` dir | Whether the API, broker and database containers are up | | `make emqx-audit` | `hummingbot-api` dir | Broker listeners, auth, ACL and any rule-engine rules | If `make doctor` is clean and something is still wrong, that is worth reporting — it means a real gap in what the check covers. # Web Dashboard Source: https://condor.hummingbot.org/getting-started/web-dashboard Browser-based interface for managing Condor The Web Interface provides a browser-based dashboard for managing trading bots, agents, and portfolio. ## Accessing the Dashboard There is no login step. Open the dashboard directly on the machine running Condor: ``` http://localhost:8088 ``` You are already signed in as `ADMIN_USER_ID`. Local mode binds `127.0.0.1`, so the dashboard is reachable from that machine only — see [Local](/getting-started/installing#local) before changing that. Run `/web` in Telegram to get a time-limited login link — see below. ### Telegram: the `/web` link Run `/web` in Telegram to get a login link: ``` 🌐 Web Dashboard Open this link in your browser: http://localhost:8088/login?token=XSNKl6bjWKGce-mcCYSWWgcyC5EvgzFrFqgCGIIop0s Link valid for 5 minutes. ``` Click the link to open the dashboard. The token authenticates you automatically—no separate login required. The link is tied to your Telegram session. You can access the dashboard from any device by opening the link. Web Dashboard ## Agent Chat (⌘K) Press **⌘K** (Mac) or **Ctrl+K** (Windows/Linux) anywhere in the dashboard to open the **Agent** chat panel — the same conversational agent you use in Telegram, docked beside whatever you're working on. Press the shortcut again to hide it. From the panel you can: * Chat with any of your agents, and run multiple sessions on different agents * Trigger actions — create or stop executors, deploy bots, place orders (sensitive actions ask for confirmation first) * Ask about what you're viewing: when a report is open, the chat knows which report it is, so you can ask things like "explain this report" or "give me the code of this routine" You can also start a voice message with **⌘+Shift+M** (**Ctrl+Shift+M**). ## Features ### Portfolio View your unified portfolio across all connected exchanges: * Real-time balance updates * Portfolio value history charts * Asset distribution visualization * 24-hour P\&L tracking ### Trade Unified trading interface combining market data and order execution: * **Charts**: OHLC candles with executor overlays * **Depth**: Order book visualization and live trades * **Positions**: Active positions with P\&L * **Config**: Create and manage executor configurations ### Bots Deploy and manage bot instances. A bot is one container that can run **multiple controllers** across different markets at once. The Bots page is organized into tabs: * **Active**: Running bots with realized, unrealized, and total P\&L, volume traded, and uptime. When a bot runs more than one controller, a combined P\&L chart aggregates them, with toggle chips to isolate any single controller. You can edit a running bot's controller config and push it live without restarting — only fields the controller marks as updatable are applied. * **Runs**: Performance history over time for the bots you've run, built from the controller snapshots the Hummingbot API records every 5 minutes (realized/unrealized P\&L and volume). * **Editor**: IDE-like editor for controllers and configs (see [Editor](#editor)). * **Backtest**: Backtest a V2 controller config against historical data. * **Archived**: Stopped bots, kept for later review. Each bot also exposes full logs with error filtering and start/stop controls. ### Editor Manage controllers and configurations in an IDE-like interface: * **Controllers**: View and edit controller Python code * **Configs**: Create, clone, and modify YAML configurations * **Upload**: Import controller files or config YAMLs * **Templates**: Create new configs from controller templates ### Executors Create and manage trading executors directly from the browser: * Grid, DCA, TWAP, Position executors * Visual configuration interface * Live performance metrics * One-click start/stop controls ### Positions Track open positions across all exchanges: * Real-time P\&L updates * Position size and leverage * Entry and mark prices * Quick access to close positions ### Agents Monitor your autonomous Trading Agent sessions: * **Overview**: Strategy summary and current state * **Sessions**: Trade history with chart overlays * **Strategy & Learnings**: View and edit agent configuration inline * Switch between multiple agent sessions ### Routines Run and monitor routines: * **Run**: Execute routines with custom parameters * **Schedule**: Set up recurring runs (every minute up to every 6 hours) * **Reports**: Browse generated HTML reports with interactive Plotly charts in a full-screen viewer; use the left/right arrow keys to step through a routine's report history ## Settings * **Servers**: Register multiple Hummingbot API servers (one per client or environment) and switch the active server from the top bar. Add, edit, set a default, or remove servers. * **API Keys**: Add exchange credentials directly in the dashboard — pick the exchange, fill in the required fields, and the credential is saved (secret fields are masked). * **Gateway**: Check Gateway status, start/stop/restart it, pull a Gateway Docker image (`latest`, `development`, or a custom tag), and view Gateway logs. ## Themes A theme toggle in the top bar cycles through three modes: **dark**, **light**, and a **color-blind** mode that uses a color-blind-friendly palette for charts and indicators. Your choice is remembered in the browser. ## Web + Telegram Sync The web dashboard shares state with Telegram: * Start a Trading Agent on Telegram → monitor in the dashboard * Deploy an executor in the dashboard → get notifications on mobile * Portfolio updates appear everywhere instantly * Same session, same data, seamless switching ## Custom Server Setup By default, Condor runs the web dashboard on `http://localhost:8088`. To access from other devices: 1. Set `WEB_URL` in your `.env` file: ```bash theme={null} WEB_URL=http://your-server-ip:8088 ``` 2. Restart Condor: ```bash theme={null} make restart ``` 3. Run `/web` in Telegram to get a new login link with the updated URL If exposing the dashboard to the internet, ensure proper firewall rules and consider using a reverse proxy with HTTPS. ## Session Management Login tokens expire after 5 minutes but your session remains active once authenticated. If your session expires: 1. Run `/web` in Telegram again 2. Click the new login link 3. Your previous state is preserved ## Tech Stack | Technology | Purpose | | -------------------- | ----------- | | Vite + React 19 | Framework | | TypeScript | Type safety | | Tailwind CSS v4 | Styling | | shadcn/ui + Radix UI | Components | ## Next Step View your balances and positions across all exchanges # Introduction Source: https://condor.hummingbot.org/introduction An open source harness for deploying and managing fleets of AI trading agents ## What is Condor? Condor is an open source **agent harness** for deploying and managing fleets of AI trading agents. It connects LLM-powered decision-making to deterministic trade execution, enabling you to run autonomous agents that observe markets, reason about strategy, and execute trades. Everything in Condor is free and open source. We're supported by exchanges as partners, so you can use everything without paying. **New to Condor?** Head to the [Learning Path](/learning-path) to find the setup and reading order that matches your experience level and use case—whether that's letting AI trade for you, running an always-on bot, or deploying to production. What's working? What's confusing? What do you wish Condor could do? Your answers go straight to the Hummingbot Foundation team and directly shape what we build next. ## What can you do with it? **Scale your trading operation** * Run dozens of agents simultaneously on shared accounts * Each agent tracks its own P\&L independently * Experiment with different strategies in parallel **Trade anywhere** * 50+ CEX connectors (Binance, Bybit, OKX, Hyperliquid, etc.) * DEX support via Gateway (Uniswap, Jupiter, Raydium) * Multi-leg strategies across exchanges and chains **Choose your execution style** * **Trading Agents**: AI-powered, make decisions each tick * **Bots**: Long-running containers for market making, grid trading * **Executors**: Single trades with defined entry/exit ## Who is it for? **Algorithmic traders** who want to add AI reasoning to their strategies without rebuilding execution infrastructure. **Quants and researchers** who want to experiment with many agents, compare performance, and iterate quickly. **Developers** who want to build trading applications on top of a battle-tested execution layer. **Crypto funds** who need multi-agent orchestration with proper P\&L isolation and audit trails. ## What makes it different? **Two-server architecture** separates LLM reasoning from trade execution. The Condor Server handles agent logic and state; the Hummingbot API handles data and execution. This means: * Execution continues even if the LLM is slow * Agents can share market data efficiently * You can swap LLM providers without touching execution code **Full observability**. Every tick is captured: the prompt sent to the LLM, its reasoning, tool calls made, and results. You can replay any decision. **Deterministic routines** move expensive computations (indicators, data processing) out of the LLM into reusable Python code. This cuts token costs and makes behavior reproducible. **Multi-agent by design**. Agents use `controller_id` to isolate their positions and P\&L on shared exchange accounts. Run 50 agents on one API key. ## Who is behind this? Condor is built by [Hummingbot Foundation](https://hummingbot.org/about/), a not-for-profit organization that maintains the open source Hummingbot trading framework used by thousands of traders worldwide. The Foundation is supported by exchange partners and community contributions. There's no VC funding or token—just open source software. ## How do I get started? Find your route by experience level and use case Understand why Condor is architected this way Install Condor and Hummingbot API Build your first trading agent Source code and issues 2-minute survey that shapes the roadmap ## Reading these docs with an agent These docs are published in a machine-readable form, so you can hand them to an LLM rather than paraphrasing them at it: | File | Contents | | --------------------------------------------------------------- | ------------------------------------------------------------------------- | | [`/llms.txt`](https://condor.hummingbot.org/llms.txt) | An index of every page with its description — small enough to paste whole | | [`/llms-full.txt`](https://condor.hummingbot.org/llms-full.txt) | The full text of every page in one file | Any page also serves as Markdown by appending `.md` to its URL — for example [`/getting-started/installing.md`](https://condor.hummingbot.org/getting-started/installing.md). Condor's own agents already read the parts of this that matter to them. This is for **your** assistant — pointing Claude, Codex or Cursor at `/llms.txt` is usually enough for it to answer setup questions against the current docs instead of its training data. # Learning Paths Source: https://condor.hummingbot.org/learning-path Find the setup and reading order that matches your experience level and use case. New to Condor, or picking up where you left off? Use the tables below to find the fastest route to what you actually want to do—no need to read every page in order. ## By experience level | Level | Goal | Reading path | Time | | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | **Beginner** | Get Condor running on your own machine with no Telegram account, and build one Trading Agent from the browser | [Installing → Local](/getting-started/installing#local) → [Web Dashboard](/getting-started/web-dashboard) → [Credentials](/getting-started/credentials) → [First Agent](/getting-started/first-agent) | \~30–45 min | | **Intermediate** | Attach Telegram so agents can reach your phone with alerts and approval requests, add real exchange keys, understand Bots/Executors/Routines, and manage running sessions | [Add Telegram alerts](/getting-started/installing#add-telegram-to-a-local-install) → [Telegram](/getting-started/telegram) → [Bots](/bots/overview) → [Executors](/executors/overview) → [Routines](/routines/overview) → [Sessions](/getting-started/sessions) | \~1–2 hrs | | **Advanced (Server)** | Deploy to a VPS behind Tailscale, understand the multi-agent architecture, and build custom agents | [Installing → Server](/getting-started/installing#server) → [Trading Agents Architecture](/trading-agents/architecture) → [Agent Builder](/condor-agent/agent-builder) → [Servers](/webui/settings#servers) | \~2–4 hrs | ## Which install do you want? The installer asks how you will drive Condor, and where it runs. Three paths, all running the same agents, bots, routines and dashboard. | | **Local** | **Local + Telegram alerts** | **Server** | | ------------------------------- | ------------------------------------------------------------------- | ------------------------------------- | -------------------------------------------------- | | **Runs on** | Your machine | Your machine | A VPS, 24/7 | | **You need** | Nothing extra | A bot token and your Telegram user id | The above, plus [Tailscale](#do-i-need-tailscale) | | **How you drive it** | Dashboard at `localhost:8088` | Telegram commands, plus `/web` | Telegram from anywhere; dashboard over the tailnet | | **Login** | **None at all** — whoever reaches the port has full trading control | Telegram authenticates you | Same, with the tailnet as the outer boundary | | **Alerts and approvals** | Only while the dashboard is open | Pushed to your phone | Pushed to your phone | | **Survives closing the laptop** | No | No | Yes | | **Best for** | Trying it out | Most people | Anything holding real capital | **Local mode has no login.** It binds `127.0.0.1` so only that machine can reach it. Exposing it (setting `WEB_HOST=0.0.0.0`) puts full trading control in reach of anyone who can hit the port—only do that behind something that authenticates, such as [Tailscale](#do-i-need-tailscale), an SSH tunnel, or an authenticating reverse proxy. You are not locked in: the usual route is Local first, Telegram alerts when you want your phone in the loop, and a server once real capital is involved — re-running `make setup` switches mode any time. Configured servers are shared and carry across; switching Telegram → Local keeps your id and with it your preferences and agent history, while a Local-only install that adds Telegram moves from id `1` to your Telegram id, so per-user history stays behind — make that hop early. Steps for each hop: [Start local, grow later](/getting-started/installing#start-local-grow-later). ## What do you want Condor to do? Condor gives you four ways to execute a trade, from fully autonomous to fully manual. Most users end up combining more than one. | | Trading Agent | Bot | Executor | Routine | | ------------------ | ---------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------ | ------------------------------------------------ | | **Decision maker** | LLM, reasons every tick | Script/Controller, fixed logic | You, or an Agent (one-shot) | Deterministic Python | | **Lifecycle** | Long-running, adaptive | Long-running, fixed | Short-lived, single operation | Scheduled or triggered | | **Best for** | AI-assisted strategy that adapts to conditions | Always-on market making or grid trading without AI in the loop | A single trade, or a stop-loss/take-profit | Reports, alerts, and indicators without LLM cost | | **Learn more** | [Trading Agents](/trading-agents/overview) | [Bots](/bots/overview) | [Executors](/executors/overview) | [Routines](/routines/overview) | ## Key features What Condor actually does, and where each is documented. Most people arrive wanting one of these. | Feature | What it does | Docs | | ---------------------- | -------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | | **Trading Agents** | LLM-driven agents that observe markets, reason about strategy, and execute through Hummingbot API | [Overview](/trading-agents/overview) | | **Agent Builder** | Build a custom agent — its playbooks, tools and risk limits — without writing the harness | [Agent Builder](/condor-agent/agent-builder) | | **Hummingbot bots** | Always-on market making, grid and directional strategies via controllers and scripts, no LLM in the loop | [Bots](/bots/overview) · [Controllers](/bots/controllers) | | **Backtesting** | Replay a controller config against historical candles before it touches capital | [Backtesting](/bots/backtesting) | | **Executors** | Single operations — a trade, a stop-loss, a TWAP, an LP position — driven by you or an agent | [Executors](/executors/overview) | | **Routines** | Deterministic Python on a schedule or trigger: indicators, alerts, rebalances. No LLM cost | [Routines](/routines/overview) | | **Reports** | Build and schedule HTML reports with charts, tables and KPIs, then read them in the dashboard | [Reports](/routines/reports) | | **Research** | Ask an agent about markets, positions or a strategy, with your live portfolio as context | [MCP Tools](/trading-agents/mcp-tools) | | **Portfolio** | Unified balances, positions and P\&L across every connected exchange and chain | [Portfolio](/webui/portfolio) | | **Manual trading** | Place and cancel orders yourself, on CEX or DEX, from Telegram or the dashboard | [Trade](/webui/trade) | | **DEX and liquidity** | Swaps and concentrated-liquidity positions through Gateway | [DEX](/webui/dex) · [Gateway](/webui/settings#gateway) | | **Web dashboard** | Browser UI for bots, agents, portfolio and a live trade panel covering CEX and DEX | [Web Dashboard](/getting-started/web-dashboard) | | **Agent sessions** | Inspect, steer and stop running agents; see what each one decided and why | [Sessions](/getting-started/sessions) | | **Multiple servers** | Drive several Hummingbot API instances — separate accounts, venues or strategies — from one Condor | [Servers](/webui/settings#servers) | | **Your choice of LLM** | Claude, Gemini, Codex, Copilot, OpenRouter, or a local model via Ollama/LM Studio | [LLM Integration](/llm-integration) | ## By use case [Trading Agents Overview](/trading-agents/overview) → [LLM Integration](/llm-integration) → [Build Your First Agent](/getting-started/first-agent) → [Managing Sessions](/getting-started/sessions) → [Agent Builder](/condor-agent/agent-builder) [Bots Overview](/bots/overview) → [Controllers](/bots/controllers) or [Scripts](/bots/scripts) → [Backtesting](/bots/backtesting) [Executors Overview](/executors/overview) → [Order Executor](/executors/order-executor) → [Position Executor](/executors/position-executor) [Routines Overview](/routines/overview) → [Creating Routines](/routines/creating-routines) → [Reports](/routines/reports) [Installing](/getting-started/installing) (local install—**Tailscale not required**; **Local mode** skips Telegram entirely) → [Credentials](/getting-started/credentials) → dry-run an agent in [Build Your First Agent](/getting-started/first-agent) [Installing → Local](/getting-started/installing#local) → [Web Dashboard](/getting-started/web-dashboard) → [Credentials](/getting-started/credentials) → [Build Your First Agent](/getting-started/first-agent) [Installing](/getting-started/installing#secure-remote-access-with-tailscale) (remote install—**Tailscale required**) → [Servers](/webui/settings#servers) → [Managing Sessions](/getting-started/sessions) [Trading Agents Architecture](/trading-agents/architecture) (multi-agent via `controller_id`) → [Global Routines](/routines/global-routines) → [Servers](/webui/settings#servers) ## Do I need Tailscale? | Situation | Tailscale needed? | | -------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | | Testing locally—Condor and Hummingbot API on one machine | No | | Same VPS, both services together | **No** — the API binds `127.0.0.1` by default; still worth it if anything ever needs off-box access | | Different machines (for example, laptop + VPS) | **Yes** | | A team or multiple devices need access | **Yes** | **Installed before the API's port lockdown? Check it.** Hummingbot API used to publish port **8000 on every interface**, including a VPS's public IP. It now binds `127.0.0.1` by default (`${API_BIND:-127.0.0.1}` in `docker-compose.yml`), and so do Postgres and the EMQX broker — but an existing stack keeps its old bindings until the containers are recreated. Run `make deploy` in `hummingbot-api`, then `make doctor`, which flags any of those ports still sitting on a public interface. If you *do* need the API reachable off-box, widen it deliberately with `API_BIND` in `hummingbot-api/.env` — prefer a specific interface over `0.0.0.0`, and with the Tailscale overlay `API_BIND=` keeps MagicDNS working without publishing anything to the internet. Note that Docker's published ports are *not* blocked by `ufw`: its rules are evaluated before ufw's, so `ufw deny 8000` leaves a widened port reachable unless you write `DOCKER-USER` rules yourself. Close the port in your cloud provider's firewall instead — that is enforced off-host. See [Installing](/getting-started/installing#secure-remote-access-with-tailscale) for the full setup, or the [Tailscale security walkthrough](https://hummingbot.org/blog/securing-condor-and-hummingbot-api-with-tailscale/) for a deeper guide. ## Something not working? Run `make doctor` from the Condor directory. It re-checks dependencies, your `.env` and `config.yml`, the AI model, whether the dashboard is on a public interface, Tailscale, and whether every configured Hummingbot API server is actually reachable and authenticating—and names the fix for each thing it finds. It is read-only and safe to run at any time. On the API host, `docker compose ps` (are the containers up) and `make tailscale-status` (is the tailnet node connected) from the `hummingbot-api` directory cover the execution layer, and `curl -u USER:PASS http://localhost:8000/` confirms the API answers an authenticated request. ## Next steps What Condor is and who it's for Why Condor is architected this way Source code and issues 2-minute survey that shapes the roadmap What's working? What's confusing? What do you wish Condor could do? Your answers go straight to the Hummingbot Foundation team and directly shape what we build next. # Integrating your LLM Source: https://condor.hummingbot.org/llm-integration Configure cloud and local LLM providers for Trading Agents Condor supports multiple LLM providers through two integration methods: * **ACP Protocol**: Native integration with Claude Code, Gemini, GitHub Copilot, and Codex * **PydanticAI**: Direct API access to OpenRouter and to local models via Ollama and LM Studio See [ACP documentation](https://agentclientprotocol.com/get-started/introduction) for the full protocol specification. Before installing Condor: Install and authenticate your LLM provider in the CLI first.
Already installed Condor? Set up your LLM provider, then restart Condor for the changes to take effect.
## Using the `/agent` Command The `/agent` command in Telegram is how you connect Condor to your LLM and run autonomous Trading Agents. From the `/agent` menu you can: * **Change LLM** — pick which installed provider to use for new sessions * **Start** — launch an agent session with the currently selected LLM * **Stop** — end the active session * **Status** — view the currently active provider and model A typical first-time flow is: install and authenticate your provider in the CLI, then in Telegram run `/agent` → **Change LLM** → pick your provider → `/agent` → **Start**. Full step-by-step instructions are in [Configure in Telegram](#configure-in-telegram). ## Supported Providers ### Cloud Providers (ACP Protocol) | Provider | Agent Key | Installation | | ------------------ | ------------- | ----------------------------------------------- | | **Claude Code** | `claude-code` | `curl -fsSL https://claude.ai/install.sh \| sh` | | **Gemini** | `gemini` | `npm install -g @google/gemini-cli` | | **GitHub Copilot** | `copilot` | `npm install -g @github/copilot-cli` | | **Codex** | `codex` | `npx @agentclientprotocol/codex-acp` | ### API Key Providers (PydanticAI) | Provider | Agent Key | Setup | | -------------- | ------------ | -------------------------------------------------------- | | **OpenRouter** | `openrouter` | Set `OPENROUTER_API_KEY` — see [OpenRouter](#openrouter) | ### Local Providers | Provider | Agent Key | Installation | | ------------- | ---------- | ------------------------------------------------ | | **Ollama** | `ollama` | `curl -fsSL https://ollama.com/install.sh \| sh` | | **LM Studio** | `lmstudio` | Download from [lmstudio.ai](https://lmstudio.ai) | ## Setup Instructions ### Claude Code Claude Code is the default and most integrated option for Condor Trading Agents. **1. Install Claude Code:** Install via CLI: ```bash theme={null} # macOS/Linux curl -fsSL https://claude.ai/install.sh | sh ``` **2. Authenticate:** ```bash theme={null} claude auth login ``` Follow the prompts to sign in with your Anthropic account. **3. Verify installation:** ```bash theme={null} claude --version claude # Run Claude and see if it starts up without issues ``` **4. Configure in Telegram:** Follow the [Configure in Telegram](#configure-in-telegram) steps and select the `Claude Code` label. ### Gemini **1. Install Gemini CLI:** ```bash theme={null} npm install -g @google/gemini-cli ``` **2. Authenticate:** Start Gemini CLI — on first run it will prompt you to authenticate: ```bash theme={null} gemini ``` Select **Login with Google** and follow the OAuth flow in your browser. Your credentials are cached locally for future sessions. Alternatively, set a `GEMINI_API_KEY` environment variable from [Google AI Studio](https://aistudio.google.com/). **3. Verify:** ```bash theme={null} gemini --version ``` **4. Configure in Telegram:** Follow the [Configure in Telegram](#configure-in-telegram) steps and select the `Gemini CLI` label. ### GitHub Copilot **1. Install prerequisites:** ```bash theme={null} # Install Copilot CLI npm install -g @github/copilot-cli ``` **2. Authenticate:** ```bash theme={null} gh auth login copilot auth login ``` **3. Verify:** ```bash theme={null} copilot --version copilot # Run Copilot and see if it starts up without issues ``` **4. Configure in Telegram:** Follow the [Configure in Telegram](#configure-in-telegram) steps and select the `Github Copilot CLI` label. ### Codex Condor connects to Codex through the Agent Client Protocol bridge (`@agentclientprotocol/codex-acp`). You'll need the Codex CLI installed and authenticated, plus the ACP bridge available for Condor to invoke. **1. Install the Codex CLI:** Download and install Codex from [openai.com/codex](https://openai.com/codex). **2. Sign in:** Open Codex and sign in with your ChatGPT account or an OpenAI API key. **3. Install the ACP bridge:** ```bash theme={null} npx @agentclientprotocol/codex-acp ``` Running this once fetches the package so Condor can invoke it via `npx`. You can also install it globally with `npm install -g @agentclientprotocol/codex-acp`. **4. Verify installation:** ```bash theme={null} codex --version codex # Run Codex and see if it starts up without issues ``` **5. Configure in Telegram:** Follow the [Configure in Telegram](#configure-in-telegram) steps and select the `ChatGPT Codex` label. ### OpenRouter OpenRouter routes requests to many models — including some free ones — through a single API key, often at lower cost than going to a provider directly. Condor connects to it through PydanticAI's OpenAI-compatible client, so no CLI install is required. **1. Get an API key:** Create a key at [openrouter.ai/keys](https://openrouter.ai/keys). **2. Set the environment variable:** Add your key to Condor's `.env`: ```bash theme={null} OPENROUTER_API_KEY=sk-or-... ``` **3. Configure in Telegram:** Run `/agent` → **Change LLM** → **OpenRouter — Pick Model**. Condor fetches the live OpenRouter catalog and lists only models that advertise tool-call support (Condor's agents depend on tool calling, so models without it are filtered out). Pick one from the list, or choose **Enter model manually** to provide a model slug. Your selection is stored as `openrouter:` — for example `openrouter:anthropic/claude-sonnet-4-5`. To run at no cost, choose one of the `openrouter/free` models. If a request fails with an insufficient-credits error, add credits to your OpenRouter account or switch to a `openrouter/free` model. ### Ollama (Local) Perfect for development, testing, and cost-effective experimentation. **1. Install Ollama:** Visit [ollama.com](https://ollama.com) and download for your platform. ```bash theme={null} # macOS/Linux curl -fsSL https://ollama.com/install.sh | sh ``` **2. Start Ollama service:** ```bash theme={null} ollama serve ``` **3. Pull a model:** ```bash theme={null} # Recommended for trading agents ollama pull llama3.1:70b # Large, capable model ollama pull qwen2.5:32b # Good balance ollama pull llama3.1:8b # Fast, lightweight ``` Verify the downloaded model is listed ```bash theme={null} ollama list ``` **Model Selection:** When you don't specify an explicit model (e.g., `agent_key: ollama`), Condor automatically selects one: 1. Checks environment variables (`CONDOR_DEFAULT_LOCAL_MODEL` or `OLLAMA_MODEL`) 2. Queries the Ollama API (`/api/tags`) and uses the first available model 3. If no model is found, prompts you to specify one explicitly **4. Configure in Telegram:** Follow the [Configure in Telegram](#configure-in-telegram) steps and select the `Ollama - Default Model` label. Local models have a reduced tool surface depending on parameter count — see [Tool Filter Modes](#tool-filter-modes). **Custom server (optional):** To point Condor at a specific model or a remote Ollama server, add the following to your Condor agent configuration (e.g., `config/agents.yaml`): ```yaml theme={null} agent_key: ollama:llama3.1:70b model_base_url: http://your-server:11434/v1 ``` ### LM Studio (Local) GUI-based local model hosting with OpenAI-compatible API. **1. Install LM Studio:** Download from [lmstudio.ai](https://lmstudio.ai) **2. Download a model:** * Open LM Studio * Browse the model library * Download a model (e.g., Qwen 2.5, Llama 3.1) **3. Start local server:** * Go to "Local Server" tab * Click "Start Server" * Default port: 1234 * Make sure a model is loaded **4. Configure in Telegram:** Follow the [Configure in Telegram](#configure-in-telegram) steps and select the `LM Studio - Default Model` label. Local models have a reduced tool surface depending on parameter count — see [Tool Filter Modes](#tool-filter-modes). **Custom port or model (optional):** To target a specific model or a non-default port, add the following to your Condor agent configuration (e.g., `config/agents.yaml`): ```yaml theme={null} agent_key: lmstudio:your-model-name model_base_url: http://localhost:1234/v1 ``` ## Configure in Telegram Once your provider is installed and authenticated, connect it to Condor from Telegram. **1. Select your provider:** Run the `/agent` command and select `Change LLM`, then choose your provider using the label from the table below. You should see a confirmation message: ``` LLM set to . New sessions will use this model. Use /agent to continue. ``` **2. Start the agent:** Run the `/agent` command again and click `Start`. Once the agent is ready you'll see: ``` Condor is ready. Send a message to start chatting. Use /agent to see options or any other command to exit. ``` You can now send a message to the agent and start chatting. ### Provider labels | Provider | Telegram label | | -------------- | --------------------------- | | Claude Code | `Claude Code` | | Gemini | `Gemini CLI` | | GitHub Copilot | `Github Copilot CLI` | | Codex | `ChatGPT Codex` | | OpenRouter | `OpenRouter — Pick Model` | | Ollama | `Ollama - Default Model` | | LM Studio | `LM Studio - Default Model` | ## Tool Filter Modes Local models automatically adjust tool availability based on size: * **Essential** (≤8B params): Minimal tools, basic operations * **Moderate** (9-32B params): Common trading operations * **Full** (>32B params, cloud): All available tools Larger models handle more complex tool interactions. Cloud providers always get full tool access. ## Choosing the Right Provider ### For Production Trading **Recommended:** Claude Code or ChatGPT Codex * High reliability and uptime * Strong reasoning capabilities * Full tool access * Consistent performance ### For Development **Recommended:** Ollama or LM Studio * No API costs * Fast iteration * Full control over model * Privacy (runs locally) ### Performance Comparison | Provider | Reasoning | Speed | Cost | Reliability | | ------------------ | --------- | ----- | ------ | ----------- | | Claude Code | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 💰💰 | ⭐⭐⭐⭐⭐ | | ChatGPT Codex | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 💰💰💰 | ⭐⭐⭐⭐⭐ | | Gemini CLI | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 💰 | ⭐⭐⭐⭐ | | GitHub Copilot CLI | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 💰💰 | ⭐⭐⭐⭐ | | Ollama (70B) | ⭐⭐⭐⭐ | ⭐⭐⭐ | Free | ⭐⭐⭐ | | Ollama (8B) | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | Free | ⭐⭐⭐ | ## Troubleshooting ### "Command not found" errors If Condor can't find your LLM CLI: 1. **Verify installation:** ```bash theme={null} which claude which gemini which copilot which codex ``` 2. **Check PATH:** ```bash theme={null} echo $PATH ``` 3. **Restart Condor** after installing LLM tools: ```bash theme={null} # If running via make make stop make run # If running via Docker docker compose restart ``` ### Changed LLM but Condor still uses the old one `Change LLM` only affects **new sessions**. Stop any active agent session with `/agent` → `Stop` (or send any non-agent command), then run `/agent` → `Start` to pick up the new provider. If it still persists, restart Condor: ```bash theme={null} # If running via make make stop && make run # If running via Docker docker compose restart ``` ### Authentication Issues **Claude Code:** ```bash theme={null} claude auth logout claude auth login ``` **Gemini:** Re-run `gemini` and select a different authentication method, or clear cached credentials and restart the interactive login: ```bash theme={null} rm -rf ~/.gemini/oauth_creds.json gemini ``` **Copilot:** ```bash theme={null} gh auth logout gh auth login copilot auth login ``` ### Local Model Connection Errors **Ollama not running:** ```bash theme={null} # Check if Ollama is running curl http://localhost:11434/api/tags # Start Ollama ollama serve ``` **LM Studio not running:** * Open LM Studio app * Go to "Local Server" tab * Click "Start Server" ### Model Not Found (Ollama) If you get "No local model found": ```bash theme={null} # List available models ollama list # Use exact model name in agent_key agent_key: ollama:llama3.1:70b-instruct-q4_0 ``` Or set a default: ```bash theme={null} export CONDOR_DEFAULT_LOCAL_MODEL=llama3.1:70b ``` ## Best Practices **Installation Order:** 1. Install LLM provider CLIs first 2. Authenticate with each provider 3. Test the CLI tools independently 4. Then install / start Condor **Security:** * Use CLI authentication tools (not environment variables when possible) * Never commit credentials to git * Use separate API keys for development and production **Model Selection:** * Start with Claude Code or ChatGPT Codex for best results * Use Ollama 70B+ for cost-effective alternatives * Avoid models under 8B for complex trading strategies ## Additional Resources Agent Client Protocol specification Official Claude Code documentation Browse available Ollama models Build your first Trading Agent # Motivation Source: https://condor.hummingbot.org/motivation Why Condor uses a two-server architecture separating agentic reasoning from deterministic execution ## The Problem There's a lot of potential in applying AI agents to trading. But how do you make it all work together? It's easy to create a simple demo, but as most algo traders and market makers know, if you really want to make money over the long term, you have to build a systematic approach. You need to: * Calculate P\&L accurately across different exchanges * Have the agent actually learn from what it's doing systematically * Run many agents that operate independently but also work together Professional market makers and rewards farmers typically run multiple trading strategies simultaneously. Managing multiple AI agents across a shared portfolio—without spinning up separate sub-accounts for each—is a core operational challenge. ## The Harness Concept Condor is an **agent harness**—a system that helps you accomplish tasks using LLMs. Similar in concept to OpenClaw, but purpose-built for trading rather than general productivity. | Harness | Focus | Architecture | | ------------ | ------------------------------------ | --------------------------------------------------- | | **OpenClaw** | General productivity (email, tasks) | Single gateway server handling LLMs and messaging | | **Condor** | Trading (data collection, execution) | Two servers separating agentic from execution layer | If you're doing trading tasks, you may not want the open, possibly insecure mode of doing things the way OpenClaw does. You want trade execution separated from the agent layer—which is how Condor is structured. ## Two-Server Architecture Condor uses a **two-server architecture** that separates the agentic layer from the execution layer: ```mermaid theme={null} flowchart TB subgraph Condor["Condor Server (Agentic)"] LLM["LLM Integration"] Telegram["Telegram Interface"] ACP["Agent Client Protocol"] end subgraph API["Hummingbot API Server (Execution)"] PostgreSQL["PostgreSQL"] EMQX["EMQX Broker"] Connectors["Exchange Connectors"] end Condor --> API API --> Exchanges["50+ CEX/DEX"] API --> Gateway["Gateway (DEX)"] API --> Bots["Bot Instances"] ``` | Server | Role | | ------------------------- | ------------------------------------------------------------ | | **Condor Server** | Interfaces with LLMs, handles user interaction via Telegram | | **Hummingbot API Server** | Deterministic execution across 50+ exchanges and blockchains | ### Why Separated? **Speed**: Deterministic algorithms execute without waiting on LLM inference. When a take-profit or stop-loss triggers, execution happens immediately. The expensive activity in trading is at the network layer—sending trades to exchanges and waiting for responses, or talking to LLMs. Separating these concerns lets each layer optimize independently. **Token Efficiency**: Instead of passing raw market data to the LLM, deterministic code handles routine operations. We found that agents waste enormous amounts of tokens processing data and computing indicators. By moving this into deterministic **routines**, we reduced session time from two minutes to under one minute while improving reliability. **Isolation & Security**: You're not giving full machine access to the LLM. Instead, the Hummingbot API dictates what types of trades can be performed and tracks the P\&L of all activity. In a live trading environment, this constraint is critical. **Standardization**: Hummingbot has connectors to 50+ exchanges—centralized exchanges like Binance, DEXs like Hyperliquid, and blockchain networks like Solana. If you say "trade 0.1 SOL," you can do that as a market order on Binance or as a swap on Jupiter, execute the same way, and get standardized results. The agent doesn't need to worry about the details of different APIs. ## The Tick Similar to how LLMs have the concept of a "turn" (you give something to the model and get something back), trading has the concept of a **tick**. A tick is one iteration of the agent's decision loop, happening at a defined time interval. ```mermaid theme={null} flowchart LR subgraph Tick["One Tick"] MD[Market Data] --> Strategy ED[Execution Data] --> Strategy Strategy --> Decision end Decision --> |"Create/Stop/Wait"| Executors Executors --> |"Next interval"| MD ``` Each tick, the agent: 1. Gathers **market data** (candles, order book, funding rates) 2. Gathers **execution data** (active executors, positions, P\&L) 3. Runs the **strategy** to decide: create something, stop something, or wait This loops continuously at the configured frequency (e.g., every 60 seconds). ## The OODA Loop The tick follows the **OODA loop**, a decision-making framework developed by military strategist John Boyd: ```mermaid theme={null} flowchart LR O[Observe] --> OR[Orient] OR --> D[Decide] D --> A[Act] A --> O ``` | Phase | Layer | Description | | ----------- | ------------- | ----------------------------------------------------------------- | | **Observe** | Deterministic | Fetch portfolio state, positions, market data via Hummingbot API | | **Orient** | Probabilistic | Load journal (learnings, state, recent actions) and build context | | **Decide** | Probabilistic | LLM reasons about strategy and determines actions | | **Act** | Deterministic | Execute via API, record results to journal | The loop is **probabilistic** in Orient and Decide (LLM-powered) and **deterministic** in Observe and Act (Hummingbot API). ## Multi-Agent Framework Think about running a hundred agents—how do they all operate independently while working together? This is the core question we address with the **Trading Standard**. ### The Problem It Solves Professional market makers and rewards farmers run multiple strategies simultaneously. Managing multiple agents across a shared portfolio—without spinning up separate sub-accounts for each—is a core operational challenge. ### How It Works * A **portfolio** is a set of accounts across different exchanges and blockchains * Multiple **agents** can operate on the same portfolio simultaneously * Each agent: * Acts on its own **allocated slice** of the portfolio * Tracks the **changes it makes** independently via `controller_id` * Has its own **P\&L tracking**, separate from others * Agents operate independently—**no sub-accounts needed** ### Why Executors Enable This Each executor has an **owner** specified when created. The agent only sees executors it created—it has a virtual portfolio and virtual executors. All activity is completely isolated. This is why the system scales: you can have 10 agents running in parallel without them having trouble understanding what actions each agent took. ### Why It Matters Scaling to dozens or hundreds of agents is what enables agents to **learn and self-improve** over time—the foundation for a truly autonomous trading system. # Creating Routines Source: https://condor.hummingbot.org/routines/creating-routines Build custom routines for your trading agents Create custom routines to extend agent capabilities with deterministic Python code. ## Routine Requirements Every routine module needs: 1. **Config** - A Pydantic BaseModel with parameters (docstring becomes the description) 2. **run(config, context)** - Async function that executes the routine and returns a string 3. **CONTINUOUS = True** (optional) - Mark as continuous routine with internal loop ## Create a Custom Routine ### 1. Create the File In your agent's `routines/` folder: ```python theme={null} # trading_agents/sol_scalper/routines/momentum_scanner.py from pydantic import BaseModel, Field from telegram.ext import ContextTypes from config_manager import get_client class Config(BaseModel): """Scan for momentum breakouts on a trading pair.""" connector_name: str = Field(default="binance_perpetual", description="Exchange connector") trading_pair: str = Field(default="SOL-USDT", description="Market to analyze") lookback: int = Field(default=20, description="Number of candles") async def run(config: Config, context: ContextTypes.DEFAULT_TYPE) -> str: """Scan for momentum breakouts.""" chat_id = context._chat_id if hasattr(context, "_chat_id") else None client = await get_client(chat_id, context=context) if not client: return "No server available" # Get candles via Hummingbot API candles = await client.market_data.get_candles( connector_name=config.connector_name, trading_pair=config.trading_pair, interval="1m", limit=config.lookback ) # Calculate momentum closes = [c["close"] for c in candles["candles"]] momentum = (closes[-1] - closes[0]) / closes[0] * 100 # Volume confirmation volumes = [c["volume"] for c in candles["candles"]] avg_volume = sum(volumes) / len(volumes) volume_surge = volumes[-1] > avg_volume * 1.5 # Generate signal if momentum > 2 and volume_surge: signal = "🟢 STRONG BULLISH" elif momentum > 1: signal = "🟡 Bullish" elif momentum < -2 and volume_surge: signal = "🔴 STRONG BEARISH" elif momentum < -1: signal = "🟠 Bearish" else: signal = "⚪ Neutral" return ( f"**Momentum Scanner** - {config.trading_pair}\n" f"Signal: {signal}\n" f"Momentum: {momentum:+.2f}%\n" f"Volume surge: {'Yes' if volume_surge else 'No'}\n" f"Price: ${closes[-1]:,.2f}" ) ``` ### 2. Register the Routine Add to your agent's config or the routine will be auto-discovered. ### 3. Use in Agent Update your `agent.md`: ```markdown theme={null} ## Entry Rules 1. Run momentum_scanner routine 2. If signal is "strong_bullish" → Open long 3. If signal is "strong_bearish" → Open short 4. Otherwise → Wait ``` ### 4. Test Run a dry run to verify: ``` /agent → SOL Scalper → Dry Run Dry Run Result: - Ran momentum_scanner - Result: {signal: "strong_bullish", momentum: 2.5%} - Decision: Would open LONG ``` ## Creating with Condor You can ask Condor to create routines for you via natural language: ``` You: Create a routine that analyzes support and resistance levels using EMAs of 7, 25, and 99 on 1-minute candles. ``` Condor generates the routine: ```python theme={null} # routines/support_resistance_ema_levels.py from pydantic import BaseModel, Field from telegram.ext import ContextTypes from config_manager import get_client class Config(BaseModel): """Analyze support/resistance levels and EMA alignment.""" connector_name: str = Field(default="binance_perpetual", description="Exchange connector") trading_pair: str = Field(default="BTC-USDT", description="Trading pair") candle_count: int = Field(default=100, description="Number of candles to analyze") lookback: int = Field(default=20, description="Lookback for S/R detection") async def run(config: Config, context: ContextTypes.DEFAULT_TYPE) -> str: """Identify support/resistance levels and EMA alignment.""" chat_id = context._chat_id if hasattr(context, "_chat_id") else None client = await get_client(chat_id, context=context) candles = await client.market_data.get_candles( connector_name=config.connector_name, trading_pair=config.trading_pair, interval="1m", limit=config.candle_count ) closes = [c["close"] for c in candles["candles"]] # Calculate EMAs ema_7 = calculate_ema(closes, 7) ema_25 = calculate_ema(closes, 25) ema_99 = calculate_ema(closes, 99) # Determine trend from EMA alignment if ema_7 > ema_25 > ema_99: trend = "🟢 Bullish" elif ema_7 < ema_25 < ema_99: trend = "🔴 Bearish" else: trend = "🟡 Mixed" # Find support/resistance levels supports, resistances = find_sr_levels(closes, config.lookback) return ( f"**EMA Analysis** - {config.trading_pair}\n" f"Trend: {trend}\n" f"EMA 7: ${ema_7:,.2f}\n" f"EMA 25: ${ema_25:,.2f}\n" f"EMA 99: ${ema_99:,.2f}\n" f"Support: ${supports[0]:,.2f}\n" f"Resistance: ${resistances[0]:,.2f}" ) ``` The routine is auto-discovered and immediately available via `/routines`. ## Routine Best Practices Same input should always produce same output. Avoid randomness or time-based logic that would make results unpredictable. Return human-readable strings for Telegram display. Use markdown formatting for clarity. Return error messages rather than raising exceptions: ```python theme={null} if not client: return "No server available" if not candles: return "No candle data available" ``` The Config class docstring becomes the routine description in the UI. Use `Field(description=...)` to document each parameter. Continuous routines must catch `asyncio.CancelledError` to clean up gracefully when stopped. ## Global vs Agent-Specific | Location | Scope | | --------------------------------- | ----------------------- | | `~/condor/routines/` | Available to all agents | | `trading_agents/{slug}/routines/` | Specific to one agent | Agent-specific routines override global ones with the same name. # Global Routines Source: https://condor.hummingbot.org/routines/global-routines Built-in routines available to all agents Global routines in `~/condor/routines/` are available to all agents. Condor includes several built-in routines for common trading tasks. ## Built-in Routines | Routine | Type | Description | | ---------------- | ---------- | -------------------------------------------------- | | `hello_world` | One-shot | Simple example routine for testing | | `market_scanner` | One-shot | Scan markets for trading opportunities | | `bot_report` | One-shot | Generate performance reports for running bots | | `arb_check` | One-shot | Check for arbitrage opportunities across exchanges | | `price_monitor` | Continuous | Monitor price and alert on threshold changes | ## Directory Structure ``` ~/condor/routines/ ├── __init__.py ├── base.py # Routine discovery and base classes ├── hello_world.py # Simple example routine ├── market_scanner.py # Market scanning routine ├── bot_report.py # Bot performance reports ├── arb_check.py # Arbitrage checking └── price_monitor.py # Continuous price monitoring ``` ## Hello World Example The simplest routine to understand the structure: ```python theme={null} # routines/hello_world.py from pydantic import BaseModel, Field from telegram.ext import ContextTypes class Config(BaseModel): """Simple hello world example routine.""" name: str = Field(default="World", description="Name to greet") repeat: int = Field(default=1, description="Number of times to repeat") uppercase: bool = Field(default=False, description="Use uppercase") async def run(config: Config, context: ContextTypes.DEFAULT_TYPE) -> str: """Execute the routine.""" greeting = f"Hello, {config.name}!" if config.uppercase: greeting = greeting.upper() return "\n".join([greeting] * config.repeat) ``` Run it from Telegram: ``` /routines → hello_world → Run Output: Hello, World! ``` ## Price Monitor (Continuous) A continuous routine that monitors price and sends alerts: ```python theme={null} # routines/price_monitor.py from pydantic import BaseModel, Field from telegram.ext import ContextTypes # Mark as continuous routine CONTINUOUS = True class Config(BaseModel): """Live price monitor with configurable alerts.""" connector: str = Field(default="binance", description="CEX connector name") trading_pair: str = Field(default="BTC-USDT", description="Trading pair to monitor") threshold_pct: float = Field(default=1.0, description="Alert threshold in %") interval_sec: int = Field(default=10, description="Check interval in seconds") async def run(config: Config, context: ContextTypes.DEFAULT_TYPE) -> str: """Monitor price continuously until cancelled.""" chat_id = context._chat_id client = await get_client(chat_id, context=context) initial_price = None last_price = None try: while True: prices = await client.market_data.get_prices( connector_name=config.connector, trading_pairs=config.trading_pair ) current_price = prices["prices"].get(config.trading_pair) if initial_price is None: initial_price = current_price last_price = current_price # Check threshold and alert if triggered change = ((current_price - last_price) / last_price) * 100 if abs(change) >= config.threshold_pct: await context.bot.send_message( chat_id=chat_id, text=f"{'📈' if change > 0 else '📉'} {config.trading_pair}: ${current_price:,.2f} ({change:+.2f}%)" ) last_price = current_price await asyncio.sleep(config.interval_sec) except asyncio.CancelledError: return "Monitor stopped" ``` ## Creating Custom Global Routines Add your own routines to `~/condor/routines/`: 1. Create a Python file with `Config` class and `run` function 2. The routine is auto-discovered on next Condor start 3. Access via `/routines` in Telegram Global routines are shared across all agents. For agent-specific routines, place them in the agent's `routines/` folder instead. # Routines Overview Source: https://condor.hummingbot.org/routines/overview Deterministic Python workflows that reduce token usage and improve reproducibility **Routines** are deterministic Python workflows that process data consistently. Unlike skills which load instructions into context for the LLM to interpret, routines are lightweight Python files that execute directly and return results—no tokens spent on interpretation. ## Why Routines Matter When testing agents, we found they waste enormous amounts of tokens processing data and computing indicators during runtime. An agent might get candles, then write Python code to compute EMAs and support/resistance—spending tokens on computation that should be deterministic. By moving this into routines: * **Session time dropped from 2 minutes to under 1 minute** * **Token usage reduced significantly** * **Results became reproducible and debuggable** If Python code can be created by the agent, it should be encapsulated in a routine so it can be reused without spending tokens. ## Routines vs Skills Skills and routines serve different purposes: | Aspect | Skills | Routines | | ---------- | --------------------------------------- | ---------------------------------------- | | Format | Markdown instructions + scripts | Python file with config class | | Execution | LLM interprets and follows instructions | Python code runs directly | | Loading | Loaded into system prompt when detected | Never loaded into memory—just executed | | Use case | Teaching agent general capabilities | Specific, repeatable tasks | | Token cost | Tokens spent interpreting instructions | Zero tokens for execution | | Output | Variable (LLM interpretation) | Deterministic (same input → same output) | **When to use routines:** Specific tasks you want done reliably every time—fetching market data, running technical analysis, generating reports, monitoring conditions. **When to use skills:** Teaching an agent how to approach a category of problems where interpretation and judgment are needed. ## Routines vs LLM Reasoning | Aspect | LLM Reasoning | Routines | | ----------- | ----------------------------- | ------------------------------ | | Execution | Probabilistic | Deterministic | | Purpose | Strategy decisions | Data processing and automation | | Variability | May produce different outputs | Same input → same output | | Cost | LLM tokens | None | | Speed | Seconds | Milliseconds | ## Routine Structure Every routine is a Python file with two components: 1. **Config class** - A Pydantic BaseModel defining the arguments/parameters 2. **Async run method** - The entry point that executes the routine ```python theme={null} # routines/hello_world.py from pydantic import BaseModel, Field from telegram.ext import ContextTypes class Config(BaseModel): """Simple hello world example routine.""" name: str = Field(default="World", description="Name to greet") repeat: int = Field(default=1, description="Number of times to repeat") uppercase: bool = Field(default=False, description="Use uppercase") async def run(config: Config, context: ContextTypes.DEFAULT_TYPE) -> str: """Execute the routine.""" greeting = f"Hello, {config.name}!" if config.uppercase: greeting = greeting.upper() return "\n".join([greeting] * config.repeat) ``` The `Config` class docstring becomes the routine's description in the UI. Use `Field(description=...)` to document each parameter. ## Execution Modes ### One-Shot Routines Execute once and return a result. Can be scheduled to run at regular intervals. ```python theme={null} # routines/top_movers.py from pydantic import BaseModel, Field from telegram.ext import ContextTypes class Config(BaseModel): """Fetch top movers from exchange.""" top_n: int = Field(default=10, description="Number of top movers") include_volume: bool = Field(default=True, description="Include volume data") include_losers: bool = Field(default=True, description="Include top losers") async def run(config: Config, context: ContextTypes.DEFAULT_TYPE) -> str: # Fetch and process exchange data async with aiohttp.ClientSession() as session: # ... fetch top movers pass return f"Top {config.top_n} gainers: ..." ``` ### Continuous Routines Run indefinitely with a while loop until stopped. Useful for monitoring and alerting. Mark with `CONTINUOUS = True`. ```python theme={null} # routines/price_monitor.py from pydantic import BaseModel, Field from telegram.ext import ContextTypes # Mark as continuous routine - has internal loop CONTINUOUS = True class Config(BaseModel): """Live price monitor with configurable alerts.""" connector: str = Field(default="binance", description="CEX connector name") trading_pair: str = Field(default="BTC-USDT", description="Trading pair to monitor") threshold_pct: float = Field(default=1.0, description="Alert threshold in %") interval_sec: int = Field(default=10, description="Check interval in seconds") async def run(config: Config, context: ContextTypes.DEFAULT_TYPE) -> str: """Monitor price continuously until cancelled.""" chat_id = context._chat_id while True: try: # Get current price and check threshold # Send alert via context.bot.send_message() if triggered await asyncio.sleep(config.interval_sec) except asyncio.CancelledError: return "Monitor stopped" ``` Continuous routines must handle `asyncio.CancelledError` to clean up gracefully when stopped. ## Running Routines ### From Telegram Access routines through the Condor menu: ``` /routines → Select routine → Configure → Run ``` Options when running: * **Run once** - Execute immediately in the current chat * **Run in background** - Execute and send output to chat when complete * **Schedule** - Run at regular intervals (e.g., every 30 seconds, hourly) To stop a running or scheduled routine: ``` /routines → Running → Select routine → Stop ``` ### From Agent Sessions Agents can invoke routines as part of their decision process—see [Calling Routines](#calling-routines) below. ## Types of Routines ### Global Routines Routines in `~/condor/routines/` are available to all agents. These include built-in routines for common tasks like technical analysis, funding rates, and volume analysis. ### Agent-Specific Routines Routines in an agent's `routines/` folder are specific to that agent: ``` trading_agents/my_scalper/ └── routines/ ├── support_resistance_ema_levels.py └── process_news.py ``` This design lets you share an agent folder with someone else—they get everything needed to run it. Agent-specific routines override global ones with the same name. ## Calling Routines Agents invoke routines via MCP tools: ```python theme={null} # From within an agent's decision process vwap = await mcp_tools.run_routine( routine="indicators.vwap", params={ "connector": "binance", "trading_pair": "BTC-USDT", "periods": 50 } ) if current_price < vwap["vwap"] * 0.98: # Price 2% below VWAP - consider buying pass ``` ## View Available Routines Ask Condor what routines are available: ``` You: What routines are available? Condor: Available routines for SOL Scalper: Agent-specific: - momentum_scanner: Scan for momentum breakouts Global: - technical_analysis: EMA, RSI, support/resistance - funding_rates: Fetch perpetual funding rates - volume_analysis: Volume profile and VWAP ``` ## Built-in Routines Condor includes several built-in routines in `~/condor/routines/`: | Routine | Type | Description | | ---------------- | ---------- | --------------------------------------------- | | `hello_world` | One-shot | Simple example routine | | `market_scanner` | One-shot | Scan markets for trading opportunities | | `bot_report` | One-shot | Generate performance reports for running bots | | `arb_check` | One-shot | Check for arbitrage opportunities | | `price_monitor` | Continuous | Monitor price and alert on threshold changes | ## Creating Routines with Condor Condor can write routines for you. Simply describe what you want: ``` You: Create a routine that monitors funding rates on Binance perpetuals and alerts me when any rate exceeds 0.01% ``` Condor generates the Python file, and you can immediately run or schedule it. This is particularly powerful because: * **Zero coding required** - Describe what you want in natural language * **Instant feedback** - Run the routine and see results immediately * **Iterative refinement** - Ask Condor to modify the routine based on output You can create routines from your phone via Telegram. Describe what you need, have Condor write the code, schedule it, and receive reports—all without touching a keyboard. # Reports Source: https://condor.hummingbot.org/routines/reports Generate HTML reports with interactive charts from routines Routines can generate HTML reports with interactive Plotly charts and markdown content. Reports are viewable in the Condor web dashboard, providing a richer experience than Telegram messages for data visualization. ## Why Reports? | Channel | Best For | | ------------ | -------------------------------------------------------- | | **Telegram** | Quick notifications, mobile alerts, simple text | | **Reports** | Interactive charts, detailed analysis, scheduled outputs | When you schedule a routine to run every hour, you may not want 24 Telegram messages per day. Instead, generate reports and check them in the dashboard when convenient. ## Viewing Reports Reports appear in the Condor web dashboard under **Routines** → **Reports**. Open one in the full-screen viewer and use the **left/right arrow keys** to step through a routine's report history. Each report includes: * Title and creation time * Source routine name and tags * Interactive HTML content (zoomable charts, expandable sections) Condor keeps a rolling buffer of the most recent reports (default 100, set by the `CONDOR_MAX_REPORTS` environment variable). Older reports are cleaned up automatically. ## Creating Reports ### 1. Import ReportBuilder ```python theme={null} from condor.reports import ReportBuilder ``` ### 2. Generate Your Data Create figures and tables as you normally would: ```python theme={null} import plotly.graph_objects as go fig = go.Figure() fig.add_trace(go.Scatter(x=dates, y=prices, name="Price")) fig.update_layout(title="Market Analysis") ``` ### 3. Build the Report `ReportBuilder` takes a title, and its methods are chainable. Tag the report with its source, add sections, then `await save()`: ```python theme={null} builder = ReportBuilder("Analysis Summary") builder.source("routine", "my_routine").tags(["analysis"]) builder.markdown("## Analysis Summary\n\nKey findings from today's scan.") builder.plotly(fig) builder.table([{"symbol": "SOL", "change": "+4.2%"}]) await builder.save() ``` ## Complete Example ```python theme={null} # routines/price_snapshot.py from pydantic import BaseModel, Field from telegram.ext import ContextTypes import plotly.graph_objects as go from config_manager import get_client from condor.reports import ReportBuilder class Config(BaseModel): """Snapshot prices for a set of pairs and save a report.""" exchange: str = Field(default="binance_perpetual", description="Exchange to query") pairs: list[str] = Field( default=["BTC-USDT", "ETH-USDT", "SOL-USDT"], description="Trading pairs to snapshot", ) async def run(config: Config, context: ContextTypes.DEFAULT_TYPE) -> str: chat_id = context._chat_id if hasattr(context, "_chat_id") else None client = await get_client(chat_id, context=context) if not client: return "No server available" prices = await client.market_data.get_prices(config.exchange, config.pairs) rows = [{"pair": pair, "price": price} for pair, price in prices.items()] fig = go.Figure(go.Bar(x=[r["pair"] for r in rows], y=[r["price"] for r in rows])) fig.update_layout(title=f"Prices on {config.exchange}") builder = ReportBuilder(f"Price Snapshot — {config.exchange}") builder.source("routine", "price_snapshot").tags(["prices"]) builder.markdown(f"## Prices on {config.exchange}") builder.plotly(fig) builder.table(rows) await builder.save() # Return text for Telegram return f"Saved price snapshot for {len(rows)} pairs" ``` ## ReportBuilder Methods All methods return the builder, so they can be chained. | Method | Description | | ------------------------------------------------ | ------------------------------------------------------------------- | | `source(source_type, source_name)` | Tag where the report came from (e.g. `"routine", "price_snapshot"`) | | `tags(tags)` | Attach a list of string tags | | `kpi(label, value, delta=None, trend="neutral")` | Add a KPI tile | | `markdown(text)` | Add markdown-formatted text | | `plotly(fig)` | Add a Plotly figure (interactive in HTML) | | `table(rows, columns=None)` | Add a table from a list of dicts | | `await save(report_id=None)` | Write the report to disk; pass an existing id to update in place | ## Use Cases ### Scheduled Market Analysis Run a routine every hour to analyze market conditions: ``` /routines → technical_analysis → Schedule → Every 1 hour ``` Instead of 24 Telegram messages, check the reports dashboard for the latest analysis with interactive charts. ### Agent Decision Logging When an agent runs a routine for analysis, the report captures what the agent "saw" when making decisions: ```python theme={null} builder = ReportBuilder("Agent Analysis") builder.source("routine", "bb_trader_analysis").tags(["agent"]) builder.markdown(f"## Agent Analysis\n\nCurrent price: ${price}\n\nSignal: {signal}") builder.plotly(indicator_chart) await builder.save() ``` This helps you understand and debug agent behavior by reviewing the data it analyzed. ### Comparing Tokens Build research routines that compare multiple assets: ```python theme={null} builder = ReportBuilder("Solana DEX Token Comparison") builder.source("routine", "dex_comparison").tags(["solana", "dex"]) builder.markdown("## Solana DEX Token Comparison") builder.plotly(market_cap_chart) builder.plotly(fee_revenue_chart) builder.table(comparison_rows) await builder.save() ``` ## Report Storage Reports are written as HTML files to the `reports/` directory in your Condor install (`~/condor/reports/`), with an index in `reports_index.json`. The web dashboard reads from this directory. ```bash theme={null} ls ~/condor/reports/ # 20260115_143000_price-snapshot_a1b2c3.html # 20260115_150000_market-scanner_d4e5f6.html ``` To clean up reports manually: ```bash theme={null} rm ~/condor/reports/*.html ``` Or use the dashboard's cleanup function under **Routines** → **Reports** → **Clean**. # Architecture Source: https://condor.hummingbot.org/trading-agents/architecture Trading Agent file structure, tick engine, and provider system Each Trading Agent is a folder-based entity with structured files that define its behavior, track state, and accumulate learnings. ## System Architecture ``` ┌──────────────────────────── Trading Agent ───────────────────────────┐ │ │ │ Strategy (agent.md) Journal (per session) Learnings.md │ │ ─────────────────── ──────────────────── ──────────── │ │ - system prompt - summary cross-session │ │ - default config - decisions lessons │ │ - skills/routines - tick-by-tick log │ │ - snapshot_N.md │ │ │ │ ┌──── TickEngine ────┐ │ │ │ every N seconds: │ │ │ Routines ───────────► │ 1. run providers │ ────► MCP Tools │ │ (deterministic │ 2. read journal │ - candles │ │ data prep) │ 3. build prompt │ - orderbook │ │ │ 4. ACP session │ - executors │ │ │ 5. capture tools │ - notify │ │ │ 6. write snapshot │ │ │ └────────────────────┘ │ │ │ └───────────────────────────────────────────────────────────────────────┘ │ ▼ Hummingbot Executors (filtered by controller_id == agent_id) ``` ## Directory Structure Each Condor Trading Agent (CTA) is a directory containing structured files: ``` trading_agents/ my_strategy/ agent.md # Strategy definition (frontmatter + LLM instructions) config.yml # Runtime configuration (editable) learnings.md # Cross-session insights (max 20) routines/ # Deterministic Python helpers trading_sessions/ session_N/ journal.md # Summary, decisions, ticks, executors snapshots/ snapshot_1.md # Full prompt + response for tick 1 snapshot_2.md dry_runs/ experiment_1.md # One-shot dry-run results ``` ### Two Memory Files | File | Scope | Purpose | | -------------- | ------------- | -------------------------------------------------- | | `journal.md` | Per-session | Short-term memory for the current trading session | | `learnings.md` | Cross-session | Long-term memory that persists across all sessions | **Journal**: What the agent did in *this* session—tick log, decisions, executor states. **Learnings**: Lessons that apply to *all* sessions—common errors, configuration tips, market insights. ## agent.md The strategy definition uses YAML frontmatter for configuration and Markdown for instructions: ```yaml theme={null} --- name: Grid Market Maker tick_interval: 60 connectors: - binance_perpetual - jupiter configs: trading_pair: SOL-USDC grid_levels: 5 spread_percentage: 0.3 limits: max_position_size_quote: 500 max_single_order_quote: 100 max_daily_loss_quote: 50 max_open_executors: 10 max_drawdown_pct: 10 --- ## Goal Provide liquidity around the mid-price while managing inventory risk. ## Strategy Rules 1. Maintain symmetric grid unless inventory exceeds threshold 2. Widen spreads during high volatility (ATR > 2%) 3. Pause trading if funding rate exceeds 0.1% against position ``` ## Configs vs Limits ### Configs Agent-suggestible parameters that control trading behavior: * The agent can *suggest* changes based on learnings * User must approve before changes take effect * Examples: `trading_pair`, `spread_percentage`, `tick_interval`, `grid_levels` ### Limits User-only guardrails enforced by the Risk Engine: * Safety boundaries the agent **cannot** exceed * Only modifiable by the user, never by the agent * Examples: `max_position_size_quote`, `max_daily_loss_quote`, `max_drawdown_pct` ## learnings.md Persists across sessions, accumulating insights: ```markdown theme={null} # Learnings ## Active Insights - [2026-03-27 14:30] Wider spreads (0.5% vs 0.3%) reduced adverse selection during Asian session - [2026-03-26 09:15] Grid rebalancing at 5% threshold outperforms 10% for SOL-USDC - [2026-03-25 16:45] Funding rate spikes above 0.05% correlate with 2-hour reversals ``` Maximum 20 entries to prevent context bloat. New insights replace older ones. ## Tick Loop Each tick (`TickEngine._tick`) executes: 1. **Resolve API client** for the configured server 2. **Run providers** — `executors` and `positions`, filtered by `controller_id` 3. **Read journal context** — learnings, summary, last 3 decisions 4. **Get risk state** — exposure, drawdown, open count. If blocked, skip LLM 5. **Build prompt** — system prompt + strategy + provider summaries + journal 6. **Spawn ACP session** with MCP servers (Hummingbot tools, market data) 7. **Persist** — write snapshot, append tick to journal, update summary ## Risk Engine The Risk Engine (`condor/trading_agent/risk.py`) tracks state and enforces limits: ```python theme={null} class RiskState: daily_pnl: float # Today's realized P&L total_exposure: float # Current open position value executor_count: int # Number of active executors drawdown_pct: float # Current drawdown percentage daily_cost: float # LLM costs today is_blocked: bool # Kill switch status block_reason: str # Why blocked (if applicable) ``` ### Pre-tick Validation Blocks the entire tick if: * `daily_pnl < -max_daily_loss_quote` * `drawdown_pct > max_drawdown_pct` * `daily_cost > max_cost_per_day_usd` ### Per-executor Validation Blocks executor creation if: * `executor_count >= max_open_executors` * `order_amount > max_single_order_quote` * `total_exposure + new_amount > max_position_size_quote` ## Providers Providers fetch deterministic data before each tick: | Provider | Output | | ----------- | ------------------------------------------------------------------------ | | `executors` | Active executors filtered by `controller_id`, with status and P\&L | | `positions` | Held positions from closed executors, with breakeven and unrealized P\&L | Provider output has two parts: * `data`: Structured data for internal tracking * `summary`: Human-readable string included in the LLM prompt ## Routines Custom Python helpers in the `routines/` directory: ```python theme={null} # routines/process_candles.py async def compute_vwap(candles): """Compute VWAP from candle data.""" total_volume = sum(c.volume for c in candles) return sum(c.close * c.volume for c in candles) / total_volume ``` Routines are deterministic—same input always produces same output. ## Inspecting Activity | Location | Content | | -------------------------------------------- | ------------------------------------------ | | `sessions/session_N/journal.md` | Chronological summary, decisions, tick log | | `sessions/session_N/snapshots/snapshot_K.md` | Full tick: prompt, response, tool calls | | `learnings.md` | Lessons the agent chose to keep | | `dry_runs/experiment_N.md` | Dry-run results | ## Injecting Information You can manually add to `learnings.md`—the agent doesn't know whether it wrote the entry or you did. Useful for: * Pre-seeding knowledge before deployment * Adding market context the agent can't observe * Correcting agent behavior **Via web dashboard**: Trading Agents → Select agent → Learnings → Edit **Via file**: Edit `trading_agents/my_strategy/learnings.md` directly # Inventory Source: https://condor.hummingbot.org/trading-agents/inventory Virtual portfolio tracking for Trading Agents **Inventory** (also called Position Hold) is the virtual portfolio that tracks a Trading Agent's cumulative trading impact. It enables accurate P\&L attribution when multiple agents share the same exchange accounts. ## Why Inventory Tracking? When multiple agents trade on shared accounts, you need to know: * Which agent made which trades * Each agent's individual P\&L * Total exposure per agent The Inventory system solves this by tracking positions per `controller_id` (agent ID). ## Position Hold Each position is uniquely keyed by `(connector_name, trading_pair)`: ```mermaid theme={null} flowchart LR subgraph Agents A1[Agent A] A2[Agent B] end subgraph PositionHold["Position Hold (Virtual)"] PH1[Agent A Positions] PH2[Agent B Positions] end subgraph Portfolio["Exchange Portfolio (Real)"] P[Shared Balances] end A1 --> PH1 A2 --> PH2 PH1 & PH2 --> P ``` Two agents can trade the same pair on the same exchange, and each maintains their own separate position with their own breakeven price and P\&L. ## Position Types | Type | Connectors | Description | | -------- | -------------------------------------------- | ------------------------------ | | **Spot** | `binance`, `coinbase`, `jupiter` | Standard buy/sell positions | | **Perp** | `binance_perpetual`, `hyperliquid_perpetual` | Leveraged long/short positions | | **LP** | `meteora`, `uniswap_v3` | Liquidity provider positions | ## Trading Pair Format ``` trading_pair = "SOL-USDT" │ │ │ └── Quote Asset (USDT) │ - P&L measured in this currency │ └── Base Asset (SOL) - The asset being traded - Amount always in this unit ``` **Key principle**: `amount` is always in base asset. All P\&L is in quote asset. ## Position State Each position tracks cumulative trading activity: | Field | Description | | ------------------- | ------------------------------- | | `buy_amount_base` | Total base asset bought | | `buy_amount_quote` | Total quote spent on buys | | `sell_amount_base` | Total base asset sold | | `sell_amount_quote` | Total quote received from sells | | `cum_fees_quote` | Cumulative fees paid | ### Derived Values **Net Amount**: `buy_amount_base - sell_amount_base` **Side**: * Positive net → Long (BUY) * Negative net → Short (SELL) * Zero net → Closed **Breakeven Price**: Volume-weighted average entry price ## P\&L Calculation All P\&L values are in **quote asset**. ### Unrealized P\&L Mark-to-market value at current price: ``` Long: (current_price - breakeven) × amount Short: (breakeven - current_price) × amount ``` ### Realized P\&L When positions are reduced (buys matched against sells): ``` matched = min(buy_amount_base, sell_amount_base) realized_pnl = (avg_sell_price - avg_buy_price) × matched ``` ### Global P\&L ``` global_pnl = unrealized_pnl + realized_pnl - fees ``` ## Example **Trade 1**: Buy 100 SOL at \$150 ``` Position: Long 100 SOL, breakeven = $150 ``` **Trade 2**: Buy 50 SOL at \$145 ``` Position: Long 150 SOL, breakeven = $148.33 (weighted average) ``` **Trade 3**: Sell 100 SOL at \$155 ``` Realized P&L: ($155 - $148.33) × 100 = +$667 Remaining: Long 50 SOL, breakeven = $148.33 ``` ## Via API ```bash theme={null} # List positions for an agent curl -u YOUR_USERNAME:YOUR_PASSWORD "http://localhost:8000/executors/positions/summary?controller_id=my-agent" ``` ## Related * [Position Handover](/executors/position-handover) - How executors add to inventory * [Executors](/executors/overview) - Trading operations that create positions # MCP & Tools Source: https://condor.hummingbot.org/trading-agents/mcp-tools Model Context Protocol servers that connect Trading Agents to Hummingbot API Trading Agents interact with exchanges and local operations through **MCP (Model Context Protocol)** servers. MCP provides a standardized way for LLMs to call tools via JSON-RPC 2.0. ## Architecture Condor runs two MCP servers: | Server | Purpose | | ------------------ | ---------------------------------------------------------------------------------- | | **hummingbot-mcp** | Trading operations via Hummingbot API (13 tools) | | **condor-mcp** | Local operations: skills, domain agents, memory, notifications, routines, journals | ```mermaid theme={null} flowchart LR Agent[Trading Agent] --> |JSON-RPC| HMCP[hummingbot-mcp] Agent --> |JSON-RPC| CMCP[condor-mcp] HMCP --> API[Hummingbot API] API --> Exchanges[CEXs & DEXs] CMCP --> Local[Local Files] CMCP --> TG[Telegram] ``` ## Hummingbot MCP Tools ### Account Management | Tool | Description | | -------------------- | ------------------------------------- | | `setup_connector()` | Add/remove exchange API credentials | | `configure_server()` | Switch between Hummingbot API servers | ### Portfolio & Holdings | Tool | Description | | -------------------------- | --------------------------------------------- | | `get_portfolio_overview()` | Unified view: balances, positions, LP, orders | ### Trading Operations | Tool | Description | | ------------------------------------------ | ------------------------------------------- | | `set_account_position_mode_and_leverage()` | Configure perpetual trading settings | | `search_history()` | Query historical orders, positions, LP | | `get_market_data()` | Prices, candles, funding rates, order books | ### Executor Management The primary trading interface for agents: | Tool | Description | | -------------------- | -------------------------------------- | | `manage_executors()` | Create, search, stop trading executors | **Supported executor types:** * `order_executor` - Single limit/market orders * `position_executor` - Directional trades with triple barrier * `grid_executor` - Multi-level grid trading * `dca_executor` - Dollar-cost averaging * `lp_executor` - Liquidity provision **Actions:** `create`, `search`, `stop`, `get_logs` ### Bot Management For advanced multi-strategy deployments: | Tool | Description | | ---------------------- | ---------------------------------------- | | `manage_bots()` | Deploy and control controller-based bots | | `manage_controllers()` | CRUD operations on controller templates | ### Market Data & Discovery | Tool | Description | | ------------------------- | ---------------------------------------- | | `explore_dex_pools()` | Discover DEX/CLMM pools with filtering | | `explore_geckoterminal()` | Free market data: networks, pools, OHLCV | ### Backtesting | Tool | Description | | ------------------------- | ---------------------------------------------------- | | `run_backtest()` | Backtest a V2 controller config over historical data | | `manage_backtest_tasks()` | Track and manage backtest tasks | DEX liquidity positions are created through `manage_executors()` (the `lp_executor` type), and Gateway itself is managed from the Condor dashboard (**Settings → Gateway**) rather than through a dedicated MCP tool. ## Condor MCP Tools ### Skills and domain agents The routing model, and the part most worth knowing: before reaching for a raw tool, an assistant looks for a **skill** (a playbook, sometimes bound to a runnable routine) and then for a **domain agent** to hand the work to. | Tool | Description | | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `manage_skill()` | Read, search, list, create, edit and delete skills. Skills belong to the assistant, not to a user — they are the shared playbooks everyone using it gets | | `consult()` | Ask a domain agent a question and block for its answer. The agent runs in the main process, where the runtime and server credentials live, and may pause on a user confirmation before acting | | `delegate()` | The unattended sibling of `consult()`. Hands a goal-oriented task to a detached agent that runs until done and then notifies the user; returns a `task_id` to poll or stop | Authoring or debugging a **routine** goes through `delegate()` to a background Condor worker rather than being written by hand — the worker reads the routine cookbook, writes the routine, tests it, and reports back. Running an existing routine is not authoring: that is just `manage_routines()`. ### Memory and code | Tool | Description | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `manage_memory()` | What the assistant remembers about *you*. Per-user, keyed to the calling user, with the writing agent recorded for audit | | `run_code()` | Ad-hoc Python in the bot process, with the same primitives a routine gets — `context`, `client`, pandas, report builders, every `condor.*` module. The scratchpad below a routine: a snippet that failed is stored whole, read back, corrected and re-run | `run_code()` executes arbitrary Python inside the bot process. Granting it to a non-admin is the `code_run` permission in **Settings → Admin**, which spells out what it reaches before you grant it. ### Notifications and routines | Tool | Description | | --------------------- | --------------------------------------- | | `send_notification()` | Send Telegram messages (Markdown/HTML) | | `manage_routines()` | Discover, run, create and edit routines | ### Trading agent operations | Tool | Description | | ------------------------------- | ---------------------------------------------------- | | `manage_trading_agent()` | Strategy CRUD and agent lifecycle (start/stop/pause) | | `trading_agent_journal_read()` | Read the journal: recent entries, learnings, state | | `trading_agent_journal_write()` | Write to the journal: actions, learnings, state | ### Environment | Tool | Description | | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `manage_servers()` | List accessible API servers | | `get_user_context()` | Current user, active server, permissions | | `get_available_models()` | Which models and providers are actually usable right now — installed CLI bridges, cloud keys present, reachable local servers and what they have loaded. Read-only, and never returns a key value, only whether its variable is set | | `manage_notes()` | Key-value persistent storage | ## Progressive Disclosure Many tools support step-by-step discovery: ``` 1. Call with no parameters → See available options 2. Call with partial parameters → See next steps 3. Call with all parameters → Execute action ``` **Example: Creating an executor** ``` Agent: manage_executors() Tool: Available executor types: order_executor, position_executor, grid_executor... Agent: manage_executors(action="create", executor_type="grid_executor") Tool: Required fields for grid_executor: - connector_name (string) - trading_pair (string) - ... Agent: manage_executors(action="create", executor_type="grid_executor", config={...}) Tool: Grid executor created (ID: exec_123) ``` ## Configuration ### Server Settings MCP servers read configuration from environment variables or `~/.hummingbot_mcp/server.yml`: | Variable | Default | Description | | ------------------------ | ----------------------- | ------------------------- | | `HUMMINGBOT_API_URL` | `http://localhost:8000` | API server URL | | `HUMMINGBOT_USERNAME` | `admin` | API username | | `HUMMINGBOT_PASSWORD` | `admin` | API password | | `HUMMINGBOT_TIMEOUT` | `30.0` | Request timeout (seconds) | | `HUMMINGBOT_MAX_RETRIES` | `3` | Retry attempts | ### Runtime Configuration Agents can switch servers at runtime: ``` Agent: configure_server() Tool: Current server: localhost:8000 (admin) Agent: configure_server(url="http://prod-server:8000", username="trader", password="xxx") Tool: Connected to prod-server:8000 ``` ## Error Handling MCP servers provide contextual error messages: * **Connection errors**: Suggests checking server URL and Docker networking * **Auth errors**: Prompts to verify credentials via `configure_server()` * **Validation errors**: Shows required fields and valid values All tools use retry logic (3 attempts with 2-second delays) for transient failures. ## Running MCP Servers MCP servers are started automatically when agents launch via ACP. For manual testing: ```bash theme={null} # Hummingbot MCP python -m mcp_servers.hummingbot_api # Condor MCP python -m mcp_servers.condor ``` ## Adding Custom Tools To add tools to an MCP server, create a function with the `@mcp.tool()` decorator: ```python theme={null} # In mcp_servers/hummingbot_api/tools/custom.py @mcp.tool() async def my_custom_tool(param1: str, param2: int = 10) -> str: """ Description of what this tool does. Args: param1: Description of param1 param2: Description of param2 (default: 10) """ # Implementation result = await do_something(param1, param2) return format_result(result) ``` Register the tool in `server.py`: ```python theme={null} from .tools.custom import my_custom_tool ``` # Overview Source: https://condor.hummingbot.org/trading-agents/overview Autonomous LLM-driven trading agents built on the Trading Agents Standard A **Trading Agent** is an autonomous, file-backed entity that runs on a fixed tick interval, reasons about the market with an LLM, and translates its decisions into real trades through Hummingbot executors. Trading Agent Architecture ## Design Philosophy The Trading Agents Standard separates what LLMs do well—reasoning under uncertainty—from what traditional software does well—reliable, repeatable execution. ```mermaid theme={null} flowchart TB subgraph Agentic["Agentic Layer (Probabilistic)"] O[Observe] --> OR[Orient] OR --> D[Decide] D --> A[Act] A --> O end subgraph Execution["Execution Layer (Deterministic)"] EX[Executors] POS[Positions] RK[Risk Engine] end A --> EX EX --> POS D --> RK ``` | Layer | Type | What It Does | | ------------- | ------------------------- | ---------------------------------------------------------------- | | **Agentic** | LLM reasoning (OODA loop) | Observes market, orients context, decides action, acts via tools | | **Execution** | Deterministic Python | Runs executors, tracks positions, enforces risk limits | The result is an agent that *thinks* like a discretionary trader but *acts* like a systematic one, with full auditability across every tick. ## Core Principle: Executor-Based Trading **Agents only act through Hummingbot executors.** Each agent spawns executors with its own `controller_id == agent_id`, which provides: 1. **Isolation**: Two agents on the same account never see or touch each other's executors 2. **Virtual Portfolio**: Each agent gets its own positions, breakeven prices, realized/unrealized P\&L 3. **Position Handover**: When an executor closes with `keep_position=true`, the agent retains the inventory ## Mental Model > An agent is a *folder* on disk and a *tick loop* in memory. The folder is its long-term memory; providers give it short-term situational awareness; the LLM is its decision function; executors are its hands; the risk engine is the wrist it can't move past. Everything an agent owns is tagged with its `controller_id`, making the system safely composable: any number of agents can share the same exchange account without stepping on each other. ## Core Components | Component | Purpose | Documentation | | -------------------------------------------- | -------------------------------------- | -------------------------------- | | [Architecture](/trading-agents/architecture) | File structure, tick engine, providers | agent.md, journals, learnings | | [Sessions](/trading-agents/sessions) | Session management, snapshots | Cross-session memory | | [Inventory](/trading-agents/inventory) | Position tracking and P\&L | Virtual portfolio | | [Executors](/executors/overview) | Trading operations | Types, lifecycle, keep\_position | | [MCP Tools](/trading-agents/mcp-tools) | LLM tool access | Market data, trading | ## Run Modes | Mode | Behavior | | ---------- | -------------------------------------------------------------------------------------------- | | `dry_run` | One tick, no trading. Pure reasoning test. Saves experiment snapshot. | | `run_once` | One tick with trading. Manual single-shot execution. | | `loop` | Standard mode. Ticks every `frequency_sec` until stopped. Creates session with full journal. | ## Quick Start **Via Telegram**: ``` /agent → Create New Agent → Configure → Start ``` **Programmatically**: ```python theme={null} from condor.trading_agent.engine import TickEngine from condor.trading_agent.strategy import StrategyStore store = StrategyStore() strategy = store.get_by_slug("my_strategy") engine = TickEngine( strategy=strategy, config={ "execution_mode": "loop", "frequency_sec": 60, "server_name": "binance_main", "total_amount_quote": 500, "risk_limits": { "max_total_exposure_quote": 1500, "max_drawdown_pct": 5, "max_open_executors": 4, }, }, ) await engine.start() ``` ## Session Continuity The `~/condor` directory stores all agent state, and Condor uses ACP (Agent Client Protocol) to connect to your LLM. This means you can: * Start a conversation on Telegram * Continue it in Claude Code * Switch to the web dashboard Same session, same agent state, same conversation history. # Sessions Source: https://condor.hummingbot.org/trading-agents/sessions Session management, journal structure, and cross-interface continuity Each time you start a Trading Agent, it creates a new **session** that tracks all activity, decisions, and state changes. ## Session Structure Sessions are stored in the agent's `trading_sessions/` directory: ``` trading_agents/my-strategy/ └── trading_sessions/ ├── session_1/ │ ├── journal.md │ └── snapshots/ ├── session_2/ │ ├── journal.md │ └── snapshots/ └── session_3/ ├── journal.md └── snapshots/ ``` ## journal.md The journal is the agent's working memory for the session. It contains: ### Summary Section High-level overview updated each tick: ```markdown theme={null} ## Summary - **Session Start**: 2026-03-27 14:00 UTC - **Current Tick**: 47 - **Active Executors**: 2 - **Session P&L**: +$127.50 - **Status**: Running ``` ### Decisions Section Key decisions made during the session: ```markdown theme={null} ## Decisions ### Tick 45 - 2026-03-27 15:32 UTC **Decision**: Open long position on SOL-USDT **Reasoning**: Price broke above 20-period high with increasing volume **Action**: Created PositionExecutor (ID: exec_045) ### Tick 38 - 2026-03-27 15:10 UTC **Decision**: Close grid executor **Reasoning**: Volatility exceeded threshold, grid levels too tight **Action**: Stopped GridExecutor (ID: exec_032) ``` ### Ticks Section Record of each OODA loop iteration: ```markdown theme={null} ## Ticks ### Tick 47 - 2026-03-27 15:35 UTC - **Portfolio Value**: $10,127.50 - **Open Positions**: 1 (SOL-USDT long 10 @ 152.30) - **Active Executors**: 2 - **Market Context**: SOL +2.3% 1h, funding 0.01% - **Action Taken**: Monitoring, no changes ``` ### Executors Section Active and completed executor states: ```markdown theme={null} ## Executors ### Active | ID | Type | Pair | Side | Entry | Current | P&L | |----|------|------|------|-------|---------|-----| | exec_045 | Position | SOL-USDT | BUY | 152.30 | 154.10 | +$18.00 | ### Completed This Session | ID | Type | Close Type | P&L | Duration | |----|------|------------|-----|----------| | exec_032 | Grid | EARLY_STOP | +$45.20 | 2h 15m | ``` ## snapshots/ Full tick snapshots for debugging and replay. This is one of the most important parts of the system: **observability**. Each snapshot captures the complete state of a tick: ```json theme={null} { "tick": 47, "timestamp": "2026-03-27T15:35:00Z", "system_prompt": "...", "agent_response": { "text": "Trend flip bullish. All conditions met, opening long.", "decision": "create_executor", "reasoning": "Price within 2% of S1, EMAs aligned bullish" }, "tool_calls": [ {"tool": "run_routine", "args": {...}, "result": {...}}, {"tool": "create_executor", "args": {...}, "result": {...}} ], "portfolio": { "total_value_quote": 10127.50, "positions": [...], "balances": {...} }, "market_data": { "SOL-USDT": { "price": 154.10, "volume_24h": 1250000000, "funding_rate": 0.0001 } }, "executors": [...], "risk_state": { "daily_pnl": 127.50, "total_exposure": 1541.00, "is_blocked": false } } ``` This lets you: * **Debug** exactly what the agent was thinking when it made a decision * **Understand** why a trade was placed or not placed * **Replay** the prompt that was passed to the agent * **Audit** all reasoning and tool calls **Retention**: Maximum 100 snapshots per session. Older snapshots are pruned automatically. ## Session Continuity The `~/condor` directory stores all agent state. Combined with ACP (Agent Client Protocol), this enables seamless continuity across interfaces: | Interface | How to Connect | | ----------------- | ---------------------------------------------------- | | **Telegram** | `/agent` → Select agent → Resume session | | **Claude Code** | `claude --agent ~/condor/trading_agents/my-strategy` | | **Web Dashboard** | Select agent from dashboard (coming soon) | When you switch interfaces: * Same session continues * Full conversation history preserved * Agent state unchanged ## Starting a New Session To start a fresh session instead of resuming: **Telegram**: `/agent` → Select agent → **New Session** **CLI**: ```bash theme={null} claude --agent ~/condor/trading_agents/my-strategy --new-session ``` ## Injecting Directives You can send real-time instructions to a running agent using **directives**. The directive will be included in the agent's next tick prompt under a `USER DIRECTIVES` section, then cleared. **Telegram**: `/agent` → Select running agent → **Inject Directive** **Programmatic**: ```python theme={null} engine.inject_directive("Reduce exposure, news event in 10 min.") ``` Common directive use cases: * Alert the agent to upcoming news events * Request exposure reduction before volatility * Override normal behavior temporarily * Provide market context the agent can't observe The agent sees the directive verbatim and decides how to act on it based on its strategy rules. ## Session Lifecycle ```mermaid theme={null} stateDiagram-v2 [*] --> Created: Start agent Created --> Running: First tick Running --> Running: Each tick Running --> Paused: User pause Paused --> Running: User resume Running --> Completed: User stop Running --> Blocked: Risk limit hit Blocked --> Running: Limits reset Completed --> [*] ``` # Bitget Workshop Source: https://condor.hummingbot.org/videos/agent-builders-cup/bitget Build an AI trading agent that runs on Bitget, a leading crypto exchange.