diff --git a/hummingbot-api-openapi.json b/hummingbot-api-openapi.json new file mode 100644 index 0000000..61056e2 --- /dev/null +++ b/hummingbot-api-openapi.json @@ -0,0 +1 @@ +{"openapi":"3.1.0","info":{"title":"Hummingbot API","description":"API for managing Hummingbot trading instances","version":"1.0.1"},"paths":{"/docker/running":{"get":{"tags":["Docker"],"summary":"Is Docker Running","description":"Check if Docker daemon is running.\n\nArgs:\n docker_service: Docker service dependency\n \nReturns:\n Dictionary indicating if Docker is running","operationId":"is_docker_running_docker_running_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBasic":[]}]}},"/docker/available-images/":{"get":{"tags":["Docker"],"summary":"Available Images","description":"Get available Docker images matching the specified name.\n\nArgs:\n image_name: Name pattern to search for in image tags\n docker_service: Docker service dependency\n \nReturns:\n Dictionary with list of available image tags","operationId":"available_images_docker_available_images__get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"image_name","in":"query","required":false,"schema":{"type":"string","title":"Image Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/docker/active-containers":{"get":{"tags":["Docker"],"summary":"Active Containers","description":"Get all currently active (running) Docker containers.\n\nArgs:\n name_filter: Optional filter to match container names (case-insensitive)\n docker_service: Docker service dependency\n \nReturns:\n List of active container information","operationId":"active_containers_docker_active_containers_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"name_filter","in":"query","required":false,"schema":{"type":"string","title":"Name Filter"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/docker/exited-containers":{"get":{"tags":["Docker"],"summary":"Exited Containers","description":"Get all exited (stopped) Docker containers.\n\nArgs:\n name_filter: Optional filter to match container names (case-insensitive)\n docker_service: Docker service dependency\n \nReturns:\n List of exited container information","operationId":"exited_containers_docker_exited_containers_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"name_filter","in":"query","required":false,"schema":{"type":"string","title":"Name Filter"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/docker/clean-exited-containers":{"post":{"tags":["Docker"],"summary":"Clean Exited Containers","description":"Remove all exited Docker containers to free up space.\n\nArgs:\n docker_service: Docker service dependency\n \nReturns:\n Response from cleanup operation","operationId":"clean_exited_containers_docker_clean_exited_containers_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBasic":[]}]}},"/docker/remove-container/{container_name}":{"post":{"tags":["Docker"],"summary":"Remove Container","description":"Remove a Hummingbot container and optionally archive its bot data.\n\nNOTE: This endpoint only works with Hummingbot containers (names starting with 'hummingbot-')\nas it archives bot-specific data from the bots/instances directory.\n\nArgs:\n container_name: Name of the Hummingbot container to remove\n archive_locally: Whether to archive data locally (default: True)\n s3_bucket: S3 bucket name for cloud archiving (optional)\n docker_service: Docker service dependency\n bot_archiver: Bot archiver service dependency\n \nReturns:\n Response from container removal operation\n \nRaises:\n HTTPException: 400 if container is not a Hummingbot container\n HTTPException: 500 if archiving fails","operationId":"remove_container_docker_remove_container__container_name__post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"container_name","in":"path","required":true,"schema":{"type":"string","title":"Container Name"}},{"name":"archive_locally","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Archive Locally"}},{"name":"s3_bucket","in":"query","required":false,"schema":{"type":"string","title":"S3 Bucket"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/docker/stop-container/{container_name}":{"post":{"tags":["Docker"],"summary":"Stop Container","description":"Stop a running Docker container.\n\nArgs:\n container_name: Name of the container to stop\n docker_service: Docker service dependency\n \nReturns:\n Response from container stop operation","operationId":"stop_container_docker_stop_container__container_name__post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"container_name","in":"path","required":true,"schema":{"type":"string","title":"Container Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/docker/start-container/{container_name}":{"post":{"tags":["Docker"],"summary":"Start Container","description":"Start a stopped Docker container.\n\nArgs:\n container_name: Name of the container to start\n docker_service: Docker service dependency\n \nReturns:\n Response from container start operation","operationId":"start_container_docker_start_container__container_name__post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"container_name","in":"path","required":true,"schema":{"type":"string","title":"Container Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/docker/pull-image/":{"post":{"tags":["Docker"],"summary":"Pull Image","description":"Initiate Docker image pull as background task.\nReturns immediately with task status for monitoring.\n\nArgs:\n image: DockerImage object containing the image name to pull\n docker_service: Docker service dependency\n \nReturns:\n Status of the pull operation initiation","operationId":"pull_image_docker_pull_image__post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DockerImage"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/docker/pull-status/":{"get":{"tags":["Docker"],"summary":"Get Pull Status","description":"Get status of all pull operations.\n\nArgs:\n docker_service: Docker service dependency\n \nReturns:\n Dictionary with all pull operations and their statuses","operationId":"get_pull_status_docker_pull_status__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/status":{"get":{"tags":["Gateway"],"summary":"Get Gateway Status","description":"Get Gateway container status.","operationId":"get_gateway_status_gateway_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayStatus"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/start":{"post":{"tags":["Gateway"],"summary":"Start Gateway","description":"Start Gateway container.","operationId":"start_gateway_gateway_start_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayConfig"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/stop":{"post":{"tags":["Gateway"],"summary":"Stop Gateway","description":"Stop Gateway container.","operationId":"stop_gateway_gateway_stop_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/restart":{"post":{"tags":["Gateway"],"summary":"Restart Gateway","description":"Restart Gateway container.\n\nIf config is provided, the container will be removed and recreated with new configuration.\nIf no config is provided, the container will be stopped and started with existing configuration.","operationId":"restart_gateway_gateway_restart_post","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/GatewayConfig"},{"type":"null"}],"title":"Config"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/logs":{"get":{"tags":["Gateway"],"summary":"Get Gateway Logs","description":"Get Gateway container logs.","operationId":"get_gateway_logs_gateway_logs_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"tail","in":"query","required":false,"schema":{"type":"integer","maximum":10000,"minimum":1,"default":100,"title":"Tail"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/gateway/connectors":{"get":{"tags":["Gateway"],"summary":"List Connectors","description":"List all available DEX connectors with their configurations.\n\nReturns connector details including name, trading types, chain, and networks.\nAll fields normalized to snake_case.","operationId":"list_connectors_gateway_connectors_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response List Connectors Gateway Connectors Get"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/connectors/{connector_name}":{"get":{"tags":["Gateway"],"summary":"Get Connector Config","description":"Get configuration for a specific DEX connector.\n\nArgs:\n connector_name: Connector name (e.g., 'meteora', 'raydium')","operationId":"get_connector_config_gateway_connectors__connector_name__get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"connector_name","in":"path","required":true,"schema":{"type":"string","title":"Connector Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Connector Config Gateway Connectors Connector Name Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Gateway"],"summary":"Update Connector Config","description":"Update configuration for a DEX connector.\n\nArgs:\n connector_name: Connector name (e.g., 'meteora', 'raydium')\n config_updates: Dict with path-value pairs to update.\n Keys can be in snake_case (e.g., {\"slippage_pct\": 0.5})\n or camelCase (e.g., {\"slippagePct\": 0.5})","operationId":"update_connector_config_gateway_connectors__connector_name__post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"connector_name","in":"path","required":true,"schema":{"type":"string","title":"Connector Name"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Config Updates"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update Connector Config Gateway Connectors Connector Name Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/gateway/apiKeys":{"get":{"tags":["Gateway"],"summary":"Get Api Keys","description":"Get all configured API keys from Gateway.\n\nReturns a dict mapping provider name to API key value.\nExample response:\n{\n \"helius\": \"46951ec2-16af-4fc0-a5df-970b0eb925b7\",\n \"infura\": \"920646320ec3463fa1b5235be9fa48d3\",\n \"coingecko\": \"CG-Rw786jTpNmV1MvRrqpDAHR6r\",\n \"etherscan\": \"\"\n}","operationId":"get_api_keys_gateway_apiKeys_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Get Api Keys Gateway Apikeys Get"}}}}},"security":[{"HTTPBasic":[]}]},"post":{"tags":["Gateway"],"summary":"Update Api Keys","description":"Update API keys in Gateway configuration.\n\nArgs:\n request: Contains api_keys dict mapping provider name to API key value\n\nExample request:\n{\n \"api_keys\": {\n \"helius\": \"new-api-key-value\",\n \"infura\": \"another-api-key\"\n }\n}\n\nNote: After updating API keys, restart Gateway for changes to take effect.","operationId":"update_api_keys_gateway_apiKeys_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateApiKeysRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Update Api Keys Gateway Apikeys Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/chains":{"get":{"tags":["Gateway"],"summary":"List Chains","description":"List all available blockchain chains and their networks.\n\nThis also serves as the networks list endpoint.","operationId":"list_chains_gateway_chains_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response List Chains Gateway Chains Get"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/pools":{"get":{"tags":["Gateway"],"summary":"List Pools Legacy","description":"[DEPRECATED] Use GET /gateway/networks/{network_id}/pools instead.\n\nList all liquidity pools for a connector and network.","operationId":"list_pools_legacy_gateway_pools_get","deprecated":true,"security":[{"HTTPBasic":[]}],"parameters":[{"name":"connector_name","in":"query","required":true,"schema":{"type":"string","description":"DEX connector (e.g., 'meteora', 'raydium')","title":"Connector Name"},"description":"DEX connector (e.g., 'meteora', 'raydium')"},{"name":"network","in":"query","required":true,"schema":{"type":"string","description":"Network (e.g., 'mainnet-beta')","title":"Network"},"description":"Network (e.g., 'mainnet-beta')"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true},"title":"Response List Pools Legacy Gateway Pools Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/gateway/networks":{"get":{"tags":["Gateway"],"summary":"List Networks","description":"List all available networks across all chains.\n\nReturns a flattened list of network IDs in the format 'chain-network'.\nThis is the primary interface for network discovery.","operationId":"list_networks_gateway_networks_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response List Networks Gateway Networks Get"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/networks/{network_id}":{"get":{"tags":["Gateway"],"summary":"Get Network Config","description":"Get configuration for a specific network.\n\nArgs:\n network_id: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta', 'ethereum-mainnet')\n\nExample: GET /gateway/networks/solana-mainnet-beta","operationId":"get_network_config_gateway_networks__network_id__get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"network_id","in":"path","required":true,"schema":{"type":"string","title":"Network Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Network Config Gateway Networks Network Id Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Gateway"],"summary":"Update Network Config","description":"Update configuration for a specific network.\n\nArgs:\n network_id: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta')\n config_updates: Dict with path-value pairs to update.\n Keys can be in snake_case (e.g., {\"node_url\": \"https://...\"})\n or camelCase (e.g., {\"nodeURL\": \"https://...\"})\n\nExample: POST /gateway/networks/solana-mainnet-beta","operationId":"update_network_config_gateway_networks__network_id__post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"network_id","in":"path","required":true,"schema":{"type":"string","title":"Network Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Config Updates"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update Network Config Gateway Networks Network Id Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/gateway/networks/{network_id}/tokens":{"get":{"tags":["Gateway"],"summary":"Get Network Tokens","description":"Get available tokens for a network.\n\nArgs:\n network_id: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta')\n search: Filter tokens by symbol or name\n\nExample: GET /gateway/networks/solana-mainnet-beta/tokens?search=USDC","operationId":"get_network_tokens_gateway_networks__network_id__tokens_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"network_id","in":"path","required":true,"schema":{"type":"string","title":"Network Id"}},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Network Tokens Gateway Networks Network Id Tokens Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Gateway"],"summary":"Add Network Token","description":"Add a custom token to Gateway's token list for a specific network.\n\nArgs:\n network_id: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta', 'ethereum-mainnet')\n token_request: Token details (address, symbol, name, decimals)\n\nExample: POST /gateway/networks/ethereum-mainnet/tokens\n{\n \"address\": \"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\",\n \"symbol\": \"USDC\",\n \"name\": \"USD Coin\",\n \"decimals\": 6\n}\n\nNote: After adding a token, restart Gateway for changes to take effect.","operationId":"add_network_token_gateway_networks__network_id__tokens_post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"network_id","in":"path","required":true,"schema":{"type":"string","title":"Network Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddTokenRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Add Network Token Gateway Networks Network Id Tokens Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/gateway/networks/{network_id}/tokens/save/{token_address}":{"post":{"tags":["Gateway"],"summary":"Save Network Token","description":"Save a token by address - auto-fetches token info from GeckoTerminal.\n\nThis is the simplest way to add a token. Just provide the address and\nthe API will fetch symbol, name, and decimals automatically.\n\nArgs:\n network_id: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta', 'ethereum-mainnet')\n token_address: Token contract address\n\nExample: POST /gateway/networks/solana-mainnet-beta/tokens/save/9QFfgxdSqH5zT7j6rZb1y6SZhw2aFtcQu2r6BuYpump","operationId":"save_network_token_gateway_networks__network_id__tokens_save__token_address__post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"network_id","in":"path","required":true,"schema":{"type":"string","title":"Network Id"}},{"name":"token_address","in":"path","required":true,"schema":{"type":"string","title":"Token Address"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Save Network Token Gateway Networks Network Id Tokens Save Token Address Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/gateway/networks/{network_id}/tokens/{token_address}":{"delete":{"tags":["Gateway"],"summary":"Delete Network Token","description":"Delete a custom token from Gateway's token list for a specific network.\n\nArgs:\n network_id: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta', 'ethereum-mainnet')\n token_address: Token contract address to delete\n\nExample: DELETE /gateway/networks/solana-mainnet-beta/tokens/9QFfgxdSqH5zT7j6rZb1y6SZhw2aFtcQu2r6BuYpump\n\nNote: After deleting a token, restart Gateway for changes to take effect.","operationId":"delete_network_token_gateway_networks__network_id__tokens__token_address__delete","security":[{"HTTPBasic":[]}],"parameters":[{"name":"network_id","in":"path","required":true,"schema":{"type":"string","title":"Network Id"}},{"name":"token_address","in":"path","required":true,"schema":{"type":"string","title":"Token Address"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Delete Network Token Gateway Networks Network Id Tokens Token Address Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/gateway/networks/{network_id}/pools":{"get":{"tags":["Gateway"],"summary":"Get Network Pools","description":"Get available pools for a network.\n\nArgs:\n network_id: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta')\n connector: Optional filter by connector (e.g., 'raydium', 'meteora', 'uniswap')\n pool_type: Optional filter by type ('amm' or 'clmm')\n search: Optional search by trading pair (e.g., 'SOL-USDC') or pool address\n\nExample: GET /gateway/networks/solana-mainnet-beta/pools?connector=raydium&type=clmm","operationId":"get_network_pools_gateway_networks__network_id__pools_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"network_id","in":"path","required":true,"schema":{"type":"string","title":"Network Id"}},{"name":"connector","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by connector (e.g., 'raydium', 'meteora')","title":"Connector"},"description":"Filter by connector (e.g., 'raydium', 'meteora')"},{"name":"pool_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by type ('amm' or 'clmm')","title":"Pool Type"},"description":"Filter by type ('amm' or 'clmm')"},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Search by trading pair or address","title":"Search"},"description":"Search by trading pair or address"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Network Pools Gateway Networks Network Id Pools Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Gateway"],"summary":"Add Network Pool","description":"Add a custom pool to Gateway's pool list for a specific network.\n\nArgs:\n network_id: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta', 'ethereum-mainnet')\n pool_request: Pool details (connector, type, base, quote, address, etc.)\n\nExample: POST /gateway/networks/solana-mainnet-beta/pools\n{\n \"connector_name\": \"raydium\",\n \"type\": \"clmm\",\n \"base\": \"SOL\",\n \"quote\": \"USDC\",\n \"address\": \"58oQChx4yWmvKdwLLZzBi4ChoCc2fqCUWBkwMihLYQo2\",\n \"base_address\": \"So11111111111111111111111111111111111111112\",\n \"quote_address\": \"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v\",\n \"fee_pct\": 0.25\n}\n\nNote: After adding a pool, restart Gateway for changes to take effect.","operationId":"add_network_pool_gateway_networks__network_id__pools_post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"network_id","in":"path","required":true,"schema":{"type":"string","title":"Network Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddPoolRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Add Network Pool Gateway Networks Network Id Pools Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/gateway/networks/{network_id}/pools/save/{pool_address}":{"post":{"tags":["Gateway"],"summary":"Save Network Pool","description":"Save a pool by address using GeckoTerminal lookup.\nThis automatically fetches pool info and token info from GeckoTerminal.\n\nArgs:\n network_id: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta')\n pool_address: Pool contract address\n\nExample: POST /gateway/networks/solana-mainnet-beta/pools/save/58oQChx4yWmvKdwLLZzBi4ChoCc2fqCUWBkwMihLYQo2\n\nNote: This will auto-add any missing tokens to the network's token list.","operationId":"save_network_pool_gateway_networks__network_id__pools_save__pool_address__post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"network_id","in":"path","required":true,"schema":{"type":"string","title":"Network Id"}},{"name":"pool_address","in":"path","required":true,"schema":{"type":"string","title":"Pool Address"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Save Network Pool Gateway Networks Network Id Pools Save Pool Address Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/gateway/networks/{network_id}/pools/{pool_address}":{"delete":{"tags":["Gateway"],"summary":"Delete Network Pool","description":"Delete a pool from Gateway's pool list for a specific network.\n\nArgs:\n network_id: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta', 'ethereum-mainnet')\n pool_address: Pool contract address to delete\n\nExample: DELETE /gateway/networks/solana-mainnet-beta/pools/58oQChx4yWmvKdwLLZzBi4ChoCc2fqCUWBkwMihLYQo2\n\nNote: After deleting a pool, restart Gateway for changes to take effect.","operationId":"delete_network_pool_gateway_networks__network_id__pools__pool_address__delete","security":[{"HTTPBasic":[]}],"parameters":[{"name":"network_id","in":"path","required":true,"schema":{"type":"string","title":"Network Id"}},{"name":"pool_address","in":"path","required":true,"schema":{"type":"string","title":"Pool Address"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Delete Network Pool Gateway Networks Network Id Pools Pool Address Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/accounts/":{"get":{"tags":["Accounts"],"summary":"List Accounts","description":"Get a list of all account names in the system.\n\nReturns:\n List of account names","operationId":"list_accounts_accounts__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"type":"string"},"type":"array","title":"Response List Accounts Accounts Get"}}}}},"security":[{"HTTPBasic":[]}]}},"/accounts/{account_name}/credentials":{"get":{"tags":["Accounts"],"summary":"List Account Credentials","description":"Get a list of all connectors that have credentials configured for a specific account.\n\nArgs:\n account_name: Name of the account to list credentials for\n\nReturns:\n List of connector names that have credentials configured\n\nRaises:\n HTTPException: 404 if account not found","operationId":"list_account_credentials_accounts__account_name__credentials_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"account_name","in":"path","required":true,"schema":{"type":"string","title":"Account Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"title":"Response List Account Credentials Accounts Account Name Credentials Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/accounts/add-account":{"post":{"tags":["Accounts"],"summary":"Add Account","description":"Create a new account with default configuration files.\n\nArgs:\n account_name: Name of the new account to create\n\nReturns:\n Success message when account is created\n\nRaises:\n HTTPException: 400 if account already exists or the account name is invalid","operationId":"add_account_accounts_add_account_post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"account_name","in":"query","required":true,"schema":{"type":"string","title":"Account Name"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/accounts/delete-account":{"post":{"tags":["Accounts"],"summary":"Delete Account","description":"Delete an account and all its associated credentials.\n\nArgs:\n account_name: Name of the account to delete\n\nReturns:\n Success message when account is deleted\n\nRaises:\n HTTPException: 400 if trying to delete master account or the account name is invalid, 404 if account not found","operationId":"delete_account_accounts_delete_account_post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"account_name","in":"query","required":true,"schema":{"type":"string","title":"Account Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/accounts/delete-credential/{account_name}/{connector_name}":{"post":{"tags":["Accounts"],"summary":"Delete Credential","description":"Delete a specific connector credential for an account.\n\nArgs:\n account_name: Name of the account\n connector_name: Name of the connector to delete credentials for\n\nReturns:\n Success message when credential is deleted\n\nRaises:\n HTTPException: 404 if credential not found","operationId":"delete_credential_accounts_delete_credential__account_name___connector_name__post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"account_name","in":"path","required":true,"schema":{"type":"string","title":"Account Name"}},{"name":"connector_name","in":"path","required":true,"schema":{"type":"string","title":"Connector Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/accounts/add-credential/{account_name}/{connector_name}":{"post":{"tags":["Accounts"],"summary":"Add Credential","description":"Add or update connector credentials (API keys) for a specific account and connector.\n\nArgs:\n account_name: Name of the account\n connector_name: Name of the connector\n credentials: Dictionary containing the connector credentials\n\nReturns:\n Success message when credentials are added\n\nRaises:\n HTTPException: 400 if there's an error adding the credentials","operationId":"add_credential_accounts_add_credential__account_name___connector_name__post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"account_name","in":"path","required":true,"schema":{"type":"string","title":"Account Name"}},{"name":"connector_name","in":"path","required":true,"schema":{"type":"string","title":"Connector Name"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Credentials"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/accounts/gateway/wallets":{"get":{"tags":["Accounts"],"summary":"List Gateway Wallets","description":"List all wallets managed by Gateway.\nGateway manages its own encrypted wallet storage.\n\nReturns:\n List of wallet information from Gateway\n\nRaises:\n HTTPException: 503 if Gateway unavailable","operationId":"list_gateway_wallets_accounts_gateway_wallets_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBasic":[]}]}},"/accounts/gateway/add-wallet":{"post":{"tags":["Accounts"],"summary":"Add Gateway Wallet","description":"Add an existing wallet to Gateway using its private key.\nGateway handles encryption and storage internally.\n\nArgs:\n wallet_credential: Wallet credentials (chain, private_key, and optional set_default)\n\nReturns:\n Wallet information from Gateway including address\n\nRaises:\n HTTPException: 503 if Gateway unavailable, 400 on validation error","operationId":"add_gateway_wallet_accounts_gateway_add_wallet_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayWalletCredential"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/accounts/gateway/wallet/set-default":{"post":{"tags":["Accounts"],"summary":"Set Default Gateway Wallet","description":"Set the default wallet for a chain in Gateway.\n\nWhen multiple wallets are configured for a chain, this endpoint allows\nswitching which wallet is used as the default for operations.\n\nArgs:\n request: Contains chain and wallet address to set as default\n\nReturns:\n Dict with success status and updated wallet info.\n\nExample: POST /accounts/gateway/wallet/set-default\n{\n \"chain\": \"solana\",\n \"address\": \"82SggYRE2Vo4jN4a2pk3aQ4SET4ctafZJGbowmCqyHx5\"\n}","operationId":"set_default_gateway_wallet_accounts_gateway_wallet_set_default_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetDefaultWalletRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Set Default Gateway Wallet Accounts Gateway Wallet Set Default Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/accounts/gateway/{chain}/{address}":{"delete":{"tags":["Accounts"],"summary":"Remove Gateway Wallet","description":"Remove a wallet from Gateway.\n\nArgs:\n chain: Blockchain chain (e.g., 'solana', 'ethereum')\n address: Wallet address to remove\n\nReturns:\n Success message\n\nRaises:\n HTTPException: 503 if Gateway unavailable","operationId":"remove_gateway_wallet_accounts_gateway__chain___address__delete","security":[{"HTTPBasic":[]}],"parameters":[{"name":"chain","in":"path","required":true,"schema":{"type":"string","title":"Chain"}},{"name":"address","in":"path","required":true,"schema":{"type":"string","title":"Address"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/connectors/":{"get":{"tags":["Connectors"],"summary":"Available Connectors","description":"Get a list of all available connectors.\n\nReturns:\n List of connector names supported by the system (excludes DEX providers which use Gateway networks)","operationId":"available_connectors_connectors__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"type":"string"},"type":"array","title":"Response Available Connectors Connectors Get"}}}}},"security":[{"HTTPBasic":[]}]}},"/connectors/{connector_name}/config-map":{"get":{"tags":["Connectors"],"summary":"Get Connector Config Map","description":"Get configuration fields required for a specific connector with type information.\n\nArgs:\n connector_name: Name of the connector to get config map for\n\nReturns:\n Dictionary mapping field names to their type information.\n Each field contains:\n - type: The expected data type (e.g., \"str\", \"SecretStr\", \"int\")\n - required: Whether the field is required","operationId":"get_connector_config_map_connectors__connector_name__config_map_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"connector_name","in":"path","required":true,"schema":{"type":"string","title":"Connector Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"object","additionalProperties":true},"title":"Response Get Connector Config Map Connectors Connector Name Config Map Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/connectors/{connector_name}/trading-rules":{"get":{"tags":["Connectors"],"summary":"Get Trading Rules","description":"Get trading rules for a connector, optionally filtered by trading pairs.\n\nThis endpoint uses the MarketDataService to access non-trading connector instances,\nwhich means no authentication or account setup is required.\n\nArgs:\n request: FastAPI request object\n connector_name: Name of the connector (e.g., 'binance', 'binance_perpetual')\n trading_pairs: Optional list of trading pairs to filter by (e.g., ['BTC-USDT', 'ETH-USDT'])\n\nReturns:\n Dictionary mapping trading pairs to their trading rules\n\nRaises:\n HTTPException: 404 if connector not found, 500 for other errors","operationId":"get_trading_rules_connectors__connector_name__trading_rules_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"connector_name","in":"path","required":true,"schema":{"type":"string","title":"Connector Name"}},{"name":"trading_pairs","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Filter by specific trading pairs","title":"Trading Pairs"},"description":"Filter by specific trading pairs"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/connectors/{connector_name}/order-types":{"get":{"tags":["Connectors"],"summary":"Get Supported Order Types","description":"Get order types supported by a specific connector.\n\nThis endpoint uses the MarketDataService to access non-trading connector instances,\nwhich means no authentication or account setup is required.\n\nArgs:\n request: FastAPI request object\n connector_name: Name of the connector (e.g., 'binance', 'binance_perpetual')\n\nReturns:\n List of supported order types (LIMIT, MARKET, LIMIT_MAKER)\n\nRaises:\n HTTPException: 404 if connector not found, 500 for other errors","operationId":"get_supported_order_types_connectors__connector_name__order_types_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"connector_name","in":"path","required":true,"schema":{"type":"string","title":"Connector Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/portfolio/state":{"post":{"tags":["Portfolio"],"summary":"Get Portfolio State","description":"Get the current state of all or filtered accounts portfolio.\n\nArgs:\n filter_request: JSON payload with filtering criteria including:\n - account_names: Optional list of account names to filter by\n - connector_names: Optional list of connector names to filter by\n - skip_gateway: If True, skip Gateway wallet balance updates for faster CEX-only queries\n - refresh: If True, refresh balances from exchanges. If False, return cached state.\n\nReturns:\n Dict containing account states with connector balances and token information","operationId":"get_portfolio_state_portfolio_state_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortfolioStateFilterRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"additionalProperties":{"items":{"additionalProperties":true,"type":"object"},"type":"array"},"type":"object"},"type":"object","title":"Response Get Portfolio State Portfolio State Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/portfolio/history":{"post":{"tags":["Portfolio"],"summary":"Get Portfolio History","description":"Get the historical state of all or filtered accounts portfolio with pagination and interval sampling.\n\nThe interval parameter allows you to control data granularity:\n- 5m: Raw data (default, collected every 5 minutes)\n- 15m: One data point every 15 minutes\n- 30m: One data point every 30 minutes\n- 1h: One data point every hour\n- 4h: One data point every 4 hours\n- 12h: One data point every 12 hours\n- 1d: One data point every day\n\nUsing larger intervals significantly reduces response size and improves performance.\n\nArgs:\n filter_request: JSON payload with filtering criteria (account_names, connector_names,\n start_time, end_time, limit, cursor, interval)\n\nReturns:\n Paginated response with historical portfolio data sampled at the requested interval","operationId":"get_portfolio_history_portfolio_history_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortfolioHistoryFilterRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/portfolio/distribution":{"post":{"tags":["Portfolio"],"summary":"Get Portfolio Distribution","description":"Get portfolio distribution by tokens with percentages across all or filtered accounts.\n\nArgs:\n filter_request: JSON payload with filtering criteria\n \nReturns:\n Dictionary with token distribution including percentages, values, and breakdown by accounts/connectors","operationId":"get_portfolio_distribution_portfolio_distribution_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortfolioDistributionFilterRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/portfolio/accounts-distribution":{"get":{"tags":["Portfolio"],"summary":"Get Accounts Distribution","description":"Get portfolio distribution by accounts with percentages.\n\nReturns:\n Dictionary with account distribution including percentages, values, and breakdown by connectors","operationId":"get_accounts_distribution_portfolio_accounts_distribution_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBasic":[]}]}},"/trading/orders":{"post":{"tags":["Trading"],"summary":"Place Trade","description":"Place a buy or sell order using a specific account and connector.\n\nArgs:\n trade_request: Trading request with account, connector, trading pair, type, amount, etc.\n accounts_service: Injected accounts service\n\nReturns:\n TradeResponse with order ID and trading details\n\nRaises:\n HTTPException: 400 for invalid parameters, 404 for account/connector not found, 500 for trade execution errors","operationId":"place_trade_trading_orders_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/trading/{account_name}/{connector_name}/orders/{client_order_id}/cancel":{"post":{"tags":["Trading"],"summary":"Cancel Order","description":"Cancel a specific order by its client order ID.\n\nArgs:\n account_name: Name of the account\n connector_name: Name of the connector\n client_order_id: Client order ID to cancel\n trading_pair: Trading pair for the order\n accounts_service: Injected accounts service\n\nReturns:\n Success message with cancelled order ID\n\nRaises:\n HTTPException: 404 if account/connector not found, 500 for cancellation errors","operationId":"cancel_order_trading__account_name___connector_name__orders__client_order_id__cancel_post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"account_name","in":"path","required":true,"schema":{"type":"string","title":"Account Name"}},{"name":"connector_name","in":"path","required":true,"schema":{"type":"string","title":"Connector Name"}},{"name":"client_order_id","in":"path","required":true,"schema":{"type":"string","title":"Client Order Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/trading/positions":{"post":{"tags":["Trading"],"summary":"Get Positions","description":"Get current positions across all or filtered perpetual connectors.\n\nThis endpoint fetches real-time position data directly from the connectors,\nincluding unrealized PnL, leverage, funding fees, and margin information.\n\nArgs:\n filter_request: JSON payload with filtering criteria\n\nReturns:\n Paginated response with position data and pagination metadata\n\nRaises:\n HTTPException: 500 if there's an error fetching positions","operationId":"get_positions_trading_positions_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PositionFilterRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/trading/orders/active":{"post":{"tags":["Trading"],"summary":"Get Active Orders","description":"Get active (in-flight) orders across all or filtered accounts and connectors.\n\nThis endpoint fetches real-time active orders directly from the connectors' in_flight_orders property,\nproviding current order status, fill amounts, and other live order data.\n\nArgs:\n filter_request: JSON payload with filtering criteria\n\nReturns:\n Paginated response with active order data and pagination metadata\n\nRaises:\n HTTPException: 500 if there's an error fetching orders","operationId":"get_active_orders_trading_orders_active_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActiveOrderFilterRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/trading/orders/search":{"post":{"tags":["Trading"],"summary":"Get Orders","description":"Get historical order data across all or filtered accounts from the database/registry.\n\nArgs:\n filter_request: JSON payload with filtering criteria\n\nReturns:\n Paginated response with historical order data and pagination metadata","operationId":"get_orders_trading_orders_search_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderFilterRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/trading/trades":{"post":{"tags":["Trading"],"summary":"Get Trades","description":"Get trade history across all or filtered accounts with complex filtering.\n\nArgs:\n filter_request: JSON payload with filtering criteria\n\nReturns:\n Paginated response with trade data and pagination metadata","operationId":"get_trades_trading_trades_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeFilterRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/trading/{account_name}/{connector_name}/position-mode":{"post":{"tags":["Trading"],"summary":"Set Position Mode","description":"Set position mode for a perpetual connector.\n\nArgs:\n account_name: Name of the account\n connector_name: Name of the perpetual connector\n position_mode: Position mode to set (HEDGE or ONEWAY)\n\nReturns:\n Success message with status\n\nRaises:\n HTTPException: 400 if not a perpetual connector or invalid position mode","operationId":"set_position_mode_trading__account_name___connector_name__position_mode_post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"account_name","in":"path","required":true,"schema":{"type":"string","title":"Account Name"}},{"name":"connector_name","in":"path","required":true,"schema":{"type":"string","title":"Connector Name"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PositionModeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Trading"],"summary":"Get Position Mode","description":"Get current position mode for a perpetual connector.\n\nArgs:\n account_name: Name of the account\n connector_name: Name of the perpetual connector\n\nReturns:\n Dictionary with current position mode, connector name, and account name\n\nRaises:\n HTTPException: 400 if not a perpetual connector","operationId":"get_position_mode_trading__account_name___connector_name__position_mode_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"account_name","in":"path","required":true,"schema":{"type":"string","title":"Account Name"}},{"name":"connector_name","in":"path","required":true,"schema":{"type":"string","title":"Connector Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/trading/{account_name}/{connector_name}/leverage":{"post":{"tags":["Trading"],"summary":"Set Leverage","description":"Set leverage for a specific trading pair on a perpetual connector.\n\nArgs:\n account_name: Name of the account\n connector_name: Name of the perpetual connector\n request: Leverage request with trading pair and leverage value\n accounts_service: Injected accounts service\n\nReturns:\n Dictionary with success status and message\n\nRaises:\n HTTPException: 400 for invalid parameters or non-perpetual connector, 404 for account/connector not found, 500 for execution errors","operationId":"set_leverage_trading__account_name___connector_name__leverage_post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"account_name","in":"path","required":true,"schema":{"type":"string","title":"Account Name"}},{"name":"connector_name","in":"path","required":true,"schema":{"type":"string","title":"Connector Name"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeverageRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/trading/funding-payments":{"post":{"tags":["Trading"],"summary":"Get Funding Payments","description":"Get funding payment history across all or filtered perpetual connectors.\n\nThis endpoint retrieves historical funding payment records including\nfunding rates, payment amounts, and position data at time of payment.\n\nArgs:\n filter_request: JSON payload with filtering criteria\n\nReturns:\n Paginated response with funding payment data and pagination metadata\n\nRaises:\n HTTPException: 500 if there's an error fetching funding payments","operationId":"get_funding_payments_trading_funding_payments_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FundingPaymentFilterRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/swap/quote":{"post":{"tags":["Gateway Swaps"],"summary":"Get Swap Quote","description":"Get a price quote for a swap.\n\nExample:\n connector: 'jupiter' (or typed: 'jupiter/router', 'meteora/clmm', 'raydium/amm')\n network: 'solana-mainnet-beta'\n trading_pair: 'SOL-USDC'\n side: 'BUY'\n amount: 1\n slippage_pct: 1 (optional; omit to use the connector's configured slippagePct)\n extra_params: {\"approximateIfNoExactOut\": false} # Solana routers\n\nReturns:\n Quote with price, expected output amount, and execution-safety fields","operationId":"get_swap_quote_gateway_swap_quote_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SwapQuoteRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SwapQuoteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/swap/execute":{"post":{"tags":["Gateway Swaps"],"summary":"Execute Swap","description":"Execute a swap transaction via router (Jupiter, 0x).\n\nExample:\n connector: 'jupiter'\n network: 'solana-mainnet-beta'\n trading_pair: 'SOL-USDC'\n side: 'BUY'\n amount: 1\n slippage_pct: 1 (optional; omit to use the connector's configured slippagePct)\n wallet_address: (optional, uses default if not provided)\n extra_params: {\"approximateIfNoExactOut\": false} # Solana routers\n\nReturns:\n Transaction hash and swap details","operationId":"execute_swap_gateway_swap_execute_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SwapExecuteRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SwapExecuteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/swaps/{transaction_hash}/status":{"get":{"tags":["Gateway Swaps"],"summary":"Get Swap Status","description":"Get status of a specific swap by transaction hash.\n\nArgs:\n transaction_hash: Transaction hash of the swap\n\nReturns:\n Swap details including current status","operationId":"get_swap_status_gateway_swaps__transaction_hash__status_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"transaction_hash","in":"path","required":true,"schema":{"type":"string","title":"Transaction Hash"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/gateway/swap/execute-quote":{"post":{"tags":["Gateway Swaps"],"summary":"Execute Swap Quote","description":"Execute a quote returned by /swap/quote, by its quote_id.\n\nThe two-step flow: quote, decide, then commit to THAT quote. /swap/execute prices\nagain at execution, which discards the price the caller saw \u2014 the whole reason dflow,\ntitan and 0x return a held quote. Router connectors only; a pool-scoped connector has\nno cached quote to execute and is rejected rather than quietly re-priced.\n\nExample:\n connector: 'jupiter'\n network: 'solana-mainnet-beta'\n quote_id: ''\n trading_pair: 'SOL-USDC'\n side: 'SELL'\n amount: 0.01\n\nReturns:\n Transaction hash and what the swap actually moved","operationId":"execute_swap_quote_gateway_swap_execute_quote_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SwapExecuteQuoteRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SwapExecuteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/swaps/search":{"post":{"tags":["Gateway Swaps"],"summary":"Search Swaps","description":"Search swap history with filters.\n\nArgs:\n network: Filter by network (e.g., 'solana-mainnet-beta')\n connector: Filter by connector (e.g., 'jupiter')\n wallet_address: Filter by wallet address\n trading_pair: Filter by trading pair (e.g., 'SOL-USDC')\n status: Filter by status (SUBMITTED, CONFIRMED, FAILED)\n start_time: Start timestamp (unix seconds)\n end_time: End timestamp (unix seconds)\n limit: Max results (default 50, max 1000)\n offset: Pagination offset\n\nReturns:\n Paginated list of swaps","operationId":"search_swaps_gateway_swaps_search_post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"network","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Network"}},{"name":"connector","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Connector"}},{"name":"wallet_address","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Address"}},{"name":"trading_pair","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trading Pair"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"start_time","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Start Time"}},{"name":"end_time","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"End Time"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/gateway/swaps/summary":{"get":{"tags":["Gateway Swaps"],"summary":"Get Swaps Summary","description":"Get swap summary statistics.\n\nArgs:\n network: Filter by network\n wallet_address: Filter by wallet address\n start_time: Start timestamp (unix seconds)\n end_time: End timestamp (unix seconds)\n\nReturns:\n Summary statistics including volume, fees, success rate","operationId":"get_swaps_summary_gateway_swaps_summary_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"network","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Network"}},{"name":"wallet_address","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Address"}},{"name":"start_time","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Start Time"}},{"name":"end_time","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"End Time"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/gateway/clmm/pool-info":{"get":{"tags":["Gateway CLMM"],"summary":"Get Clmm Pool Info","description":"Get detailed information about a CLMM pool by pool address.\n\nArgs:\n connector: CLMM connector (e.g., 'meteora', 'raydium')\n network: Network ID in 'chain-network' format (e.g., 'solana-mainnet-beta')\n pool_address: Pool contract address\n bin_count: If > 0, include the per-tick liquidity distribution (`bins`)\n around the active tick. Meteora always returns its bins and ignores\n this; orca, raydium, uniswap and pancakeswap honour it.\n\nExample:\n GET /gateway/clmm/pool-info?connector=meteora&network=solana-mainnet-beta\n &pool_address=2sf5NYcY4zUPXUSmG6f66mskb24t5F8S11pC1Nz5nQT3\n\nReturns:\n Pool information including liquidity, price, bins (for Meteora), etc.\n All field names are returned in snake_case format.","operationId":"get_clmm_pool_info_gateway_clmm_pool_info_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"connector","in":"query","required":true,"schema":{"type":"string","title":"Connector"}},{"name":"network","in":"query","required":true,"schema":{"type":"string","title":"Network"}},{"name":"pool_address","in":"query","required":true,"schema":{"type":"string","title":"Pool Address"}},{"name":"bin_count","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Bin Count"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CLMMPoolInfoResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/gateway/clmm/pools":{"get":{"tags":["Gateway CLMM"],"summary":"Get Clmm Pools","description":"Get list of available CLMM pools for a connector via Gateway.\n\nSupports: meteora, orca\n\nArgs:\n connector: CLMM connector (meteora, orca)\n network: Solana network name (bare, default 'mainnet-beta')\n page: Page number (default: 0)\n limit: Results per page (default: 50, max: 100)\n search_term: Search query to filter pools (optional)\n sort_key: Sort by field (volume, tvl, etc.)\n order_by: Sort order (asc, desc)\n include_unknown: Include pools with unverified tokens\n\nExample:\n GET /gateway/clmm/pools?connector=meteora&search_term=SOL&limit=20\n\nReturns:\n List of available pools with trading pairs, addresses, liquidity, volume, APR, etc.","operationId":"get_clmm_pools_gateway_clmm_pools_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"connector","in":"query","required":true,"schema":{"type":"string","title":"Connector"}},{"name":"network","in":"query","required":false,"schema":{"type":"string","description":"Solana network name (bare, e.g. 'mainnet-beta'); meteora/orca are Solana-only","default":"mainnet-beta","title":"Network"},"description":"Solana network name (bare, e.g. 'mainnet-beta'); meteora/orca are Solana-only"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Page number","default":0,"title":"Page"},"description":"Page number"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Results per page (max 100)","default":50,"title":"Limit"},"description":"Results per page (max 100)"},{"name":"search_term","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Search query to filter pools","title":"Search Term"},"description":"Search query to filter pools"},{"name":"sort_key","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Sort key (volume, tvl, etc.)","default":"volume","title":"Sort Key"},"description":"Sort key (volume, tvl, etc.)"},{"name":"order_by","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Sort order (asc, desc)","default":"desc","title":"Order By"},"description":"Sort order (asc, desc)"},{"name":"include_unknown","in":"query","required":false,"schema":{"type":"boolean","description":"Include pools with unverified tokens","default":true,"title":"Include Unknown"},"description":"Include pools with unverified tokens"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CLMMPoolListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/gateway/clmm/open":{"post":{"tags":["Gateway CLMM"],"summary":"Open Clmm Position","description":"Open a NEW CLMM position with initial liquidity.\n\nExample:\n connector: 'meteora'\n network: 'solana-mainnet-beta'\n pool_address: '2sf5NYcY4zUPXUSmG6f66mskb24t5F8S11pC1Nz5nQT3'\n lower_price: 150\n upper_price: 250\n base_token_amount: 0.01\n quote_token_amount: 2\n slippage_pct: 1 (optional; omit to use the connector's configured slippagePct)\n wallet_address: (optional)\n extra_params: {\"strategyType\": 0} # Meteora-specific\n\nReturns:\n Transaction hash and position address. position_address is None when the\n transaction was submitted but not yet confirmed \u2014 poll the transaction; the\n poller's discovery sweep records the position once it lands on-chain.","operationId":"open_clmm_position_gateway_clmm_open_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CLMMOpenPositionRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CLMMOpenPositionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/clmm/add":{"post":{"tags":["Gateway CLMM"],"summary":"Add Liquidity To Clmm Position","description":"Add MORE liquidity to an EXISTING CLMM position.\n\nExample:\n connector: 'meteora'\n network: 'solana-mainnet-beta'\n position_address: '...'\n base_token_amount: 0.5\n quote_token_amount: 50.0\n slippage_pct: 1 (optional; omit to use the connector's configured slippagePct)\n wallet_address: (optional)\n extra_params: {\"strategyType\": 0} # Meteora-specific\n\nReturns:\n Transaction hash","operationId":"add_liquidity_to_clmm_position_gateway_clmm_add_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CLMMAddLiquidityRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/clmm/remove":{"post":{"tags":["Gateway CLMM"],"summary":"Remove Liquidity From Clmm Position","description":"Remove SOME liquidity from a CLMM position (partial removal).\n\nExample:\n connector: 'meteora'\n network: 'solana-mainnet-beta'\n position_address: '...'\n percentage_to_remove: 50\n slippage_pct: 1 (optional; Orca only \u2014 other connectors ignore it)\n wallet_address: (optional)\n\nReturns:\n Transaction hash","operationId":"remove_liquidity_from_clmm_position_gateway_clmm_remove_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CLMMRemoveLiquidityRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/clmm/close":{"post":{"tags":["Gateway CLMM"],"summary":"Close Clmm Position","description":"CLOSE a CLMM position completely (removes all liquidity and collects pending fees).\n\nExample:\n connector: 'meteora'\n network: 'solana-mainnet-beta'\n position_address: '...'\n wallet_address: (optional)\n\nReturns:\n Transaction hash and collected fee amounts","operationId":"close_clmm_position_gateway_clmm_close_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CLMMClosePositionRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CLMMClosePositionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/clmm/collect-fees":{"post":{"tags":["Gateway CLMM"],"summary":"Collect Fees From Clmm Position","description":"Collect accumulated fees from a CLMM liquidity position.\n\nExample:\n connector: 'meteora'\n network: 'solana-mainnet-beta'\n position_address: '...'\n wallet_address: (optional)\n\nReturns:\n Transaction hash and collected fee amounts","operationId":"collect_fees_from_clmm_position_gateway_clmm_collect_fees_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CLMMCollectFeesRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CLMMCollectFeesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/clmm/positions_owned":{"post":{"tags":["Gateway CLMM"],"summary":"Get Clmm Positions Owned","description":"Get all CLMM liquidity positions owned by a wallet.\n\nMirrors Gateway's /trading/clmm/positions-owned, which takes no pool filter:\nevery CLMM position the wallet owns on the connector is returned, each row\ncarrying its own pool_address. (The old pool_address request field was a\nsilent no-op \u2014 Gateway never read it and the response was never filtered.)\n\nExample:\n connector: 'meteora'\n network: 'solana-mainnet-beta'\n wallet_address: (optional, uses default if not provided)\n\nReturns:\n List of CLMM position information","operationId":"get_clmm_positions_owned_gateway_clmm_positions_owned_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CLMMPositionsOwnedRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/CLMMPositionInfo"},"type":"array","title":"Response Get Clmm Positions Owned Gateway Clmm Positions Owned Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/clmm/quote-position":{"post":{"tags":["Gateway CLMM"],"summary":"Quote Clmm Position","description":"Quote a candidate CLMM position before opening or adding liquidity.\n\nMirrors Gateway's GET /trading/clmm/quote-position: returns the base/quote\nsplit the pool would actually take for the given range and deposit amounts\n(and which side limits it), without signing or submitting anything.","operationId":"quote_clmm_position_gateway_clmm_quote_position_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CLMMQuotePositionRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CLMMQuotePositionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/clmm/create-pool":{"post":{"tags":["Gateway CLMM"],"summary":"Create Clmm Pool","description":"Create a new (empty) CLMM pool \u2014 liquidity is added afterwards by opening positions.\n\nMirrors Gateway's POST /trading/clmm/create-pool (which shares the AMM\ncreate-pool response shape). Connector-specific params ride extra_params\nunder Gateway's own names \u2014 the same contract as open's extra_params.","operationId":"create_clmm_pool_gateway_clmm_create_pool_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CLMMCreatePoolRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMMCreatePoolResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/clmm/position-info":{"get":{"tags":["Gateway CLMM"],"summary":"Get Clmm Position Info","description":"Get a single CLMM position by its address.\n\nMirrors Gateway's GET /trading/clmm/position-info. Gateway reports a missing\nor closed position as an error (500/404), surfaced here as 404.","operationId":"get_clmm_position_info_gateway_clmm_position_info_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"connector","in":"query","required":true,"schema":{"type":"string","title":"Connector"}},{"name":"network","in":"query","required":true,"schema":{"type":"string","title":"Network"}},{"name":"position_address","in":"query","required":true,"schema":{"type":"string","title":"Position Address"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CLMMPositionInfo"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/gateway/clmm/positions/{position_address}/events":{"get":{"tags":["Gateway CLMM"],"summary":"Get Clmm Position Events","description":"Get event history for a CLMM position.\n\nArgs:\n position_address: Position NFT address\n event_type: Filter by event type (OPEN, ADD_LIQUIDITY, REMOVE_LIQUIDITY, COLLECT_FEES, CLOSE,\n DISCOVERED \u2014 written by the poller for positions it found on-chain, with a\n synthetic discovered_ transaction hash)\n limit: Max events to return\n\nReturns:\n List of position events","operationId":"get_clmm_position_events_gateway_clmm_positions__position_address__events_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"position_address","in":"path","required":true,"schema":{"type":"string","title":"Position Address"}},{"name":"event_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Event Type"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/gateway/clmm/positions/search":{"post":{"tags":["Gateway CLMM"],"summary":"Search Clmm Positions","description":"Search CLMM positions with filters.\n\nArgs:\n network: Filter by network (e.g., 'solana-mainnet-beta')\n connector: Filter by connector (e.g., 'meteora')\n wallet_address: Filter by wallet address\n trading_pair: Filter by trading pair (e.g., 'SOL-USDC'; a token outside Gateway's\n token list is stored under its full mint address instead of a symbol)\n status: Filter by status (OPEN, CLOSED)\n position_addresses: Filter by specific position addresses (list of addresses)\n limit: Max results (default 50, max 1000)\n offset: Pagination offset\n refresh: If True, refresh position data from Gateway before returning (default False)\n\nReturns:\n Paginated list of positions","operationId":"search_clmm_positions_gateway_clmm_positions_search_post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"network","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Network"}},{"name":"connector","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Connector"}},{"name":"wallet_address","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Address"}},{"name":"trading_pair","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trading Pair"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"position_addresses","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"title":"Position Addresses"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Refresh"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/gateway/amm/pool-info":{"get":{"tags":["Gateway AMM"],"summary":"Get Amm Pool Info","description":"Get AMM pool information (reserves, price, base fee) by pool address.","operationId":"get_amm_pool_info_gateway_amm_pool_info_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"connector","in":"query","required":true,"schema":{"type":"string","title":"Connector"}},{"name":"network","in":"query","required":true,"schema":{"type":"string","title":"Network"}},{"name":"pool_address","in":"query","required":true,"schema":{"type":"string","title":"Pool Address"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMMPoolInfoResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/gateway/amm/position-info":{"get":{"tags":["Gateway AMM"],"summary":"Get Amm Position Info","description":"Get a wallet's aggregate liquidity in an AMM pool plus a per-position breakdown (DAMM v2).","operationId":"get_amm_position_info_gateway_amm_position_info_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"connector","in":"query","required":true,"schema":{"type":"string","title":"Connector"}},{"name":"network","in":"query","required":true,"schema":{"type":"string","title":"Network"}},{"name":"pool_address","in":"query","required":true,"schema":{"type":"string","title":"Pool Address"}},{"name":"wallet_address","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Address"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMMPositionInfoResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/gateway/amm/positions-owned":{"post":{"tags":["Gateway AMM"],"summary":"Get Amm Positions Owned","description":"List all of a wallet's AMM positions across pools (Meteora DAMM v2 only).\n\nFungible-LP AMMs (raydium, uniswap, pancakeswap) have no enumerable positions; Gateway rejects\nthem with a 400, surfaced here unchanged. Use position-info with a specific pool address instead.","operationId":"get_amm_positions_owned_gateway_amm_positions_owned_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMMPositionsOwnedRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/AMMPositionInfoResponse"},"type":"array","title":"Response Get Amm Positions Owned Gateway Amm Positions Owned Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/amm/quote-liquidity":{"post":{"tags":["Gateway AMM"],"summary":"Quote Amm Liquidity","description":"Quote a two-sided liquidity deposit.","operationId":"quote_amm_liquidity_gateway_amm_quote_liquidity_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMMQuoteLiquidityRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMMQuoteLiquidityResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/amm/add-liquidity":{"post":{"tags":["Gateway AMM"],"summary":"Add Amm Liquidity","description":"Add two-sided liquidity to an AMM pool.\n\nMeteora DAMM v2: pass position_address to add to that NFT position; omit it to open a new one.\nFungible-LP AMMs ignore position_address.","operationId":"add_amm_liquidity_gateway_amm_add_liquidity_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMMAddLiquidityRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMMTransactionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/amm/remove-liquidity":{"post":{"tags":["Gateway AMM"],"summary":"Remove Amm Liquidity","description":"Remove liquidity from an AMM pool.\n\nMeteora DAMM v2 requires position_address (positions are NFTs); Gateway rejects a missing one\nwith a 400, surfaced here unchanged, so \"remove 100%\" is a true exit of the named position.","operationId":"remove_amm_liquidity_gateway_amm_remove_liquidity_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMMRemoveLiquidityRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMMTransactionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/amm/create-pool":{"post":{"tags":["Gateway AMM"],"summary":"Create Amm Pool","description":"Create and seed a new AMM pool.\n\nSeed price priority: initial_price \u2192 quote_token_amount ratio \u2192 live market price (anti-snipe).\nConnector-specific params ride extra_params under Gateway's own names (configAddress for\nmeteora \u2014 required there, ammConfigIndex for raydium) \u2014 the same contract as clmm open's\nextra_params. Seeding slippage for uniswap/pancakeswap is the standard slippage_pct field.","operationId":"create_amm_pool_gateway_amm_create_pool_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMMCreatePoolRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMMCreatePoolResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/gateway/amm/events/search":{"post":{"tags":["Gateway AMM"],"summary":"Search Amm Events","description":"Search recorded AMM liquidity writes, newest first.\n\nThis is the AMM history: ADD_LIQUIDITY, REMOVE_LIQUIDITY and CREATE_POOL with their\non-chain amounts and gas. Current holdings are not here \u2014 read those live from\n/gateway/amm/position-info, which is the only authority on them.","operationId":"search_amm_events_gateway_amm_events_search_post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"connector","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Connector"}},{"name":"network","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Network"}},{"name":"wallet_address","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Address"}},{"name":"pool_address","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pool Address"}},{"name":"event_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Event Type"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/gateway/amm/positions/search":{"post":{"tags":["Gateway AMM"],"summary":"Search Amm Positions","description":"Search tracked AMM positions (Meteora DAMM v2 NFTs), newest first.\n\nFungible-LP AMMs never appear here \u2014 they have no position identity. Their holdings\ncome from /gateway/amm/position-info and their history from /gateway/amm/events/search.","operationId":"search_amm_positions_gateway_amm_positions_search_post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"connector","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Connector"}},{"name":"network","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Network"}},{"name":"wallet_address","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Address"}},{"name":"pool_address","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pool Address"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bot-orchestration/status":{"get":{"tags":["Bot Orchestration"],"summary":"Get Active Bots Status","description":"Get the status of all active bots.\n\nArgs:\n bots_manager: Bot orchestrator service dependency\n\nReturns:\n Dictionary with status and data containing all active bot statuses","operationId":"get_active_bots_status_bot_orchestration_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBasic":[]}]}},"/bot-orchestration/mqtt":{"get":{"tags":["Bot Orchestration"],"summary":"Get Mqtt Status","description":"Get MQTT connection status and discovered bots.\n\nArgs:\n bots_manager: Bot orchestrator service dependency\n\nReturns:\n Dictionary with MQTT connection status, discovered bots, and broker information","operationId":"get_mqtt_status_bot_orchestration_mqtt_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBasic":[]}]}},"/bot-orchestration/controller-performance-latest":{"get":{"tags":["Bot Orchestration"],"summary":"Get Latest Controller Performance","description":"Get the most recent performance snapshot for each bot/controller.\nOptionally filter by bot_name.","operationId":"get_latest_controller_performance_bot_orchestration_controller_performance_latest_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"bot_name","in":"query","required":false,"schema":{"type":"string","title":"Bot Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bot-orchestration/controller-performance-history":{"get":{"tags":["Bot Orchestration"],"summary":"Get Controller Performance History","description":"Get historical controller performance snapshots with pagination and interval sampling.","operationId":"get_controller_performance_history_bot_orchestration_controller_performance_history_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"bot_name","in":"query","required":false,"schema":{"type":"string","title":"Bot Name"}},{"name":"controller_id","in":"query","required":false,"schema":{"type":"string","title":"Controller Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"default":100,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","title":"Cursor"}},{"name":"start_time","in":"query","required":false,"schema":{"type":"string","title":"Start Time"}},{"name":"end_time","in":"query","required":false,"schema":{"type":"string","title":"End Time"}},{"name":"interval","in":"query","required":false,"schema":{"type":"string","pattern":"^(5m|15m|30m|1h|4h|12h|1d)$","default":"5m","title":"Interval"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bot-orchestration/{bot_name}/status":{"get":{"tags":["Bot Orchestration"],"summary":"Get Bot Status","description":"Get the status of a specific bot.\n\nArgs:\n bot_name: Name of the bot to get status for\n bots_manager: Bot orchestrator service dependency\n\nReturns:\n Dictionary with bot status information\n\nRaises:\n HTTPException: 404 if bot not found","operationId":"get_bot_status_bot_orchestration__bot_name__status_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"bot_name","in":"path","required":true,"schema":{"type":"string","title":"Bot Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bot-orchestration/{bot_name}/history":{"get":{"tags":["Bot Orchestration"],"summary":"Get Bot History","description":"Get trading history for a bot with optional parameters.\n\nArgs:\n bot_name: Name of the bot to get history for\n days: Number of days of history to retrieve (0 for all)\n verbose: Whether to include verbose output\n precision: Decimal precision for numerical values\n timeout: Timeout in seconds for the operation\n bots_manager: Bot orchestrator service dependency\n\nReturns:\n Dictionary with bot trading history","operationId":"get_bot_history_bot_orchestration__bot_name__history_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"bot_name","in":"path","required":true,"schema":{"type":"string","title":"Bot Name"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Days"}},{"name":"verbose","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Verbose"}},{"name":"precision","in":"query","required":false,"schema":{"type":"integer","title":"Precision"}},{"name":"timeout","in":"query","required":false,"schema":{"type":"number","default":30.0,"title":"Timeout"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bot-orchestration/start-bot":{"post":{"tags":["Bot Orchestration"],"summary":"Start Bot","description":"Start a bot with the specified configuration.\n\nArgs:\n action: StartBotAction containing bot configuration parameters\n bots_manager: Bot orchestrator service dependency\n\nReturns:\n Dictionary with status and response from bot start operation","operationId":"start_bot_bot_orchestration_start_bot_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartBotAction"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/bot-orchestration/stop-bot":{"post":{"tags":["Bot Orchestration"],"summary":"Stop Bot","description":"Stop a bot with the specified configuration.\n\nArgs:\n action: StopBotAction containing bot stop parameters\n bots_manager: Bot orchestrator service dependency\n\nReturns:\n Dictionary with status and response from bot stop operation","operationId":"stop_bot_bot_orchestration_stop_bot_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StopBotAction"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/bot-orchestration/bot-runs":{"get":{"tags":["Bot Orchestration"],"summary":"Get Bot Runs","description":"Get bot runs with optional filtering.\n\nArgs:\n bot_name: Filter by bot name\n account_name: Filter by account name\n strategy_type: Filter by strategy type (script or controller)\n strategy_name: Filter by strategy name\n run_status: Filter by run status (CREATED, RUNNING, STOPPED, ERROR)\n deployment_status: Filter by deployment status (DEPLOYED, FAILED, ARCHIVED)\n limit: Maximum number of results to return\n offset: Number of results to skip\n include_final_status: Include the final status snapshot for each run. Off by\n default because the blob can be ~89 KB per record (~99% of the payload);\n use GET /bot-runs/{bot_run_id} to fetch it for a single run.\n bots_manager: Bot orchestrator service dependency\n\nReturns:\n List of bot runs with their details","operationId":"get_bot_runs_bot_orchestration_bot_runs_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"bot_name","in":"query","required":false,"schema":{"type":"string","title":"Bot Name"}},{"name":"account_name","in":"query","required":false,"schema":{"type":"string","title":"Account Name"}},{"name":"strategy_type","in":"query","required":false,"schema":{"type":"string","title":"Strategy Type"}},{"name":"strategy_name","in":"query","required":false,"schema":{"type":"string","title":"Strategy Name"}},{"name":"run_status","in":"query","required":false,"schema":{"type":"string","title":"Run Status"}},{"name":"deployment_status","in":"query","required":false,"schema":{"type":"string","title":"Deployment Status"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"include_final_status","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Final Status"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bot-orchestration/bot-runs/stats":{"get":{"tags":["Bot Orchestration"],"summary":"Get Bot Run Stats","description":"Get statistics about bot runs.\n\nArgs:\n bots_manager: Bot orchestrator service dependency\n\nReturns:\n Bot run statistics","operationId":"get_bot_run_stats_bot_orchestration_bot_runs_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBasic":[]}]}},"/bot-orchestration/bot-runs/{bot_run_id}":{"get":{"tags":["Bot Orchestration"],"summary":"Get Bot Run By Id","description":"Get a specific bot run by ID.\n\nArgs:\n bot_run_id: ID of the bot run\n bots_manager: Bot orchestrator service dependency\n\nReturns:\n Bot run details\n\nRaises:\n HTTPException: 404 if bot run not found","operationId":"get_bot_run_by_id_bot_orchestration_bot_runs__bot_run_id__get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"bot_run_id","in":"path","required":true,"schema":{"type":"integer","title":"Bot Run Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Bot Orchestration"],"summary":"Delete Bot Run","description":"Delete a bot run record by ID.\n\nArgs:\n bot_run_id: ID of the bot run to delete\n bots_manager: Bot orchestrator service dependency\n\nReturns:\n Confirmation of deletion\n\nRaises:\n HTTPException: 404 if bot run not found","operationId":"delete_bot_run_bot_orchestration_bot_runs__bot_run_id__delete","security":[{"HTTPBasic":[]}],"parameters":[{"name":"bot_run_id","in":"path","required":true,"schema":{"type":"integer","title":"Bot Run Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bot-orchestration/stop-and-archive-bot/{bot_name}":{"post":{"tags":["Bot Orchestration"],"summary":"Stop And Archive Bot","description":"Gracefully stop a bot and archive its data in the background.\nThis initiates a background task that will:\n1. Stop the bot trading process via MQTT\n2. Wait 15 seconds for graceful shutdown\n3. Monitor and stop the Docker container\n4. Archive the bot data (locally or to S3)\n5. Remove the container\n\nReturns immediately with a success message while the process continues in the background.","operationId":"stop_and_archive_bot_bot_orchestration_stop_and_archive_bot__bot_name__post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"bot_name","in":"path","required":true,"schema":{"type":"string","title":"Bot Name"}},{"name":"skip_order_cancellation","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Skip Order Cancellation"}},{"name":"archive_locally","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Archive Locally"}},{"name":"s3_bucket","in":"query","required":false,"schema":{"type":"string","title":"S3 Bucket"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bot-orchestration/deploy-v2-controllers":{"post":{"tags":["Bot Orchestration"],"summary":"Deploy V2 Controllers","description":"Deploy a V2 strategy with controllers by generating the script config and creating the instance.\nThis endpoint simplifies the deployment process for V2 controller strategies.\n\nArgs:\n deployment: V2ControllerDeployment configuration\n docker_manager: Docker service dependency\n\nReturns:\n Dictionary with deployment response and generated configuration details\n\nRaises:\n HTTPException: 500 if deployment fails","operationId":"deploy_v2_controllers_bot_orchestration_deploy_v2_controllers_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2ControllerDeployment"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/bot-orchestration/deploy-v2-script":{"post":{"tags":["Bot Orchestration"],"summary":"Deploy V2 Script","description":"Deploy a V2 script bot with optional script configuration.\nThis endpoint creates and starts a Hummingbot instance running the specified script.\n\nArgs:\n deployment: V2ScriptDeployment configuration containing instance name, credentials,\n optional script name and configuration\n docker_manager: Docker service dependency\n db_manager: Database manager dependency\n\nReturns:\n Dictionary with deployment response including instance details\n\nRaises:\n HTTPException: 500 if deployment fails","operationId":"deploy_v2_script_bot_orchestration_deploy_v2_script_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2ScriptDeployment"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/controllers/":{"get":{"tags":["Controllers"],"summary":"List Controllers","description":"List all controllers organized by type.\n\nDetects both single-file controllers (controller.py) and\npackage-style controllers (controller/controller.py).\n\nReturns:\n Dictionary mapping controller types to lists of controller names","operationId":"list_controllers_controllers__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"type":"object","title":"Response List Controllers Controllers Get"}}}}},"security":[{"HTTPBasic":[]}]}},"/controllers/configs/":{"get":{"tags":["Controllers"],"summary":"List Controller Configs","description":"List all controller configurations with metadata.\n\nReturns:\n List of controller configuration objects with name, controller_name, controller_type, and other metadata","operationId":"list_controller_configs_controllers_configs__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Response List Controller Configs Controllers Configs Get"}}}}},"security":[{"HTTPBasic":[]}]}},"/controllers/configs/{config_name}":{"get":{"tags":["Controllers"],"summary":"Get Controller Config","description":"Get controller configuration by config name.\n\nArgs:\n config_name: Name of the configuration file to retrieve\n\nReturns:\n Dictionary with controller configuration\n\nRaises:\n HTTPException: 404 if configuration not found","operationId":"get_controller_config_controllers_configs__config_name__get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"config_name","in":"path","required":true,"schema":{"type":"string","title":"Config Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Controller Config Controllers Configs Config Name Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Controllers"],"summary":"Create Or Update Controller Config","description":"Create or update controller configuration.\n\nArgs:\n config_name: Name of the configuration file\n config: Configuration dictionary to save\n\nReturns:\n Success message when configuration is saved\n\nRaises:\n HTTPException: 400 if save error occurs","operationId":"create_or_update_controller_config_controllers_configs__config_name__post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"config_name","in":"path","required":true,"schema":{"type":"string","title":"Config Name"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Config"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Controllers"],"summary":"Delete Controller Config","description":"Delete controller configuration.\n\nArgs:\n config_name: Name of the configuration file to delete\n\nReturns:\n Success message when configuration is deleted\n\nRaises:\n HTTPException: 404 if configuration not found","operationId":"delete_controller_config_controllers_configs__config_name__delete","security":[{"HTTPBasic":[]}],"parameters":[{"name":"config_name","in":"path","required":true,"schema":{"type":"string","title":"Config Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/controllers/{controller_type}/{controller_name}":{"get":{"tags":["Controllers"],"summary":"Get Controller","description":"Get controller content by type and name.\n\nSupports both single-file controllers (controller.py) and\npackage-style controllers (controller/controller.py).\n\nArgs:\n controller_type: Type of the controller\n controller_name: Name of the controller\n\nReturns:\n Dictionary with controller name, type, and content\n\nRaises:\n HTTPException: 404 if controller not found","operationId":"get_controller_controllers__controller_type___controller_name__get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"controller_type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ControllerType"}},{"name":"controller_name","in":"path","required":true,"schema":{"type":"string","title":"Controller Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"title":"Response Get Controller Controllers Controller Type Controller Name Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Controllers"],"summary":"Create Or Update Controller","description":"Create or update a controller.\n\nIf controller exists as a package (folder), updates the file inside.\nOtherwise creates/updates as a single file.\n\nArgs:\n controller_type: Type of controller to create/update\n controller_name: Name of the controller (from URL path)\n controller: Controller object with content (and optional type for validation)\n\nReturns:\n Success message when controller is saved\n\nRaises:\n HTTPException: 400 if controller type mismatch or save error","operationId":"create_or_update_controller_controllers__controller_type___controller_name__post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"controller_type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ControllerType"}},{"name":"controller_name","in":"path","required":true,"schema":{"type":"string","title":"Controller Name"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Controller"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Controllers"],"summary":"Delete Controller","description":"Delete a controller.\n\nHandles both single-file and package-style controllers.\n\nArgs:\n controller_type: Type of the controller\n controller_name: Name of the controller to delete\n\nReturns:\n Success message when controller is deleted\n\nRaises:\n HTTPException: 404 if controller not found","operationId":"delete_controller_controllers__controller_type___controller_name__delete","security":[{"HTTPBasic":[]}],"parameters":[{"name":"controller_type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ControllerType"}},{"name":"controller_name","in":"path","required":true,"schema":{"type":"string","title":"Controller Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/controllers/{controller_type}/{controller_name}/config/template":{"get":{"tags":["Controllers"],"summary":"Get Controller Config Template","description":"Get controller configuration template with default values.\n\nArgs:\n controller_type: Type of the controller\n controller_name: Name of the controller\n\nReturns:\n Dictionary with configuration template and default values\n\nRaises:\n HTTPException: 404 if controller configuration class not found","operationId":"get_controller_config_template_controllers__controller_type___controller_name__config_template_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"controller_type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ControllerType"}},{"name":"controller_name","in":"path","required":true,"schema":{"type":"string","title":"Controller Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/controllers/{controller_type}/{controller_name}/config/validate":{"post":{"tags":["Controllers"],"summary":"Validate Controller Config","description":"Validate controller configuration against the controller's config class.\n\nArgs:\n controller_type: Type of the controller\n controller_name: Name of the controller\n config: Configuration dictionary to validate\n\nReturns:\n Success message if configuration is valid\n\nRaises:\n HTTPException: 400 if validation fails","operationId":"validate_controller_config_controllers__controller_type___controller_name__config_validate_post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"controller_type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ControllerType"}},{"name":"controller_name","in":"path","required":true,"schema":{"type":"string","title":"Controller Name"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Config"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/controllers/bots/{bot_name}/configs":{"get":{"tags":["Controllers"],"summary":"Get Bot Controller Configs","description":"Get all controller configurations for a specific bot.\n\nArgs:\n bot_name: Name of the bot to get configurations for\n\nReturns:\n List of controller configurations for the bot\n\nRaises:\n HTTPException: 404 if bot not found","operationId":"get_bot_controller_configs_controllers_bots__bot_name__configs_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"bot_name","in":"path","required":true,"schema":{"type":"string","title":"Bot Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true},"title":"Response Get Bot Controller Configs Controllers Bots Bot Name Configs Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/controllers/bots/{bot_name}/{controller_name}/config":{"post":{"tags":["Controllers"],"summary":"Update Bot Controller Config","description":"Update controller configuration for a specific bot.\n\nArgs:\n bot_name: Name of the bot\n controller_name: Name of the controller to update\n config: Configuration dictionary to update with\n\nReturns:\n Success message when configuration is updated\n\nRaises:\n HTTPException: 404 if bot or controller not found, 400 if update error","operationId":"update_bot_controller_config_controllers_bots__bot_name___controller_name__config_post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"bot_name","in":"path","required":true,"schema":{"type":"string","title":"Bot Name"}},{"name":"controller_name","in":"path","required":true,"schema":{"type":"string","title":"Controller Name"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Config"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/scripts/":{"get":{"tags":["Scripts"],"summary":"List Scripts","description":"List all available scripts.\n\nReturns:\n List of script names (without .py extension)","operationId":"list_scripts_scripts__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"type":"string"},"type":"array","title":"Response List Scripts Scripts Get"}}}}},"security":[{"HTTPBasic":[]}]}},"/scripts/configs/":{"get":{"tags":["Scripts"],"summary":"List Script Configs","description":"List all script configurations with metadata.\n\nReturns:\n List of script configuration objects with name, script_file_name, and other metadata","operationId":"list_script_configs_scripts_configs__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Response List Script Configs Scripts Configs Get"}}}}},"security":[{"HTTPBasic":[]}]}},"/scripts/configs/{config_name}":{"get":{"tags":["Scripts"],"summary":"Get Script Config","description":"Get script configuration by config name.\n\nArgs:\n config_name: Name of the configuration file to retrieve\n \nReturns:\n Dictionary with script configuration\n \nRaises:\n HTTPException: 404 if configuration not found","operationId":"get_script_config_scripts_configs__config_name__get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"config_name","in":"path","required":true,"schema":{"type":"string","title":"Config Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Script Config Scripts Configs Config Name Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Scripts"],"summary":"Create Or Update Script Config","description":"Create or update script configuration.\n\nArgs:\n config_name: Name of the configuration file\n config: Configuration dictionary to save\n \nReturns:\n Success message when configuration is saved\n \nRaises:\n HTTPException: 400 if save error occurs","operationId":"create_or_update_script_config_scripts_configs__config_name__post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"config_name","in":"path","required":true,"schema":{"type":"string","title":"Config Name"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Config"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Scripts"],"summary":"Delete Script Config","description":"Delete script configuration.\n\nArgs:\n config_name: Name of the configuration file to delete\n \nReturns:\n Success message when configuration is deleted\n \nRaises:\n HTTPException: 404 if configuration not found","operationId":"delete_script_config_scripts_configs__config_name__delete","security":[{"HTTPBasic":[]}],"parameters":[{"name":"config_name","in":"path","required":true,"schema":{"type":"string","title":"Config Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/scripts/{script_name}":{"get":{"tags":["Scripts"],"summary":"Get Script","description":"Get script content by name.\n\nArgs:\n script_name: Name of the script to retrieve\n \nReturns:\n Dictionary with script name and content\n \nRaises:\n HTTPException: 404 if script not found","operationId":"get_script_scripts__script_name__get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"script_name","in":"path","required":true,"schema":{"type":"string","title":"Script Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"title":"Response Get Script Scripts Script Name Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Scripts"],"summary":"Create Or Update Script","description":"Create or update a script.\n\nArgs:\n script_name: Name of the script (from URL path)\n script: Script object with content\n \nReturns:\n Success message when script is saved\n \nRaises:\n HTTPException: 400 if save error occurs","operationId":"create_or_update_script_scripts__script_name__post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"script_name","in":"path","required":true,"schema":{"type":"string","title":"Script Name"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Script"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Scripts"],"summary":"Delete Script","description":"Delete a script.\n\nArgs:\n script_name: Name of the script to delete\n \nReturns:\n Success message when script is deleted\n \nRaises:\n HTTPException: 404 if script not found","operationId":"delete_script_scripts__script_name__delete","security":[{"HTTPBasic":[]}],"parameters":[{"name":"script_name","in":"path","required":true,"schema":{"type":"string","title":"Script Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/scripts/{script_name}/config/template":{"get":{"tags":["Scripts"],"summary":"Get Script Config Template","description":"Get script configuration template with default values.\n\nArgs:\n script_name: Name of the script to get template for\n \nReturns:\n Dictionary with configuration template and default values\n \nRaises:\n HTTPException: 404 if script configuration class not found","operationId":"get_script_config_template_scripts__script_name__config_template_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"script_name","in":"path","required":true,"schema":{"type":"string","title":"Script Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Script Config Template Scripts Script Name Config Template Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/market-data/candles":{"post":{"tags":["Market Data"],"summary":"Get Candles","description":"Get real-time candles data for a specific trading pair.\n\nThis endpoint uses the MarketDataProvider to get or create a candles feed that will\nautomatically start and maintain real-time updates. Subsequent requests with the same\nconfiguration will reuse the existing feed for up-to-date data.\n\nArgs:\n request: FastAPI request object\n candles_config: Configuration for the candles including connector, trading_pair, interval, and max_records\n\nReturns:\n Real-time candles data or error message","operationId":"get_candles_market_data_candles_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CandlesConfigRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/market-data/historical-candles":{"post":{"tags":["Market Data"],"summary":"Get Historical Candles","description":"Get historical candles data for a specific trading pair.\n\nArgs:\n config: Configuration for historical candles including connector, trading pair, interval, start and end time\n\nReturns:\n Historical candles data or error message","operationId":"get_historical_candles_market_data_historical_candles_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HistoricalCandlesConfig"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/market-data/active-feeds":{"get":{"tags":["Market Data"],"summary":"Get Active Feeds","description":"Get information about currently active market data feeds.\n\nArgs:\n request: FastAPI request object to access application state\n\nReturns:\n Dictionary with active feeds information including last access times and expiration","operationId":"get_active_feeds_market_data_active_feeds_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBasic":[]}]}},"/market-data/settings":{"get":{"tags":["Market Data"],"summary":"Get Market Data Settings","description":"Get current market data settings for debugging.\n\nReturns:\n Dictionary with current market data configuration including cleanup and timeout settings","operationId":"get_market_data_settings_market_data_settings_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBasic":[]}]}},"/market-data/available-candle-connectors":{"get":{"tags":["Market Data"],"summary":"Get Available Candle Connectors","description":"Get list of available connectors that support candle data feeds.\n\nReturns:\n List of connector names that can be used for fetching candle data","operationId":"get_available_candle_connectors_market_data_available_candle_connectors_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBasic":[]}]}},"/market-data/prices":{"post":{"tags":["Market Data"],"summary":"Get Prices","description":"Get current prices for specified trading pairs from a connector.\n\nArgs:\n request: Price request with connector name and trading pairs\n market_data_manager: Injected market data feed manager\n\nReturns:\n Current prices for the specified trading pairs\n\nRaises:\n HTTPException: 500 if there's an error fetching prices","operationId":"get_prices_market_data_prices_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PricesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/market-data/tickers":{"get":{"tags":["Market Data"],"summary":"Get Tickers","description":"Get tickers grouped by connector, with 24h base and quote volume where available.\n\nWithout ``connectors`` this returns the collected pool as-is. Naming connectors fetches\nthem on demand (concurrently) through keyless public data connectors when the cache is\nmissing or stale, so it works for exchanges no API keys are configured for; those\nconnectors then join the background refresh cycle.\n\nA connector that fails does not remove the others from the response: it is reported under\n``errors`` alongside the successful results. An error status is returned only when nothing\ncould be served at all.","operationId":"get_tickers_market_data_tickers_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"connectors","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Restrict to these connectors. Accepts a comma-separated list or the parameter repeated. Omit to return the whole collected pool.","title":"Connectors"},"description":"Restrict to these connectors. Accepts a comma-separated list or the parameter repeated. Omit to return the whole collected pool."},{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","description":"Force a fresh fetch, ignoring the cache","default":false,"title":"Refresh"},"description":"Force a fresh fetch, ignoring the cache"},{"name":"max_age","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"description":"Accept cached tickers up to this age in seconds","title":"Max Age"},"description":"Accept cached tickers up to this age in seconds"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TickersResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/market-data/rates":{"post":{"tags":["Market Data"],"summary":"Get Rates","description":"Resolve cross-rates for trading pairs from the collected ticker pool.\n\nRates are resolved via direct, reverse or bridged paths. When ``connector`` is set, only\nthat exchange's tickers are used; otherwise the merged multi-exchange pool is used.","operationId":"get_rates_market_data_rates_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RatesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/market-data/rate/{trading_pair}":{"get":{"tags":["Market Data"],"summary":"Get Single Rate","description":"Resolve a cross-rate for a single ``BASE-QUOTE`` trading pair from the ticker pool.\n\nPass ``?connector=`` to restrict resolution to a single exchange's tickers.","operationId":"get_single_rate_market_data_rate__trading_pair__get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"trading_pair","in":"path","required":true,"schema":{"type":"string","title":"Trading Pair"}},{"name":"connector","in":"query","required":false,"schema":{"type":"string","title":"Connector"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SingleRateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/market-data/pool-prices":{"get":{"tags":["Market Data"],"summary":"Get Pool Prices","description":"Get a snapshot of the merged price pool used for cross-rate resolution.","operationId":"get_pool_prices_market_data_pool_prices_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoolPricesResponse"}}}}},"security":[{"HTTPBasic":[]}]}},"/market-data/funding-info":{"post":{"tags":["Market Data"],"summary":"Get Funding Info","description":"Get funding information for a perpetual trading pair.\n\nArgs:\n request: Funding info request with connector name and trading pair\n market_data_manager: Injected market data feed manager\n\nReturns:\n Funding information including rates, timestamps, and prices\n\nRaises:\n HTTPException: 400 for non-perpetual connectors, 500 for other errors","operationId":"get_funding_info_market_data_funding_info_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FundingInfoRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FundingInfoResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/market-data/order-book":{"post":{"tags":["Market Data"],"summary":"Get Order Book","description":"Get order book snapshot with specified depth.\n\nArgs:\n request: Order book request with connector, trading pair, and depth\n market_data_manager: Injected market data feed manager\n\nReturns:\n Order book snapshot with bids and asks\n\nRaises:\n HTTPException: 500 if there's an error fetching order book","operationId":"get_order_book_market_data_order_book_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderBookRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderBookResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/market-data/order-book/price-for-volume":{"post":{"tags":["Market Data"],"summary":"Get Price For Volume","description":"Get the price required to fill a specific volume on the order book.\n\nArgs:\n request: Request with connector, trading pair, volume, and side\n market_data_manager: Injected market data feed manager\n\nReturns:\n Order book query result with price and volume information","operationId":"get_price_for_volume_market_data_order_book_price_for_volume_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceForVolumeRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderBookQueryResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/market-data/order-book/volume-for-price":{"post":{"tags":["Market Data"],"summary":"Get Volume For Price","description":"Get the volume available at a specific price level on the order book.\n\nArgs:\n request: Request with connector, trading pair, price, and side\n market_data_manager: Injected market data feed manager\n\nReturns:\n Order book query result with volume information","operationId":"get_volume_for_price_market_data_order_book_volume_for_price_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VolumeForPriceRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderBookQueryResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/market-data/order-book/price-for-quote-volume":{"post":{"tags":["Market Data"],"summary":"Get Price For Quote Volume","description":"Get the price required to fill a specific quote volume on the order book.\n\nArgs:\n request: Request with connector, trading pair, quote volume, and side\n market_data_manager: Injected market data feed manager\n\nReturns:\n Order book query result with price and volume information","operationId":"get_price_for_quote_volume_market_data_order_book_price_for_quote_volume_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceForQuoteVolumeRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderBookQueryResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/market-data/order-book/quote-volume-for-price":{"post":{"tags":["Market Data"],"summary":"Get Quote Volume For Price","description":"Get the quote volume available at a specific price level on the order book.\n\nArgs:\n request: Request with connector, trading pair, price, and side\n market_data_manager: Injected market data feed manager\n\nReturns:\n Order book query result with quote volume information","operationId":"get_quote_volume_for_price_market_data_order_book_quote_volume_for_price_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteVolumeForPriceRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderBookQueryResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/market-data/order-book/vwap-for-volume":{"post":{"tags":["Market Data"],"summary":"Get Vwap For Volume","description":"Get the VWAP (Volume Weighted Average Price) for a specific volume on the order book.\n\nArgs:\n request: Request with connector, trading pair, volume, and side\n market_data_manager: Injected market data feed manager\n\nReturns:\n Order book query result with VWAP information","operationId":"get_vwap_for_volume_market_data_order_book_vwap_for_volume_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VWAPForVolumeRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderBookQueryResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/market-data/trading-pair/add":{"post":{"tags":["Market Data"],"summary":"Add Trading Pair","description":"Initialize order book for a trading pair.\n\nThis endpoint dynamically adds a trading pair to a connector's order book tracker.\nIt uses the best available connector (trading connectors are preferred over data connectors).\n\nArgs:\n request: Request with connector name, trading pair, optional account name, and timeout\n\nReturns:\n TradingPairResponse with success status and message\n\nRaises:\n HTTPException: 500 if initialization fails","operationId":"add_trading_pair_market_data_trading_pair_add_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddTradingPairRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradingPairResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/market-data/trading-pair/remove":{"post":{"tags":["Market Data"],"summary":"Remove Trading Pair","description":"Remove a trading pair from order book tracking.\n\nThis endpoint removes a trading pair from a connector's order book tracker,\ncleaning up resources for pairs that are no longer needed.\n\nArgs:\n request: Request with connector name, trading pair, and optional account name\n\nReturns:\n TradingPairResponse with success status and message\n\nRaises:\n HTTPException: 500 if removal fails","operationId":"remove_trading_pair_market_data_trading_pair_remove_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveTradingPairRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradingPairResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/market-data/order-book/diagnostics/{connector_name}":{"get":{"tags":["Market Data"],"summary":"Get Order Book Diagnostics","description":"Get diagnostics for a connector's order book tracker.\n\nReturns detailed information about the order book tracker status including:\n- Task status (running/crashed)\n- WebSocket connection status\n- Metrics (messages processed, latency, etc.)\n- Current order book state\n\nArgs:\n connector_name: The connector to diagnose (e.g., \"binance\")\n account_name: Optional account name for trading connectors\n\nReturns:\n Diagnostic information dictionary","operationId":"get_order_book_diagnostics_market_data_order_book_diagnostics__connector_name__get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"connector_name","in":"path","required":true,"schema":{"type":"string","title":"Connector Name"}},{"name":"account_name","in":"query","required":false,"schema":{"type":"string","title":"Account Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/market-data/order-book/restart/{connector_name}":{"post":{"tags":["Market Data"],"summary":"Restart Order Book Tracker","description":"Restart the order book tracker for a connector.\n\nUse this endpoint when the order book is stale (WebSocket disconnected).\nThis will:\n1. Stop the existing order book tracker\n2. Restart it with the same trading pairs\n3. Wait for the WebSocket to reconnect\n\nArgs:\n connector_name: The connector to restart (e.g., \"binance\")\n account_name: Optional account name for trading connectors\n\nReturns:\n Restart status with success/failure and trading pairs","operationId":"restart_order_book_tracker_market_data_order_book_restart__connector_name__post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"connector_name","in":"path","required":true,"schema":{"type":"string","title":"Connector Name"}},{"name":"account_name","in":"query","required":false,"schema":{"type":"string","title":"Account Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backtesting/run":{"post":{"tags":["Backtesting"],"summary":"Run Backtesting","description":"Run a backtest synchronously. Returns results directly (may timeout for long backtests).","operationId":"run_backtesting_backtesting_run_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BacktestingConfig"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/backtesting/tasks":{"get":{"tags":["Backtesting"],"summary":"List Backtest Tasks","description":"List all backtest tasks with their status (results excluded for brevity).","operationId":"list_backtest_tasks_backtesting_tasks_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBasic":[]}]},"post":{"tags":["Backtesting"],"summary":"Create Backtest Task","description":"Submit a backtest as a background task. Returns task ID for polling.","operationId":"create_backtest_task_backtesting_tasks_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BacktestingConfig"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/backtesting/tasks/{task_id}":{"get":{"tags":["Backtesting"],"summary":"Get Backtest Task","description":"Get a backtest task by ID, including results if completed.","operationId":"get_backtest_task_backtesting_tasks__task_id__get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Backtesting"],"summary":"Delete Backtest Task","description":"Cancel a running task or remove a completed one.","operationId":"delete_backtest_task_backtesting_tasks__task_id__delete","security":[{"HTTPBasic":[]}],"parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/archived-bots/":{"get":{"tags":["Archived Bots"],"summary":"List Databases","description":"List all available database files in the system.\n\nReturns:\n List of database file paths","operationId":"list_databases_archived_bots__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"type":"string"},"type":"array","title":"Response List Databases Archived Bots Get"}}}}},"security":[{"HTTPBasic":[]}]}},"/archived-bots/{db_path}":{"delete":{"tags":["Archived Bots"],"summary":"Delete Archived Bot","description":"Delete an archived bot and its entire directory.\nAlso attempts to delete matching BotRun records from PostgreSQL (best-effort).\n\nArgs:\n db_path: Path to the database file (as returned by list_databases)\n\nReturns:\n Confirmation message with the deleted bot name and count of cleaned PG records","operationId":"delete_archived_bot_archived_bots__db_path__delete","security":[{"HTTPBasic":[]}],"parameters":[{"name":"db_path","in":"path","required":true,"schema":{"type":"string","title":"Db Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/archived-bots/{db_path}/status":{"get":{"tags":["Archived Bots"],"summary":"Get Database Status","description":"Get status information for a specific database.\n\nArgs:\n db_path: Path to the database file\n\nReturns:\n Database status including table health","operationId":"get_database_status_archived_bots__db_path__status_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"db_path","in":"path","required":true,"schema":{"type":"string","title":"Db Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/archived-bots/{db_path}/summary":{"get":{"tags":["Archived Bots"],"summary":"Get Database Summary","description":"Get a summary of database contents including basic statistics.\n\nArgs:\n db_path: Full path to the database file\n\nReturns:\n Summary statistics of the database contents","operationId":"get_database_summary_archived_bots__db_path__summary_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"db_path","in":"path","required":true,"schema":{"type":"string","title":"Db Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/archived-bots/{db_path}/performance":{"get":{"tags":["Archived Bots"],"summary":"Get Database Performance","description":"Get trade-based performance analysis for a bot database.\n\nArgs:\n db_path: Full path to the database file\n\nReturns:\n Trade-based performance metrics with rolling calculations","operationId":"get_database_performance_archived_bots__db_path__performance_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"db_path","in":"path","required":true,"schema":{"type":"string","title":"Db Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/archived-bots/{db_path}/trades":{"get":{"tags":["Archived Bots"],"summary":"Get Database Trades","description":"Get trade history from a database.\n\nArgs:\n db_path: Full path to the database file\n limit: Maximum number of trades to return\n offset: Offset for pagination\n\nReturns:\n List of trades with pagination info","operationId":"get_database_trades_archived_bots__db_path__trades_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"db_path","in":"path","required":true,"schema":{"type":"string","title":"Db Path"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","description":"Limit number of trades returned","default":100,"title":"Limit"},"description":"Limit number of trades returned"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","description":"Offset for pagination","default":0,"title":"Offset"},"description":"Offset for pagination"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/archived-bots/{db_path}/orders":{"get":{"tags":["Archived Bots"],"summary":"Get Database Orders","description":"Get order history from a database.\n\nArgs:\n db_path: Full path to the database file\n limit: Maximum number of orders to return\n offset: Offset for pagination\n status: Optional status filter\n\nReturns:\n List of orders with pagination info","operationId":"get_database_orders_archived_bots__db_path__orders_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"db_path","in":"path","required":true,"schema":{"type":"string","title":"Db Path"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","description":"Limit number of orders returned","default":100,"title":"Limit"},"description":"Limit number of orders returned"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","description":"Offset for pagination","default":0,"title":"Offset"},"description":"Offset for pagination"},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by order status","title":"Status"},"description":"Filter by order status"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/archived-bots/{db_path}/executors":{"get":{"tags":["Archived Bots"],"summary":"Get Database Executors","description":"Get executor data from a database.\n\nArgs:\n db_path: Full path to the database file\n\nReturns:\n List of executors with their configurations and results","operationId":"get_database_executors_archived_bots__db_path__executors_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"db_path","in":"path","required":true,"schema":{"type":"string","title":"Db Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/archived-bots/{db_path}/positions":{"get":{"tags":["Archived Bots"],"summary":"Get Database Positions","description":"Get position data from a database.\n\nArgs:\n db_path: Full path to the database file\n limit: Maximum number of positions to return\n offset: Offset for pagination\n\nReturns:\n List of positions with pagination info","operationId":"get_database_positions_archived_bots__db_path__positions_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"db_path","in":"path","required":true,"schema":{"type":"string","title":"Db Path"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","description":"Limit number of positions returned","default":100,"title":"Limit"},"description":"Limit number of positions returned"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","description":"Offset for pagination","default":0,"title":"Offset"},"description":"Offset for pagination"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/archived-bots/{db_path}/controllers":{"get":{"tags":["Archived Bots"],"summary":"Get Database Controllers","description":"Get controller data from a database.\n\nArgs:\n db_path: Full path to the database file\n\nReturns:\n List of controllers that were running with their configurations","operationId":"get_database_controllers_archived_bots__db_path__controllers_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"db_path","in":"path","required":true,"schema":{"type":"string","title":"Db Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/storage/":{"get":{"tags":["Storage"],"summary":"Get Storage Overview","description":"Get disk usage overview for bots directories (archived, instances, conf).","operationId":"get_storage_overview_storage__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBasic":[]}]}},"/system/resources":{"get":{"tags":["System"],"summary":"Get System Resources","description":"Get host machine CPU, RAM, and disk usage.\n\nReturns:\n Dictionary with current CPU, memory, and disk utilization for the host.","operationId":"get_system_resources_system_resources_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBasic":[]}]}},"/executors/":{"post":{"tags":["Executors"],"summary":"Create Executor","description":"Create and start a new executor.\n\nSupported executor types:\n- **position_executor**: Single position with triple barrier (stop loss, take profit, time limit)\n- **grid_executor**: Grid trading with multiple levels\n- **dca_executor**: Dollar-cost averaging with multiple entry points\n- **twap_executor**: Time-weighted average price execution\n- **arbitrage_executor**: Cross-exchange arbitrage\n- **xemm_executor**: Cross-exchange market making\n- **order_executor**: Simple order execution\n- **lp_executor**: Liquidity provider position on CLMM DEXs (Meteora, Raydium, etc.)\n\nThe `executor_config` must include:\n- `type`: One of the executor types above\n- `connector_name`: Exchange connector (e.g., \"binance\", \"binance_perpetual\")\n- `trading_pair`: Trading pair (e.g., \"BTC-USDT\")\n- Additional type-specific configuration (see /executors/types/{type}/config for details)\n\nReturns the created executor ID and initial status.","operationId":"create_executor_executors__post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateExecutorRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateExecutorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/executors/search":{"post":{"tags":["Executors"],"summary":"List Executors","description":"Get list of executors with optional filtering.\n\nReturns active executors from memory combined with completed executors from database.\n\nFilters:\n- `account_names`: Filter by specific accounts\n- `connector_names`: Filter by connectors\n- `trading_pairs`: Filter by trading pairs\n- `executor_types`: Filter by executor types\n- `status`: Filter by status (RUNNING, TERMINATED, etc.)\n\nReturns paginated list of executor summaries.","operationId":"list_executors_executors_search_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecutorFilterRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}},"/executors/summary":{"get":{"tags":["Executors"],"summary":"Get Executors Summary","description":"Get summary statistics for all executors.\n\nReturns aggregate information including:\n- Total active/completed executor counts\n- Total PnL and volume\n- Breakdown by executor type, connector, and status","operationId":"get_executors_summary_executors_summary_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecutorsSummaryResponse"}}}}},"security":[{"HTTPBasic":[]}]}},"/executors/performance":{"get":{"tags":["Executors"],"summary":"Get Performance Report","description":"Get a performance report for executors.\n\nAggregates metrics from all completed executors (optionally filtered by controller_id):\n- Realized PnL (from completed executors, excluding POSITION_HOLD close type)\n- Unrealized PnL (from active executors + position holds)\n- Global PnL (realized + unrealized)\n- Fees and volume totals\n- Win rate and Sharpe ratio\n- Breakdown by executor type\n- Active position count\n\nQuery parameters:\n- **controller_id**: Filter by controller ID (omit for all controllers)","operationId":"get_performance_report_executors_performance_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"controller_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Controller Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PerformanceReportResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/executors/{executor_id}/logs":{"get":{"tags":["Executors"],"summary":"Get Executor Logs","description":"Get captured log entries for a specific executor.\n\nReturns log entries from the in-memory ring buffer. Only available for\nactive executors - logs are cleared when the executor completes.\n\nQuery parameters:\n- **level**: Filter by log level (ERROR, WARNING, INFO, DEBUG)\n- **limit**: Maximum entries to return (default 50)","operationId":"get_executor_logs_executors__executor_id__logs_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"executor_id","in":"path","required":true,"schema":{"type":"string","title":"Executor Id"}},{"name":"level","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Level"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecutorLogsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/executors/types/available":{"get":{"tags":["Executors"],"summary":"Get Available Executor Types","description":"Get list of available executor types with descriptions.\n\nReturns information about each supported executor type.","operationId":"get_available_executor_types_executors_types_available_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBasic":[]}]}},"/executors/{executor_id}":{"get":{"tags":["Executors"],"summary":"Get Executor","description":"Get detailed information about a specific executor.\n\nChecks active executors in memory first, then falls back to database for completed executors.\n\nReturns full executor information including:\n- Current status and PnL\n- Full configuration\n- Executor-specific custom information","operationId":"get_executor_executors__executor_id__get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"executor_id","in":"path","required":true,"schema":{"type":"string","title":"Executor Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecutorDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/executors/{executor_id}/stop":{"post":{"tags":["Executors"],"summary":"Stop Executor","description":"Stop an active executor.\n\nOptions:\n- `keep_position`: If true, keeps any open position (for position executors).\n If false, the executor will attempt to close all positions before stopping.\n\nReturns confirmation of the stop action.","operationId":"stop_executor_executors__executor_id__stop_post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"executor_id","in":"path","required":true,"schema":{"type":"string","title":"Executor Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StopExecutorRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StopExecutorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/executors/positions/orphaned":{"get":{"tags":["Executors"],"summary":"Get Orphaned Positions","description":"List terminated executors that may still own an on-chain position.\n\nCovers three orphan classes:\n- Involuntary holds: close_type POSITION_HOLD with hold_reason set (an LP close\n that exhausted its retries): the position is live on-chain with no automated\n owner.\n- Legacy FAILED records whose final state still reported a position_address\n (force-stop stragglers, records persisted by older executors).\n- Executors terminated by SYSTEM_CLEANUP after an API restart: their on-chain\n state was never persisted, so they need external reconciliation.\n\nThis is a DB-side listing. Before recovering, cross-check candidates against\non-chain reality via the gateway positions-owned endpoints\n(/trading/clmm/positions-owned, /trading/amm/positions-owned).","operationId":"get_orphaned_positions_executors_positions_orphaned_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrphanedPositionsResponse"}}}}},"security":[{"HTTPBasic":[]}]}},"/executors/{executor_id}/resolve-orphan":{"post":{"tags":["Executors"],"summary":"Resolve Orphaned Position","description":"Mark an orphaned position as recovered.\n\nCall after the stranded on-chain position has been closed (or adopted)\nexternally. Removes the executor from /executors/positions/orphaned and from\nagent-facing orphan warnings. Only valid for terminated executors that are\norphan candidates: an involuntary hold (POSITION_HOLD with hold_reason or the\norphaned_position flag), FAILED, or SYSTEM_CLEANUP.","operationId":"resolve_orphaned_position_executors__executor_id__resolve_orphan_post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"executor_id","in":"path","required":true,"schema":{"type":"string","title":"Executor Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/executors/positions/summary":{"get":{"tags":["Executors"],"summary":"Get Positions Summary","description":"Get summary of all held positions from executors stopped with keep_position=True.\n\nReturns aggregate information including:\n- Total number of active position holds\n- Total realized PnL across all positions\n- Total unrealized PnL (when market rates are available)\n- List of all positions with breakeven prices and PnL\n\nQuery parameters:\n- **controller_id**: Filter positions by controller ID","operationId":"get_positions_summary_executors_positions_summary_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"controller_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Controller Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PositionsSummaryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/executors/positions/{connector_name}/{trading_pair}":{"get":{"tags":["Executors"],"summary":"Get Position Held","description":"Get held position for a specific connector/trading pair.\n\nReturns the aggregated position from executors stopped with keep_position=True,\nincluding breakeven prices, matched/unmatched volume, realized PnL, and unrealized PnL.\n\nQuery parameters:\n- **controller_id**: Controller ID (default \"main\")","operationId":"get_position_held_executors_positions__connector_name___trading_pair__get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"connector_name","in":"path","required":true,"schema":{"type":"string","title":"Connector Name"}},{"name":"trading_pair","in":"path","required":true,"schema":{"type":"string","title":"Trading Pair"}},{"name":"account_name","in":"query","required":false,"schema":{"type":"string","default":"master_account","title":"Account Name"}},{"name":"controller_id","in":"query","required":false,"schema":{"type":"string","default":"main","title":"Controller Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PositionHoldResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Executors"],"summary":"Clear Position Held","description":"Clear a held position (after manual close or full exit).\n\nThis removes the position from tracking but preserves historical data\nin completed executors.\n\nQuery parameters:\n- **controller_id**: Controller ID (default \"main\")","operationId":"clear_position_held_executors_positions__connector_name___trading_pair__delete","security":[{"HTTPBasic":[]}],"parameters":[{"name":"connector_name","in":"path","required":true,"schema":{"type":"string","title":"Connector Name"}},{"name":"trading_pair","in":"path","required":true,"schema":{"type":"string","title":"Trading Pair"}},{"name":"account_name","in":"query","required":false,"schema":{"type":"string","default":"master_account","title":"Account Name"}},{"name":"controller_id","in":"query","required":false,"schema":{"type":"string","default":"main","title":"Controller Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/executors/types/{executor_type}/config":{"get":{"tags":["Executors"],"summary":"Get Executor Config Schema","description":"Get configuration schema for a specific executor type.\n\nReturns detailed information about each configuration field including:\n- **name**: Field name\n- **type**: Data type (str, int, Decimal, enum, etc.)\n- **description**: Field description\n- **required**: Whether the field is required\n- **default**: Default value if any\n- **constraints**: Validation constraints (min, max, pattern, etc.)\n- **enum_values**: Possible values for enum types\n\nAlso returns nested type definitions for complex fields.","operationId":"get_executor_config_schema_executors_types__executor_type__config_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"executor_type","in":"path","required":true,"schema":{"type":"string","title":"Executor Type"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/":{"get":{"summary":"Root","description":"API root endpoint returning basic information.","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AMMAddLiquidityRequest":{"properties":{"connector":{"type":"string","title":"Connector","description":"AMM connector (e.g., 'meteora', 'raydium', 'uniswap')"},"network":{"type":"string","title":"Network","description":"Network ID in 'chain-network' format (e.g., 'solana-mainnet-beta')"},"pool_address":{"type":"string","title":"Pool Address","description":"Pool contract address"},"base_token_amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Base Token Amount","description":"Amount of base token to add"},"quote_token_amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Quote Token Amount","description":"Amount of quote token to add"},"slippage_pct":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Slippage Pct","description":"Maximum slippage percentage; omit to use the connector's configured slippagePct"},"wallet_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Address","description":"Wallet address (optional, uses default)"},"position_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Position Address","description":"Meteora position to add to (omit = new position)"}},"type":"object","required":["connector","network","pool_address","base_token_amount","quote_token_amount"],"title":"AMMAddLiquidityRequest","description":"Request to add two-sided liquidity to an AMM pool."},"AMMCreatePoolRequest":{"properties":{"connector":{"type":"string","title":"Connector","description":"AMM connector (e.g., 'meteora', 'raydium', 'uniswap')"},"network":{"type":"string","title":"Network","description":"Network ID in 'chain-network' format (e.g., 'solana-mainnet-beta')"},"base_token":{"type":"string","title":"Base Token","description":"Base token symbol or address (becomes the pool base)"},"quote_token":{"type":"string","title":"Quote Token","description":"Quote token symbol or address (becomes the pool quote)"},"base_token_amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Base Token Amount","description":"Amount of base token to seed the pool with"},"quote_token_amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Quote Token Amount","description":"Amount of quote to seed (sets price if given)"},"initial_price":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Initial Price","description":"Initial price (quote per base); overrides quote amount"},"slippage_pct":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Slippage Pct","description":"Seeding slippage percentage (uniswap/pancakeswap only); omit to use the connector's configured slippagePct"},"wallet_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Address","description":"Wallet address (optional, uses default)"},"extra_params":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Extra Params","description":"Connector-specific create params, passed through to Gateway under its own names: configAddress (meteora DAMM v2, required there), ammConfigIndex (raydium CPMM). Unknown keys are rejected."}},"type":"object","required":["connector","network","base_token","quote_token","base_token_amount"],"title":"AMMCreatePoolRequest","description":"Request to create and seed a new AMM pool."},"AMMCreatePoolResponse":{"properties":{"signature":{"type":"string","title":"Signature","description":"Transaction signature (Solana) or transaction hash (EVM)"},"status":{"type":"string","title":"Status","description":"Transaction status: SUBMITTED, CONFIRMED or FAILED. Mapped from Gateway's TransactionStatus enum by the same helper the swap and CLMM surfaces use, so one vocabulary spans all three."},"poolAddress":{"type":"string","title":"Pooladdress","description":"Address of the newly created pool"},"price":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Price","description":"Initial price the pool was seeded at (quote per base)"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data","description":"Connector-specific confirmed-tx details"}},"type":"object","required":["signature","status","poolAddress"],"title":"AMMCreatePoolResponse","description":"Response after creating an AMM pool."},"AMMPoolInfoResponse":{"properties":{"address":{"type":"string","title":"Address","description":"Pool address"},"baseTokenAddress":{"type":"string","title":"Basetokenaddress","description":"Base token contract address"},"quoteTokenAddress":{"type":"string","title":"Quotetokenaddress","description":"Quote token contract address"},"feePct":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Feepct","description":"Pool base fee percentage"},"price":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Price","description":"Current pool price (quote per base)"},"baseTokenAmount":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Basetokenamount","description":"Total base token liquidity"},"quoteTokenAmount":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Quotetokenamount","description":"Total quote token liquidity"}},"type":"object","required":["address","baseTokenAddress","quoteTokenAddress","feePct","price","baseTokenAmount","quoteTokenAmount"],"title":"AMMPoolInfoResponse","description":"Response with AMM pool information (constant-product / DAMM v2)."},"AMMPositionDetail":{"properties":{"positionAddress":{"type":"string","title":"Positionaddress","description":"Individual position (NFT) address"},"lpTokenAmount":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Lptokenamount","description":"Liquidity held by this position (LP units)"},"baseTokenAmount":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Basetokenamount","description":"Base token amount in this position"},"quoteTokenAmount":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Quotetokenamount","description":"Quote token amount in this position"}},"type":"object","required":["positionAddress","lpTokenAmount","baseTokenAmount","quoteTokenAmount"],"title":"AMMPositionDetail","description":"Per-position breakdown entry (one NFT position). Non-fungible-LP AMMs only."},"AMMPositionInfoResponse":{"properties":{"poolAddress":{"type":"string","title":"Pooladdress","description":"Pool address"},"walletAddress":{"type":"string","title":"Walletaddress","description":"Wallet address"},"baseTokenAddress":{"type":"string","title":"Basetokenaddress","description":"Base token contract address"},"quoteTokenAddress":{"type":"string","title":"Quotetokenaddress","description":"Quote token contract address"},"lpTokenAmount":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Lptokenamount","description":"Aggregate LP units across positions"},"baseTokenAmount":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Basetokenamount","description":"Aggregate base token amount"},"quoteTokenAmount":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Quotetokenamount","description":"Aggregate quote token amount"},"price":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Price","description":"Current pool price (quote per base)"},"positions":{"anyOf":[{"items":{"$ref":"#/components/schemas/AMMPositionDetail"},"type":"array"},{"type":"null"}],"title":"Positions","description":"Per-NFT position breakdown"}},"type":"object","required":["poolAddress","walletAddress","baseTokenAddress","quoteTokenAddress","lpTokenAmount","baseTokenAmount","quoteTokenAmount","price"],"title":"AMMPositionInfoResponse","description":"Wallet's aggregate liquidity in an AMM pool, plus a per-position breakdown (DAMM v2)."},"AMMPositionsOwnedRequest":{"properties":{"connector":{"type":"string","title":"Connector","description":"AMM connector (meteora only; fungible-LP AMMs rejected)"},"network":{"type":"string","title":"Network","description":"Network ID in 'chain-network' format (e.g., 'solana-mainnet-beta')"},"wallet_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Address","description":"Wallet address (optional, uses default)"}},"type":"object","required":["connector","network"],"title":"AMMPositionsOwnedRequest","description":"Request to list all of a wallet's AMM positions across pools (Meteora only)."},"AMMQuoteLiquidityRequest":{"properties":{"connector":{"type":"string","title":"Connector","description":"AMM connector (e.g., 'meteora', 'raydium', 'uniswap')"},"network":{"type":"string","title":"Network","description":"Network ID in 'chain-network' format (e.g., 'solana-mainnet-beta')"},"pool_address":{"type":"string","title":"Pool Address","description":"Pool contract address"},"base_token_amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Base Token Amount","description":"Amount of base token to deposit"},"quote_token_amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Quote Token Amount","description":"Amount of quote token to deposit"},"slippage_pct":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Slippage Pct","description":"Maximum slippage percentage"}},"type":"object","required":["connector","network","pool_address","base_token_amount","quote_token_amount"],"title":"AMMQuoteLiquidityRequest","description":"Request to quote a two-sided liquidity deposit."},"AMMQuoteLiquidityResponse":{"properties":{"baseLimited":{"type":"boolean","title":"Baselimited","description":"Whether the base side is the limiting side"},"baseTokenAmount":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Basetokenamount","description":"Base token amount to deposit"},"quoteTokenAmount":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Quotetokenamount","description":"Quote token amount to deposit"},"baseTokenAmountMax":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Basetokenamountmax","description":"Max base token amount"},"quoteTokenAmountMax":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Quotetokenamountmax","description":"Max quote token amount"}},"type":"object","required":["baseLimited","baseTokenAmount","quoteTokenAmount","baseTokenAmountMax","quoteTokenAmountMax"],"title":"AMMQuoteLiquidityResponse","description":"Response with a two-sided deposit quote."},"AMMRemoveLiquidityRequest":{"properties":{"connector":{"type":"string","title":"Connector","description":"AMM connector (e.g., 'meteora', 'raydium', 'uniswap')"},"network":{"type":"string","title":"Network","description":"Network ID in 'chain-network' format (e.g., 'solana-mainnet-beta')"},"pool_address":{"type":"string","title":"Pool Address","description":"Pool contract address"},"percentage_to_remove":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Percentage To Remove","description":"Percentage of liquidity to remove (0-100)"},"slippage_pct":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Slippage Pct","description":"Maximum slippage percentage"},"wallet_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Address","description":"Wallet address (optional, uses default)"},"position_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Position Address","description":"Meteora position to remove from (required for meteora)"}},"type":"object","required":["connector","network","pool_address","percentage_to_remove"],"title":"AMMRemoveLiquidityRequest","description":"Request to remove liquidity from an AMM pool."},"AMMTransactionResponse":{"properties":{"signature":{"type":"string","title":"Signature","description":"Transaction signature (Solana) or transaction hash (EVM)"},"status":{"type":"string","title":"Status","description":"Transaction status: SUBMITTED, CONFIRMED or FAILED. Mapped from Gateway's TransactionStatus enum by the same helper the swap and CLMM surfaces use, so one vocabulary spans all three."},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data","description":"Connector-specific confirmed-tx details"}},"type":"object","required":["signature","status"],"title":"AMMTransactionResponse","description":"Chain-neutral write response. `signature` holds the tx signature (Solana) or tx hash (EVM)."},"ActiveOrderFilterRequest":{"properties":{"limit":{"type":"integer","maximum":1000.0,"minimum":1.0,"title":"Limit","description":"Number of items per page","default":100},"cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor","description":"Cursor for next page"},"account_names":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Account Names","description":"List of account names to filter by"},"connector_names":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Connector Names","description":"List of connector names to filter by"},"trading_pairs":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Trading Pairs","description":"List of trading pairs to filter by"}},"type":"object","title":"ActiveOrderFilterRequest","description":"Request model for filtering active orders"},"AddPoolRequest":{"properties":{"connector_name":{"type":"string","title":"Connector Name","description":"DEX connector name (e.g., 'raydium', 'meteora')"},"type":{"type":"string","title":"Type","description":"Pool type ('clmm' or 'amm')"},"network":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Network","description":"Network name (e.g., 'mainnet-beta') - optional for /networks/{network_id}/pools"},"address":{"type":"string","title":"Address","description":"Pool contract address"},"base":{"type":"string","title":"Base","description":"Base token symbol"},"quote":{"type":"string","title":"Quote","description":"Quote token symbol"},"base_address":{"type":"string","title":"Base Address","description":"Base token contract address"},"quote_address":{"type":"string","title":"Quote Address","description":"Quote token contract address"},"fee_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Fee Pct","description":"Pool fee percentage (e.g., 0.25)"}},"type":"object","required":["connector_name","type","address","base","quote","base_address","quote_address"],"title":"AddPoolRequest","description":"Request to add a liquidity pool"},"AddTokenRequest":{"properties":{"address":{"type":"string","title":"Address","description":"Token contract address"},"symbol":{"type":"string","title":"Symbol","description":"Token symbol"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Token name (defaults to symbol)"},"decimals":{"type":"integer","title":"Decimals","description":"Number of decimals for the token"}},"type":"object","required":["address","symbol","decimals"],"title":"AddTokenRequest","description":"Request to add a custom token to Gateway"},"AddTradingPairRequest":{"properties":{"connector_name":{"type":"string","title":"Connector Name","description":"Name of the connector (e.g., 'binance', 'binance_perpetual')"},"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair to add (e.g., 'BTC-USDT')"},"account_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Account Name","description":"Optional account name for trading connector preference"},"timeout":{"type":"number","maximum":120.0,"minimum":1.0,"title":"Timeout","description":"Timeout in seconds for order book initialization","default":30.0}},"type":"object","required":["connector_name","trading_pair"],"title":"AddTradingPairRequest","description":"Request model for adding a trading pair to order book tracking"},"BacktestingConfig":{"properties":{"start_time":{"type":"integer","title":"Start Time","default":1735689600},"end_time":{"type":"integer","title":"End Time","default":1738368000},"backtesting_resolution":{"type":"string","title":"Backtesting Resolution","default":"1m"},"trade_cost":{"type":"number","title":"Trade Cost","default":0.0006},"config":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"string"}],"title":"Config"}},"type":"object","required":["config"],"title":"BacktestingConfig"},"CLMMAddLiquidityRequest":{"properties":{"connector":{"type":"string","title":"Connector","description":"CLMM connector (e.g., 'meteora', 'raydium', 'uniswap')"},"network":{"type":"string","title":"Network","description":"Network ID in 'chain-network' format (e.g., 'solana-mainnet-beta')"},"position_address":{"type":"string","title":"Position Address","description":"Existing position address to add liquidity to"},"base_token_amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Base Token Amount","description":"Amount of base token to add"},"quote_token_amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Quote Token Amount","description":"Amount of quote token to add"},"slippage_pct":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Slippage Pct","description":"Maximum slippage percentage; omit to use the connector's configured slippagePct"},"wallet_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Address","description":"Wallet address (optional, uses default if not provided)"},"extra_params":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Extra Params","description":"Additional connector-specific parameters"}},"type":"object","required":["connector","network","position_address"],"title":"CLMMAddLiquidityRequest","description":"Request to add MORE liquidity to an EXISTING CLMM position"},"CLMMClosePositionRequest":{"properties":{"connector":{"type":"string","title":"Connector","description":"CLMM connector (e.g., 'meteora', 'raydium', 'uniswap')"},"network":{"type":"string","title":"Network","description":"Network ID in 'chain-network' format (e.g., 'solana-mainnet-beta')"},"position_address":{"type":"string","title":"Position Address","description":"Position address to close"},"slippage_pct":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Slippage Pct","description":"Maximum acceptable slippage percentage for the withdrawal. Enforced by orca, uniswap and pancakeswap; meteora, raydium and pancakeswap-sol close with no minimum-amount check at all, so it changes nothing there. Omit to use the connector's configured slippagePct. An executor widening this across retries is what it exists for: a narrow in-range close can fail on slippage at the configured value with no way to say \"accept more to get out\"."},"pool_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pool Address","description":"Pool the position belongs to. Informational only \u2014 neither Gateway's call nor the fee snapshot needs it, and unrecorded positions work without it"},"wallet_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Address","description":"Wallet address (optional, uses default if not provided)"}},"type":"object","required":["connector","network","position_address"],"title":"CLMMClosePositionRequest","description":"Request to CLOSE a CLMM position completely (removes all liquidity and closes position)"},"CLMMClosePositionResponse":{"properties":{"transaction_hash":{"type":"string","title":"Transaction Hash","description":"Transaction hash"},"position_address":{"type":"string","title":"Position Address","description":"Position address"},"base_fee_collected":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Base Fee Collected","description":"Base token fees collected"},"quote_fee_collected":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Quote Fee Collected","description":"Quote token fees collected"},"status":{"type":"string","title":"Status","description":"Transaction status","default":"submitted"},"base_token_amount_removed":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Base Token Amount Removed","description":"Base liquidity actually withdrawn on-chain"},"quote_token_amount_removed":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Quote Token Amount Removed","description":"Quote liquidity actually withdrawn on-chain"},"position_rent_refunded":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Position Rent Refunded","description":"Native token rent refunded when the position account closed"}},"type":"object","required":["transaction_hash","position_address"],"title":"CLMMClosePositionResponse","description":"Response after closing a position: fees collected plus what the close returned.\n\nThe removed amounts and rent refund come from Gateway's confirmed transaction data,\nso they are None for submitted-not-confirmed transactions."},"CLMMCollectFeesRequest":{"properties":{"connector":{"type":"string","title":"Connector","description":"CLMM connector (e.g., 'meteora', 'raydium', 'uniswap')"},"network":{"type":"string","title":"Network","description":"Network ID in 'chain-network' format (e.g., 'solana-mainnet-beta')"},"position_address":{"type":"string","title":"Position Address","description":"Position address to collect fees from"},"pool_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pool Address","description":"Pool the position belongs to. Informational only \u2014 neither Gateway's call nor the fee snapshot needs it, and unrecorded positions work without it"},"wallet_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Address","description":"Wallet address (optional, uses default if not provided)"}},"type":"object","required":["connector","network","position_address"],"title":"CLMMCollectFeesRequest","description":"Request to collect fees from a CLMM position"},"CLMMCollectFeesResponse":{"properties":{"transaction_hash":{"type":"string","title":"Transaction Hash","description":"Transaction hash"},"position_address":{"type":"string","title":"Position Address","description":"Position address"},"base_fee_collected":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Base Fee Collected","description":"Base token fees collected"},"quote_fee_collected":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Quote Fee Collected","description":"Quote token fees collected"},"status":{"type":"string","title":"Status","description":"Transaction status","default":"submitted"}},"type":"object","required":["transaction_hash","position_address"],"title":"CLMMCollectFeesResponse","description":"Response after collecting fees"},"CLMMCreatePoolRequest":{"properties":{"connector":{"type":"string","title":"Connector","description":"CLMM connector (e.g., 'meteora', 'raydium', 'orca', 'uniswap')"},"network":{"type":"string","title":"Network","description":"Network ID in 'chain-network' format (e.g., 'solana-mainnet-beta')"},"base_token":{"type":"string","title":"Base Token","description":"Base token symbol or address"},"quote_token":{"type":"string","title":"Quote Token","description":"Quote token symbol or address"},"initial_price":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Initial Price","description":"Initial price (quote per base); market price when omitted"},"wallet_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Address","description":"Wallet address (optional, uses default)"},"extra_params":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Extra Params","description":"Connector-specific create params, passed through to Gateway under its own names: binStep (meteora, orca), feeBps (meteora; required for uniswap/pancakeswap \u2014 the V3 fee tier in basis points), ammConfigIndex (raydium, pancakeswap-sol). Unknown keys are rejected."}},"type":"object","required":["connector","network","base_token","quote_token"],"title":"CLMMCreatePoolRequest","description":"Request to create a new (empty) CLMM pool \u2014 liquidity is added by opening positions.\n\nMirrors Gateway's POST /trading/clmm/create-pool. Connector extras are consumed\nonly by their owning connector."},"CLMMOpenPositionRequest":{"properties":{"connector":{"type":"string","title":"Connector","description":"CLMM connector (e.g., 'meteora', 'raydium', 'uniswap')"},"network":{"type":"string","title":"Network","description":"Network ID in 'chain-network' format (e.g., 'solana-mainnet-beta')"},"pool_address":{"type":"string","title":"Pool Address","description":"Pool contract address"},"lower_price":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Lower Price","description":"Lower price for position range"},"upper_price":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Upper Price","description":"Upper price for position range"},"base_token_amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Base Token Amount","description":"Amount of base token to add"},"quote_token_amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Quote Token Amount","description":"Amount of quote token to add"},"slippage_pct":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Slippage Pct","description":"Maximum slippage percentage; omit to use the connector's configured slippagePct"},"wallet_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Address","description":"Wallet address (optional, uses default if not provided)"},"extra_params":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Extra Params","description":"Additional connector-specific parameters"}},"type":"object","required":["connector","network","pool_address","lower_price","upper_price"],"title":"CLMMOpenPositionRequest","description":"Request to open a new CLMM position with initial liquidity"},"CLMMOpenPositionResponse":{"properties":{"transaction_hash":{"type":"string","title":"Transaction Hash","description":"Transaction hash"},"position_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Position Address","description":"Address of the newly created position. None when the transaction was submitted but not yet confirmed (Gateway only knows the address once the tx lands) \u2014 poll the transaction; the poller records the position once it appears on-chain"},"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair"},"pool_address":{"type":"string","title":"Pool Address","description":"Pool address"},"lower_price":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Lower Price","description":"Lower price bound"},"upper_price":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Upper Price","description":"Upper price bound"},"base_token_amount_added":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Base Token Amount Added","description":"Base amount actually added on-chain (confirmed txs only; the requested amount otherwise)"},"quote_token_amount_added":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Quote Token Amount Added","description":"Quote amount actually added on-chain (confirmed txs only; the requested amount otherwise)"},"position_rent":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Position Rent","description":"Native token locked as rent for the position account (refunded on close)"},"status":{"type":"string","title":"Status","description":"Transaction status","default":"submitted"}},"type":"object","required":["transaction_hash","trading_pair","pool_address","lower_price","upper_price"],"title":"CLMMOpenPositionResponse","description":"Response after opening a new CLMM position"},"CLMMPoolBin":{"properties":{"binId":{"type":"integer","title":"Binid","description":"Bin identifier"},"price":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Price","description":"Price at this bin"},"baseTokenAmount":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Basetokenamount","description":"Base token amount in bin"},"quoteTokenAmount":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Quotetokenamount","description":"Quote token amount in bin"}},"type":"object","required":["binId","price","baseTokenAmount","quoteTokenAmount"],"title":"CLMMPoolBin","description":"Individual bin in a CLMM pool (e.g., Meteora)","example":{"base_token_amount":19656.740028,"bin_id":-374,"price":0.47366592950616504,"quote_token_amount":18197.718539}},"CLMMPoolInfoResponse":{"properties":{"address":{"type":"string","title":"Address","description":"Pool address"},"baseTokenAddress":{"type":"string","title":"Basetokenaddress","description":"Base token contract address"},"quoteTokenAddress":{"type":"string","title":"Quotetokenaddress","description":"Quote token contract address"},"binStep":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Binstep","description":"Bin step (Meteora DLMM only)"},"feePct":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Feepct","description":"Pool fee percentage"},"price":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Price","description":"Current pool price"},"baseTokenAmount":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Basetokenamount","description":"Total base token liquidity"},"quoteTokenAmount":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Quotetokenamount","description":"Total quote token liquidity"},"activeBinId":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Activebinid","description":"Currently active bin/tick ID"},"bins":{"items":{"$ref":"#/components/schemas/CLMMPoolBin"},"type":"array","title":"Bins","description":"List of bins with liquidity"}},"type":"object","required":["address","baseTokenAddress","quoteTokenAddress","feePct","price","baseTokenAmount","quoteTokenAmount"],"title":"CLMMPoolInfoResponse","description":"Response with detailed CLMM pool information","example":{"active_bin_id":-374,"address":"5hbf9JP8k5zdrZp9pokPypFQoBse5mGCmW6nqodurGcd","base_token_address":"METvsvVRapdj9cFLzq4Tr43xK4tAjQfwX76z3n6mWQL","base_token_amount":8645709.142366,"bin_step":20,"bins":[],"fee_pct":0.2,"price":0.47366592950616504,"quote_token_address":"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v","quote_token_amount":1095942.335132}},"CLMMPoolListItem":{"properties":{"address":{"type":"string","title":"Address","description":"Pool address"},"name":{"type":"string","title":"Name","description":"Pool name (e.g., 'SOL-USDC')"},"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair derived from tokens"},"mint_x":{"type":"string","title":"Mint X","description":"Base token mint address"},"mint_y":{"type":"string","title":"Mint Y","description":"Quote token mint address"},"bin_step":{"type":"integer","title":"Bin Step","description":"Bin step / tick spacing"},"current_price":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Current Price","description":"Current pool price"},"liquidity":{"type":"string","title":"Liquidity","description":"Total value locked (TVL) in USD"},"base_fee_percentage":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Base Fee Percentage","description":"Base fee percentage"},"apr":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Apr","description":"Annual percentage rate"},"apy":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Apy","description":"Annual percentage yield"},"volume_24h":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Volume 24H","description":"24h trading volume"},"fees_24h":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Fees 24H","description":"24h fees collected"}},"type":"object","required":["address","name","trading_pair","mint_x","mint_y","bin_step","current_price","liquidity"],"title":"CLMMPoolListItem","description":"Individual pool item in CLMM pool listing - matches Gateway fetch-pools response"},"CLMMPoolListResponse":{"properties":{"pools":{"items":{"$ref":"#/components/schemas/CLMMPoolListItem"},"type":"array","title":"Pools","description":"List of available pools"},"total":{"type":"integer","title":"Total","description":"Total number of matching pools"},"page":{"type":"integer","title":"Page","description":"Current page number"},"page_size":{"type":"integer","title":"Page Size","description":"Number of pools per page"}},"type":"object","required":["pools","total","page","page_size"],"title":"CLMMPoolListResponse","description":"Response with list of available CLMM pools - matches Gateway fetch-pools response"},"CLMMPositionInfo":{"properties":{"position_address":{"type":"string","title":"Position Address","description":"Position address"},"pool_address":{"type":"string","title":"Pool Address","description":"Pool address"},"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair (address-derived identifiers, not symbols)"},"base_token":{"type":"string","title":"Base Token","description":"Base token identifier (derived from the token address; not a symbol)"},"quote_token":{"type":"string","title":"Quote Token","description":"Quote token identifier (derived from the token address; not a symbol)"},"base_token_amount":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Base Token Amount","description":"Base token amount in position"},"quote_token_amount":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Quote Token Amount","description":"Quote token amount in position"},"current_price":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Current Price","description":"Current pool price"},"lower_price":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Lower Price","description":"Lower price bound"},"upper_price":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Upper Price","description":"Upper price bound"},"base_fee_amount":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Base Fee Amount","description":"Base token uncollected fees"},"quote_fee_amount":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Quote Fee Amount","description":"Quote token uncollected fees"},"lower_bin_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Lower Bin Id","description":"Lower bin ID (Meteora)"},"upper_bin_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Upper Bin Id","description":"Upper bin ID (Meteora)"},"in_range":{"type":"boolean","title":"In Range","description":"Whether position is currently in range"}},"type":"object","required":["position_address","pool_address","trading_pair","base_token","quote_token","base_token_amount","quote_token_amount","current_price","lower_price","upper_price","in_range"],"title":"CLMMPositionInfo","description":"Information about a CLMM liquidity position.\n\nNote: in_range here is a bool (live Gateway read); the DB-backed\n/clmm/positions/search endpoint reports in_range as the string enum\nIN_RANGE / OUT_OF_RANGE / UNKNOWN (three states, so not collapsible to bool)."},"CLMMPositionsOwnedRequest":{"properties":{"connector":{"type":"string","title":"Connector","description":"CLMM connector (e.g., 'meteora', 'raydium', 'uniswap')"},"network":{"type":"string","title":"Network","description":"Network ID in 'chain-network' format (e.g., 'solana-mainnet-beta')"},"wallet_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Address","description":"Wallet address (optional, uses default if not provided)"}},"type":"object","required":["connector","network"],"title":"CLMMPositionsOwnedRequest","description":"Request to get all CLMM positions owned by a wallet.\n\nMirrors Gateway's /trading/clmm/positions-owned, which takes no pool filter \u2014\nevery CLMM position the wallet owns on the connector is returned, each row\ncarrying its own pool_address."},"CLMMQuotePositionRequest":{"properties":{"connector":{"type":"string","title":"Connector","description":"CLMM connector (e.g., 'meteora', 'raydium', 'orca')"},"network":{"type":"string","title":"Network","description":"Network ID in 'chain-network' format (e.g., 'solana-mainnet-beta')"},"pool_address":{"type":"string","title":"Pool Address","description":"Pool contract address"},"lower_price":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Lower Price","description":"Lower price bound"},"upper_price":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Upper Price","description":"Upper price bound"},"base_token_amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Base Token Amount","description":"Base amount to deposit (one side may be omitted)"},"quote_token_amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Quote Token Amount","description":"Quote amount to deposit (one side may be omitted)"},"slippage_pct":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Slippage Pct","description":"Max acceptable slippage percentage"}},"type":"object","required":["connector","network","pool_address","lower_price","upper_price"],"title":"CLMMQuotePositionRequest","description":"Request to quote a candidate CLMM position before opening or adding.\n\nMirrors Gateway's GET /trading/clmm/quote-position: given the price range and\none or both deposit amounts, returns the actual base/quote split the pool\nwould take (and which side limits it) without signing anything."},"CLMMQuotePositionResponse":{"properties":{"baseLimited":{"type":"boolean","title":"Baselimited","description":"True when the base side limits the deposit"},"baseTokenAmount":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Basetokenamount","description":"Base amount the position would take"},"quoteTokenAmount":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Quotetokenamount","description":"Quote amount the position would take"},"baseTokenAmountMax":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Basetokenamountmax","description":"Base ceiling after slippage"},"quoteTokenAmountMax":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Quotetokenamountmax","description":"Quote ceiling after slippage"}},"type":"object","required":["baseLimited","baseTokenAmount","quoteTokenAmount","baseTokenAmountMax","quoteTokenAmountMax"],"title":"CLMMQuotePositionResponse","description":"Gateway's position quote: the deposit split the pool would actually take."},"CLMMRemoveLiquidityRequest":{"properties":{"connector":{"type":"string","title":"Connector","description":"CLMM connector (e.g., 'meteora', 'raydium', 'uniswap')"},"network":{"type":"string","title":"Network","description":"Network ID in 'chain-network' format (e.g., 'solana-mainnet-beta')"},"position_address":{"type":"string","title":"Position Address","description":"Position address to remove liquidity from"},"percentage_to_remove":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Percentage To Remove","description":"Percentage of liquidity to remove (0-100)"},"slippage_pct":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Slippage Pct","description":"Maximum slippage percentage. Only honored by the Orca connector; omit to use the connector's configured slippagePct"},"wallet_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Address","description":"Wallet address (optional, uses default if not provided)"}},"type":"object","required":["connector","network","position_address","percentage_to_remove"],"title":"CLMMRemoveLiquidityRequest","description":"Request to remove SOME liquidity from a CLMM position (partial removal)"},"CandlesConfigRequest":{"properties":{"connector_name":{"type":"string","title":"Connector Name"},"trading_pair":{"type":"string","title":"Trading Pair"},"interval":{"type":"string","title":"Interval","default":"1m"},"max_records":{"type":"integer","title":"Max Records","default":500}},"type":"object","required":["connector_name","trading_pair"],"title":"CandlesConfigRequest","description":"The CandlesConfig class is a data class that stores the configuration of a Candle object.\nIt has the following attributes:\n- connector: str\n- trading_pair: str\n- interval: str\n- max_records: int"},"Controller":{"properties":{"content":{"type":"string","title":"Content","description":"Controller source code"},"type":{"anyOf":[{"$ref":"#/components/schemas/ControllerType"},{"type":"null"}],"description":"Controller type (optional for flexibility)"}},"type":"object","required":["content"],"title":"Controller","description":"Controller file content"},"ControllerType":{"type":"string","enum":["directional_trading","market_making","generic"],"title":"ControllerType","description":"Types of controllers available"},"CreateExecutorRequest":{"properties":{"account_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Account Name","description":"Account name to use (defaults to master_account)"},"controller_id":{"type":"string","title":"Controller Id","description":"Controller ID that owns this executor (for per-agent isolation)","default":"main"},"executor_config":{"additionalProperties":true,"type":"object","title":"Executor Config","description":"Executor configuration. Must include 'type' field and executor-specific parameters."}},"type":"object","required":["executor_config"],"title":"CreateExecutorRequest","description":"Request to create a new executor.","examples":[{"description":"Create a position executor with triple barrier","summary":"Position Executor","value":{"account_name":"master_account","executor_config":{"amount":"0.01","connector_name":"binance_perpetual","leverage":10,"side":"BUY","trading_pair":"BTC-USDT","triple_barrier_config":{"stop_loss":"0.02","take_profit":"0.04","time_limit":3600},"type":"position_executor"}}},{"description":"Create an LP position on a CLMM DEX","summary":"LP Executor","value":{"account_name":"master_account","executor_config":{"base_amount":"0","connector_name":"solana-mainnet-beta","extra_params":{"strategyType":0},"keep_position":false,"lower_price":"80","lp_provider":"meteora/clmm","pool_address":"HTvjzsfX3yU6BUodCjZ5vZkUrAxMDTrBs3CJaq43ashR","quote_amount":"10.0","side":"BUY","trading_pair":"SOL-USDC","type":"lp_executor","upper_price":"100"}}}]},"CreateExecutorResponse":{"properties":{"executor_id":{"type":"string","title":"Executor Id","description":"Unique executor identifier"},"executor_type":{"type":"string","title":"Executor Type","description":"Type of executor created"},"connector_name":{"type":"string","title":"Connector Name","description":"Connector name"},"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair"},"controller_id":{"type":"string","title":"Controller Id","description":"Controller that owns this executor","default":"main"},"status":{"type":"string","title":"Status","description":"Initial status"},"created_at":{"type":"string","title":"Created At","description":"Creation timestamp (ISO format)"}},"type":"object","required":["executor_id","executor_type","connector_name","trading_pair","status","created_at"],"title":"CreateExecutorResponse","description":"Response after creating an executor."},"DockerImage":{"properties":{"image_name":{"type":"string","title":"Image Name","description":"Docker image name with optional tag (e.g., 'hummingbot/hummingbot:latest')"}},"type":"object","required":["image_name"],"title":"DockerImage"},"ExecutorDetailResponse":{"properties":{"executor_id":{"type":"string","title":"Executor Id","description":"Unique executor identifier"},"executor_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Executor Type","description":"Type of executor"},"account_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Account Name","description":"Account name"},"connector_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Connector Name","description":"Connector name"},"trading_pair":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trading Pair","description":"Trading pair"},"side":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Side","description":"Trade side (BUY/SELL) if applicable"},"status":{"type":"string","title":"Status","description":"Current status (RUNNING, TERMINATED, etc.)"},"is_active":{"type":"boolean","title":"Is Active","description":"Whether the executor is active"},"is_trading":{"type":"boolean","title":"Is Trading","description":"Whether the executor has open trades"},"timestamp":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Timestamp","description":"Creation timestamp (Unix)"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At","description":"Creation timestamp (ISO format)"},"close_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Close Type","description":"How the executor was closed (if applicable)"},"close_timestamp":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Close Timestamp","description":"Close timestamp (Unix)"},"controller_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Controller Id","description":"ID of the controller that spawned this executor"},"net_pnl_quote":{"type":"number","title":"Net Pnl Quote","description":"Net PnL in quote currency"},"net_pnl_pct":{"type":"number","title":"Net Pnl Pct","description":"Net PnL percentage"},"cum_fees_quote":{"type":"number","title":"Cum Fees Quote","description":"Cumulative fees in quote currency"},"filled_amount_quote":{"type":"number","title":"Filled Amount Quote","description":"Total filled amount in quote currency"},"volume_traded_quote":{"type":"number","title":"Volume Traded Quote","description":"Trading volume generated, in quote currency. The same number as filled_amount_quote for any executor that places orders \u2014 the amount it filled IS its volume. Deliberately different for an LP executor, whose filled amount is the capital it deposited: depositing capital trades nothing. An LP position's volume is derived from the fees it earned, which are a fixed fraction of the swaps that crossed its range, and is 0 while it has earned none.","default":0.0},"error_count":{"type":"integer","title":"Error Count","description":"Number of ERROR-level log entries captured","default":0},"last_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Error","description":"Most recent error message, if any"},"config":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Config","description":"Full executor configuration"},"custom_info":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Custom Info","description":"Executor-specific custom information"}},"type":"object","required":["executor_id","executor_type","account_name","connector_name","trading_pair","status","is_active","is_trading","net_pnl_quote","net_pnl_pct","cum_fees_quote","filled_amount_quote"],"title":"ExecutorDetailResponse","description":"Detailed response for a single executor.","example":{"account_name":"master_account","connector_name":"binance_perpetual","created_at":"2024-01-15T10:30:00Z","cum_fees_quote":1.25,"executor_id":"abc123...","executor_type":"position_executor","filled_amount_quote":5000.0,"is_active":true,"is_trading":true,"net_pnl_pct":2.5,"net_pnl_quote":125.5,"side":"BUY","status":"RUNNING","timestamp":1705315800.0,"trading_pair":"BTC-USDT","volume_traded_quote":5000.0}},"ExecutorFilterRequest":{"properties":{"limit":{"type":"integer","maximum":1000.0,"minimum":1.0,"title":"Limit","description":"Number of items per page","default":100},"cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor","description":"Cursor for next page"},"account_names":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Account Names","description":"Filter by account names"},"connector_names":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Connector Names","description":"Filter by connector names"},"trading_pairs":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Trading Pairs","description":"Filter by trading pairs"},"executor_types":{"anyOf":[{"items":{"type":"string","enum":["position_executor","grid_executor","dca_executor","arbitrage_executor","twap_executor","xemm_executor","order_executor","lp_executor"]},"type":"array"},{"type":"null"}],"title":"Executor Types","description":"Filter by executor types"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","description":"Filter by status (RUNNING, TERMINATED, etc.)"},"controller_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Controller Ids","description":"Filter by controller IDs"}},"type":"object","title":"ExecutorFilterRequest","description":"Request to filter and list executors."},"ExecutorLogEntry":{"properties":{"timestamp":{"type":"string","title":"Timestamp","description":"ISO-format timestamp"},"level":{"type":"string","title":"Level","description":"Log level (DEBUG, INFO, WARNING, ERROR)"},"message":{"type":"string","title":"Message","description":"Log message"},"exc_info":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exc Info","description":"Exception traceback if present"}},"type":"object","required":["timestamp","level","message"],"title":"ExecutorLogEntry","description":"A single log entry from an executor."},"ExecutorLogsResponse":{"properties":{"executor_id":{"type":"string","title":"Executor Id","description":"Executor identifier"},"logs":{"items":{"$ref":"#/components/schemas/ExecutorLogEntry"},"type":"array","title":"Logs","description":"Log entries"},"total_count":{"type":"integer","title":"Total Count","description":"Total number of log entries (before limit)"}},"type":"object","required":["executor_id","logs","total_count"],"title":"ExecutorLogsResponse","description":"Response for executor log entries."},"ExecutorTypeBreakdown":{"properties":{"executor_type":{"type":"string","title":"Executor Type","description":"Executor type name"},"total":{"type":"integer","title":"Total","description":"Total executors of this type"},"completed":{"type":"integer","title":"Completed","description":"Completed executors"},"running":{"type":"integer","title":"Running","description":"Currently running executors"},"pnl_quote":{"type":"number","title":"Pnl Quote","description":"Net PnL in quote currency"},"volume_quote":{"type":"number","title":"Volume Quote","description":"Total volume traded in quote currency for this executor type. Volume GENERATED, not capital deployed."},"fees_quote":{"type":"number","title":"Fees Quote","description":"Cumulative fees in quote currency"}},"type":"object","required":["executor_type","total","completed","running","pnl_quote","volume_quote","fees_quote"],"title":"ExecutorTypeBreakdown","description":"Performance breakdown for a single executor type."},"ExecutorsSummaryResponse":{"properties":{"total_active":{"type":"integer","title":"Total Active","description":"Number of active executors"},"total_pnl_quote":{"type":"number","title":"Total Pnl Quote","description":"Total PnL across active executors"},"total_volume_quote":{"type":"number","title":"Total Volume Quote","description":"Total volume traded across active executors. Volume GENERATED, not capital deployed \u2014 see volume_traded_quote on an executor."},"by_type":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Type","description":"Executor count by type"},"by_connector":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Connector","description":"Executor count by connector"},"by_status":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Status","description":"Executor count by status"}},"type":"object","required":["total_active","total_pnl_quote","total_volume_quote","by_type","by_connector","by_status"],"title":"ExecutorsSummaryResponse","description":"Summary of active executors.","example":{"by_connector":{"binance":1,"binance_perpetual":4},"by_status":{"RUNNING":5},"by_type":{"grid_executor":2,"position_executor":3},"total_active":5,"total_pnl_quote":1234.56,"total_volume_quote":50000.0}},"FundingInfoRequest":{"properties":{"connector_name":{"type":"string","title":"Connector Name","description":"Name of the connector"},"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair to get funding info for"}},"type":"object","required":["connector_name","trading_pair"],"title":"FundingInfoRequest","description":"Request model for getting funding info"},"FundingInfoResponse":{"properties":{"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair"},"funding_rate":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Funding Rate","description":"Current funding rate"},"next_funding_time":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Next Funding Time","description":"Next funding time timestamp"},"mark_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Mark Price","description":"Mark price"},"index_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Index Price","description":"Index price"}},"type":"object","required":["trading_pair","funding_rate","next_funding_time","mark_price","index_price"],"title":"FundingInfoResponse","description":"Response for funding info"},"FundingPaymentFilterRequest":{"properties":{"limit":{"type":"integer","maximum":1000.0,"minimum":1.0,"title":"Limit","description":"Number of items per page","default":100},"start_time":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Start Time","description":"Start time as Unix timestamp in milliseconds"},"end_time":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"End Time","description":"End time as Unix timestamp in milliseconds"},"cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor","description":"Cursor for next page"},"account_names":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Account Names","description":"List of account names to filter by"},"connector_names":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Connector Names","description":"List of connector names to filter by"},"trading_pair":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trading Pair","description":"Filter by trading pair"}},"type":"object","title":"FundingPaymentFilterRequest","description":"Request model for filtering funding payments"},"GatewayConfig":{"properties":{"image":{"type":"string","title":"Image","description":"Docker image for Gateway","default":"hummingbot/gateway:development"},"port":{"type":"integer","title":"Port","description":"Port for Gateway API","default":15888}},"type":"object","title":"GatewayConfig","description":"Configuration for Gateway container deployment.\n\nThe Gateway always runs secured (TLS + mTLS); there is intentionally no ``dev_mode`` and no\n``passphrase`` field (SEC-048):\n- A Gateway that holds wallet keys must never be served over plain HTTP, so the API does not\n support a dev/insecure mode.\n- The Gateway (v2.x) uses a single ``GATEWAY_PASSPHRASE`` for *both* TLS cert-key decryption\n and wallet encryption, and the shared mTLS cert set must be decryptable by this API's\n clients (which use ``CONFIG_PASSWORD``). The passphrase is therefore always\n ``CONFIG_PASSWORD``; a separate value would only break the API<->Gateway mTLS chain."},"GatewayStatus":{"properties":{"running":{"type":"boolean","title":"Running","description":"Whether Gateway container is running"},"container_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Container Id","description":"Container ID if running"},"image":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image","description":"Image used for the container"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At","description":"Container creation timestamp"},"port":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Port","description":"Port Gateway is running on"}},"type":"object","required":["running"],"title":"GatewayStatus","description":"Status information for Gateway instance"},"GatewayWalletCredential":{"properties":{"chain":{"type":"string","title":"Chain","description":"Blockchain chain (e.g., 'solana', 'ethereum')"},"private_key":{"type":"string","title":"Private Key","description":"Wallet private key"},"set_default":{"type":"boolean","title":"Set Default","description":"Set as default wallet for this chain","default":true}},"type":"object","required":["chain","private_key"],"title":"GatewayWalletCredential","description":"Credentials for adding an existing wallet to Gateway"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HistoricalCandlesConfig":{"properties":{"connector_name":{"type":"string","title":"Connector Name"},"trading_pair":{"type":"string","title":"Trading Pair"},"interval":{"type":"string","title":"Interval"},"start_time":{"type":"integer","title":"Start Time"},"end_time":{"type":"integer","title":"End Time"}},"type":"object","required":["connector_name","trading_pair","interval","start_time","end_time"],"title":"HistoricalCandlesConfig"},"LeverageRequest":{"properties":{"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair (e.g., BTC-USDT)"},"leverage":{"type":"integer","maximum":125.0,"minimum":1.0,"title":"Leverage","description":"Leverage value (typically 1-125)"}},"type":"object","required":["trading_pair","leverage"],"title":"LeverageRequest","description":"Request model for setting leverage on perpetual connectors"},"OrderBookLevel":{"properties":{"price":{"type":"number","title":"Price","description":"Price level"},"amount":{"type":"number","title":"Amount","description":"Amount at this price level"}},"type":"object","required":["price","amount"],"title":"OrderBookLevel","description":"Single order book level"},"OrderBookQueryResult":{"properties":{"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair"},"is_buy":{"type":"boolean","title":"Is Buy","description":"Query side (buy/sell)"},"query_volume":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Query Volume","description":"Queried volume"},"query_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Query Price","description":"Queried price"},"result_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Result Price","description":"Resulting price"},"result_volume":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Result Volume","description":"Resulting volume"},"result_quote_volume":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Result Quote Volume","description":"Resulting quote volume"},"average_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Average Price","description":"Average/VWAP price"},"timestamp":{"type":"number","title":"Timestamp","description":"Query timestamp"}},"type":"object","required":["trading_pair","is_buy","timestamp"],"title":"OrderBookQueryResult","description":"Response for order book query operations"},"OrderBookRequest":{"properties":{"connector_name":{"type":"string","title":"Connector Name","description":"Name of the connector"},"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair"},"depth":{"type":"integer","maximum":1000.0,"minimum":1.0,"title":"Depth","description":"Number of price levels to return","default":10}},"type":"object","required":["connector_name","trading_pair"],"title":"OrderBookRequest","description":"Request model for getting order book data"},"OrderBookResponse":{"properties":{"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair"},"bids":{"items":{"$ref":"#/components/schemas/OrderBookLevel"},"type":"array","title":"Bids","description":"Bid levels (highest to lowest)"},"asks":{"items":{"$ref":"#/components/schemas/OrderBookLevel"},"type":"array","title":"Asks","description":"Ask levels (lowest to highest)"},"timestamp":{"type":"number","title":"Timestamp","description":"Snapshot timestamp"}},"type":"object","required":["trading_pair","bids","asks","timestamp"],"title":"OrderBookResponse","description":"Response for order book data"},"OrderFilterRequest":{"properties":{"limit":{"type":"integer","maximum":1000.0,"minimum":1.0,"title":"Limit","description":"Number of items per page","default":100},"start_time":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Start Time","description":"Start time as Unix timestamp in milliseconds"},"end_time":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"End Time","description":"End time as Unix timestamp in milliseconds"},"cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor","description":"Cursor for next page"},"account_names":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Account Names","description":"List of account names to filter by"},"connector_names":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Connector Names","description":"List of connector names to filter by"},"trading_pairs":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Trading Pairs","description":"List of trading pairs to filter by"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","description":"Order status filter"}},"type":"object","title":"OrderFilterRequest","description":"Request model for filtering orders with multiple criteria"},"OrphanedPositionRecord":{"properties":{"executor_id":{"type":"string","title":"Executor Id","description":"Executor identifier"},"executor_type":{"type":"string","title":"Executor Type","description":"Executor type (e.g. lp_executor)"},"account_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Account Name","description":"Account name"},"connector_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Connector Name","description":"Connector name. For lp_executor this is the network id (e.g. 'solana-mainnet-beta'), not the DEX - see lp_provider for the DEX"},"trading_pair":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trading Pair","description":"Trading pair"},"lp_provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lp Provider","description":"DEX connector that holds the position (e.g. 'orca/clmm'), read from the executor config. Pass its base name to the CLMM close endpoint"},"pool_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pool Address","description":"Pool the position was opened against, read from the executor config. Required to close a position that was never recorded in the API database (LP-executor positions never are)"},"controller_id":{"type":"string","title":"Controller Id","description":"Controller/agent grouping label","default":"main"},"close_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Close Type","description":"POSITION_HOLD (involuntary hold), FAILED, or SYSTEM_CLEANUP"},"closed_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Closed At","description":"Termination timestamp (ISO format)"},"position_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Position Address","description":"On-chain position address (None for restart cleanups, which never persisted state)"},"state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State","description":"Executor state at termination (e.g. FAILED, CLOSING)"},"hold_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hold Reason","description":"Why the hold was involuntary (e.g. close_retries_exhausted); None for legacy FAILED/SYSTEM_CLEANUP records"},"needs_onchain_reconciliation":{"type":"boolean","title":"Needs Onchain Reconciliation","description":"True when the position address is unknown and on-chain state must be checked externally","default":false}},"type":"object","required":["executor_id","executor_type"],"title":"OrphanedPositionRecord","description":"A terminated executor that may still own an on-chain position."},"OrphanedPositionsResponse":{"properties":{"count":{"type":"integer","title":"Count","description":"Number of orphan candidates"},"orphans":{"items":{"$ref":"#/components/schemas/OrphanedPositionRecord"},"type":"array","title":"Orphans","description":"Orphan candidate records"}},"type":"object","required":["count","orphans"],"title":"OrphanedPositionsResponse","description":"Terminated executors that may have stranded on-chain positions."},"PaginatedResponse":{"properties":{"data":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Data"},"pagination":{"additionalProperties":true,"type":"object","title":"Pagination"}},"type":"object","required":["data","pagination"],"title":"PaginatedResponse","description":"Generic paginated response.","example":{"data":[],"pagination":{"has_more":true,"limit":100,"next_cursor":"2024-01-10T12:00:00","total_count":500}}},"PerformanceReportResponse":{"properties":{"controller_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Controller Id","description":"Controller ID filter (None = all)"},"total_executors":{"type":"integer","title":"Total Executors","description":"Total executor count"},"by_status":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Status","description":"Executor count by status"},"pnl_total_quote":{"type":"number","title":"Pnl Total Quote","description":"Realized PnL from completed executors in quote currency"},"unrealized_pnl_quote":{"type":"number","title":"Unrealized Pnl Quote","description":"Unrealized PnL from active executors and position holds"},"global_pnl_quote":{"type":"number","title":"Global Pnl Quote","description":"Global PnL (realized + unrealized)"},"pnl_pct_avg":{"type":"number","title":"Pnl Pct Avg","description":"Average PnL percentage across completed executors"},"fees_total_quote":{"type":"number","title":"Fees Total Quote","description":"Total cumulative fees in quote currency"},"volume_total_quote":{"type":"number","title":"Volume Total Quote","description":"Total volume traded in quote currency. Volume GENERATED, not capital deployed: an LP position's deposit is excluded, and the volume its range actually saw is derived from the fees it earned."},"win_rate":{"type":"number","title":"Win Rate","description":"Win rate: fraction of completed executors with positive PnL"},"sharpe_ratio":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Sharpe Ratio","description":"Sharpe ratio of PnL returns (null if <2 executors)"},"by_type":{"items":{"$ref":"#/components/schemas/ExecutorTypeBreakdown"},"type":"array","title":"By Type","description":"Performance breakdown by executor type"},"active_positions":{"type":"integer","title":"Active Positions","description":"Number of active position holds"}},"type":"object","required":["total_executors","by_status","pnl_total_quote","unrealized_pnl_quote","global_pnl_quote","pnl_pct_avg","fees_total_quote","volume_total_quote","win_rate","by_type","active_positions"],"title":"PerformanceReportResponse","description":"Performance report for executors, optionally filtered by controller_id."},"PoolPricesResponse":{"properties":{"quote_token":{"type":"string","title":"Quote Token","description":"Configured global quote token"},"prices_count":{"type":"integer","title":"Prices Count","description":"Number of prices in the pool"},"prices":{"additionalProperties":{"type":"number"},"type":"object","title":"Prices","description":"Trading pair to price mapping"}},"type":"object","required":["quote_token","prices_count","prices"],"title":"PoolPricesResponse","description":"Snapshot of the merged price pool used for cross-rate resolution."},"PortfolioDistributionFilterRequest":{"properties":{"account_names":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Account Names","description":"List of account names to filter by"},"connector_names":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Connector Names","description":"List of connector names to filter by"}},"type":"object","title":"PortfolioDistributionFilterRequest","description":"Request model for filtering portfolio distribution"},"PortfolioHistoryFilterRequest":{"properties":{"limit":{"type":"integer","maximum":1000.0,"minimum":1.0,"title":"Limit","description":"Number of items per page","default":100},"start_time":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Start Time","description":"Start time as Unix timestamp in milliseconds"},"end_time":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"End Time","description":"End time as Unix timestamp in milliseconds"},"cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor","description":"Cursor for next page"},"account_names":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Account Names","description":"List of account names to filter by"},"connector_names":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Connector Names","description":"List of connector names to filter by"},"interval":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Interval","description":"Data sampling interval: 5m, 15m, 30m, 1h, 4h, 12h, 1d. Default is 5m (raw data)","default":"5m"}},"type":"object","title":"PortfolioHistoryFilterRequest","description":"Request model for filtering portfolio history"},"PortfolioStateFilterRequest":{"properties":{"account_names":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Account Names","description":"List of account names to filter by"},"connector_names":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Connector Names","description":"List of connector names to filter by"},"skip_gateway":{"type":"boolean","title":"Skip Gateway","description":"Skip Gateway wallet balance updates for faster CEX-only queries","default":false},"refresh":{"type":"boolean","title":"Refresh","description":"If True, refresh balances from exchanges. If False, return cached state.","default":false}},"type":"object","title":"PortfolioStateFilterRequest","description":"Request model for filtering portfolio state"},"PositionFilterRequest":{"properties":{"limit":{"type":"integer","maximum":1000.0,"minimum":1.0,"title":"Limit","description":"Number of items per page","default":100},"cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor","description":"Cursor for next page"},"account_names":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Account Names","description":"List of account names to filter by"},"connector_names":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Connector Names","description":"List of connector names to filter by"}},"type":"object","title":"PositionFilterRequest","description":"Request model for filtering positions"},"PositionHoldResponse":{"properties":{"trading_pair":{"type":"string","title":"Trading Pair"},"connector_name":{"type":"string","title":"Connector Name"},"account_name":{"type":"string","title":"Account Name"},"controller_id":{"type":"string","title":"Controller Id","description":"Controller that owns this position","default":"main"},"buy_amount_base":{"type":"number","title":"Buy Amount Base"},"buy_amount_quote":{"type":"number","title":"Buy Amount Quote"},"sell_amount_base":{"type":"number","title":"Sell Amount Base"},"sell_amount_quote":{"type":"number","title":"Sell Amount Quote"},"net_amount_base":{"type":"number","title":"Net Amount Base"},"buy_breakeven_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Buy Breakeven Price"},"sell_breakeven_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Sell Breakeven Price"},"matched_amount_base":{"type":"number","title":"Matched Amount Base"},"unmatched_amount_base":{"type":"number","title":"Unmatched Amount Base"},"position_side":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Position Side"},"realized_pnl_quote":{"type":"number","title":"Realized Pnl Quote"},"cum_fees_quote":{"type":"number","title":"Cum Fees Quote","default":0.0},"unrealized_pnl_quote":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Unrealized Pnl Quote"},"executor_count":{"type":"integer","title":"Executor Count"},"executor_ids":{"items":{"type":"string"},"type":"array","title":"Executor Ids"},"last_updated":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Updated"}},"type":"object","required":["trading_pair","connector_name","account_name","buy_amount_base","buy_amount_quote","sell_amount_base","sell_amount_quote","net_amount_base","buy_breakeven_price","sell_breakeven_price","matched_amount_base","unmatched_amount_base","position_side","realized_pnl_quote","executor_count","executor_ids","last_updated"],"title":"PositionHoldResponse","description":"API response model for PositionHold."},"PositionModeRequest":{"properties":{"position_mode":{"type":"string","title":"Position Mode","description":"Position mode (HEDGE or ONEWAY)"}},"type":"object","required":["position_mode"],"title":"PositionModeRequest","description":"Request model for setting position mode on perpetual connectors"},"PositionsSummaryResponse":{"properties":{"total_positions":{"type":"integer","title":"Total Positions","description":"Number of active position holds"},"total_realized_pnl":{"type":"number","title":"Total Realized Pnl","description":"Total realized PnL across all positions"},"total_unrealized_pnl":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Total Unrealized Pnl","description":"Total unrealized PnL (None if no rates available)"},"positions":{"items":{"$ref":"#/components/schemas/PositionHoldResponse"},"type":"array","title":"Positions","description":"List of position holds"}},"type":"object","required":["total_positions","total_realized_pnl","positions"],"title":"PositionsSummaryResponse","description":"Summary of all held positions."},"PriceForQuoteVolumeRequest":{"properties":{"connector_name":{"type":"string","title":"Connector Name","description":"Name of the connector"},"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair"},"is_buy":{"type":"boolean","title":"Is Buy","description":"True for buy side, False for sell side"},"quote_volume":{"type":"number","title":"Quote Volume","description":"Quote volume to query price for"}},"type":"object","required":["connector_name","trading_pair","is_buy","quote_volume"],"title":"PriceForQuoteVolumeRequest","description":"Request model for getting price for a specific quote volume"},"PriceForVolumeRequest":{"properties":{"connector_name":{"type":"string","title":"Connector Name","description":"Name of the connector"},"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair"},"is_buy":{"type":"boolean","title":"Is Buy","description":"True for buy side, False for sell side"},"volume":{"type":"number","title":"Volume","description":"Volume to query price for"}},"type":"object","required":["connector_name","trading_pair","is_buy","volume"],"title":"PriceForVolumeRequest","description":"Request model for getting price for a specific volume"},"PriceRequest":{"properties":{"connector_name":{"type":"string","title":"Connector Name","description":"Name of the connector"},"trading_pairs":{"items":{"type":"string"},"type":"array","title":"Trading Pairs","description":"List of trading pairs to get prices for"}},"type":"object","required":["connector_name","trading_pairs"],"title":"PriceRequest","description":"Request model for getting prices"},"PricesResponse":{"properties":{"connector":{"type":"string","title":"Connector","description":"Connector name"},"prices":{"additionalProperties":{"type":"number"},"type":"object","title":"Prices","description":"Trading pair to price mapping"},"timestamp":{"type":"number","title":"Timestamp","description":"Response timestamp"}},"type":"object","required":["connector","prices","timestamp"],"title":"PricesResponse","description":"Response for prices data"},"QuoteVolumeForPriceRequest":{"properties":{"connector_name":{"type":"string","title":"Connector Name","description":"Name of the connector"},"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair"},"is_buy":{"type":"boolean","title":"Is Buy","description":"True for buy side, False for sell side"},"price":{"type":"number","title":"Price","description":"Price to query quote volume for"}},"type":"object","required":["connector_name","trading_pair","is_buy","price"],"title":"QuoteVolumeForPriceRequest","description":"Request model for getting quote volume at a specific price"},"RateRequest":{"properties":{"trading_pairs":{"items":{"type":"string"},"type":"array","title":"Trading Pairs","description":"Trading pairs to price (e.g., ['BTC-USDT', 'ETH-USDT'])"},"connector":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Connector","description":"If set, resolve rates using only this connector's tickers"}},"type":"object","required":["trading_pairs"],"title":"RateRequest","description":"Request for cross-rates from the ticker pool."},"RatesResponse":{"properties":{"quote_token":{"type":"string","title":"Quote Token","description":"Configured global quote token"},"connector":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Connector","description":"Connector used, if scoped"},"rates":{"additionalProperties":{"anyOf":[{"type":"number"},{"type":"null"}]},"type":"object","title":"Rates","description":"Trading pair to rate mapping (None if not resolvable)"}},"type":"object","required":["quote_token","rates"],"title":"RatesResponse","description":"Cross-rates for the requested trading pairs."},"RemoveTradingPairRequest":{"properties":{"connector_name":{"type":"string","title":"Connector Name","description":"Name of the connector"},"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair to remove"},"account_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Account Name","description":"Optional account name for trading connector preference"}},"type":"object","required":["connector_name","trading_pair"],"title":"RemoveTradingPairRequest","description":"Request model for removing a trading pair from order book tracking"},"Script":{"properties":{"content":{"type":"string","title":"Content","description":"Script source code"}},"type":"object","required":["content"],"title":"Script","description":"Script file content"},"SetDefaultWalletRequest":{"properties":{"chain":{"type":"string","title":"Chain","description":"Blockchain chain (e.g., 'solana', 'ethereum')"},"address":{"type":"string","title":"Address","description":"Wallet address to set as default"}},"type":"object","required":["chain","address"],"title":"SetDefaultWalletRequest","description":"Request to set the default wallet for a chain"},"SingleRateResponse":{"properties":{"trading_pair":{"type":"string","title":"Trading Pair","description":"The trading pair"},"rate":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Rate","description":"The resolved rate (None if not found)"},"quote_token":{"type":"string","title":"Quote Token","description":"Configured global quote token"},"connector":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Connector","description":"Connector used, if scoped"}},"type":"object","required":["trading_pair","rate","quote_token"],"title":"SingleRateResponse","description":"Cross-rate for a single trading pair."},"StartBotAction":{"properties":{"bot_name":{"type":"string","title":"Bot Name","description":"Name of the bot instance to act upon"},"log_level":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Log Level","description":"Logging level (DEBUG, INFO, WARNING, ERROR)"},"script":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Script","description":"Script name to run (without .py extension)"},"conf":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conf","description":"Configuration file name (without .yml extension)"},"async_backend":{"type":"boolean","title":"Async Backend","description":"Whether to run in async backend mode","default":false}},"type":"object","required":["bot_name"],"title":"StartBotAction","description":"Action to start a bot"},"StopBotAction":{"properties":{"bot_name":{"type":"string","title":"Bot Name","description":"Name of the bot instance to act upon"},"skip_order_cancellation":{"type":"boolean","title":"Skip Order Cancellation","description":"Whether to skip cancelling open orders when stopping","default":false},"async_backend":{"type":"boolean","title":"Async Backend","description":"Whether to run in async backend mode","default":false}},"type":"object","required":["bot_name"],"title":"StopBotAction","description":"Action to stop a bot"},"StopExecutorRequest":{"properties":{"keep_position":{"type":"boolean","title":"Keep Position","description":"Whether to keep the position open (for position executors)","default":false}},"type":"object","title":"StopExecutorRequest","description":"Request to stop an executor."},"StopExecutorResponse":{"properties":{"executor_id":{"type":"string","title":"Executor Id","description":"Executor identifier"},"status":{"type":"string","title":"Status","description":"New status: 'stopping', or 'already_terminated' when the stop was a no-op"},"keep_position":{"type":"boolean","title":"Keep Position","description":"Whether position was kept open"},"close_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Close Type","description":"Final close_type when already terminated"},"position_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Position Address","description":"On-chain position address from the executor's final state, if any"},"orphaned_position":{"type":"boolean","title":"Orphaned Position","description":"True when the executor terminated with a live on-chain position that needs recovery","default":false},"hold_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hold Reason","description":"Why a POSITION_HOLD terminal was involuntary (e.g. close_retries_exhausted); None for voluntary holds"}},"type":"object","required":["executor_id","status","keep_position"],"title":"StopExecutorResponse","description":"Response after stopping an executor."},"SwapExecuteQuoteRequest":{"properties":{"connector":{"type":"string","title":"Connector","description":"Router connector the quote came from (e.g., 'jupiter', '0x')"},"network":{"type":"string","title":"Network","description":"Network ID in 'chain-network' format (e.g., 'solana-mainnet-beta')"},"quote_id":{"type":"string","title":"Quote Id","description":"quote_id from a prior /swap/quote on the same connector"},"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair the quote was for (e.g., 'SOL-USDC'). Gateway identifies the swap by quote_id alone; this is what the recorded trade is filed under."},"side":{"type":"string","title":"Side","description":"Trade side the quote was for: 'BUY' or 'SELL'"},"amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Amount","description":"Base-token amount the quote was for, recorded as the request"},"wallet_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Address","description":"Wallet address (optional, uses default if not provided)"}},"type":"object","required":["connector","network","quote_id","trading_pair","side","amount"],"title":"SwapExecuteQuoteRequest","description":"Request to execute a quote the caller already has.\n\nThe two-step flow \u2014 quote, decide, then commit to that quote \u2014 is the reason dflow,\ntitan and 0x return a held price at all. Routing them through /swap/execute instead\nthrows the quote away and prices again, which is what every swap on record did,\nbecause until now nothing downstream exposed Gateway's execute-quote route."},"SwapExecuteRequest":{"properties":{"connector":{"type":"string","title":"Connector","description":"DEX router connector (e.g., 'jupiter', '0x')"},"network":{"type":"string","title":"Network","description":"Network ID in 'chain-network' format (e.g., 'solana-mainnet-beta')"},"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair (e.g., 'SOL-USDC')"},"side":{"type":"string","title":"Side","description":"Trade side: 'BUY' or 'SELL'"},"amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Amount","description":"Amount denominated in the BASE token (SELL: base to sell; BUY: base to receive)"},"slippage_pct":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Slippage Pct","description":"Maximum slippage percentage; omit to use the connector's configured slippagePct"},"wallet_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Address","description":"Wallet address (optional, uses default if not provided)"},"extra_params":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Extra Params","description":"Connector-specific params passed through to Gateway under its own names: approximateIfNoExactOut (Solana routers). Unknown keys are rejected."}},"type":"object","required":["connector","network","trading_pair","side","amount"],"title":"SwapExecuteRequest","description":"Request to execute a swap"},"SwapExecuteResponse":{"properties":{"transaction_hash":{"type":"string","title":"Transaction Hash","description":"Transaction hash"},"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair"},"side":{"type":"string","title":"Side","description":"Trade side"},"amount":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Amount","description":"Amount REQUESTED, denominated in the base token (SELL: base sold; BUY: base wanted). This is the request echoed back, not the fill \u2014 see input_amount / output_amount for what actually moved."},"input_amount":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Input Amount","description":"Amount actually spent, denominated in the input token (quote for BUY, base for SELL). None until the transaction confirms."},"output_amount":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Output Amount","description":"Amount actually received, denominated in the output token (base for BUY, quote for SELL). None until the transaction confirms."},"price":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Price","description":"Executed price in quote per base, computed from the amounts that moved. None until the transaction confirms."},"status":{"type":"string","title":"Status","description":"Transaction status","default":"submitted"}},"type":"object","required":["transaction_hash","trading_pair","side","amount"],"title":"SwapExecuteResponse","description":"Response after executing swap.\n\n`amount` is what was asked for; the three fill fields are what happened. They were\nmissing entirely, so a caller reconciling a position against this response was\nreconciling against its own intent: a BUY of 1000 tokens that delivered 951.68\nanswered `amount: 1000` under the description \"Amount swapped\". Every one of these\nnumbers was already in hand \u2014 the same call writes them to the swap history \u2014 so the\nonly way to learn what a swap did was to execute it, discard the answer, and search\nthe history by transaction hash."},"SwapQuoteRequest":{"properties":{"connector":{"type":"string","title":"Connector","description":"DEX router connector (e.g., 'jupiter', '0x')"},"network":{"type":"string","title":"Network","description":"Network ID in 'chain-network' format (e.g., 'solana-mainnet-beta', 'ethereum-mainnet')"},"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair in BASE-QUOTE format (e.g., 'SOL-USDC')"},"side":{"type":"string","title":"Side","description":"Trade side: 'BUY' or 'SELL'"},"amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Amount","description":"Amount denominated in the BASE token (SELL: base to sell; BUY: base to receive \u2014 Gateway quotes BUY as ExactOut)"},"slippage_pct":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Slippage Pct","description":"Maximum slippage percentage; omit to use the connector's configured slippagePct"},"extra_params":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Extra Params","description":"Connector-specific params passed through to Gateway under its own names: approximateIfNoExactOut (Solana routers). Unknown keys are rejected."}},"type":"object","required":["connector","network","trading_pair","side","amount"],"title":"SwapQuoteRequest","description":"Request for swap price quote"},"SwapQuoteResponse":{"properties":{"base":{"type":"string","title":"Base","description":"Base token symbol"},"quote":{"type":"string","title":"Quote","description":"Quote token symbol"},"price":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Price","description":"Quoted price (base/quote)"},"amount":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Amount","description":"Amount specified in request (BUY: base amount to receive, SELL: base amount to sell)"},"amount_in":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Amount In","description":"Actual input amount (BUY: quote to spend, SELL: base to sell)"},"amount_out":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Amount Out","description":"Actual output amount (BUY: base to receive, SELL: quote to receive)"},"min_amount_out":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Min Amount Out","description":"Minimum output the transaction will accept after slippage"},"max_amount_in":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Max Amount In","description":"Maximum input the transaction will spend after slippage"},"price_impact_pct":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Price Impact Pct","description":"Price impact of this trade size on the route"},"pool_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pool Address","description":"Pool the quote was priced against"},"route_path":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Route Path","description":"Route taken (router connectors)"},"slippage_pct":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Slippage Pct","description":"Slippage percentage Gateway applied to the quote (the request value when Gateway omits it)"},"quote_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Quote Id","description":"Identifier for this quote, on the router connectors that hold a price. Pass it to /swap/execute-quote to execute THIS quote instead of re-pricing. Absent on pool-scoped connectors, which price against the pool at execution time."},"approximation":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Approximation","description":"True when amount_out is an ESTIMATE rather than the exact-out amount asked for. A BUY is an ExactOut order, and many thin tokens have no ExactOut route, so Gateway falls back to quoting the sell leg and then quoting that input forward \u2014 which pays the pool fee and crosses the spread twice. Measured at a near-constant ~2.5% across eleven pools spanning $17 to $1,963 of liquidity, and it is reached for ONLY on the thin, high-fee pools where it hurts most. The caller is not overcharged; the order is silently resized, which is what matters to a strategy that asked for a specific quantity. Set extra_params={'approximateIfNoExactOut': false} to require an exact route."}},"type":"object","required":["base","quote","price","amount"],"title":"SwapQuoteResponse","description":"Swap quote, re-framed from Gateway's token-flow response into trading-pair terms.\n\nGateway's quote-swap routes speak tokenIn/tokenOut; this keeps the base/quote +\nside framing bots use and passes Gateway's execution-safety fields through in\nsnake_case. No gas estimate: Gateway's quote does not return one."},"TickerInfo":{"properties":{"price":{"type":"number","title":"Price","description":"Mid price (or last price when bid/ask unavailable)"},"base_volume":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Base Volume","description":"24h volume denominated in the base asset"},"quote_volume":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Quote Volume","description":"24h volume denominated in the quote asset"},"timestamp":{"type":"number","title":"Timestamp","description":"Collection timestamp"}},"type":"object","required":["price","timestamp"],"title":"TickerInfo","description":"A single collected ticker."},"TickersResponse":{"properties":{"tickers":{"additionalProperties":{"additionalProperties":{"$ref":"#/components/schemas/TickerInfo"},"type":"object"},"type":"object","title":"Tickers","description":"Connector to {trading pair: ticker} mapping"},"counts":{"additionalProperties":{"type":"integer"},"type":"object","title":"Counts","description":"Number of tickers per connector"},"updated_at":{"additionalProperties":{"anyOf":[{"type":"number"},{"type":"null"}]},"type":"object","title":"Updated At","description":"Unix timestamp of the last successful fetch, per connector"},"errors":{"additionalProperties":{"type":"string"},"type":"object","title":"Errors","description":"Connectors that could not be fetched, with the reason. Present only when some requested connectors succeeded and others failed"}},"type":"object","required":["tickers"],"title":"TickersResponse","description":"Tickers grouped by connector."},"TradeFilterRequest":{"properties":{"limit":{"type":"integer","maximum":1000.0,"minimum":1.0,"title":"Limit","description":"Number of items per page","default":100},"start_time":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Start Time","description":"Start time as Unix timestamp in milliseconds"},"end_time":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"End Time","description":"End time as Unix timestamp in milliseconds"},"cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor","description":"Cursor for next page"},"account_names":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Account Names","description":"List of account names to filter by"},"connector_names":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Connector Names","description":"List of connector names to filter by"},"trading_pairs":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Trading Pairs","description":"List of trading pairs to filter by"},"trade_types":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Trade Types","description":"List of trade types to filter by (BUY/SELL)"}},"type":"object","title":"TradeFilterRequest","description":"Request model for filtering trades"},"TradeRequest":{"properties":{"account_name":{"type":"string","title":"Account Name","description":"Name of the account to trade with"},"connector_name":{"type":"string","title":"Connector Name","description":"Name of the connector/exchange"},"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair (e.g., BTC-USDT)"},"trade_type":{"type":"string","enum":["BUY","SELL"],"title":"Trade Type","description":"Whether to buy or sell"},"amount":{"anyOf":[{"type":"number","exclusiveMinimum":0.0},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Amount","description":"Amount to trade"},"order_type":{"type":"string","enum":["LIMIT","MARKET","LIMIT_MAKER"],"title":"Order Type","description":"Type of order","default":"LIMIT"},"price":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Price","description":"Price for limit orders"},"position_action":{"type":"string","enum":["OPEN","CLOSE"],"title":"Position Action","description":"Position action for perpetual contracts (OPEN/CLOSE)","default":"OPEN"}},"type":"object","required":["account_name","connector_name","trading_pair","trade_type","amount"],"title":"TradeRequest","description":"Request model for placing trades"},"TradeResponse":{"properties":{"order_id":{"type":"string","title":"Order Id","description":"Client order ID assigned by the connector"},"account_name":{"type":"string","title":"Account Name","description":"Account used for the trade"},"connector_name":{"type":"string","title":"Connector Name","description":"Connector used for the trade"},"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair"},"trade_type":{"type":"string","title":"Trade Type","description":"Trade type"},"amount":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Amount","description":"Trade amount"},"order_type":{"type":"string","title":"Order Type","description":"Order type"},"price":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Price","description":"Order price"},"status":{"type":"string","title":"Status","description":"Order status","default":"submitted"}},"type":"object","required":["order_id","account_name","connector_name","trading_pair","trade_type","amount","order_type","price"],"title":"TradeResponse","description":"Response model for trade execution"},"TradingPairResponse":{"properties":{"success":{"type":"boolean","title":"Success","description":"Whether the operation succeeded"},"connector_name":{"type":"string","title":"Connector Name","description":"Name of the connector"},"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair that was added/removed"},"message":{"type":"string","title":"Message","description":"Status message"}},"type":"object","required":["success","connector_name","trading_pair","message"],"title":"TradingPairResponse","description":"Response model for trading pair management operations"},"UpdateApiKeysRequest":{"properties":{"api_keys":{"additionalProperties":true,"type":"object","title":"Api Keys","description":"Dict mapping provider name to API key value (e.g., {'helius': 'abc123', 'infura': 'xyz789'})"}},"type":"object","required":["api_keys"],"title":"UpdateApiKeysRequest","description":"Request to update Gateway API keys"},"V2ControllerDeployment":{"properties":{"instance_name":{"type":"string","title":"Instance Name","description":"Unique name for the bot instance"},"credentials_profile":{"type":"string","title":"Credentials Profile","description":"Name of the credentials profile to use"},"controllers_config":{"items":{"type":"string"},"type":"array","title":"Controllers Config","description":"List of controller configuration files to use (without .yml extension)"},"max_global_drawdown_quote":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max Global Drawdown Quote","description":"Maximum allowed global drawdown in quote usually USDT"},"max_controller_drawdown_quote":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max Controller Drawdown Quote","description":"Maximum allowed per-controller drawdown in quote usually USDT"},"image":{"type":"string","title":"Image","description":"Docker image for the Hummingbot instance","default":"hummingbot/hummingbot:latest"},"script_config":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Script Config","description":"Generated script configuration file name"},"headless":{"type":"boolean","title":"Headless","description":"Run in headless mode (no UI)","default":false}},"type":"object","required":["instance_name","credentials_profile","controllers_config"],"title":"V2ControllerDeployment","description":"Configuration for deploying a bot with controllers"},"V2ScriptDeployment":{"properties":{"instance_name":{"type":"string","title":"Instance Name","description":"Unique name for the bot instance"},"credentials_profile":{"type":"string","title":"Credentials Profile","description":"Name of the credentials profile to use"},"image":{"type":"string","title":"Image","description":"Docker image for the Hummingbot instance","default":"hummingbot/hummingbot:latest"},"script":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Script","description":"Script name to run (without .py extension)"},"script_config":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Script Config","description":"Script configuration file name (without .yml extension)"},"headless":{"type":"boolean","title":"Headless","description":"Run in headless mode (no UI)","default":false}},"type":"object","required":["instance_name","credentials_profile"],"title":"V2ScriptDeployment","description":"Configuration for deploying a bot with a script"},"VWAPForVolumeRequest":{"properties":{"connector_name":{"type":"string","title":"Connector Name","description":"Name of the connector"},"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair"},"is_buy":{"type":"boolean","title":"Is Buy","description":"True for buy side, False for sell side"},"volume":{"type":"number","title":"Volume","description":"Volume to calculate VWAP for"}},"type":"object","required":["connector_name","trading_pair","is_buy","volume"],"title":"VWAPForVolumeRequest","description":"Request model for getting VWAP for a specific volume"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VolumeForPriceRequest":{"properties":{"connector_name":{"type":"string","title":"Connector Name","description":"Name of the connector"},"trading_pair":{"type":"string","title":"Trading Pair","description":"Trading pair"},"is_buy":{"type":"boolean","title":"Is Buy","description":"True for buy side, False for sell side"},"price":{"type":"number","title":"Price","description":"Price to query volume for"}},"type":"object","required":["connector_name","trading_pair","is_buy","price"],"title":"VolumeForPriceRequest","description":"Request model for getting volume at a specific price"}},"securitySchemes":{"HTTPBasic":{"type":"http","scheme":"basic"}}}} \ No newline at end of file diff --git a/hummingbot_api_client/__init__.py b/hummingbot_api_client/__init__.py index c898d0a..1ad6d98 100644 --- a/hummingbot_api_client/__init__.py +++ b/hummingbot_api_client/__init__.py @@ -2,5 +2,5 @@ from .sync_client import SyncHummingbotAPIClient from .ws import MarketDataWebSocket, ExecutorsWebSocket, WebSocketRouter -__version__ = "1.5.7" +__version__ = "1.5.8" __all__ = ["HummingbotAPIClient", "SyncHummingbotAPIClient", "MarketDataWebSocket", "ExecutorsWebSocket", "WebSocketRouter"] \ No newline at end of file diff --git a/hummingbot_api_client/client.py b/hummingbot_api_client/client.py index b05633a..1bd52d8 100644 --- a/hummingbot_api_client/client.py +++ b/hummingbot_api_client/client.py @@ -15,7 +15,6 @@ GatewayCLMMRouter, MarketDataRouter, PortfolioRouter, - RateOracleRouter, ScriptsRouter, TradingRouter ) @@ -49,7 +48,6 @@ def __init__( self._gateway_amm: Optional[GatewayAMMRouter] = None self._market_data: Optional[MarketDataRouter] = None self._portfolio: Optional[PortfolioRouter] = None - self._rate_oracle: Optional[RateOracleRouter] = None self._scripts: Optional[ScriptsRouter] = None self._trading: Optional[TradingRouter] = None self._ws: Optional[WebSocketRouter] = None @@ -77,7 +75,6 @@ async def init(self) -> None: self._gateway_amm = GatewayAMMRouter(self._session, self.base_url) self._market_data = MarketDataRouter(self._session, self.base_url) self._portfolio = PortfolioRouter(self._session, self.base_url) - self._rate_oracle = RateOracleRouter(self._session, self.base_url) self._scripts = ScriptsRouter(self._session, self.base_url) self._trading = TradingRouter(self._session, self.base_url) self._ws = WebSocketRouter(self._session, self.base_url, self._username, self._password) @@ -101,7 +98,6 @@ async def close(self) -> None: self._gateway_amm = None self._market_data = None self._portfolio = None - self._rate_oracle = None self._scripts = None self._trading = None self._ws = None @@ -204,13 +200,6 @@ def portfolio(self) -> PortfolioRouter: raise RuntimeError("Client not initialized. Call await client.init() first.") return self._portfolio - @property - def rate_oracle(self) -> RateOracleRouter: - """Access the rate oracle router.""" - if self._rate_oracle is None: - raise RuntimeError("Client not initialized. Call await client.init() first.") - return self._rate_oracle - @property def scripts(self) -> ScriptsRouter: """Access the scripts router.""" diff --git a/hummingbot_api_client/routers/__init__.py b/hummingbot_api_client/routers/__init__.py index 81ec9e1..cda4e2f 100644 --- a/hummingbot_api_client/routers/__init__.py +++ b/hummingbot_api_client/routers/__init__.py @@ -13,7 +13,6 @@ from .gateway_amm import GatewayAMMRouter from .market_data import MarketDataRouter from .portfolio import PortfolioRouter -from .rate_oracle import RateOracleRouter from .scripts import ScriptsRouter from .trading import TradingRouter @@ -33,7 +32,6 @@ "GatewayAMMRouter", "MarketDataRouter", "PortfolioRouter", - "RateOracleRouter", "ScriptsRouter", "TradingRouter" ] \ No newline at end of file diff --git a/hummingbot_api_client/routers/base.py b/hummingbot_api_client/routers/base.py index efb5f13..67adcda 100644 --- a/hummingbot_api_client/routers/base.py +++ b/hummingbot_api_client/routers/base.py @@ -1,6 +1,8 @@ -from typing import Optional +from typing import Optional, Union import aiohttp +QueryParams = Union[dict, list] + class BaseRouter: def __init__(self, session: aiohttp.ClientSession, base_url: str): @@ -57,7 +59,7 @@ async def _get(self, path: str, params: Optional[dict] = None): ) return await response.json() - async def _post(self, path: str, json: Optional[dict] = None, params: Optional[dict] = None) -> dict: + async def _post(self, path: str, json: Optional[dict] = None, params: Optional[QueryParams] = None) -> dict: """Perform a POST request and return JSON response.""" url = f"{self.base_url}/{path.lstrip('/')}" async with self.session.post(url, json=json, params=params) as response: diff --git a/hummingbot_api_client/routers/bot_orchestration.py b/hummingbot_api_client/routers/bot_orchestration.py index c6e8cf1..f21859a 100644 --- a/hummingbot_api_client/routers/bot_orchestration.py +++ b/hummingbot_api_client/routers/bot_orchestration.py @@ -88,36 +88,6 @@ async def stop_bot( } return await self._post("/bot-orchestration/stop-bot", json=stop_bot_action) - async def import_strategy_for_bot( - self, - bot_name: str, - strategy: str - ) -> Dict[str, Any]: - """ - Import a strategy configuration for a bot. - - Args: - bot_name: Name of the bot instance - strategy: Strategy name to import - """ - data = {"strategy": strategy} - return await self._post(f"/bot-orchestration/{bot_name}/import-strategy", json=data) - - async def configure_bot( - self, - bot_name: str, - params: Dict[str, Any] - ) -> Dict[str, Any]: - """ - Configure bot parameters. - - Args: - bot_name: Name of the bot instance - params: Dictionary of configuration parameters - """ - data = {"params": params} - return await self._post(f"/bot-orchestration/{bot_name}/config", json=data) - async def stop_and_archive_bot( self, bot_name: str, diff --git a/hummingbot_api_client/routers/docker.py b/hummingbot_api_client/routers/docker.py index fb6648c..65a7051 100644 --- a/hummingbot_api_client/routers/docker.py +++ b/hummingbot_api_client/routers/docker.py @@ -12,7 +12,7 @@ async def is_running(self) -> bool: async def get_available_images(self, image_name: Optional[str]) -> Dict[str, Any]: """Get available Docker images matching the specified name.""" - return await self._get(f"/docker/available-images", params={"image_name": image_name}) + return await self._get("/docker/available-images/", params={"image_name": image_name}) async def get_active_containers(self, name_filter: Optional[str] = None) -> Dict[str, Any]: """Get all currently active (running) Docker containers.""" @@ -29,27 +29,39 @@ async def clean_exited_containers(self) -> Dict[str, Any]: return await self._post("/docker/clean-exited-containers") # Container Management - async def get_container_status(self, container_name: str) -> Dict[str, Any]: - """Get detailed status information for a specific container.""" - return await self._get(f"/docker/container/{container_name}/status") - + # + # There is no per-container status route. Read a container's state from + # get_active_containers() / get_exited_containers(), both of which take a + # name_filter. async def start_container(self, container_name: str) -> Dict[str, Any]: """Start a stopped container.""" - return await self._post(f"/docker/container/{container_name}/start") - + return await self._post(f"/docker/start-container/{container_name}") + async def stop_container(self, container_name: str) -> Dict[str, Any]: """Stop a running container.""" - return await self._post(f"/docker/container/{container_name}/stop") - - async def remove_container(self, container_name: str, force: bool = False) -> Dict[str, Any]: - """Remove a container.""" - params = {"force": force} if force else None - return await self._delete(f"/docker/container/{container_name}", params=params) + return await self._post(f"/docker/stop-container/{container_name}") + + async def remove_container( + self, + container_name: str, + archive_locally: bool = True, + s3_bucket: Optional[str] = None, + ) -> Dict[str, Any]: + """Remove a Hummingbot container, archiving its bot data first. + + The route only accepts containers named `hummingbot-*`: removal is bound up + with archiving the instance's data, which only those have. + """ + params: Dict[str, Any] = {"archive_locally": archive_locally} + if s3_bucket: + params["s3_bucket"] = s3_bucket + return await self._post(f"/docker/remove-container/{container_name}", params=params) # Image Management async def pull_image(self, image_name: str, tag: str = "latest") -> Dict[str, Any]: """Pull a Docker image from registry.""" - return await self._post("/docker/pull-image/", json={"name": image_name, "tag": tag}) + # The route takes one `image_name` carrying the tag, not a name/tag pair. + return await self._post("/docker/pull-image/", json={"image_name": f"{image_name}:{tag}"}) async def get_pull_status(self) -> Dict[str, Any]: """Get the status of image pull operations.""" diff --git a/hummingbot_api_client/routers/executors.py b/hummingbot_api_client/routers/executors.py index 44f6a05..f685171 100644 --- a/hummingbot_api_client/routers/executors.py +++ b/hummingbot_api_client/routers/executors.py @@ -142,7 +142,19 @@ async def get_executor(self, executor_id: str) -> Dict[str, Any]: executor_id: The executor ID to retrieve Returns: - Executor details + Executor details. Two amounts that are easy to conflate: + `filled_amount_quote` is the capital deployed, `volume_traded_quote` is + the volume generated. They are the same number for an executor that + places orders — the amount it filled IS its volume — and deliberately + different for an LP executor, whose deposit trades nothing. An LP + position's volume is derived from the fees it earned and reads 0 until + it has earned some. + + For a Gateway swap, `custom_info` carries `transaction_hash` (the + on-chain signature; `order_id` is internal and appears nowhere on + chain), `swap_provider`, `wallet_address`, and the LIVE `slippage_pct` — + above the configured start means earlier attempts failed on slippage and + this one paid to get through. Example: executor = await client.executors.get_executor("exec_123") diff --git a/hummingbot_api_client/routers/gateway.py b/hummingbot_api_client/routers/gateway.py index 2f64128..f056a0f 100644 --- a/hummingbot_api_client/routers/gateway.py +++ b/hummingbot_api_client/routers/gateway.py @@ -160,67 +160,6 @@ async def list_pools( params={"connector_name": connector_name, "network": network} ) - async def add_pool( - self, - connector_name: str, - pool_type: str, - network: str, - base: str, - quote: str, - address: str - ) -> Dict[str, Any]: - """ - Add a custom liquidity pool. - - Args: - connector_name: DEX connector name - pool_type: Type of pool - network: Network name - base: Base token symbol - quote: Quote token symbol - address: Pool address - """ - pool_data = { - "connector_name": connector_name, - "type": pool_type, - "network": network, - "base": base, - "quote": quote, - "address": address - } - return await self._post("/gateway/pools", json=pool_data) - - async def delete_pool( - self, - connector: str, - network: str, - pool_type: str, - address: str - ) -> Dict[str, Any]: - """ - Delete a liquidity pool from Gateway's pool list. - - Args: - connector: DEX connector (e.g., 'meteora', 'raydium', 'uniswap') - network: Network name (e.g., 'mainnet-beta', 'mainnet') - pool_type: Pool type (e.g., 'CLMM', 'AMM') - address: Pool contract address to remove - - Example: - await client.gateway.delete_pool( - connector='meteora', - network='mainnet-beta', - pool_type='CLMM', - address='2sf5NYcY4zUPXUSmG6f66mskb24t5F8S11pC1Nz5nQT3' - ) - """ - params = { - "connector_name": connector, - "network": network, - "pool_type": pool_type.lower() # Gateway expects lowercase (amm, clmm) - } - return await self._delete(f"/gateway/pools/{address}", params=params) - # ============================================ # Networks (Primary Endpoints) # ============================================ diff --git a/hummingbot_api_client/routers/gateway_amm.py b/hummingbot_api_client/routers/gateway_amm.py index 6be5e27..8d70bd9 100644 --- a/hummingbot_api_client/routers/gateway_amm.py +++ b/hummingbot_api_client/routers/gateway_amm.py @@ -67,54 +67,6 @@ async def get_positions_owned( request_data["wallet_address"] = wallet_address return await self._post("/gateway/amm/positions-owned", json=request_data) - async def get_swap_quote( - self, - connector: str, - network: str, - pool_address: str, - base_token: str, - side: str, - amount: Decimal, - slippage_pct: Optional[Decimal] = None, - ) -> Dict[str, Any]: - """Quote a swap against a specific AMM pool (pool-scoped, not router).""" - request_data = { - "connector": connector, - "network": network, - "pool_address": pool_address, - "base_token": base_token, - "side": side, - "amount": str(amount), - } - if slippage_pct is not None: - request_data["slippage_pct"] = str(slippage_pct) - return await self._post("/gateway/amm/quote-swap", json=request_data) - - async def execute_swap( - self, - connector: str, - network: str, - pool_address: str, - base_token: str, - side: str, - amount: Decimal, - slippage_pct: Optional[Decimal] = None, - wallet_address: Optional[str] = None, - ) -> Dict[str, Any]: - """Execute a swap against a specific AMM pool.""" - request_data = { - "connector": connector, - "network": network, - "pool_address": pool_address, - "base_token": base_token, - "side": side, - "amount": str(amount), - "slippage_pct": str(slippage_pct) if slippage_pct is not None else "1.0", - } - if wallet_address: - request_data["wallet_address"] = wallet_address - return await self._post("/gateway/amm/execute-swap", json=request_data) - async def get_liquidity_quote( self, connector: str, @@ -159,8 +111,9 @@ async def add_liquidity( "pool_address": pool_address, "base_token_amount": str(base_token_amount), "quote_token_amount": str(quote_token_amount), - "slippage_pct": str(slippage_pct) if slippage_pct is not None else "1.0", } + if slippage_pct is not None: + request_data["slippage_pct"] = str(slippage_pct) if wallet_address: request_data["wallet_address"] = wallet_address if position_address: @@ -206,18 +159,19 @@ async def create_pool( base_token_amount: Decimal, quote_token_amount: Optional[Decimal] = None, initial_price: Optional[Decimal] = None, - config_address: Optional[str] = None, - fee_config_index: Optional[int] = None, - gas_price: Optional[Decimal] = None, - max_gas: Optional[int] = None, + slippage_pct: Optional[Decimal] = None, wallet_address: Optional[str] = None, + extra_params: Optional[Dict[str, Any]] = None, ) -> Dict[str, Any]: """ Create and seed a new AMM pool. Seed price priority: initial_price -> quote_token_amount ratio -> live market price - (anti-snipe). Only base_token_amount is required. Connector extras are sent only when set: - config_address (meteora, required), fee_config_index (raydium), gas_price/max_gas (uniswap). + (anti-snipe). Only base_token_amount is required. slippage_pct bounds the seeding + deposit on EVM connectors; omit to use the connector's configured slippage. + Connector-specific params ride extra_params under Gateway's own names: + configAddress (meteora, required there), ammConfigIndex (raydium). + Unknown keys are rejected by the API with a 400. """ request_data = { "connector": connector, @@ -230,14 +184,82 @@ async def create_pool( request_data["quote_token_amount"] = str(quote_token_amount) if initial_price is not None: request_data["initial_price"] = str(initial_price) - if config_address is not None: - request_data["config_address"] = config_address - if fee_config_index is not None: - request_data["fee_config_index"] = fee_config_index - if gas_price is not None: - request_data["gas_price"] = str(gas_price) - if max_gas is not None: - request_data["max_gas"] = max_gas + if slippage_pct is not None: + request_data["slippage_pct"] = str(slippage_pct) if wallet_address: request_data["wallet_address"] = wallet_address + if extra_params: + request_data["extra_params"] = extra_params return await self._post("/gateway/amm/create-pool", json=request_data) + + async def search_events( + self, + connector: Optional[str] = None, + network: Optional[str] = None, + wallet_address: Optional[str] = None, + pool_address: Optional[str] = None, + event_type: Optional[str] = None, + status: Optional[str] = None, + limit: int = 50, + offset: int = 0, + ) -> Dict[str, Any]: + """ + 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 get_position_info(), which is the only authority on them. + + Args: + connector: Filter by connector (e.g. 'meteora', 'raydium') + network: Filter by network (e.g. 'solana-mainnet-beta') + wallet_address: Filter by wallet address + pool_address: Filter by pool address + event_type: ADD_LIQUIDITY, REMOVE_LIQUIDITY or CREATE_POOL + status: Filter by status + limit: Max results (default 50, capped at 1000 by the API) + offset: Pagination offset + """ + return await self._post("/gateway/amm/events/search", params=self._search_params( + connector=connector, network=network, wallet_address=wallet_address, + pool_address=pool_address, event_type=event_type, status=status, + limit=limit, offset=offset, + )) + + async def search_positions( + self, + connector: Optional[str] = None, + network: Optional[str] = None, + wallet_address: Optional[str] = None, + pool_address: Optional[str] = None, + status: Optional[str] = None, + limit: int = 50, + offset: int = 0, + ) -> Dict[str, Any]: + """ + 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 get_position_info() and their history from search_events(). + + Args: + connector: Filter by connector (e.g. 'meteora') + network: Filter by network (e.g. 'solana-mainnet-beta') + wallet_address: Filter by wallet address + pool_address: Filter by pool address + status: Filter by status (OPEN, CLOSED) + limit: Max results (default 50, capped at 1000 by the API) + offset: Pagination offset + """ + return await self._post("/gateway/amm/positions/search", params=self._search_params( + connector=connector, network=network, wallet_address=wallet_address, + pool_address=pool_address, status=status, limit=limit, offset=offset, + )) + + @staticmethod + def _search_params(limit: int, offset: int, **filters: Optional[str]) -> Dict[str, Any]: + """hapi declares the search filters as query parameters on a POST — a JSON body + is silently ignored, so they must ride the query string.""" + params: Dict[str, Any] = {"limit": limit, "offset": offset} + params.update({key: value for key, value in filters.items() if value is not None}) + return params diff --git a/hummingbot_api_client/routers/gateway_clmm.py b/hummingbot_api_client/routers/gateway_clmm.py index 0a82b9e..b01c0fc 100644 --- a/hummingbot_api_client/routers/gateway_clmm.py +++ b/hummingbot_api_client/routers/gateway_clmm.py @@ -12,7 +12,8 @@ async def get_pool_info( self, connector: str, network: str, - pool_address: str + pool_address: str, + bin_count: int = 0 ) -> Dict[str, Any]: """ Get detailed information about a CLMM pool by pool address. @@ -21,33 +22,43 @@ async def get_pool_info( 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 price. Meteora always returns its bins and ignores + this; orca, raydium, uniswap and pancakeswap compute them on request. + Defaults to 0, which skips the extra on-chain reads. Returns: - Pool information including liquidity, price, bins (for Meteora), etc. + Pool information including liquidity, price, and bins. All field names are returned in snake_case format. - Raises: - HTTPError (400): For Raydium, returns error if pool is a Standard AMM pool - instead of a CLMM pool. This endpoint only supports Concentrated - Liquidity (CLMM) pools. - Example: pool_info = await client.gateway_clmm.get_pool_info( connector='meteora', network='solana-mainnet-beta', pool_address='2sf5NYcY4zUPXUSmG6f66mskb24t5F8S11pC1Nz5nQT3' ) + + # With the bin distribution around the active price + pool_info = await client.gateway_clmm.get_pool_info( + connector='orca', + network='solana-mainnet-beta', + pool_address='Czfq3xZZDmsdGdUyrNLtRhGc47cXcZtLG4crryfu44zE', + bin_count=11 + ) """ params = { "connector": connector, "network": network, "pool_address": pool_address } + if bin_count: + params["bin_count"] = bin_count return await self._get("/gateway/clmm/pool-info", params=params) async def get_pools( self, connector: str, + network: str = "mainnet-beta", page: int = 0, limit: int = 50, search_term: Optional[str] = None, @@ -62,6 +73,7 @@ async def get_pools( Args: connector: CLMM connector (e.g., 'meteora') + network: Solana network name, bare (meteora/orca are Solana-only) page: Page number (default: 0) limit: Results per page (default: 50, max: 100) search_term: Search term to filter pools (optional) @@ -83,6 +95,7 @@ async def get_pools( """ params = { "connector": connector, + "network": network, "page": page, "limit": min(limit, 100), # Cap at 100 "include_unknown": str(include_unknown).lower() # Convert boolean to lowercase string @@ -120,7 +133,8 @@ async def open_position( upper_price: Upper price bound base_token_amount: Amount of base token to provide (optional) quote_token_amount: Amount of quote token to provide (optional) - slippage_pct: Slippage percentage tolerance (default: 1.0) + slippage_pct: Slippage percentage tolerance. Omit to use the + connector's configured slippage; 0 is a real value. wallet_address: Wallet address (uses default if not provided) extra_params: Additional connector-specific parameters (e.g., {"strategyType": 0} for Meteora) @@ -151,9 +165,10 @@ async def open_position( "network": network, "pool_address": pool_address, "lower_price": str(lower_price), - "upper_price": str(upper_price), - "slippage_pct": str(slippage_pct) if slippage_pct else "1.0" + "upper_price": str(upper_price) } + if slippage_pct is not None: + request_data["slippage_pct"] = str(slippage_pct) if base_token_amount is not None: request_data["base_token_amount"] = str(base_token_amount) if quote_token_amount is not None: @@ -165,11 +180,119 @@ async def open_position( return await self._post("/gateway/clmm/open", json=request_data) + async def add_liquidity( + self, + connector: str, + network: str, + position_address: str, + base_token_amount: Optional[Decimal] = None, + quote_token_amount: Optional[Decimal] = None, + slippage_pct: Optional[Decimal] = None, + wallet_address: Optional[str] = None, + extra_params: Optional[Dict[str, Any]] = None + ) -> Dict[str, Any]: + """ + Add MORE liquidity to an EXISTING position, keeping its current price range. + + To open a new position instead, use open_position(). + + Args: + connector: CLMM connector (e.g., 'meteora') + network: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta') + position_address: Existing position NFT address + base_token_amount: Amount of base token to add (optional) + quote_token_amount: Amount of quote token to add (optional) + slippage_pct: Slippage percentage tolerance. Omit to use the + connector's configured slippage; 0 is a real value. + wallet_address: Wallet address (uses default if not provided) + extra_params: Additional connector-specific parameters (e.g., {"strategyType": 0} for Meteora) + + Returns: + Transaction hash and resulting position amounts + + Example: + result = await client.gateway_clmm.add_liquidity( + connector='meteora', + network='solana-mainnet-beta', + position_address='...', + base_token_amount=Decimal('0.5'), + quote_token_amount=Decimal('50') + ) + """ + request_data = { + "connector": connector, + "network": network, + "position_address": position_address + } + if slippage_pct is not None: + request_data["slippage_pct"] = str(slippage_pct) + if base_token_amount is not None: + request_data["base_token_amount"] = str(base_token_amount) + if quote_token_amount is not None: + request_data["quote_token_amount"] = str(quote_token_amount) + if wallet_address: + request_data["wallet_address"] = wallet_address + if extra_params: + request_data["extra_params"] = extra_params + + return await self._post("/gateway/clmm/add", json=request_data) + + async def remove_liquidity( + self, + connector: str, + network: str, + position_address: str, + percentage_to_remove: Decimal, + slippage_pct: Optional[Decimal] = None, + wallet_address: Optional[str] = None + ) -> Dict[str, Any]: + """ + Remove SOME liquidity from a position (partial withdrawal). + + The position account survives even at percentage_to_remove=100, which leaves + it empty but open. To withdraw everything AND close the position, use + close_position(). + + Args: + connector: CLMM connector (e.g., 'meteora') + network: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta') + position_address: Position NFT address + percentage_to_remove: Percentage of liquidity to remove (0-100) + slippage_pct: Slippage percentage tolerance (orca only; other + connectors ignore it). Omit to use the connector's configured + slippage. + wallet_address: Wallet address (uses default if not provided) + + Returns: + Transaction hash and removed token amounts + + Example: + result = await client.gateway_clmm.remove_liquidity( + connector='meteora', + network='solana-mainnet-beta', + position_address='...', + percentage_to_remove=Decimal('50') + ) + """ + request_data = { + "connector": connector, + "network": network, + "position_address": position_address, + "percentage_to_remove": str(percentage_to_remove) + } + if slippage_pct is not None: + request_data["slippage_pct"] = str(slippage_pct) + if wallet_address: + request_data["wallet_address"] = wallet_address + + return await self._post("/gateway/clmm/remove", json=request_data) + async def close_position( self, connector: str, network: str, position_address: str, + pool_address: Optional[str] = None, wallet_address: Optional[str] = None ) -> Dict[str, Any]: """ @@ -179,6 +302,10 @@ async def close_position( connector: CLMM connector (e.g., 'meteora') network: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta') position_address: Position NFT address + pool_address: Pool the position belongs to. Only needed for positions the API never + recorded - notably positions opened by an lp_executor, which the bot opens straight + against Gateway. Without it those closes fail with 400. GET + /executors/positions/orphaned reports the pool for each orphan. wallet_address: Wallet address (uses default if not provided) Returns: @@ -196,12 +323,22 @@ async def close_position( position_address='...' ) print(f"Collected fees: {result['base_fee_collected']} base, {result['quote_fee_collected']} quote") + + # Closing an orphaned lp_executor position, whose pool the API never recorded + result = await client.gateway_clmm.close_position( + connector='orca', + network='solana-mainnet-beta', + position_address='H4vD69DsraHjHyKvRwRPHVGe2aJkvAUaNK5tMif2CiNw', + pool_address='Czfq3xZZDmsdGdUyrNLtRhGc47cXcZtLG4crryfu44zE' + ) """ request_data = { "connector": connector, "network": network, "position_address": position_address } + if pool_address: + request_data["pool_address"] = pool_address if wallet_address: request_data["wallet_address"] = wallet_address @@ -212,6 +349,7 @@ async def collect_fees( connector: str, network: str, position_address: str, + pool_address: Optional[str] = None, wallet_address: Optional[str] = None ) -> Dict[str, Any]: """ @@ -221,6 +359,8 @@ async def collect_fees( connector: CLMM connector (e.g., 'meteora') network: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta') position_address: Position NFT address + pool_address: Pool the position belongs to. Only needed for positions the API never + recorded (see close_position); without it those calls fail with 400. wallet_address: Wallet address (uses default if not provided) Returns: @@ -239,43 +379,133 @@ async def collect_fees( "network": network, "position_address": position_address } + if pool_address: + request_data["pool_address"] = pool_address if wallet_address: request_data["wallet_address"] = wallet_address return await self._post("/gateway/clmm/collect-fees", json=request_data) - async def get_positions_owned( + async def quote_position( self, connector: str, network: str, pool_address: str, + lower_price: Decimal, + upper_price: Decimal, + base_token_amount: Optional[Decimal] = None, + quote_token_amount: Optional[Decimal] = None, + slippage_pct: Optional[Decimal] = None + ) -> Dict[str, Any]: + """ + Quote a candidate CLMM position before opening or adding liquidity. + + 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. + + Returns: + Dict with base_limited, base_token_amount, quote_token_amount, + base_token_amount_max, quote_token_amount_max. + """ + request_data = { + "connector": connector, + "network": network, + "pool_address": pool_address, + "lower_price": str(lower_price), + "upper_price": str(upper_price), + } + if base_token_amount is not None: + request_data["base_token_amount"] = str(base_token_amount) + if quote_token_amount is not None: + request_data["quote_token_amount"] = str(quote_token_amount) + if slippage_pct is not None: + request_data["slippage_pct"] = str(slippage_pct) + + return await self._post("/gateway/clmm/quote-position", json=request_data) + + async def create_pool( + self, + connector: str, + network: str, + base_token: str, + quote_token: str, + initial_price: Optional[Decimal] = None, + wallet_address: Optional[str] = None, + extra_params: Optional[Dict[str, Any]] = None + ) -> Dict[str, Any]: + """ + Create a new (empty) CLMM pool - liquidity is added by opening positions. + + Connector-specific params ride extra_params under Gateway's own names: + binStep (meteora, orca), feeBps (meteora, uniswap, pancakeswap), + ammConfigIndex (raydium, pancakeswap-sol). Unknown keys are rejected + by the API with a 400. + """ + request_data = { + "connector": connector, + "network": network, + "base_token": base_token, + "quote_token": quote_token, + } + if initial_price is not None: + request_data["initial_price"] = str(initial_price) + if wallet_address: + request_data["wallet_address"] = wallet_address + if extra_params: + request_data["extra_params"] = extra_params + + return await self._post("/gateway/clmm/create-pool", json=request_data) + + async def get_position_info( + self, + connector: str, + network: str, + position_address: str + ) -> Dict[str, Any]: + """ + Get a single CLMM position by its address. + + Raises a 404 error when the position does not exist or is closed. + """ + params = { + "connector": connector, + "network": network, + "position_address": position_address + } + return await self._get("/gateway/clmm/position-info", params=params) + + async def get_positions_owned( + self, + connector: str, + network: str, wallet_address: Optional[str] = None ) -> List[Dict[str, Any]]: """ - Get all liquidity positions owned by a wallet for a specific pool. + Get ALL liquidity positions owned by a wallet on a connector. + + Mirrors Gateway's /trading/clmm/positions-owned, which takes no pool + filter — each returned row carries its own pool_address. Args: connector: CLMM connector (e.g., 'meteora') network: Network ID in format 'chain-network' (e.g., 'solana-mainnet-beta') - pool_address: Pool contract address wallet_address: Wallet address (uses default if not provided) Returns: - List of position information for the specified pool + List of position information for the wallet Example: positions = await client.gateway_clmm.get_positions_owned( connector='meteora', - network='solana-mainnet-beta', - pool_address='2sf5NYcY4zUPXUSmG6f66mskb24t5F8S11pC1Nz5nQT3' + network='solana-mainnet-beta' ) for pos in positions: print(f"Position: {pos['position_address']} - In Range: {pos['in_range']}") """ request_data = { "connector": connector, - "network": network, - "pool_address": pool_address + "network": network } if wallet_address: request_data["wallet_address"] = wallet_address @@ -354,22 +584,26 @@ async def search_positions( for position in results['data']: print(f"Position: {position['position_address']} - {position['in_range']}") """ - request_data = { - "limit": limit, - "offset": offset, - "refresh": refresh - } + # hapi declares these as query parameters on a POST — a JSON body is + # silently ignored, so the filters must ride the query string. + # position_addresses repeats as multiple query keys, hence the tuples. + params = [ + ("limit", str(limit)), + ("offset", str(offset)), + ("refresh", "true" if refresh else "false"), + ] if network is not None: - request_data["network"] = network + params.append(("network", network)) if connector is not None: - request_data["connector"] = connector + params.append(("connector", connector)) if wallet_address is not None: - request_data["wallet_address"] = wallet_address + params.append(("wallet_address", wallet_address)) if trading_pair is not None: - request_data["trading_pair"] = trading_pair + params.append(("trading_pair", trading_pair)) if status is not None: - request_data["status"] = status + params.append(("status", status)) if position_addresses is not None: - request_data["position_addresses"] = position_addresses + for address in position_addresses: + params.append(("position_addresses", address)) - return await self._post("/gateway/clmm/positions/search", json=request_data) + return await self._post("/gateway/clmm/positions/search", params=params) diff --git a/hummingbot_api_client/routers/gateway_swap.py b/hummingbot_api_client/routers/gateway_swap.py index 99daa05..de2d1c3 100644 --- a/hummingbot_api_client/routers/gateway_swap.py +++ b/hummingbot_api_client/routers/gateway_swap.py @@ -15,7 +15,8 @@ async def get_swap_quote( trading_pair: str, side: str, amount: Decimal, - slippage_pct: Optional[Decimal] = None + slippage_pct: Optional[Decimal] = None, + extra_params: Optional[Dict[str, Any]] = None ) -> Dict[str, Any]: """ Get a price quote for a swap via router (Jupiter, 0x). @@ -26,10 +27,22 @@ async def get_swap_quote( trading_pair: Trading pair in format 'BASE-QUOTE' (e.g., 'SOL-USDC') side: Trade side - 'BUY' or 'SELL' amount: Amount to trade - slippage_pct: Optional slippage percentage (default: 1.0) + slippage_pct: Optional slippage percentage. Omit to use the + connector's configured slippage; 0 is a real value. + extra_params: Optional connector-specific params under Gateway's own + key names. Supported: approximateIfNoExactOut (bool) for the + jupiter/dflow/okx/titan routers. Returns: - Quote with price, expected output amount, and gas estimate + Quote with price, expected output amount, and gas estimate. + `approximation` is True when amount_out is an ESTIMATE rather than + the exact-out amount asked for: a BUY is an ExactOut order, and a + thin token with no ExactOut route is quoted by pricing the sell leg + and quoting that input forward, which costs roughly 2.5%. The order + is silently resized rather than overcharged, so check this whenever + the quantity is what matters. Pass + extra_params={'approximateIfNoExactOut': False} to require an exact + route instead. Example: quote = await client.gateway_swap.get_swap_quote( @@ -37,8 +50,7 @@ async def get_swap_quote( network='solana-mainnet-beta', trading_pair='SOL-USDC', side='BUY', - amount=Decimal('1'), - slippage_pct=Decimal('1.0') + amount=Decimal('1') ) """ request_data = { @@ -46,9 +58,12 @@ async def get_swap_quote( "network": network, "trading_pair": trading_pair, "side": side, - "amount": str(amount), - "slippage_pct": str(slippage_pct) if slippage_pct else "1.0" + "amount": str(amount) } + if slippage_pct is not None: + request_data["slippage_pct"] = str(slippage_pct) + if extra_params is not None: + request_data["extra_params"] = extra_params return await self._post("/gateway/swap/quote", json=request_data) async def execute_swap( @@ -59,7 +74,8 @@ async def execute_swap( side: str, amount: Decimal, slippage_pct: Optional[Decimal] = None, - wallet_address: Optional[str] = None + wallet_address: Optional[str] = None, + extra_params: Optional[Dict[str, Any]] = None ) -> Dict[str, Any]: """ Execute a swap transaction via router (Jupiter, 0x). @@ -70,8 +86,12 @@ async def execute_swap( trading_pair: Trading pair in format 'BASE-QUOTE' (e.g., 'SOL-USDC') side: Trade side - 'BUY' or 'SELL' amount: Amount to trade - slippage_pct: Optional slippage percentage (default: 1.0) + slippage_pct: Optional slippage percentage. Omit to use the + connector's configured slippage; 0 is a real value. wallet_address: Optional wallet address (uses default if not provided) + extra_params: Optional connector-specific params under Gateway's own + key names. Supported: approximateIfNoExactOut (bool) for the + jupiter/dflow/okx/titan routers. Returns: Transaction hash and swap details @@ -82,8 +102,7 @@ async def execute_swap( network='solana-mainnet-beta', trading_pair='SOL-USDC', side='BUY', - amount=Decimal('1'), - slippage_pct=Decimal('1.0') + amount=Decimal('1') ) print(f"Transaction hash: {result['transaction_hash']}") """ @@ -92,14 +111,70 @@ async def execute_swap( "network": network, "trading_pair": trading_pair, "side": side, - "amount": str(amount), - "slippage_pct": str(slippage_pct) if slippage_pct else "1.0" + "amount": str(amount) } + if slippage_pct is not None: + request_data["slippage_pct"] = str(slippage_pct) if wallet_address: request_data["wallet_address"] = wallet_address + if extra_params is not None: + request_data["extra_params"] = extra_params return await self._post("/gateway/swap/execute", json=request_data) + async def execute_quote( + self, + connector: str, + network: str, + quote_id: str, + trading_pair: str, + side: str, + amount: Decimal, + wallet_address: Optional[str] = None + ) -> Dict[str, Any]: + """ + Execute a quote returned by get_swap_quote, by its quote_id. + + The two-step flow: quote, decide, then commit to THAT quote. execute_swap prices + again at execution and discards the price the caller saw, which is the whole + value of a held quote on dflow, titan and 0x. Router connectors only — a + pool-scoped connector has no cached quote and the API rejects it with a 400 + rather than quietly re-pricing. + + Args: + connector: Router connector the quote came from (e.g. 'jupiter', '0x') + network: Network ID in format 'chain-network' (e.g. 'solana-mainnet-beta') + quote_id: The quote_id field of a prior get_swap_quote response + trading_pair: The pair the quote was for, in 'BASE-QUOTE' form + side: The side the quote was for - 'BUY' or 'SELL' + amount: The base-token amount the quote was for + wallet_address: Optional wallet address (uses default if not provided) + + Returns: + Transaction hash and what the swap actually moved + + Example: + quote = await client.gateway_swap.get_swap_quote( + connector='jupiter', network='solana-mainnet-beta', + trading_pair='SOL-USDC', side='SELL', amount=Decimal('0.01')) + result = await client.gateway_swap.execute_quote( + connector='jupiter', network='solana-mainnet-beta', + quote_id=quote['quote_id'], trading_pair='SOL-USDC', + side='SELL', amount=Decimal('0.01')) + """ + request_data = { + "connector": connector, + "network": network, + "quote_id": quote_id, + "trading_pair": trading_pair, + "side": side, + "amount": str(amount) + } + if wallet_address: + request_data["wallet_address"] = wallet_address + + return await self._post("/gateway/swap/execute-quote", json=request_data) + async def get_swap_status( self, transaction_hash: str @@ -160,25 +235,27 @@ async def search_swaps( for swap in results['data']: print(f"Swap: {swap['trading_pair']} - {swap['status']}") """ - request_data = {} + # hapi declares these as query parameters on a POST — a JSON body is + # silently ignored, so the filters must ride the query string. + params = {} if network is not None: - request_data["network"] = network + params["network"] = network if connector is not None: - request_data["connector"] = connector + params["connector"] = connector if wallet_address is not None: - request_data["wallet_address"] = wallet_address + params["wallet_address"] = wallet_address if trading_pair is not None: - request_data["trading_pair"] = trading_pair + params["trading_pair"] = trading_pair if status is not None: - request_data["status"] = status + params["status"] = status if start_time is not None: - request_data["start_time"] = start_time + params["start_time"] = str(start_time) if end_time is not None: - request_data["end_time"] = end_time - request_data["limit"] = limit - request_data["offset"] = offset + params["end_time"] = str(end_time) + params["limit"] = str(limit) + params["offset"] = str(offset) - return await self._post("/gateway/swaps/search", json=request_data) + return await self._post("/gateway/swaps/search", params=params) async def get_swaps_summary( self, @@ -204,8 +281,8 @@ async def get_swaps_summary( network='solana-mainnet-beta', wallet_address='ABC...' ) - print(f"Total volume: {summary['total_volume']}") - print(f"Success rate: {summary['success_rate']}") + for token, volume in summary['volume_by_quote_token'].items(): + print(f"Volume ({token}): {volume}") """ params = {} if network is not None: diff --git a/hummingbot_api_client/routers/market_data.py b/hummingbot_api_client/routers/market_data.py index 33ef835..91071d5 100644 --- a/hummingbot_api_client/routers/market_data.py +++ b/hummingbot_api_client/routers/market_data.py @@ -204,8 +204,8 @@ async def get_rates( Rates are resolved via direct, reverse or bridged paths, so a pair no exchange lists directly still prices as long as the pool bridges it. - Note this is the market-data pool, not the rate oracle: for the oracle's own - rates use `client.rate_oracle.get_rates`. + This is the market-data pool. The API exposes no rate-oracle routes, so this is + the only cross-rate source the client has. Args: trading_pairs: Single trading pair or list of trading pairs (e.g., "BTC-USDT" diff --git a/hummingbot_api_client/routers/rate_oracle.py b/hummingbot_api_client/routers/rate_oracle.py deleted file mode 100644 index dcc2873..0000000 --- a/hummingbot_api_client/routers/rate_oracle.py +++ /dev/null @@ -1,95 +0,0 @@ -from typing import Optional, Dict, Any, List -from .base import BaseRouter - - -class RateOracleRouter(BaseRouter): - """Rate Oracle router for managing rate oracle configuration and retrieving rates.""" - - async def get_available_sources(self) -> List[str]: - """Get list of all available rate oracle sources.""" - return await self._get("/rate-oracle/sources") - - async def get_config(self) -> Dict[str, Any]: - """ - Get current rate oracle configuration. - - Returns: - Current rate oracle configuration including source, global token, and available sources - """ - return await self._get("/rate-oracle/config") - - async def update_config( - self, - rate_oracle_source: Optional[str] = None, - global_token_name: Optional[str] = None, - global_token_symbol: Optional[str] = None - ) -> Dict[str, Any]: - """ - Update rate oracle configuration. - - Args: - rate_oracle_source: New rate oracle source name (e.g., "binance", "coingecko") - global_token_name: New global token name (e.g., "USDT") - global_token_symbol: New global token symbol (e.g., "$") - - Returns: - Updated configuration with success status - """ - update = {} - if rate_oracle_source is not None: - update["rate_oracle_source"] = {"name": rate_oracle_source} - if global_token_name is not None or global_token_symbol is not None: - global_token = {} - if global_token_name is not None: - global_token["global_token_name"] = global_token_name - if global_token_symbol is not None: - global_token["global_token_symbol"] = global_token_symbol - update["global_token"] = global_token - return await self._put("/rate-oracle/config", json=update) - - async def get_rates(self, trading_pairs: List[str]) -> Dict[str, Any]: - """ - Get rates for specified trading pairs. - - Args: - trading_pairs: List of trading pairs (e.g., ["BTC-USDT", "ETH-USDT"]) - - Returns: - Rates for the requested trading pairs - """ - return await self._post("/rate-oracle/rates", json={"trading_pairs": trading_pairs}) - - async def get_rate(self, trading_pair: str) -> Dict[str, Any]: - """ - Get rate for a single trading pair. - - Args: - trading_pair: Trading pair in format BASE-QUOTE (e.g., "BTC-USDT") - - Returns: - Rate for the specified trading pair - """ - return await self._get(f"/rate-oracle/rate/{trading_pair}") - - async def get_rate_async(self, trading_pair: str) -> Dict[str, Any]: - """ - Get rate for a trading pair using async fetch (direct from exchange). - - This bypasses cached prices and fetches directly from the source. - - Args: - trading_pair: Trading pair in format BASE-QUOTE (e.g., "BTC-USDT") - - Returns: - Rate for the specified trading pair - """ - return await self._get(f"/rate-oracle/rate-async/{trading_pair}") - - async def get_cached_prices(self) -> Dict[str, Any]: - """ - Get all cached prices from the rate oracle. - - Returns: - Dictionary with source, quote_token, prices_count, and all cached prices - """ - return await self._get("/rate-oracle/prices") diff --git a/hummingbot_api_client/sync_client.py b/hummingbot_api_client/sync_client.py index df943ac..4fbc374 100644 --- a/hummingbot_api_client/sync_client.py +++ b/hummingbot_api_client/sync_client.py @@ -19,7 +19,6 @@ from .routers.gateway_clmm import GatewayCLMMRouter from .routers.market_data import MarketDataRouter from .routers.portfolio import PortfolioRouter - from .routers.rate_oracle import RateOracleRouter from .routers.scripts import ScriptsRouter from .routers.trading import TradingRouter @@ -84,7 +83,6 @@ def __init__( self.gateway_clmm: GatewayCLMMRouter self.market_data: MarketDataRouter self.portfolio: PortfolioRouter - self.rate_oracle: RateOracleRouter self.scripts: ScriptsRouter self.trading: TradingRouter @@ -162,8 +160,8 @@ def _wrap_routers(self): router_attrs = [ 'accounts', 'archived_bots', 'backtesting', 'bot_orchestration', 'connectors', 'controllers', 'docker', 'executors', 'gateway', - 'gateway_swap', 'gateway_clmm', 'market_data', 'portfolio', - 'rate_oracle', 'scripts', 'trading' + 'gateway_swap', 'gateway_clmm', 'gateway_amm', 'market_data', + 'portfolio', 'scripts', 'trading' ] for router_name in router_attrs: diff --git a/pyproject.toml b/pyproject.toml index 52dce8e..44601fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "hummingbot-api-client" -version = "1.5.7" +version = "1.5.8" description = "An async Python client for Hummingbot API" readme = "README.md" requires-python = ">=3.8" diff --git a/test/test_calls_match_api_spec.py b/test/test_calls_match_api_spec.py new file mode 100644 index 0000000..a5dca51 --- /dev/null +++ b/test/test_calls_match_api_spec.py @@ -0,0 +1,213 @@ +"""Every call this client makes must exist on hummingbot-api, with the keys it sends. + +The routers are hand-written wrappers around hummingbot-api's HTTP surface. Nothing +here is generated and nothing is checked at import time, so a route the API renames or +drops stays in this client as a method that looks fine and 404s the first time someone +calls it. That is how the whole rate-oracle module survived after the API stopped +serving `/rate-oracle/*`, and how `pull_image` kept sending `{name, tag}` after the +route started requiring `{image_name}` — a 422 on every call, invisible because the one +caller in the wild mocked the client in its tests. + +Three checks against the vendored spec: + +- Every path+method a router calls is served by the API. +- Every literal query key it sends is declared by that route. +- Every literal body key it sends is declared by that route's request model. + +Calls whose path or keys are computed rather than literal are counted, not checked — +the last test fails if that count ever swamps the checked ones, which would mean these +checks had quietly stopped covering the client. + +Refresh the spec when adopting an API change: + + curl -s -u : http://localhost:8000/openapi.json > hummingbot-api-openapi.json +""" +import ast +import json +import re +from pathlib import Path + +import pytest + +_REPO_ROOT = Path(__file__).resolve().parent.parent +SPEC_PATH = _REPO_ROOT / "hummingbot-api-openapi.json" +ROUTERS_PATH = _REPO_ROOT / "hummingbot_api_client" / "routers" + +# BaseRouter's request helpers, mapped to the HTTP method each one sends. +HELPERS = {"_get": "get", "_post": "post", "_put": "put", "_delete": "delete", "_patch": "patch"} + +# Stands in for an interpolated path segment. Any value matches a spec `{param}`, so the +# placeholder only has to be something no literal segment would ever be. +PLACEHOLDER = "\x00" + + +def _spec() -> dict: + return json.loads(SPEC_PATH.read_text()) + + +def _routes(spec: dict) -> list: + """[(method, raw_path, compiled_matcher, operation)] for every operation in the spec.""" + out = [] + for raw, operations in spec["paths"].items(): + pattern = re.compile("^" + re.sub(r"\{[^}]+\}", "[^/]+", raw) + "$") + for method, operation in operations.items(): + if method in HELPERS.values(): + out.append((method, raw, pattern, operation)) + return out + + +class _Calls(ast.NodeVisitor): + """Collects `self._get("/path", params={...}, json={...})` from a router module.""" + + def __init__(self): + self.found = [] + + def visit_Call(self, node): + func = node.func + if isinstance(func, ast.Attribute) and func.attr in HELPERS: + keywords = {kw.arg: kw.value for kw in node.keywords} + self.found.append({ + "line": node.lineno, + "method": HELPERS[func.attr], + "path": self._path(node.args[0] if node.args else None), + "params": self._keys(keywords.get("params")), + "json": self._keys(keywords.get("json")), + }) + self.generic_visit(node) + + @staticmethod + def _path(node): + """The literal path, with interpolated segments replaced by PLACEHOLDER.""" + if isinstance(node, ast.Constant) and isinstance(node.value, str): + return node.value + if isinstance(node, ast.JoinedStr): + return "".join( + part.value if isinstance(part, ast.Constant) else PLACEHOLDER + for part in node.values + ) + return None + + @staticmethod + def _keys(node): + """Literal string keys of a dict or list-of-pairs; None if not readable statically.""" + if isinstance(node, ast.Dict): + if any(key is None for key in node.keys): # {**spread} + return None + if not all(isinstance(key, ast.Constant) for key in node.keys): + return None + return {key.value for key in node.keys} + if isinstance(node, ast.List): # [("limit", ...), ("network", ...)] + keys = set() + for element in node.elts: + if not isinstance(element, ast.Tuple) or not element.elts: + return None + first = element.elts[0] + if not isinstance(first, ast.Constant): + return None + keys.add(first.value) + return keys + return None + + +def _client_calls() -> list: + calls = [] + for module in sorted(ROUTERS_PATH.glob("*.py")): + visitor = _Calls() + visitor.visit(ast.parse(module.read_text())) + for call in visitor.found: + call["module"] = module.name + calls.append(call) + return calls + + +CALLS = _client_calls() +SPEC = _spec() +ROUTES = _routes(SPEC) + + +def _match(method: str, path: str): + """The spec operation serving this call, or (None, None). + + BaseRouter builds `f"{base_url}/{path.lstrip('/')}"`, so a missing leading slash is + not drift. A missing *trailing* slash is: FastAPI answers it with a 307 the client + happens to follow, which works but turns every such call into two round trips. + """ + path = "/" + path.lstrip("/") + for spec_method, raw, pattern, operation in ROUTES: + if spec_method == method and pattern.match(path): + return raw, operation + return None, None + + +def _query_names(operation: dict) -> set: + return {p["name"] for p in operation.get("parameters", []) if p.get("in") == "query"} + + +def _body_names(operation: dict, spec: dict): + """Declared body properties, or None when the route takes a free-form object.""" + body = operation.get("requestBody") + if not body: + return None + schema = body["content"]["application/json"]["schema"] + if "$ref" in schema: + schema = spec["components"]["schemas"][schema["$ref"].split("/")[-1]] + return set(schema["properties"]) if "properties" in schema else None + + +def _readable() -> list: + return [call for call in CALLS if call["path"] is not None] + + +def _ids(calls): + return [f"{c['module']}:{c['line']}:{c['method']}" for c in calls] + + +@pytest.mark.parametrize("call", _readable(), ids=_ids(_readable())) +def test_the_route_the_call_addresses_is_served(call): + raw, operation = _match(call["method"], call["path"]) + assert operation is not None, ( + f"{call['module']}:{call['line']} calls {call['method'].upper()} {call['path']}, " + f"which hummingbot-api does not serve. Either the route was renamed and this " + f"wrapper should follow it, or the route is gone and the wrapper should be too — " + f"a method that can only 404 is worse than no method. Spec: {SPEC_PATH.name}." + ) + + +@pytest.mark.parametrize("call", _readable(), ids=_ids(_readable())) +def test_the_keys_the_call_sends_are_declared(call): + raw, operation = _match(call["method"], call["path"]) + if operation is None: + pytest.skip("route is missing; the test above reports it") + + if call["params"] is not None: + undeclared = sorted(call["params"] - _query_names(operation)) + assert not undeclared, ( + f"{call['module']}:{call['line']} sends query keys {raw} does not declare: " + f"{undeclared}. FastAPI ignores an undeclared query parameter, so the filter " + "or flag is dropped in silence and the call returns the unfiltered result." + ) + + if call["json"] is not None: + declared = _body_names(operation, SPEC) + if declared is not None: + undeclared = sorted(call["json"] - declared) + assert not undeclared, ( + f"{call['module']}:{call['line']} sends body keys {raw} does not declare: " + f"{undeclared}. Pydantic rejects the request outright when a required " + "field is missing, so this is a 422 on every call." + ) + + +def test_the_checks_above_cover_most_of_the_client(): + """A regex or visitor gone stale would leave these tests passing over nothing.""" + assert len(SPEC["paths"]) > 100, "The vendored spec looks truncated" + assert len(CALLS) > 100, ( + f"Only {len(CALLS)} calls found across {ROUTERS_PATH.name}/ — has BaseRouter " + "stopped using the _get/_post helpers?" + ) + computed = [c for c in CALLS if c["path"] is None] + assert len(computed) < len(CALLS) // 10, ( + f"{len(computed)} of {len(CALLS)} calls build their path dynamically, so these " + "checks no longer cover most of the client: " + + ", ".join(f"{c['module']}:{c['line']}" for c in computed) + )