Stage 07.4.3.4 — Telegram strong signal alerts via EventBus

This commit is contained in:
2026-05-03 09:15:34 +03:00
parent 24c910fade
commit af2d27761f
6 changed files with 280 additions and 1 deletions

View File

View 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()

View File

@@ -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,
},
)