Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
6d4350f
(feat) bump hbot client version
cardosofede Dec 5, 2025
6ddc34b
(feat) fix ui issues
cardosofede Dec 5, 2025
7a690a3
(feat) add pmm mister
cardosofede Dec 5, 2025
7bf4dd8
(feat) fix token cache
cardosofede Dec 8, 2025
7447f54
(feat) improve pmm mister controller
cardosofede Dec 10, 2025
f3fbee8
(feat) improve dex commands
cardosofede Dec 10, 2025
a4f52cd
(feat) add env example
cardosofede Dec 10, 2025
5dd556c
(feat) support only ethereum and solana
cardosofede Dec 10, 2025
73b7c2c
(feat) improve pools formatting
cardosofede Dec 10, 2025
90d5d85
Update README.md with clone repo
nikspz Dec 10, 2025
9384d90
fix /start command error
david-hummingbot Dec 10, 2025
380422e
Add checks for condor_bot_data.pickle existence
david-hummingbot Dec 10, 2025
943582f
Simplify setup by removing pickle file handling
david-hummingbot Dec 10, 2025
cdd6107
Implement persistence handling for Condor bot
david-hummingbot Dec 10, 2025
ff4f039
Change volume mapping for condor bot data
david-hummingbot Dec 10, 2025
d931547
(feat) improve controllers cancel option
cardosofede Dec 10, 2025
bf24977
(feat) add wallet selection for networks
cardosofede Dec 10, 2025
8298113
(feat) add decorators for gateway and hbot api requirements
cardosofede Dec 10, 2025
9e7a389
(feat) bump hbot client version
cardosofede Dec 10, 2025
d60f499
(feat) add defualt server by chat id
cardosofede Dec 10, 2025
c1ec7fe
(feat) improve dex menu
cardosofede Dec 10, 2025
9168019
(feat) add server by default chat id
cardosofede Dec 10, 2025
c9fb0e6
(feat) client by chat id
cardosofede Dec 10, 2025
4a957ae
(feat) unify in setup env
cardosofede Dec 10, 2025
3ac5cb9
(feat) improve dex menu
cardosofede Dec 10, 2025
cf2dad6
(feat) improve grid limits
cardosofede Dec 10, 2025
5737049
(feat) improve portfolio
cardosofede Dec 10, 2025
244a081
(feat) add refresh func
cardosofede Dec 10, 2025
a3cf060
(feat) improve pools handling
cardosofede Dec 10, 2025
617bbe2
(feat) apply mikes suggestion
cardosofede Dec 10, 2025
892b273
(feat) improve server status
cardosofede Dec 10, 2025
d41fe1c
(feat) improve bots formatting
cardosofede Dec 10, 2025
8fdad30
(feat) improve bots formatting
cardosofede Dec 10, 2025
26e4cbf
add docker build workflow
david-hummingbot Dec 11, 2025
61b4f63
add dockerignore and workflow
david-hummingbot Dec 11, 2025
93b6ed8
Merge branch 'main' into feat/upgrade_one
david-hummingbot Dec 11, 2025
3aad1a6
(feat) improve dex with pics
cardosofede Dec 12, 2025
a0a9916
(feat) avoid api keys of routers and amms
cardosofede Dec 12, 2025
1e1428d
(feat) improve grid strike menu
cardosofede Dec 12, 2025
09abd7c
(feat) merge menues
cardosofede Dec 12, 2025
c422308
(feat) improve config menu and width
cardosofede Dec 12, 2025
598d417
(feat) improve formatting style
cardosofede Dec 15, 2025
9f5ca83
(feat) use shared viz
cardosofede Dec 15, 2025
12e5cb9
(feat) improvce gatewya config
cardosofede Dec 15, 2025
baaf306
(feat) remove gateway connectors
cardosofede Dec 15, 2025
b31bf68
(feat) improve grid flow
cardosofede Dec 15, 2025
03a7b3a
(feat) improve dex experience
cardosofede Dec 15, 2025
34e3feb
(feat) simplify grid config
cardosofede Dec 16, 2025
f69c489
(feat) improve grid strike analisis
cardosofede Dec 16, 2025
5756705
(feat) refactor pmm mister
cardosofede Dec 16, 2025
bb087d3
(feat) add cache invalidation
cardosofede Dec 16, 2025
8e43dfa
(feat) add chat id to get the client
cardosofede Dec 16, 2025
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
12 changes: 12 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.env
servers.yml
data/
__pycache__/
.git/
*.pyc
/data
/logs
/build
/dist
/.git
/.github
43 changes: 43 additions & 0 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Docker Build & Push (condor)

