Stage 07.1 - auto trading skeleton UI, state machine and mock controls
This commit is contained in:
@@ -6,29 +6,72 @@ from aiogram import F, Router
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.types import CallbackQuery, InlineKeyboardMarkup, Message
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
from aiogram.exceptions import TelegramBadRequest
|
||||
|
||||
from src.telegram.menus import AUTO_TEXT
|
||||
from src.telegram.ui.common import mode_line
|
||||
from src.trading.auto.service import AutoTradeService
|
||||
|
||||
|
||||
router = Router(name="auto")
|
||||
|
||||
|
||||
def _status_label(status: str) -> str:
|
||||
mapping = {
|
||||
"OFF": "⚪ Выключена",
|
||||
"OBSERVING": "👀 Наблюдение",
|
||||
"RUNNING": "🟢 Активна",
|
||||
}
|
||||
return mapping.get(status, status)
|
||||
|
||||
|
||||
def _auto_keyboard() -> InlineKeyboardMarkup:
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
# 1 ряд
|
||||
builder.button(text="▶️ Start", callback_data="auto:start")
|
||||
builder.button(text="👀 Watch", callback_data="auto:observe")
|
||||
builder.button(text="🛑 Stop", callback_data="auto:stop")
|
||||
|
||||
# 2 ряд
|
||||
builder.button(text="🛠️ Настройки", callback_data="settings:auto")
|
||||
builder.adjust(1)
|
||||
|
||||
builder.adjust(3, 1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def _build_auto_text() -> str:
|
||||
state = AutoTradeService().get_state()
|
||||
|
||||
strategy = state.strategy or "—"
|
||||
risk = f"{state.risk_percent:.1f}%" if state.risk_percent is not None else "—"
|
||||
|
||||
return (
|
||||
"<b>🤖 Автоторговля</b>\n"
|
||||
f"{mode_line()}"
|
||||
f"Статус: {_status_label(state.status)}\n"
|
||||
f"Стратегия: {strategy}\n"
|
||||
f"Инструмент: {state.symbol}\n"
|
||||
f"Риск: {risk}\n"
|
||||
f"PnL: {state.pnl_usd:.2f} USD"
|
||||
)
|
||||
|
||||
|
||||
async def _render_auto_screen(
|
||||
target_message: Message,
|
||||
*,
|
||||
edit_mode: bool,
|
||||
) -> None:
|
||||
text = _build_auto_text()
|
||||
|
||||
if edit_mode:
|
||||
await target_message.edit_text(AUTO_TEXT, reply_markup=_auto_keyboard())
|
||||
try:
|
||||
await target_message.edit_text(text, reply_markup=_auto_keyboard())
|
||||
except TelegramBadRequest as exc:
|
||||
if "message is not modified" in str(exc).lower():
|
||||
return
|
||||
raise
|
||||
else:
|
||||
await target_message.answer(AUTO_TEXT, reply_markup=_auto_keyboard())
|
||||
await target_message.answer(text, reply_markup=_auto_keyboard())
|
||||
|
||||
|
||||
@router.message(F.text.in_({"🤖 Автоторговля", "🤖 Авто"}))
|
||||
@@ -46,4 +89,37 @@ async def open_auto_from_callback(callback: CallbackQuery, state: FSMContext) ->
|
||||
return
|
||||
|
||||
await _render_auto_screen(callback.message, edit_mode=True)
|
||||
await callback.answer()
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@router.callback_query(F.data == "auto:start")
|
||||
async def auto_start(callback: CallbackQuery) -> None:
|
||||
service = AutoTradeService()
|
||||
_, message = service.start()
|
||||
|
||||
if callback.message is not None:
|
||||
await _render_auto_screen(callback.message, edit_mode=True)
|
||||
|
||||
await callback.answer(message)
|
||||
|
||||
|
||||
@router.callback_query(F.data == "auto:observe")
|
||||
async def auto_observe(callback: CallbackQuery) -> None:
|
||||
service = AutoTradeService()
|
||||
_, message = service.observe()
|
||||
|
||||
if callback.message is not None:
|
||||
await _render_auto_screen(callback.message, edit_mode=True)
|
||||
|
||||
await callback.answer(message)
|
||||
|
||||
|
||||
@router.callback_query(F.data == "auto:stop")
|
||||
async def auto_stop(callback: CallbackQuery) -> None:
|
||||
service = AutoTradeService()
|
||||
_, message = service.stop()
|
||||
|
||||
if callback.message is not None:
|
||||
await _render_auto_screen(callback.message, edit_mode=True)
|
||||
|
||||
await callback.answer(message)
|
||||
Reference in New Issue
Block a user