# Modul-14: Notification-Service **Status: FREIGEGEBEN ✅** · Container `Modul-14-Notification` · Image `notification:0.1.0` ## Zweck Zentrale **Benachrichtigungen für Trading- und Systemereignisse**. Konsumiert relevante Events aus den RabbitMQ-Exchanges der Module 05–10 und liefert kompakte, strukturierte Meldungen — **V1 primär über Telegram** (`TelegramProvider`), provider-neutral angelegt. **Restriktionen (Nutzer-Vorgabe):** - **keine KI/ML**, **keine Trading-Entscheidungen**, **keine Orders**. - **FAIL-SAFE:** Ein Notification-Ausfall darf die Trading-Pipeline **NIEMALS blockieren**. ## Architektur / Datenfluss ``` RabbitMQ (market.signals/.rankings/.risk/.portfolio/.execution/.journal) │ NotificationConsumer (notification.input) ▼ Modul-14-Notification ── NotificationService ──> RulesEngine + DedupGuard │ │ Spam-Schutz ├── providers/ NotificationProvider-ABC → Telegram / Fake ├── rules/ RulesEngine (Regeln) + DedupGuard (Dedup/Cooldown/Rate-Limit) ├── storage/ Persistenz notification_log (PostgreSQL, Modul-01) ├── core/ formatter (kompakte Nachricht) + service (Retry/Backoff) └── api/main.py REST-API (intern, Port 55014) Event → RabbitMQ → Modul-14 → notification_log → Provider (Telegram) → Chat ``` ## API (Docker-intern, Port 55014, kein öffentliches Port-Mapping) ``` GET /health liveness GET /health/ready readiness (postgresql, consumer_ready, provider_configured) GET /notifications/recent letzte Notifications GET /notifications/{id} einzelne Notification POST /notifications/test Test-Sendung (nur konfigurierte Destinationen) ``` ## Konsumierte Events & Routing-Keys | Event-Typ | Exchange | Routing-Key | Default-Severity | Default aktiv | |-----------|----------|-------------|------------------|---------------| | `SIGNAL_DETECTED` | `market.signals` | `strategy.signal.detected` | INFO | ❌ (deaktiviert) | | `SIGNAL_RANKED` | `market.rankings` | `signal.ranked` | INFO | ❌ (deaktiviert) | | `RISK_APPROVED` | `market.risk` | `risk.approved` | INFO | ❌ (deaktiviert) | | `RISK_REJECTED` | `market.risk` | `risk.rejected` | WARNING | ❌ (deaktiviert) | | `TRADE_APPROVED` | `market.portfolio` | `trade.approved` | INFO | ✅ | | `PORTFOLIO_REJECTED` | `market.portfolio` | `portfolio.rejected` | WARNING | ✅ | | `ORDER_SUBMITTED` | `market.execution` | `order.submitted` | INFO | ❌ (deaktiviert) | | `ORDER_FILLED` | `market.execution` | `order.filled` | INFO | ✅ | | `ORDER_REJECTED` | `market.execution` | `order.rejected` | CRITICAL | ✅ | | `ORDER_FAILED` | `market.execution` | `order.failed` | CRITICAL | ✅ | | `TRADE_RECORDED` | `market.journal` | `trade.recorded` | INFO | ✅ | Die Regeln sind **pro Event-Typ konfigurierbar** (aktiv/inaktiv, Severity, Provider, Destination, Cooldown, Rate-Limit). **Unbekannte Event-Typen werden sicher ignoriert** (kein Crash). V1 sind die rauschigen Events (SIGNAL_*, RISK_*, ORDER_SUBMITTED) standardmäßig deaktiviert. ## Severity-Hierarchie ``` INFO < WARNING < CRITICAL ``` Regel-Severity überschreibt den Event-Standard. V1: alle über Telegram. ## Spam-Schutz (DedupGuard) Mehrstufig, thread-sicher (Lock): 1. **Idempotenz** — gleiche `event_id`/`source_event_id` erzeugt nie eine zweite Notification (DB-unique über `source_event_id` + `event_processed`-Check). 2. **Dedup** — identische Meldungen (gleicher Text/Event-Typ) in kurzem Fenster werden zusammengefasst. 3. **Cooldown** — pro Event-Typ ein Mindestabstand (`cooldown_seconds`, Default 60s). 4. **Rate-Limit** — maximale Notifications pro Minute (`rate_limit_per_minute`, Default 30). 5. **Burst-Schutz** — Ansturm vieler Events wird aufs Limit begrenzt. ## Retry / Dead (FAIL-SAFE) - Provider nicht erreichbar → `RETRY_PENDING` mit **begrenzten Retries** (exponentielles Backoff). - `max_attempts` (Default 5) erreicht → **DEAD** (keine Endlosschleife). - **Provider nicht konfiguriert** (fehlender Token/Chat-ID) → **sofort DEAD** mit `error_code=NOT_CONFIGURED` (keine sinnlosen Retries). - Nach Provider-Recovery werden **neue Events wieder normal gesendet** (Retry betrifft nur die jeweilige Notification). ## Eigene Tabelle (Migration) `notification_log`: `notification_id` (PK), `source_event_id`, `event_type`, `severity`, `provider`, `destination`, `status` (`SENT`/`FAILED`/`RETRY_PENDING`/`DEAD`), `attempts`, `created_at`, `sent_at`, `error_code`, `error_message`. ## Sicherheit - **KEINE Secrets in DB/Logs/Doku/Commits.** Telegram-Bot-Token/Chat-ID ausschließlich über Environment (Compose `TELEGRAM_BOT_TOKEN`, `TELEGRAM_CHAT_ID`), nie hart kodiert. - `/notifications/test` sendet **nur an konfigurierte Destinationen**. ## Tests - `tests/test_notification.py`: **14/14 grün** (Event→Notification, Idempotenz, Dedup, Cooldown, Rate-Limit, Telegram-Ausfall blockiert nicht, Retry+Backoff, Max→DEAD, keine Secrets, unbekannter Typ ignoriert, Severity/Regeln, FakeProvider, Reconnect). ## E2E-Verifikation (VPS, 20.08.2026) — alle 7 Punkte grün 1. **Fake-Telegram-E2E** (Modul-14 auf `http://fake-telegram:55000`, Test-Token/Chat-ID nur als ENV): echtes `TRADE_RECORDED` publiziert → **Event → RabbitMQ → Modul-14 → notification_log → Fake-Telegram** komplett durchlaufen. DB: `status=SENT`, `sent_at` gesetzt, `attempts=0`, **genau 1 Zustellung, kein Duplikat**. Fake-Telegram RAW: `{"chat_id": "E2E_TEST_CHAT_114", "text": "TRADE RECORDED | NVDA | LONG | OPEN"}`. 2. **NO_CONFIG-Fix**: Container ohne Token → Event → `status=DEAD, attempts=1, error_code=NOT_CONFIGURED` (sofortiges DEAD, keine 5 sinnlosen Retries). 3. **Fehlerfall-E2E**: Fake-Telegram gestoppt → Notification `RETRY_PENDING` mit Backoff (attempts 2→4, `NETWORK_ERROR`), **Trading-Pipeline unbeeinflusst**, Max-Retries (`attempts=5`) → `DEAD`. Nach Fake-Telegram-Recovery → neues Event wieder `SENT`. 4. **RabbitMQ-Reconnect**: kontrollierter Restart → Modul-14 reconnectet automatisch (Backoff 4s→8s→16s, „Consumer verbunden"), **genau 1 Consumer** an `notification.input`, keine Zombies, keine verlorenen/duplizierten Notifications (nach Reconnect: 1 Zustellung, 0 Duplikate). 5. **Spam-/Sicherheitschecks** (praktisch): unbekannter Typ `UNKNOWN_EVENT_XYZ` → ignoriert; gleiche `event_id` 2× → nur 1 Notification (Idempotenz); gleicher Event-Typ 2× → Cooldown greift; deaktivierter `SIGNAL_DETECTED` → keine Notification. **Keine Tokens/Chat-IDs in Logs/API/Doku/DB** (0 Token-Leaks). 6. **Health/Readiness**: `/health` + `/health/ready` → 200 (`postgresql=true, consumer_ready=true, provider_configured=…`). Keine Applikations-Tracebacks im Normalbetrieb (nur erwartete Reconnect-Logs beim Test). Port 55014 nur intern (`expose`, kein Host-Port). 7. **Forgejo-Doku + Commit-ID** (siehe Freigabe unten), temporäre Test-Credentials entfernt, Remote token-/key-frei. ## Freigabe - **20.08.2026: Modul-14-Notification-Service vom Nutzer FREIGEGEBEN ✅** (nach vollständiger E2E-Verifikation der Punkte 1–7, alle grün). - Keine weiteren technischen Änderungen an Modul-14. ## Compose / Betrieb - Netzwerk `trading-modules`, DB-Host `Modul-01-PostgreSQL`, RabbitMQ `Modul-02-RabbitMQ`. - Port 55014 nur intern (`expose`), `restart: unless-stopped`, non-root (uid 1001). - Token/Chat-ID via ENV (`TELEGRAM_BOT_TOKEN`, `TELEGRAM_CHAT_ID`, `TELEGRAM_API_BASE`). ## Nächste Module - **Modul-15 ff.** — noch Platzhalter (alpine), NICHT gestartet. --- ## Geändert ``` Geändert von: Rain Ocampo Datum: 20.08.2026 Grund: Modul-14-Notification-Service dokumentiert — E2E-verifiziert (Punkte 1–7 grün), API/Events/Severity/Retry-Dead/Spam-Schutz/Fake-E2E dokumentiert, Status FREIGEGEBEN. ```