on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches:
- main

jobs:
docker-build:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Determine Docker tag
id: vars
run: |
if [[ "${{ github.event_name }}" == "push" ]]; then
echo "TAG=latest" >> $GITHUB_ENV
else
echo "TAG=development" >> $GITHUB_ENV
fi

- name: Build and push Docker image (multi-arch)
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: hummingbot/condor:${{ env.TAG }}
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ A Telegram bot for monitoring and trading with Hummingbot via the Backend API.
**Prerequisites:** Python 3.11+, Conda, Hummingbot Backend API running, Telegram Bot Token

```bash
# Install
# clone repo
git clone https://github.com/hummingbot/condor.git
cd condor
# environment setup
conda env create -f environment.yml
conda activate condor

Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ services:
- .env
volumes:
# Persist bot data (user preferences, trading context, etc.)
- ./condor_bot_data.pickle:/app/condor_bot_data.pickle
- ./data:/app/data
# Mount servers config
- ./servers.yml:/app/servers.yml
environment:
Expand Down
214 changes: 212 additions & 2 deletions handlers/bots/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,14 @@
show_controller_detail,
handle_stop_controller,
handle_confirm_stop_controller,
handle_quick_stop_controller,
handle_quick_start_controller,
handle_stop_bot,
handle_confirm_stop_bot,
show_bot_logs,
handle_back_to_bot,
handle_refresh_bot,
handle_refresh_controller,
# Controller chart & edit
show_controller_chart,
show_controller_edit,
Expand All @@ -44,7 +47,27 @@
show_controller_configs_menu,
show_configs_list,
handle_configs_page,
# Unified configs menu with multi-select
show_configs_by_type,
show_type_selector,
handle_cfg_toggle,
handle_cfg_page,
handle_cfg_clear_selection,
handle_cfg_delete_confirm,
handle_cfg_delete_execute,
handle_cfg_deploy,
# Edit loop
handle_cfg_edit_loop,
show_cfg_edit_form,
handle_cfg_edit_field,
process_cfg_edit_input,
handle_cfg_edit_prev,
handle_cfg_edit_next,
handle_cfg_edit_save,
handle_cfg_edit_save_all,
handle_cfg_edit_cancel,
show_new_grid_strike_form,
show_new_pmm_mister_form,
show_config_form,
handle_set_field,
handle_toggle_side,
Expand Down Expand Up @@ -86,6 +109,11 @@
handle_gs_wizard_amount,
handle_gs_accept_prices,
handle_gs_back_to_prices,
handle_gs_back_to_connector,
handle_gs_back_to_pair,
handle_gs_back_to_side,
handle_gs_back_to_leverage,
handle_gs_back_to_amount,
handle_gs_interval_change,
handle_gs_wizard_take_profit,
handle_gs_edit_id,
Expand All @@ -100,6 +128,21 @@
handle_gs_review_back,
handle_gs_edit_price,
process_gs_wizard_input,
# PMM Mister wizard
handle_pmm_wizard_connector,
handle_pmm_wizard_pair,
handle_pmm_wizard_leverage,
handle_pmm_wizard_allocation,
handle_pmm_wizard_spreads,
handle_pmm_wizard_tp,
handle_pmm_save,
handle_pmm_review_back,
handle_pmm_edit_id,
handle_pmm_edit_field,
handle_pmm_set_field,
handle_pmm_edit_advanced,
handle_pmm_adv_setting,
process_pmm_wizard_input,
)
from ._shared import clear_bots_state, SIDE_LONG, SIDE_SHORT

Expand Down Expand Up @@ -133,12 +176,13 @@ async def bots_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> No
# Check if specific bot name was provided
if update.message and context.args and len(context.args) > 0:
bot_name = context.args[0]
chat_id = update.effective_chat.id
# For direct command with bot name, show detail view
from utils.telegram_formatters import format_bot_status, format_error_message
from ._shared import get_bots_client

try:
client = await get_bots_client()
client = await get_bots_client(chat_id)
bot_status = await client.bot_orchestration.get_bot_status(bot_name)
response_message = format_bot_status(bot_status)
await msg.reply_text(response_message, parse_mode="MarkdownV2")
Expand Down Expand Up @@ -192,9 +236,73 @@ async def bots_callback_handler(update: Update, context: ContextTypes.DEFAULT_TY
elif main_action == "list_configs":
await show_configs_list(update, context)

# Unified configs menu with multi-select
elif main_action == "cfg_select_type":
await show_type_selector(update, context)

elif main_action == "cfg_type":
if len(action_parts) > 1:
controller_type = action_parts[1]
await show_configs_by_type(update, context, controller_type)

elif main_action == "cfg_toggle":
if len(action_parts) > 1:
config_id = action_parts[1]
await handle_cfg_toggle(update, context, config_id)

elif main_action == "cfg_page":
if len(action_parts) > 1:
page = int(action_parts[1])
await handle_cfg_page(update, context, page)

elif main_action == "cfg_clear_selection":
await handle_cfg_clear_selection(update, context)

elif main_action == "cfg_deploy":
await handle_cfg_deploy(update, context)

elif main_action == "cfg_delete_confirm":
await handle_cfg_delete_confirm(update, context)

elif main_action == "cfg_delete_execute":
await handle_cfg_delete_execute(update, context)

# Edit loop handlers
elif main_action == "cfg_edit_loop":
await handle_cfg_edit_loop(update, context)

elif main_action == "cfg_edit_form":
await show_cfg_edit_form(update, context)

elif main_action == "cfg_edit_field":
if len(action_parts) > 1:
field_name = action_parts[1]
await handle_cfg_edit_field(update, context, field_name)

elif main_action == "cfg_edit_prev":
await handle_cfg_edit_prev(update, context)

elif main_action == "cfg_edit_next":
await handle_cfg_edit_next(update, context)

elif main_action == "cfg_edit_save":
await handle_cfg_edit_save(update, context)

elif main_action == "cfg_edit_save_all":
await handle_cfg_edit_save_all(update, context)

elif main_action == "cfg_edit_cancel":
await handle_cfg_edit_cancel(update, context)

elif main_action == "noop":
pass # Do nothing - used for pagination display button

elif main_action == "new_grid_strike":
await show_new_grid_strike_form(update, context)

elif main_action == "new_pmm_mister":
await show_new_pmm_mister_form(update, context)

elif main_action == "edit_config":
if len(action_parts) > 1:
config_index = int(action_parts[1])
Expand Down Expand Up @@ -327,6 +435,21 @@ async def bots_callback_handler(update: Update, context: ContextTypes.DEFAULT_TY
elif main_action == "gs_back_to_prices":
await handle_gs_back_to_prices(update, context)

elif main_action == "gs_back_to_connector":
await handle_gs_back_to_connector(update, context)

elif main_action == "gs_back_to_pair":
await handle_gs_back_to_pair(update, context)

elif main_action == "gs_back_to_side":
await handle_gs_back_to_side(update, context)

elif main_action == "gs_back_to_leverage":
await handle_gs_back_to_leverage(update, context)

elif main_action == "gs_back_to_amount":
await handle_gs_back_to_amount(update, context)

elif main_action == "gs_interval":
if len(action_parts) > 1:
interval = action_parts[1]
Expand Down Expand Up @@ -372,6 +495,65 @@ async def bots_callback_handler(update: Update, context: ContextTypes.DEFAULT_TY
elif main_action == "gs_review_back":
await handle_gs_review_back(update, context)

# PMM Mister wizard
elif main_action == "pmm_connector":
if len(action_parts) > 1:
connector = action_parts[1]
await handle_pmm_wizard_connector(update, context, connector)

elif main_action == "pmm_pair":
if len(action_parts) > 1:
pair = action_parts[1]
await handle_pmm_wizard_pair(update, context, pair)

elif main_action == "pmm_leverage":
if len(action_parts) > 1:
leverage = int(action_parts[1])
await handle_pmm_wizard_leverage(update, context, leverage)

elif main_action == "pmm_alloc":
if len(action_parts) > 1:
allocation = float(action_parts[1])
await handle_pmm_wizard_allocation(update, context, allocation)

elif main_action == "pmm_spreads":
if len(action_parts) > 1:
spreads = action_parts[1]
await handle_pmm_wizard_spreads(update, context, spreads)

elif main_action == "pmm_tp":
if len(action_parts) > 1:
tp = float(action_parts[1])
await handle_pmm_wizard_tp(update, context, tp)

elif main_action == "pmm_save":
await handle_pmm_save(update, context)

elif main_action == "pmm_review_back":
await handle_pmm_review_back(update, context)

elif main_action == "pmm_edit_id":
await handle_pmm_edit_id(update, context)

elif main_action == "pmm_edit":
if len(action_parts) > 1:
field = action_parts[1]
await handle_pmm_edit_field(update, context, field)

elif main_action == "pmm_set":
if len(action_parts) > 2:
field = action_parts[1]
value = action_parts[2]
await handle_pmm_set_field(update, context, field, value)

elif main_action == "pmm_edit_advanced":
await handle_pmm_edit_advanced(update, context)

elif main_action == "pmm_adv":
if len(action_parts) > 1:
setting = action_parts[1]
await handle_pmm_adv_setting(update, context, setting)

# Bot detail
elif main_action == "bot_detail":
if len(action_parts) > 1:
Expand Down Expand Up @@ -409,6 +591,17 @@ async def bots_callback_handler(update: Update, context: ContextTypes.DEFAULT_TY
elif main_action == "confirm_stop_ctrl":
await handle_confirm_stop_controller(update, context)

# Quick stop/start controller (from bot detail view)
elif main_action == "stop_ctrl_quick":
if len(action_parts) > 1:
idx = int(action_parts[1])
await handle_quick_stop_controller(update, context, idx)

elif main_action == "start_ctrl_quick":
if len(action_parts) > 1:
idx = int(action_parts[1])
await handle_quick_start_controller(update, context, idx)

# Stop bot (uses context)
elif main_action == "stop_bot":
await handle_stop_bot(update, context)
Expand All @@ -427,6 +620,11 @@ async def bots_callback_handler(update: Update, context: ContextTypes.DEFAULT_TY
elif main_action == "refresh_bot":
await handle_refresh_bot(update, context)

elif main_action == "refresh_ctrl":
if len(action_parts) > 1:
idx = int(action_parts[1])
await handle_refresh_controller(update, context, idx)

else:
logger.warning(f"Unknown bots action: {action}")
await query.message.reply_text(f"Unknown action: {action}")
Expand Down Expand Up @@ -465,7 +663,10 @@ async def bots_message_handler(update: Update, context: ContextTypes.DEFAULT_TYP
# Handle controller config field input
if bots_state.startswith("set_field:"):
await process_field_input(update, context, user_input)
# Handle live controller field input
# Handle live controller bulk edit input
elif bots_state == "ctrl_bulk_edit":
await process_controller_field_input(update, context, user_input)
# Handle live controller field input (legacy single field)
elif bots_state.startswith("ctrl_set:"):
await process_controller_field_input(update, context, user_input)
# Handle deploy field input (legacy form)
Expand All @@ -483,6 +684,15 @@ async def bots_message_handler(update: Update, context: ContextTypes.DEFAULT_TYP
# Handle Grid Strike wizard input
elif bots_state == "gs_wizard_input":
await process_gs_wizard_input(update, context, user_input)
# Handle PMM Mister wizard input
elif bots_state == "pmm_wizard_input":
await process_pmm_wizard_input(update, context, user_input)
# Handle config edit loop field input (legacy single field)
elif bots_state.startswith("cfg_edit_input:"):
await process_cfg_edit_input(update, context, user_input)
# Handle config bulk edit (key=value format)
elif bots_state == "cfg_bulk_edit":
await process_cfg_edit_input(update, context, user_input)
else:
logger.debug(f"Unhandled bots state: {bots_state}")

Expand Down
Loading