07.4.3.19.4 — Journal Runtime Standardization & Export Layer

This commit is contained in:
2026-05-10 15:26:49 +03:00
parent 1692cb4d81
commit 8024cd9d9a
13 changed files with 343 additions and 325 deletions

View File

@@ -16,8 +16,8 @@ class TelegramNotificationChannel:
if bot is None or chat_id is None:
JournalService().log_warning(
"notification_target_missing",
"Telegram notification target is not registered.",
"notification_error",
"Не удалось отправить Telegram-уведомление: получатель не настроен.",
{
"title": message.title,
"priority": message.priority,
@@ -36,8 +36,8 @@ class TelegramNotificationChannel:
except TelegramRetryAfter as exc:
JournalService().log_warning(
"notification_telegram_retry_after",
"Telegram notification delayed by retry-after.",
"notification_error",
"Не удалось отправить Telegram-уведомление: Telegram ограничил частоту отправки.",
{
"title": message.title,
"retry_after": exc.retry_after,
@@ -49,8 +49,8 @@ class TelegramNotificationChannel:
except Exception as exc:
JournalService().log_error(
"notification_telegram_error",
"Telegram notification failed.",
"notification_error",
"Не удалось отправить Telegram-уведомление.",
{
"title": message.title,
"error": str(exc),

View File

@@ -7,9 +7,7 @@ from src.notifications.dedupe import NotificationDedupe
from src.notifications.models import NotificationMessage
from src.notifications.templates.execution import build_execution_notification
from src.notifications.templates.signal import build_signal_notification
from src.runtime_events.event_types import RuntimeEventType
from src.runtime_events.models import RuntimeEvent
from src.trading.journal.service import JournalService
class NotificationService:
@@ -17,112 +15,15 @@ class NotificationService:
message = self._build_message(event)
if message is None:
JournalService().log_info(
"runtime_event_ignored",
"Runtime event has no notification template.",
{
"event_type": event.event_type.value,
"source": event.source,
"title": event.title,
"priority": event.priority,
"dedupe_key": event.dedupe_key,
},
)
return
if not NotificationDedupe.should_send(message.dedupe_key):
self._log_suppressed(event, message)
return
sent = await TelegramNotificationChannel().send(message)
if sent:
self._log_sent(event, message)
await TelegramNotificationChannel().send(message)
def _build_message(self, event: RuntimeEvent) -> NotificationMessage | None:
return (
build_signal_notification(event)
or build_execution_notification(event)
)
def _log_sent(self, event: RuntimeEvent, message: NotificationMessage) -> None:
if event.event_type == RuntimeEventType.AUTO_SIGNAL_READY:
signal = str(event.payload.get("signal") or "").upper()
JournalService().log_ui_info(
event_type="auto_strong_signal_alert_sent",
message=f"Отправлено уведомление о сильном сигнале {signal}.",
screen="auto",
action="strong_signal_alert",
payload={
**event.payload,
"runtime_event_type": event.event_type.value,
"runtime_source": event.source,
"priority": message.priority.upper(),
"dedupe_key": message.dedupe_key,
},
)
return
if event.event_type in {
RuntimeEventType.POSITION_OPENED,
RuntimeEventType.POSITION_CLOSED,
RuntimeEventType.POSITION_FLIPPED,
}:
JournalService().log_ui_info(
event_type="auto_execution_alert_sent",
message="Отправлено Telegram-уведомление по paper execution.",
screen="auto",
action="execution_alert",
payload={
**event.payload,
"runtime_event_type": event.event_type.value,
"runtime_source": event.source,
"priority": message.priority.upper(),
"dedupe_key": message.dedupe_key,
},
)
return
JournalService().log_info(
"notification_sent",
"Runtime notification sent.",
{
"event_type": event.event_type.value,
"source": event.source,
"title": event.title,
"priority": event.priority,
"dedupe_key": message.dedupe_key,
},
)
def _log_suppressed(self, event: RuntimeEvent, message: NotificationMessage) -> None:
if event.event_type == RuntimeEventType.AUTO_SIGNAL_READY:
signal = str(event.payload.get("signal") or "").upper()
JournalService().log_ui_info(
event_type="auto_strong_signal_alert_suppressed",
message=f"Повторное уведомление о сильном сигнале {signal} подавлено.",
screen="auto",
action="strong_signal_alert",
payload={
**event.payload,
"runtime_event_type": event.event_type.value,
"runtime_source": event.source,
"priority": message.priority.upper(),
"dedupe_key": message.dedupe_key,
},
)
return
JournalService().log_info(
"notification_suppressed_duplicate",
"Duplicate notification suppressed.",
{
"event_type": event.event_type.value,
"source": event.source,
"title": event.title,
"priority": event.priority,
"dedupe_key": message.dedupe_key,
},
)