diff --git a/app/src/core/config.py b/app/src/core/config.py index e87a28f..c0f1a99 100644 --- a/app/src/core/config.py +++ b/app/src/core/config.py @@ -48,6 +48,9 @@ class Settings: db_user: str db_password: str + # Debag helper + debug_enabled: bool + # helper: demo/live mode def is_demo_mode(self) -> bool: return "demo" in self.exchange_base_url.lower() @@ -87,6 +90,7 @@ def load_settings() -> Settings: app_env=os.getenv("APP_ENV", "dev").strip() or "dev", log_level=os.getenv("LOG_LEVEL", "INFO").strip().upper() or "INFO", tz=os.getenv("TZ", "Europe/Minsk").strip() or "Europe/Minsk", + debug_enabled=_parse_bool(os.getenv("DEBUG_ENABLED", "false")), # Exchange exchange_enabled=_parse_bool(os.getenv("EXCHANGE_ENABLED", "false")), diff --git a/app/src/telegram/handlers/debug.py b/app/src/telegram/handlers/debug.py index e69de29..21a5679 100644 --- a/app/src/telegram/handlers/debug.py +++ b/app/src/telegram/handlers/debug.py @@ -0,0 +1,111 @@ +# app/src/telegram/handlers/debug.py + +from __future__ import annotations + +from aiogram import F, Router +from aiogram.types import Message + +from src.core.config import load_settings +from src.trading.auto.runner import AutoTradeRunner +from src.trading.auto.service import AutoTradeService +from src.trading.journal.service import JournalService + + +router = Router(name="debug") + + +def _debug_enabled() -> bool: + return load_settings().debug_enabled + + +@router.message(F.text.startswith("/debug_signal")) +async def debug_signal(message: Message) -> None: + if not _debug_enabled(): + await message.answer("Debug mode выключен.") + return + + parts = (message.text or "").split() + signal = parts[1].upper() if len(parts) > 1 else "BUY" + + service = AutoTradeService() + state = service.debug_force_signal( + signal=signal, + confidence=0.9, + repeat_count=2, + reason=f"DEBUG FORCE {signal}", + ) + + AutoTradeRunner.start() + await AutoTradeRunner.process_last_event_now() + + JournalService().log_ui_info( + event_type="debug_signal_forced", + message=f"Debug-сигнал принудительно установлен: {signal}.", + screen="debug", + action="debug_signal", + user_id=message.from_user.id if message.from_user else None, + chat_id=message.chat.id, + payload={ + "signal": state.last_signal, + "decision_status": state.decision_status, + "confidence": state.last_signal_confidence, + "repeat_count": state.last_signal_repeat_count, + }, + ) + + await message.answer( + "✅ Debug signal forced\n\n" + f"Signal: {state.last_signal}\n" + f"Decision: {state.decision_status}\n" + f"Confidence: {state.last_signal_confidence:.2f}\n" + f"Repeats: {state.last_signal_repeat_count}" + ) + + +@router.message(F.text == "/debug_ready") +async def debug_ready(message: Message) -> None: + if not _debug_enabled(): + await message.answer("Debug mode выключен.") + return + + service = AutoTradeService() + state = service.debug_force_signal( + signal="BUY", + confidence=0.95, + repeat_count=2, + reason="DEBUG READY BUY", + ) + + AutoTradeRunner.start() + await AutoTradeRunner.process_last_event_now() + + await message.answer( + "✅ Debug READY создан\n\n" + f"Signal: {state.last_signal}\n" + f"Decision: {state.decision_status}" + ) + + +@router.message(F.text == "/debug_state") +async def debug_state(message: Message) -> None: + if not _debug_enabled(): + await message.answer("Debug mode выключен.") + return + + state = AutoTradeService().get_state() + + await message.answer( + "Debug Auto State\n\n" + f"Status: {state.status}\n" + f"Symbol: {state.symbol}\n" + f"Strategy: {state.strategy}\n" + f"Risk: {state.risk_percent}\n" + f"Leverage: {state.leverage}\n\n" + f"Signal: {state.last_signal}\n" + f"Repeats: {state.last_signal_repeat_count}\n" + f"Confidence: {state.last_signal_confidence:.2f}\n" + f"Decision: {state.decision_status}\n\n" + f"Position: {state.position_side}\n" + f"Entry: {state.entry_price}\n" + f"PnL: {state.unrealized_pnl_usd}" + ) \ No newline at end of file diff --git a/app/src/telegram/routers.py b/app/src/telegram/routers.py index 03db76b..0acc8d9 100644 --- a/app/src/telegram/routers.py +++ b/app/src/telegram/routers.py @@ -12,6 +12,7 @@ from src.telegram.handlers.start import router as start_router from src.telegram.handlers.system import router as system_router from src.telegram.handlers.trade.main import router as trade_main_router from src.telegram.handlers.trade.new_order import router as trade_new_order_router +from src.telegram.handlers.debug import router as debug_router def setup_routers(dispatcher: Dispatcher) -> None: @@ -24,4 +25,5 @@ def setup_routers(dispatcher: Dispatcher) -> None: dispatcher.include_router(trade_new_order_router) dispatcher.include_router(auto_router) dispatcher.include_router(journal_router) + dispatcher.include_router(debug_router) dispatcher.include_router(system_router) \ No newline at end of file diff --git a/app/src/trading/auto/runner.py b/app/src/trading/auto/runner.py index ff01243..dff1ffa 100644 --- a/app/src/trading/auto/runner.py +++ b/app/src/trading/auto/runner.py @@ -147,6 +147,11 @@ class AutoTradeRunner: await asyncio.sleep(cls._analysis_interval_seconds) + @classmethod + async def process_last_event_now(cls) -> None: + state = AutoTradeService().get_state() + await cls._handle_important_event(state) + @classmethod async def _handle_important_event(cls, state) -> None: event_type, payload = EventBus.last_event() diff --git a/app/src/trading/auto/service.py b/app/src/trading/auto/service.py index 924daa3..d6697ef 100644 --- a/app/src/trading/auto/service.py +++ b/app/src/trading/auto/service.py @@ -32,6 +32,59 @@ class AutoTradeService: _last_signal_payload: dict | None = None _same_signal_count = 0 + # debug: принудительно выставить сигнал и decision + def debug_force_signal( + self, + *, + signal: str, + confidence: float = 0.9, + repeat_count: int = 2, + reason: str = "DEBUG SIGNAL", + ) -> AutoTradeState: + state = self.get_state() + + normalized_signal = signal.strip().upper() + if normalized_signal not in {"BUY", "SELL", "HOLD"}: + normalized_signal = "HOLD" + + previous_signal = state.last_signal + previous_decision_status = state.decision_status + + state.last_signal = normalized_signal + state.last_signal_repeat_count = repeat_count + state.last_signal_confidence = confidence + state.last_signal_reason = reason + + if normalized_signal == "HOLD": + state.decision_status = "WAITING" + state.decision_reason = "Debug HOLD." + state.is_signal_confirmed = False + state.is_signal_ready = False + else: + state.decision_status = "READY" + state.decision_reason = "Debug READY signal." + state.is_signal_confirmed = True + state.is_signal_ready = True + + EventBus.emit( + "auto_decision_changed", + { + "previous_signal": previous_signal, + "previous_decision_status": previous_decision_status, + "decision_status": state.decision_status, + "signal": state.last_signal, + "repeat_count": state.last_signal_repeat_count, + "confidence": state.last_signal_confidence, + "symbol": state.symbol, + "strategy": state.strategy, + "leverage": state.leverage, + "reason": state.last_signal_reason, + "debug": True, + }, + ) + + return state + # получить текущее состояние автоторговли def get_state(self) -> AutoTradeState: if not self._state.symbol: diff --git a/docs/roadmap/master-roadmap.md b/docs/roadmap/master-roadmap.md index 84088f5..b07b232 100644 --- a/docs/roadmap/master-roadmap.md +++ b/docs/roadmap/master-roadmap.md @@ -129,19 +129,19 @@ ✔ confidence scoring ✔ UI integration -### 07.4.3.1 — UI Optimization +#### 07.4.3.1 — UI Optimization ✅ ✔ compact auto screen ✔ state-based rendering (OFF / RUNNING / OBSERVING) ✔ minimal trading layout ✔ duplicate info removal -### 07.4.3.2 — Engine Decoupling (NEXT) +#### 07.4.3.2 — Engine Decoupling (NEXT) ✅ ✔ split analysis / UI refresh ✔ fast price polling (1s) ✔ slow UI updates (event-driven / 60s) ✔ anti-flood protection -### 07.4.3.3 — Paper Position & Execution Engine +#### 07.4.3.3 — Paper Position & Execution Engine ✅ - добавлен ExecutionEngine - реализованы paper-позиции (LONG / SHORT) - интеграция с AutoTradeService @@ -149,7 +149,7 @@ - логирование paper execution - EventBus события (paper_position_opened) -### Stage 07.4.3.4 — Telegram Strong Signal Alerts +#### Stage 07.4.3.4 — Telegram Strong Signal Alerts ✅ - EventBus-driven уведомления - Фильтрация READY сигналов - Поддержка BUY / SELL @@ -157,6 +157,15 @@ - Интеграция с Journal - Runner полностью управляет Telegram-уведомлениями +#### Stage 07.4.3.5 — Debug Commands & Test Mode ✅ +- DEBUG_ENABLED env flag +- debug_force_signal API +- instant EventBus processing +- Telegram debug commands +- state inspection (/debug_state) +- journal logging for debug actions +- full pipeline testing without market dependency + ### 07.4.4 ⏳ Grid Strategy diff --git a/docs/roadmap/stage-07-auto-trading-roadmap.md b/docs/roadmap/stage-07-auto-trading-roadmap.md index 47f4b3e..662411f 100644 --- a/docs/roadmap/stage-07-auto-trading-roadmap.md +++ b/docs/roadmap/stage-07-auto-trading-roadmap.md @@ -96,7 +96,7 @@ --- -### 07.4.3.1 — UI Optimization +#### 07.4.3.1 — UI Optimization ✔ компактный экран автоторговли ✔ разделение OFF / ACTIVE / OBSERVING ✔ убраны дубли (WAITING / HOLD и т.д.) @@ -104,7 +104,7 @@ --- -### 07.4.3.2 — Engine Decoupling (NEXT) +#### 07.4.3.2 — Engine Decoupling (NEXT) ⏳ разделение: - analysis loop (частый) - UI loop (редкий) @@ -119,7 +119,7 @@ --- -### Stage 07.4.3.3 — Paper Position & Execution Engine +#### Stage 07.4.3.3 — Paper Position & Execution Engine - добавлен ExecutionEngine - реализованы paper-позиции (LONG / SHORT) - интеграция с AutoTradeService @@ -129,7 +129,7 @@ --- -## Stage 07.4.3.4 — Telegram Strong Signal Alerts +#### Stage 07.4.3.4 — Telegram Strong Signal Alerts - EventBus-driven уведомления - Фильтрация READY сигналов - Поддержка BUY / SELL @@ -144,6 +144,18 @@ --- +#### Stage 07.4.3.5 — Debug Commands & Test Mode ✅ + +- DEBUG_ENABLED env flag +- debug_force_signal API +- instant EventBus processing +- Telegram debug commands +- state inspection (/debug_state) +- journal logging for debug actions +- full pipeline testing without market dependency + +--- + ### 07.4.4 ⏳ Grid strategy diff --git a/docs/stages/stage-07_4_3_4-telegram_alerts-final.md b/docs/stages/stage-07_4_3_4-telegram_alerts-final.md new file mode 100644 index 0000000..63944e1 --- /dev/null +++ b/docs/stages/stage-07_4_3_4-telegram_alerts-final.md @@ -0,0 +1,281 @@ +# Stage 07.4.3.4 — Telegram Strong Signal Alerts via EventBus + +## Цель этапа + +Добавить отдельные Telegram-уведомления для сильных торговых сигналов автоторговли. + +Уведомление должно отправляться только тогда, когда сигнал действительно готов к действию: + +- сигнал `BUY` или `SELL`; +- `decision_status = READY`; +- `is_signal_ready = True`; +- событие пришло через `EventBus`; +- уведомление не дублируется на каждом цикле анализа. + +--- + +## Что реализовано + +### 1. EventBus-событие для READY-сигналов + +В `AutoTradeService` событие `auto_decision_changed` теперь передаёт расширенный payload: + +```python +EventBus.emit( + "auto_decision_changed", + { + "previous_decision_status": previous_decision_status, + "decision_status": state.decision_status, + "signal": state.last_signal, + "repeat_count": state.last_signal_repeat_count, + "confidence": state.last_signal_confidence, + "symbol": state.symbol, + "strategy": state.strategy, + "leverage": state.leverage, + "reason": state.last_signal_reason, + }, +) +``` + +Это позволяет runner-слою принимать решение об отправке уведомления без прямой связи со стратегией. + +--- + +### 2. AutoTradeRunner обрабатывает важные события + +В `AutoTradeRunner` добавлена обработка последнего события из `EventBus`. + +Runner проверяет: + +```text +event_type == auto_decision_changed +decision_status == READY +signal in BUY / SELL +``` + +Если условия выполнены — отправляется отдельное Telegram-сообщение. + +--- + +### 3. Отдельное Telegram-уведомление + +Формат уведомления: + +```text +🚨 Сильный сигнал 🟢 BUY + +BTC/USD_LEVERAGE · TREND · x2 +Confidence: 0.90 +Repeats: 2 + +Причина: DEBUG FORCE BUY +``` + +Для `SELL` используется красный индикатор. + +--- + +### 4. Защита от дублей + +Добавлен ключ последнего уведомления: + +```python +_last_strong_alert_key +``` + +Ключ строится из: + +```text +symbol + strategy + signal + repeat_count + confidence + decision_status +``` + +Если такой же alert уже был отправлен — повторная отправка блокируется. + +--- + +### 5. Логирование уведомлений + +При успешной отправке уведомления пишется событие журнала: + +```text +auto_strong_signal_alert_sent +``` + +Payload содержит: + +- symbol; +- strategy; +- signal; +- repeat_count; +- confidence; +- leverage; +- reason. + +--- + +### 6. Debug-команды для проверки + +Для проверки сильных сигналов без ожидания рынка добавлены debug-команды. + +Добавлен флаг: + +```env +DEBUG_ENABLED=true +``` + +Команды: + +```text +/debug_signal BUY +/debug_signal SELL +/debug_ready +/debug_state +``` + +При выключенном debug mode команды не выполняются: + +```env +DEBUG_ENABLED=false +``` + +--- + +### 7. Мгновенная обработка debug-события + +Добавлен публичный метод runner-а: + +```python +AutoTradeRunner.process_last_event_now() +``` + +Он нужен для debug-сценариев, чтобы событие `READY BUY / SELL` было обработано сразу, а не ожидало следующего цикла автоторговли. + +--- + +## Изменённые файлы + +```text +app/src/trading/auto/service.py +app/src/trading/auto/runner.py +app/src/core/config.py +app/src/telegram/handlers/debug.py +app/src/telegram/routers.py +``` + +--- + +## Проверка + +### 1. Включить debug mode + +```env +DEBUG_ENABLED=true +``` + +### 2. Запустить приложение + +```bash +python -m src.main +``` + +### 3. Открыть экран автоторговли + +Нужно, чтобы `AutoTradeRunner` зарегистрировал `bot` и `chat_id`. + +### 4. Выполнить команду + +```text +/debug_signal BUY +``` + +Ожидаемый результат: + +```text +🚨 Сильный сигнал 🟢 BUY + +BTC/USD_LEVERAGE · TREND · x2 +Confidence: 0.90 +Repeats: 2 + +Причина: DEBUG FORCE BUY +``` + +Также приходит подтверждение debug-команды: + +```text +✅ Debug signal forced +Signal: BUY +Decision: READY +Confidence: 0.90 +Repeats: 2 +``` + +### 5. Проверить журнал + +В журнале должно появиться событие: + +```text +auto_strong_signal_alert_sent +``` + +--- + +## Важное замечание по безопасности + +Debug-команды можно оставлять в коде, но в боевом режиме они должны быть выключены: + +```env +DEBUG_ENABLED=false +``` + +Это важно, потому что debug-команды могут вручную выставлять `READY BUY / SELL`. + +--- + +## Ограничения текущего этапа + +Пока не реализовано: + +- cooldown между alert-ами; +- настройка включения/выключения alert-ов из UI; +- разные уровни сигналов; +- подписки на отдельные типы уведомлений; +- уведомления по закрытию позиции. + +--- + +## Результат + +Этап добавил полноценный механизм Telegram-уведомлений для сильных сигналов через событийную архитектуру. + +Теперь цепочка работает так: + +```text +Strategy +↓ +AutoTradeService +↓ +EventBus +↓ +AutoTradeRunner +↓ +Telegram alert +``` + +--- + +## Следующий этап + +Рекомендуемый следующий этап: + +```text +Stage 07.4.3.5 — Position lifecycle and PnL +``` + +Цели следующего этапа: + +- закрытие paper-позиции; +- пересчёт PnL; +- логирование закрытия; +- обновление UI позиции; +- подготовка к Risk Engine. diff --git a/docs/stages/stage-07_4_3_5-debug-mode.md b/docs/stages/stage-07_4_3_5-debug-mode.md new file mode 100644 index 0000000..0a6ae33 --- /dev/null +++ b/docs/stages/stage-07_4_3_5-debug-mode.md @@ -0,0 +1,272 @@ +# Stage 07.4.3.5 — Debug Commands & Test Mode + +## Цель этапа + +Добавить безопасный механизм тестирования автоторговли без зависимости от рыночных условий. + +Этап позволяет: + +- принудительно генерировать сигналы BUY / SELL / READY; +- тестировать Telegram-уведомления; +- тестировать Execution Engine; +- проверять состояние автоторговли в реальном времени. + +--- + +## Что реализовано + +### 1. Debug Mode через ENV + +Добавлен флаг: + +```env +DEBUG_ENABLED=true +``` + +При значении `false`: + +- debug-команды полностью отключены; +- любые попытки их вызвать возвращают сообщение "Debug mode выключен". + +Это обеспечивает безопасность для production. + +--- + +### 2. Debug API в AutoTradeService + +Добавлен метод: + +```python +debug_force_signal(...) +``` + +Позволяет вручную задать: + +- signal (BUY / SELL / HOLD) +- confidence +- repeat_count +- reason + +Метод: + +- обновляет `AutoTradeState`; +- выставляет `decision_status = READY` (для BUY/SELL); +- эмитит событие `auto_decision_changed`. + +--- + +### 3. Интеграция с EventBus + +После debug-сигнала: + +```text +AutoTradeService +↓ +EventBus.emit(...) +↓ +AutoTradeRunner +``` + +Это позволяет тестировать всю цепочку без изменения стратегий. + +--- + +### 4. Мгновенная обработка событий + +Добавлен метод: + +```python +AutoTradeRunner.process_last_event_now() +``` + +Он позволяет: + +- обработать событие сразу; +- не ждать следующий цикл (`run_cycle`); +- использовать debug-команды в реальном времени. + +--- + +### 5. Telegram Debug Commands + +Добавлен handler: + +```text +app/src/telegram/handlers/debug.py +``` + +Доступные команды: + +```text +/debug_signal BUY +/debug_signal SELL +/debug_ready +/debug_state +``` + +--- + +### 6. Поведение команд + +#### `/debug_signal BUY` + +- выставляет сигнал BUY; +- делает его READY; +- запускает обработку EventBus; +- отправляет alert (если включён этап 07.4.3.4). + +--- + +#### `/debug_ready` + +- shortcut для READY BUY; +- максимальная уверенность; +- используется для быстрого теста execution и alert. + +--- + +#### `/debug_state` + +Показывает текущее состояние: + +```text +Status +Symbol +Strategy +Risk +Leverage + +Signal +Repeats +Confidence +Decision + +Position +Entry +PnL +``` + +--- + +### 7. Логирование + +Добавлено событие: + +```text +debug_signal_forced +``` + +Позволяет: + +- отслеживать тестовые действия; +- отделять debug от реальных сигналов. + +--- + +## Изменённые файлы + +```text +app/src/core/config.py +app/src/trading/auto/service.py +app/src/trading/auto/runner.py +app/src/telegram/handlers/debug.py +app/src/telegram/routers.py +``` + +--- + +## Проверка + +### 1. Включить debug + +```env +DEBUG_ENABLED=true +``` + +--- + +### 2. Запустить приложение + +```bash +python -m src.main +``` + +--- + +### 3. Открыть автоторговлю + +Важно: чтобы runner получил bot/chat_id. + +--- + +### 4. Выполнить команды + +```text +/debug_signal BUY +/debug_signal SELL +/debug_ready +/debug_state +``` + +--- + +### 5. Ожидаемый результат + +- приходит debug confirmation; +- приходит Telegram alert (если READY); +- обновляется UI; +- пишется журнал. + +--- + +## Важное замечание + +Debug-режим нельзя оставлять включённым в production: + +```env +DEBUG_ENABLED=false +``` + +Иначе: + +- можно форсировать сделки; +- можно обойти стратегию; +- нарушается безопасность. + +--- + +## Ограничения + +Пока не реализовано: + +- авторизация debug-команд (по user_id); +- rate limit debug-команд; +- отдельный debug-чат; +- UI toggle debug mode. + +--- + +## Результат + +Добавлен полноценный test harness для автоторговли. + +Теперь можно тестировать: + +```text +Strategy → Signal → Decision → EventBus → Runner → Telegram → Execution +``` + +без ожидания рынка. + +--- + +## Следующий этап + +```text +Stage 07.4.3.6 — Position lifecycle & PnL +``` + +- закрытие позиции; +- расчёт прибыли; +- обновление UI; +- подготовка к Risk Engine.