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.integrations.exchange.market_data_runner import MarketDataRunner
|
||||
from src.trading.auto.service import AutoTradeService
|
||||
from src.trading.journal.service import JournalService
|
||||
|
||||
|
||||
class AutoTradeRunner:
|
||||
@@ -33,6 +34,7 @@ class AutoTradeRunner:
|
||||
_last_ui_refresh_at: float = 0.0
|
||||
_last_event_version: int = 0
|
||||
_retry_after_until: float = 0.0
|
||||
_last_strong_alert_key: str | None = None
|
||||
|
||||
@classmethod
|
||||
def register_screen(
|
||||
@@ -139,11 +141,94 @@ class AutoTradeRunner:
|
||||
|
||||
if has_important_event:
|
||||
cls._last_event_version = current_event_version
|
||||
await cls._handle_important_event(state)
|
||||
|
||||
await cls._refresh_screen(force=has_important_event)
|
||||
|
||||
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
|
||||
async def _refresh_screen(cls, *, force: bool = False) -> None:
|
||||
now = time.monotonic()
|
||||
|
||||
@@ -349,6 +349,10 @@ class AutoTradeService:
|
||||
"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,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -149,6 +149,14 @@
|
||||
- логирование paper execution
|
||||
- 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
|
||||
⏳ 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
|
||||
- реализованы paper-позиции (LONG / SHORT)
|
||||
- интеграция с 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
|
||||
⏳ 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