diff --git a/app/src/telegram/handlers/auto.py b/app/src/telegram/handlers/auto.py index 43f1799..a50995d 100644 --- a/app/src/telegram/handlers/auto.py +++ b/app/src/telegram/handlers/auto.py @@ -15,6 +15,15 @@ from src.trading.auto.service import AutoTradeService router = Router(name="auto") +def _strategy_label(strategy: str | None) -> str: + mapping = { + "TREND": "📈 Trend Following", + "GRID": "🧩 Grid Trading", + "SCALP": "⚡ Scalping", + } + return mapping.get(strategy or "", "—") + + def _status_label(status: str) -> str: mapping = { "OFF": "⚪ Выключена", @@ -42,7 +51,7 @@ def _auto_keyboard() -> InlineKeyboardMarkup: def _build_auto_text() -> str: state = AutoTradeService().get_state() - strategy = state.strategy or "—" + strategy = _strategy_label(state.strategy) risk = f"{state.risk_percent:.1f}%" if state.risk_percent is not None else "—" return ( diff --git a/app/src/telegram/handlers/system.py b/app/src/telegram/handlers/system.py index 0a5a037..706aa5b 100644 --- a/app/src/telegram/handlers/system.py +++ b/app/src/telegram/handlers/system.py @@ -11,6 +11,7 @@ from src.core.system_status import build_system_text, get_system_snapshot, has_s from src.core.config import load_settings from src.core.constants import APP_NAME, APP_VERSION from src.trading.journal.service import JournalService +from src.trading.auto.service import AutoTradeService router = Router(name="system") @@ -169,24 +170,141 @@ async def open_auto_settings(callback: CallbackQuery) -> None: await callback.answer("Сообщение не найдено", show_alert=True) return + state = AutoTradeService().get_state() + + strategy_map = { + "TREND": "📈 Trend Following", + "GRID": "🧩 Grid Trading", + "SCALP": "⚡ Scalping", + } + strategy = strategy_map.get(state.strategy or "", "—") + risk = f"{state.risk_percent:.1f}%" if state.risk_percent is not None else "—" + text = ( "🤖 Автоторговля\n\n" "СИСТЕМА · Настройки\n\n" - "Статус: —\n" - "Стратегия: —\n" - "Риск: —\n\n" - "В разработке." + f"Стратегия: {strategy}\n" + f"Инструмент: {state.symbol}\n" + f"Риск: {risk}\n\n" + "Выберите настройку:" ) builder = InlineKeyboardBuilder() + builder.button(text="🧠 Стратегия", callback_data="settings:auto_strategy") + builder.button(text="📈 Инструмент", callback_data="settings:auto_symbol") + builder.button(text="🛡️ Риск", callback_data="settings:auto_risk") builder.button(text="⬅️ Назад", callback_data="system:management") builder.button(text="🤖 Автоторговля", callback_data="auto:home") - builder.adjust(2) + builder.adjust(2, 1, 2) await callback.message.edit_text(text, reply_markup=builder.as_markup()) await callback.answer() +@router.callback_query(F.data == "settings:auto_strategy") +async def open_auto_strategy_settings(callback: CallbackQuery) -> None: + if callback.message is None: + await callback.answer("Сообщение не найдено", show_alert=True) + return + + text = ( + "🧠 Стратегия\n\n" + "СИСТЕМА · Настройки · Автоторговля\n\n" + "Выберите стратегию:" + ) + + builder = InlineKeyboardBuilder() + builder.button(text="📈 Trend", callback_data="settings:auto_strategy:trend") + builder.button(text="🧩 Grid", callback_data="settings:auto_strategy:grid") + builder.button(text="⚡ Scalp", callback_data="settings:auto_strategy:scalp") + builder.button(text="⬅️ Назад", callback_data="settings:auto") + builder.adjust(3, 1) + + await callback.message.edit_text(text, reply_markup=builder.as_markup()) + await callback.answer() + + +@router.callback_query(F.data.startswith("settings:auto_strategy:")) +async def set_auto_strategy(callback: CallbackQuery) -> None: + strategy = callback.data.split(":", 2)[2] + AutoTradeService().set_strategy(strategy.upper()) + + if callback.message is not None: + await open_auto_settings(callback) + + await callback.answer("Стратегия обновлена") + + +@router.callback_query(F.data == "settings:auto_symbol") +async def open_auto_symbol_settings(callback: CallbackQuery) -> None: + if callback.message is None: + await callback.answer("Сообщение не найдено", show_alert=True) + return + + settings = load_settings() + + text = ( + "📈 Инструмент\n\n" + "СИСТЕМА · Настройки · Автоторговля\n\n" + "Выберите инструмент:" + ) + + builder = InlineKeyboardBuilder() + builder.button(text=settings.default_symbol, callback_data=f"settings:auto_symbol:{settings.default_symbol}") + builder.button(text="BTCUSDT", callback_data="settings:auto_symbol:BTCUSDT") + builder.button(text="ETHUSDT", callback_data="settings:auto_symbol:ETHUSDT") + builder.button(text="⬅️ Назад", callback_data="settings:auto") + builder.adjust(1, 2, 1) + + await callback.message.edit_text(text, reply_markup=builder.as_markup()) + await callback.answer() + + +@router.callback_query(F.data.startswith("settings:auto_symbol:")) +async def set_auto_symbol(callback: CallbackQuery) -> None: + symbol = callback.data.split(":", 2)[2] + AutoTradeService().set_symbol(symbol) + + if callback.message is not None: + await open_auto_settings(callback) + + await callback.answer("Инструмент обновлён") + + +@router.callback_query(F.data == "settings:auto_risk") +async def open_auto_risk_settings(callback: CallbackQuery) -> None: + if callback.message is None: + await callback.answer("Сообщение не найдено", show_alert=True) + return + + text = ( + "🛡️ Риск\n\n" + "СИСТЕМА · Настройки · Автоторговля\n\n" + "Выберите риск на сделку:" + ) + + builder = InlineKeyboardBuilder() + builder.button(text="0.5%", callback_data="settings:auto_risk:0.5") + builder.button(text="1.0%", callback_data="settings:auto_risk:1.0") + builder.button(text="2.0%", callback_data="settings:auto_risk:2.0") + builder.button(text="⬅️ Назад", callback_data="settings:auto") + builder.adjust(3, 1) + + await callback.message.edit_text(text, reply_markup=builder.as_markup()) + await callback.answer() + + +@router.callback_query(F.data.startswith("settings:auto_risk:")) +async def set_auto_risk(callback: CallbackQuery) -> None: + risk = float(callback.data.split(":", 2)[2]) + AutoTradeService().set_risk_percent(risk) + + if callback.message is not None: + await open_auto_settings(callback) + + await callback.answer("Риск обновлён") + + @router.callback_query(F.data == "settings:trade") async def open_trade_settings(callback: CallbackQuery) -> None: if callback.message is None: diff --git a/app/src/trading/auto/service.py b/app/src/trading/auto/service.py index c94bda5..e441e16 100644 --- a/app/src/trading/auto/service.py +++ b/app/src/trading/auto/service.py @@ -49,4 +49,19 @@ class AutoTradeService: return state, "Автоторговля уже выключена." state.status = "OFF" - return state, "Автоторговля выключена." \ No newline at end of file + return state, "Автоторговля выключена." + + def set_symbol(self, symbol: str) -> AutoTradeState: + state = self.get_state() + state.symbol = symbol + return state + + def set_strategy(self, strategy: str) -> AutoTradeState: + state = self.get_state() + state.strategy = strategy + return state + + def set_risk_percent(self, risk_percent: float) -> AutoTradeState: + state = self.get_state() + state.risk_percent = risk_percent + return state \ No newline at end of file diff --git a/docs/decisions/0016-auto-trading-settings-in-memory.md b/docs/decisions/0016-auto-trading-settings-in-memory.md new file mode 100644 index 0000000..62a4713 --- /dev/null +++ b/docs/decisions/0016-auto-trading-settings-in-memory.md @@ -0,0 +1,19 @@ +# 0016 — Auto Trading Settings in Memory + +## Решение + +На этапе 07.2 настройки автоторговли хранятся в памяти процесса. + +## Причины + +- быстрее реализовать MVP; + +- не усложнять storage migration; + +- достаточно для тестирования UI / logic. + +## Последствия + +После перезапуска бота настройки сбрасываются. + +В будущем настройки будут храниться в БД / config. \ No newline at end of file diff --git a/docs/roadmap/master-roadmap.md b/docs/roadmap/master-roadmap.md index b5134bd..dfb6131 100644 --- a/docs/roadmap/master-roadmap.md +++ b/docs/roadmap/master-roadmap.md @@ -93,7 +93,9 @@ ✔ mock controls ### 07.2 -⏳ real settings +✔ real settings +✔ strategy presets +✔ risk presets ### 07.3 ⏳ background loop diff --git a/docs/roadmap/stage-07-roadmap.md b/docs/roadmap/stage-07-roadmap.md index 545ebc5..9389a8c 100644 --- a/docs/roadmap/stage-07-roadmap.md +++ b/docs/roadmap/stage-07-roadmap.md @@ -16,9 +16,10 @@ ## 07.2 — Real settings -⏳ стратегия -⏳ риск -⏳ символ +✔ стратегия +✔ риск +✔ символ +✔ presets UI --- diff --git a/docs/stages/stage-07_2-auto-trading-real-settings.md b/docs/stages/stage-07_2-auto-trading-real-settings.md new file mode 100644 index 0000000..bdbdb1d --- /dev/null +++ b/docs/stages/stage-07_2-auto-trading-real-settings.md @@ -0,0 +1,91 @@ +# Stage 07.2 — Auto Trading Real Settings + +## Что сделано + +Реализованы реальные настройки автоторговли. + +--- + +## 1. Настройки автоторговли + +Экран: + +🤖 Автоторговля → 🛠️ Настройки + +Теперь показывает реальные значения: + +- Стратегия +- Инструмент +- Риск + +--- + +## 2. Настройка стратегии + +Добавлен выбор стратегии: + +- 📈 Trend Following +- 🧩 Grid Trading +- ⚡ Scalping + +В state сохраняются short-code: + +- TREND +- GRID +- SCALP + +--- + +## 3. Настройка инструмента + +Добавлен выбор торгового инструмента: + +Примеры: + +- BTCUSD +- BTCUSDT +- ETHUSDT + +Значение влияет на экран автоторговли. + +--- + +## 4. Настройка риска + +Добавлен выбор риска: + +- 0.5% +- 1.0% +- 2.0% + +Значение влияет на расчёт будущего position sizing. + +--- + +## 5. Service layer update + +В AutoTradeService добавлены методы: + +- set_symbol() +- set_strategy() +- set_risk_percent() + +--- + +## 6. UI improvement + +Стратегии отображаются в friendly-виде: + +- 📈 Trend Following +- 🧩 Grid Trading +- ⚡ Scalping + +--- + +## Commit + +``` +git add . +git commit -m "Stage 07.2 - auto trading real settings and strategy presets" +git push +``` \ No newline at end of file