Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 11 additions & 48 deletions .github/workflows/docker_buildx_workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,55 +5,20 @@ on:
types: [closed]
branches:
- main
- development
release:
types: [published, edited]

jobs:
build_pr:
if: github.event_name == 'pull_request' && github.event.pull_request.merged == true
build_and_push:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/[email protected]

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/[email protected]

- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build and push Development Image
if: github.base_ref == 'development'
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: hummingbot/hummingbot-api:development

- name: Build and push Latest Image
if: github.base_ref == 'main'
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: hummingbot/hummingbot-api:latest

build_release:
if: github.event_name == 'release'
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/[email protected]
- name: Extract version from main.py
id: get_version
run: |
VERSION=$(grep -E '^VERSION *= *' main.py | head -1 | sed -E 's/^VERSION *= *["\x27]?([^"\x27]*)["\x27]?/\1/')
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT

- name: Set up QEMU
uses: docker/setup-qemu-action@v3
Expand All @@ -67,14 +32,12 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Extract tag name
id: get_tag
run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/}

- name: Build and push
- name: Build and push Docker images (latest and versioned)
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: hummingbot/hummingbot-api:${{ steps.get_tag.outputs.VERSION }}
tags: |
hummingbot/hummingbot-api:latest
hummingbot/hummingbot-api:${{ steps.get_version.outputs.VERSION }}
79 changes: 18 additions & 61 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
# Load environment variables early
load_dotenv()

VERSION = "1.0.1"

# Monkey patch save_to_yml to prevent writes to library directory
def patched_save_to_yml(yml_path, cm):
"""Patched version of save_to_yml that prevents writes to library directory"""
Expand All @@ -21,51 +23,6 @@ def patched_save_to_yml(yml_path, cm):
from hummingbot.client.config import config_helpers
config_helpers.save_to_yml = patched_save_to_yml

# Monkey patch start_network to conditionally start order book tracker
# async def patched_start_network(self):
# """
# Patched version of start_network that conditionally starts the order book tracker.
# Only starts order book tracker when trading pairs are configured to avoid issues.
# """
# import logging
# from hummingbot.core.utils.async_utils import safe_ensure_future
#
# logger = logging.getLogger(__name__)
# logger.debug(f"Starting network for {self.__class__.__name__} (patched)")
#
# # Stop any existing network first
# self._stop_network()
#
# # Check if we have trading pairs configured
# has_trading_pairs = hasattr(self, '_trading_pairs') and len(self._trading_pairs) > 0
#
# # Start order book tracker only if we have trading pairs
# if has_trading_pairs:
# logger.debug(f"Starting order book tracker for {self.__class__.__name__} with {len(self._trading_pairs)} trading pairs")
# self.order_book_tracker.start()
# else:
# logger.debug(f"Skipping order book tracker for {self.__class__.__name__} - no trading pairs configured")
#
# # Start the essential polling tasks if trading is required
# if self.is_trading_required:
# try:
# self._trading_rules_polling_task = safe_ensure_future(self._trading_rules_polling_loop())
# self._trading_fees_polling_task = safe_ensure_future(self._trading_fees_polling_loop())
# self._status_polling_task = safe_ensure_future(self._status_polling_loop())
# self._user_stream_tracker_task = self._create_user_stream_tracker_task()
# self._user_stream_event_listener_task = safe_ensure_future(self._user_stream_event_listener())
# self._lost_orders_update_task = safe_ensure_future(self._lost_orders_update_polling_loop())
#
# logger.debug(f"Started network tasks for {self.__class__.__name__}")
# except Exception as e:
# logger.error(f"Error starting network for {self.__class__.__name__}: {e}")
# else:
# logger.debug(f"Trading not required for {self.__class__.__name__}, skipping network start")
#
# # Apply the start_network patch - this will be applied to ExchangePyBase after import
# from hummingbot.connector.exchange_py_base import ExchangePyBase
# ExchangePyBase.start_network = patched_start_network

from hummingbot.core.rate_oracle.rate_oracle import RateOracle

from fastapi import Depends, FastAPI, HTTPException, status
Expand Down Expand Up @@ -128,26 +85,26 @@ async def lifespan(app: FastAPI):
secrets_manager = ETHKeyFileSecretManger(password=settings.security.config_password)
BackendAPISecurity.store_password_verification(secrets_manager)
logging.info("Created password verification file for master_account")

# Initialize MarketDataProvider with empty connectors (will use non-trading connectors)
market_data_provider = MarketDataProvider(connectors={})

# Initialize MarketDataFeedManager with lifecycle management
market_data_feed_manager = MarketDataFeedManager(
market_data_provider=market_data_provider,
rate_oracle=RateOracle.get_instance(),
cleanup_interval=settings.market_data.cleanup_interval,
feed_timeout=settings.market_data.feed_timeout
)

# Initialize services
bots_orchestrator = BotsOrchestrator(
broker_host=settings.broker.host,
broker_port=settings.broker.port,
broker_username=settings.broker.username,
broker_password=settings.broker.password
)

accounts_service = AccountsService(
account_update_interval=settings.app.account_update_interval,
market_data_feed_manager=market_data_feed_manager
Expand All @@ -158,34 +115,34 @@ async def lifespan(app: FastAPI):
settings.aws.secret_key,
settings.aws.s3_default_bucket_name
)

# Initialize database
await accounts_service.ensure_db_initialized()

# Store services in app state
app.state.bots_orchestrator = bots_orchestrator
app.state.accounts_service = accounts_service
app.state.docker_service = docker_service
app.state.bot_archiver = bot_archiver
app.state.market_data_feed_manager = market_data_feed_manager

# Start services
bots_orchestrator.start()
accounts_service.start()
market_data_feed_manager.start()

yield

# Shutdown services
bots_orchestrator.stop()
await accounts_service.stop()

# Stop market data feed manager (which will stop all feeds)
market_data_feed_manager.stop()

# Clean up docker service
docker_service.cleanup()

# Close database connections
await accounts_service.db_manager.close()

Expand All @@ -194,7 +151,7 @@ async def lifespan(app: FastAPI):
app = FastAPI(
title="Hummingbot API",
description="API for managing Hummingbot trading instances",
version="1.0.0",
version=VERSION,
lifespan=lifespan,
)

Expand Down Expand Up @@ -249,7 +206,7 @@ def auth_user(
async def root():
"""API root endpoint returning basic information."""
return {
"name": "Backend API",
"version": "0.2.0",
"name": "Hummingbot API",
"version": VERSION,
"status": "running",
}
}
11 changes: 2 additions & 9 deletions routers/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,11 @@ async def list_controller_configs():
config_name = config_file.replace('.yml', '')
try:
config = fs_util.read_yaml_file(f"conf/controllers/{config_file}")
configs.append({
"config_name": config_name,
"controller_name": config.get("controller_name", "unknown"),
"controller_type": config.get("controller_type", "unknown"),
"connector_name": config.get("connector_name", "unknown"),
"trading_pair": config.get("trading_pair", "unknown"),
"total_amount_quote": config.get("total_amount_quote", 0)
})
configs.append(config)
except Exception as e:
# If config is malformed, still include it with basic info
configs.append({
"config_name": config_name,
"id": config_name,
"controller_name": "error",
"controller_type": "error",
"error": str(e)
Expand Down