Stage 07.4.3.4 — Telegram strong signal alerts via EventBus
This commit is contained in:
0
app/src/telegram/handlers/debug.py
Normal file
0
app/src/telegram/handlers/debug.py
Normal file
@@ -12,6 +12,7 @@ from aiogram.exceptions import TelegramBadRequest, TelegramRetryAfter
|
|||||||
from src.core.event_bus import EventBus
|
from src.core.event_bus import EventBus
|
||||||
from src.integrations.exchange.market_data_runner import MarketDataRunner
|
from src.integrations.exchange.market_data_runner import MarketDataRunner
|
||||||
from src.trading.auto.service import AutoTradeService
|
from src.trading.auto.service import AutoTradeService
|
||||||
|
from src.trading.journal.service import JournalService
|
||||||
|
|
||||||
|
|
||||||
class AutoTradeRunner:
|
class AutoTradeRunner:
|
||||||
@@ -33,6 +34,7 @@ class AutoTradeRunner:
|
|||||||
_last_ui_refresh_at: float = 0.0
|
_last_ui_refresh_at: float = 0.0
|
||||||
_last_event_version: int = 0
|
_last_event_version: int = 0
|
||||||
_retry_after_until: float = 0.0
|
_retry_after_until: float = 0.0
|
||||||
|
_last_strong_alert_key: str | None = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def register_screen(
|
def register_screen(
|
||||||
@@ -139,11 +141,94 @@ class AutoTradeRunner:
|
|||||||
|
|
||||||
if has_important_event:
|
if has_important_event:
|
||||||
cls._last_event_version = current_event_version
|
cls._last_event_version = current_event_version
|
||||||
|
await cls._handle_important_event(state)
|
||||||
|
|
||||||
await cls._refresh_screen(force=has_important_event)
|
await cls._refresh_screen(force=has_important_event)
|
||||||
|
|
||||||
await asyncio.sleep(cls._analysis_interval_seconds)
|
await asyncio.sleep(cls._analysis_interval_seconds)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def _handle_important_event(cls, state) -> None:
|
||||||
|
event_type, payload = EventBus.last_event()
|
||||||
|
|
||||||
|
if event_type != "auto_decision_changed":
|
||||||
|
return
|
||||||
|
|
||||||
|
if payload.get("decision_status") != "READY":
|
||||||
|
return
|
||||||
|
|
||||||
|
signal = str(payload.get("signal", "")).upper()
|
||||||
|
if signal not in {"BUY", "SELL"}:
|
||||||
|
return
|
||||||
|
|
||||||
|
await cls._send_strong_signal_alert(state=state, payload=payload)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def _send_strong_signal_alert(cls, *, state, payload: dict) -> None:
|
||||||
|
if cls._bot is None or cls._chat_id is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
signal = str(payload.get("signal", "")).upper()
|
||||||
|
symbol = str(payload.get("symbol") or state.symbol or "—")
|
||||||
|
strategy = str(payload.get("strategy") or state.strategy or "—")
|
||||||
|
repeat_count = int(payload.get("repeat_count") or state.last_signal_repeat_count or 0)
|
||||||
|
confidence = float(payload.get("confidence") or state.last_signal_confidence or 0.0)
|
||||||
|
leverage = payload.get("leverage") if payload.get("leverage") is not None else state.leverage
|
||||||
|
reason = str(payload.get("reason") or state.last_signal_reason or "—")
|
||||||
|
|
||||||
|
alert_key = (
|
||||||
|
f"{symbol}:{strategy}:{signal}:"
|
||||||
|
f"{repeat_count}:{confidence:.2f}:{state.decision_status}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if alert_key == cls._last_strong_alert_key:
|
||||||
|
return
|
||||||
|
|
||||||
|
cls._last_strong_alert_key = alert_key
|
||||||
|
|
||||||
|
signal_icon = {
|
||||||
|
"BUY": "🟢",
|
||||||
|
"SELL": "🔴",
|
||||||
|
}.get(signal, "🚨")
|
||||||
|
|
||||||
|
leverage_text = f"x{leverage:g}" if isinstance(leverage, (int, float)) else "—"
|
||||||
|
|
||||||
|
text = (
|
||||||
|
f"<b>🚨 Сильный сигнал {signal_icon} {signal}</b>\n\n"
|
||||||
|
f"{symbol} · {strategy} · {leverage_text}\n"
|
||||||
|
f"Confidence: {confidence:.2f}\n"
|
||||||
|
f"Repeats: {repeat_count}\n\n"
|
||||||
|
f"Причина: {reason}"
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
await cls._bot.send_message(
|
||||||
|
chat_id=cls._chat_id,
|
||||||
|
text=text,
|
||||||
|
)
|
||||||
|
|
||||||
|
JournalService().log_ui_info(
|
||||||
|
event_type="auto_strong_signal_alert_sent",
|
||||||
|
message=f"Отправлено уведомление о сильном сигнале {signal}.",
|
||||||
|
screen="auto",
|
||||||
|
action="strong_signal_alert",
|
||||||
|
payload={
|
||||||
|
"symbol": symbol,
|
||||||
|
"strategy": strategy,
|
||||||
|
"signal": signal,
|
||||||
|
"repeat_count": repeat_count,
|
||||||
|
"confidence": confidence,
|
||||||
|
"leverage": leverage,
|
||||||
|
"reason": reason,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
except TelegramRetryAfter as exc:
|
||||||
|
cls._retry_after_until = time.monotonic() + exc.retry_after + 5
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def _refresh_screen(cls, *, force: bool = False) -> None:
|
async def _refresh_screen(cls, *, force: bool = False) -> None:
|
||||||
now = time.monotonic()
|
now = time.monotonic()
|
||||||
|
|||||||
@@ -349,6 +349,10 @@ class AutoTradeService:
|
|||||||
"signal": state.last_signal,
|
"signal": state.last_signal,
|
||||||
"repeat_count": state.last_signal_repeat_count,
|
"repeat_count": state.last_signal_repeat_count,
|
||||||
"confidence": state.last_signal_confidence,
|
"confidence": state.last_signal_confidence,
|
||||||
|
"symbol": state.symbol,
|
||||||
|
"strategy": state.strategy,
|
||||||
|
"leverage": state.leverage,
|
||||||
|
"reason": state.last_signal_reason,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -149,6 +149,14 @@
|
|||||||
- логирование paper execution
|
- логирование paper execution
|
||||||
- EventBus события (paper_position_opened)
|
- EventBus события (paper_position_opened)
|
||||||
|
|
||||||
|
### Stage 07.4.3.4 — Telegram Strong Signal Alerts
|
||||||
|
- EventBus-driven уведомления
|
||||||
|
- Фильтрация READY сигналов
|
||||||
|
- Поддержка BUY / SELL
|
||||||
|
- Анти-спам (deduplication)
|
||||||
|
- Интеграция с Journal
|
||||||
|
- Runner полностью управляет Telegram-уведомлениями
|
||||||
|
|
||||||
### 07.4.4
|
### 07.4.4
|
||||||
⏳ Grid Strategy
|
⏳ Grid Strategy
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Stage 07.4.3.3 — Paper Position & Execution Engine ✅
|
### Stage 07.4.3.3 — Paper Position & Execution Engine
|
||||||
- добавлен ExecutionEngine
|
- добавлен ExecutionEngine
|
||||||
- реализованы paper-позиции (LONG / SHORT)
|
- реализованы paper-позиции (LONG / SHORT)
|
||||||
- интеграция с AutoTradeService
|
- интеграция с AutoTradeService
|
||||||
@@ -129,6 +129,21 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Stage 07.4.3.4 — Telegram Strong Signal Alerts
|
||||||
|
- EventBus-driven уведомления
|
||||||
|
- Фильтрация READY сигналов
|
||||||
|
- Поддержка BUY / SELL
|
||||||
|
- Анти-спам (deduplication)
|
||||||
|
- Интеграция с Journal
|
||||||
|
- Runner полностью управляет Telegram-уведомлениями
|
||||||
|
|
||||||
|
➡️ Подготовка к:
|
||||||
|
- Debug tools
|
||||||
|
- Execution lifecycle
|
||||||
|
- Real trading notifications
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### 07.4.4
|
### 07.4.4
|
||||||
⏳ Grid strategy
|
⏳ Grid strategy
|
||||||
|
|
||||||
|
|||||||
167
docs/stages/stage-07_4_3_4-telegram_alerts.md
Normal file
167
docs/stages/stage-07_4_3_4-telegram_alerts.md
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
# Stage 07.4.3.4 — Telegram Strong Signal Alerts
|
||||||
|
|
||||||
|
## 📌 Цель этапа
|
||||||
|
|
||||||
|
Добавить систему уведомлений в Telegram при появлении **сильного торгового сигнала**:
|
||||||
|
|
||||||
|
- Только при `BUY / SELL`
|
||||||
|
- Только при `decision_status = READY`
|
||||||
|
- Без спама (дедупликация)
|
||||||
|
- Через EventBus (без прямой связи UI ↔ стратегия)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧠 Архитектурная идея
|
||||||
|
|
||||||
|
Strategy → AutoTradeService → EventBus → AutoTradeRunner → Telegram
|
||||||
|
|
||||||
|
- Strategy генерирует сигнал
|
||||||
|
- Service обновляет decision state
|
||||||
|
- EventBus фиксирует изменение
|
||||||
|
- Runner реагирует и отправляет сообщение
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ Реализация
|
||||||
|
|
||||||
|
### 1. EventBus расширен
|
||||||
|
|
||||||
|
Добавлены дополнительные поля в событие:
|
||||||
|
|
||||||
|
```python
|
||||||
|
EventBus.emit("auto_decision_changed", {
|
||||||
|
"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,
|
||||||
|
})
|
||||||
|
```
|
||||||
|
### 2. AutoTradeRunner — обработка событий
|
||||||
|
|
||||||
|
Добавлены методы:
|
||||||
|
_handle_important_event()
|
||||||
|
_send_strong_signal_alert()
|
||||||
|
|
||||||
|
Логика:
|
||||||
|
|
||||||
|
* слушаем EventBus.version()
|
||||||
|
* проверяем last_event()
|
||||||
|
* фильтруем только READY + BUY/SELL
|
||||||
|
* отправляем уведомление
|
||||||
|
|
||||||
|
### 3. Анти-спам защита
|
||||||
|
|
||||||
|
Добавлено поле:
|
||||||
|
_last_strong_alert_key
|
||||||
|
|
||||||
|
Ключ:
|
||||||
|
symbol + strategy + signal + repeat + confidence + decision_status
|
||||||
|
|
||||||
|
👉 Если ключ совпадает — сообщение не отправляется
|
||||||
|
|
||||||
|
### 4. Telegram уведомление
|
||||||
|
|
||||||
|
Формат сообщения:
|
||||||
|
|
||||||
|
🚨 Сильный сигнал 🟢 BUY
|
||||||
|
|
||||||
|
BTC/USD · TREND · x2
|
||||||
|
Confidence: 0.82
|
||||||
|
Repeats: 3
|
||||||
|
|
||||||
|
Причина: Пробой уровня сопротивления
|
||||||
|
|
||||||
|
### 5. Логирование
|
||||||
|
|
||||||
|
Добавлено событие:
|
||||||
|
auto_strong_signal_alert_sent
|
||||||
|
|
||||||
|
С payload:
|
||||||
|
|
||||||
|
* symbol
|
||||||
|
* strategy
|
||||||
|
* signal
|
||||||
|
* confidence
|
||||||
|
* repeat_count
|
||||||
|
* leverage
|
||||||
|
* reason
|
||||||
|
|
||||||
|
## ✅ Критерии готовности
|
||||||
|
|
||||||
|
✔ Уведомление приходит только при:
|
||||||
|
|
||||||
|
* BUY или SELL
|
||||||
|
* decision_status = READY
|
||||||
|
|
||||||
|
✔ Не приходит при:
|
||||||
|
|
||||||
|
* HOLD
|
||||||
|
* CONFIRMING
|
||||||
|
* BLOCKED
|
||||||
|
|
||||||
|
✔ Не спамит при каждом цикле
|
||||||
|
|
||||||
|
✔ Логируется в Journal
|
||||||
|
|
||||||
|
✔ Не ломает UI
|
||||||
|
|
||||||
|
## 🧪 Тестирование
|
||||||
|
|
||||||
|
Рекомендуемый способ
|
||||||
|
|
||||||
|
Форсирование события:
|
||||||
|
|
||||||
|
```
|
||||||
|
EventBus.emit("auto_decision_changed", {
|
||||||
|
"decision_status": "READY",
|
||||||
|
"signal": "BUY",
|
||||||
|
"repeat_count": 2,
|
||||||
|
"confidence": 0.9,
|
||||||
|
"symbol": state.symbol,
|
||||||
|
"strategy": state.strategy,
|
||||||
|
"leverage": state.leverage,
|
||||||
|
"reason": "TEST SIGNAL",
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚠️ Ограничения
|
||||||
|
|
||||||
|
* Нет cooldown между сигналами
|
||||||
|
* Нет разных уровней сигналов (weak/strong)
|
||||||
|
* Нет user-level подписок
|
||||||
|
* Нет отключения уведомлений
|
||||||
|
|
||||||
|
## 🚀 Следующий этап
|
||||||
|
|
||||||
|
Stage 07.4.3.5 — Debug Commands & Test Mode
|
||||||
|
|
||||||
|
Добавим:
|
||||||
|
|
||||||
|
* /debug signal BUY
|
||||||
|
* /debug ready
|
||||||
|
* test_mode=True
|
||||||
|
* override стратегии
|
||||||
|
|
||||||
|
## 📦 Итог
|
||||||
|
|
||||||
|
Этап завершает переход к event-driven архитектуре автоторговли:
|
||||||
|
|
||||||
|
* UI отвязан от логики
|
||||||
|
* Runner управляет Telegram
|
||||||
|
* Service управляет сигналами
|
||||||
|
* EventBus связывает всё
|
||||||
|
|
||||||
|
Это фундамент для:
|
||||||
|
|
||||||
|
* Execution Engine
|
||||||
|
* Risk Engine
|
||||||
|
* Real trading
|
||||||
|
|
||||||
|
## 💾 Коммит
|
||||||
|
|
||||||
|
git add .
|
||||||
|
git commit -m "Stage 07.4.3.4 — Telegram strong signal alerts via EventBus"
|
||||||
Reference in New Issue
Block a user