diff --git a/src/main.py b/src/main.py
index 58fb62d..ba14d64 100644
--- a/src/main.py
+++ b/src/main.py
@@ -29,7 +29,7 @@ from src.db import init_db, log_trade
from src.logging.decision_logger import DecisionLogger
from src.logging_config import setup_logging
from src.markets.schedule import MarketInfo, get_next_market_open, get_open_markets
-from src.notifications.telegram_client import TelegramClient
+from src.notifications.telegram_client import TelegramClient, TelegramCommandHandler
logger = logging.getLogger(__name__)
@@ -575,6 +575,40 @@ async def run(settings: Settings) -> None:
enabled=settings.TELEGRAM_ENABLED,
)
+ # Initialize Telegram command handler
+ command_handler = TelegramCommandHandler(telegram)
+
+ # Register basic commands
+ async def handle_start() -> None:
+ """Handle /start command."""
+ message = (
+ "🤖 The Ouroboros Trading Bot\n\n"
+ "AI-powered global stock trading agent with real-time notifications.\n\n"
+ "Available commands:\n"
+ "/help - Show this help message\n"
+ "/status - Current trading status\n"
+ "/positions - View holdings\n"
+ "/stop - Pause trading\n"
+ "/resume - Resume trading"
+ )
+ await telegram.send_message(message)
+
+ async def handle_help() -> None:
+ """Handle /help command."""
+ message = (
+ "📖 Available Commands\n\n"
+ "/start - Welcome message\n"
+ "/help - Show available commands\n"
+ "/status - Trading status (mode, markets, P&L)\n"
+ "/positions - Current holdings\n"
+ "/stop - Pause trading\n"
+ "/resume - Resume trading"
+ )
+ await telegram.send_message(message)
+
+ command_handler.register_command("start", handle_start)
+ command_handler.register_command("help", handle_help)
+
# Initialize volatility hunter
volatility_analyzer = VolatilityAnalyzer(min_volume_surge=2.0, min_price_change=1.0)
market_scanner = MarketScanner(
@@ -621,6 +655,12 @@ async def run(settings: Settings) -> None:
except Exception as exc:
logger.warning("System startup notification failed: %s", exc)
+ # Start command handler
+ try:
+ await command_handler.start_polling()
+ except Exception as exc:
+ logger.warning("Failed to start command handler: %s", exc)
+
try:
# Branch based on trading mode
if settings.TRADE_MODE == "daily":
@@ -831,6 +871,7 @@ async def run(settings: Settings) -> None:
pass # Normal — timeout means it's time for next cycle
finally:
# Clean up resources
+ await command_handler.stop_polling()
await broker.close()
await telegram.close()
db_conn.close()
diff --git a/tests/test_telegram_commands.py b/tests/test_telegram_commands.py
index c271b49..91332dd 100644
--- a/tests/test_telegram_commands.py
+++ b/tests/test_telegram_commands.py
@@ -253,6 +253,103 @@ class TestUpdateHandling:
await handler._handle_update(update)
+class TestBasicCommands:
+ """Test basic command implementations."""
+
+ @pytest.mark.asyncio
+ async def test_start_command_content(self) -> None:
+ """Start command contains welcome message and command list."""
+ client = TelegramClient(bot_token="123:abc", chat_id="456", enabled=True)
+ handler = TelegramCommandHandler(client)
+
+ mock_resp = AsyncMock()
+ mock_resp.status = 200
+ mock_resp.__aenter__ = AsyncMock(return_value=mock_resp)
+ mock_resp.__aexit__ = AsyncMock(return_value=False)
+
+ async def mock_start() -> None:
+ """Mock /start handler."""
+ message = (
+ "🤖 The Ouroboros Trading Bot\n\n"
+ "AI-powered global stock trading agent with real-time notifications.\n\n"
+ "Available commands:\n"
+ "/help - Show this help message\n"
+ "/status - Current trading status\n"
+ "/positions - View holdings\n"
+ "/stop - Pause trading\n"
+ "/resume - Resume trading"
+ )
+ await client.send_message(message)
+
+ handler.register_command("start", mock_start)
+
+ with patch("aiohttp.ClientSession.post", return_value=mock_resp) as mock_post:
+ update = {
+ "update_id": 1,
+ "message": {
+ "chat": {"id": 456},
+ "text": "/start",
+ },
+ }
+
+ await handler._handle_update(update)
+
+ # Verify message was sent
+ assert mock_post.call_count == 1
+ payload = mock_post.call_args.kwargs["json"]
+ assert "Ouroboros Trading Bot" in payload["text"]
+ assert "/help" in payload["text"]
+ assert "/status" in payload["text"]
+
+ @pytest.mark.asyncio
+ async def test_help_command_content(self) -> None:
+ """Help command lists all available commands."""
+ client = TelegramClient(bot_token="123:abc", chat_id="456", enabled=True)
+ handler = TelegramCommandHandler(client)
+
+ mock_resp = AsyncMock()
+ mock_resp.status = 200
+ mock_resp.__aenter__ = AsyncMock(return_value=mock_resp)
+ mock_resp.__aexit__ = AsyncMock(return_value=False)
+
+ async def mock_help() -> None:
+ """Mock /help handler."""
+ message = (
+ "📖 Available Commands\n\n"
+ "/start - Welcome message\n"
+ "/help - Show available commands\n"
+ "/status - Trading status (mode, markets, P&L)\n"
+ "/positions - Current holdings\n"
+ "/stop - Pause trading\n"
+ "/resume - Resume trading"
+ )
+ await client.send_message(message)
+
+ handler.register_command("help", mock_help)
+
+ with patch("aiohttp.ClientSession.post", return_value=mock_resp) as mock_post:
+ update = {
+ "update_id": 1,
+ "message": {
+ "chat": {"id": 456},
+ "text": "/help",
+ },
+ }
+
+ await handler._handle_update(update)
+
+ # Verify message was sent
+ assert mock_post.call_count == 1
+ payload = mock_post.call_args.kwargs["json"]
+ assert "Available Commands" in payload["text"]
+ assert "/start" in payload["text"]
+ assert "/help" in payload["text"]
+ assert "/status" in payload["text"]
+ assert "/positions" in payload["text"]
+ assert "/stop" in payload["text"]
+ assert "/resume" in payload["text"]
+
+
class TestGetUpdates:
"""Test getUpdates API interaction."""