trading-system-docs/notes/trading/system-docs/modul-14-notification.md

147 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
type: Modul
_organized: true
---
# 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 0510 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 17, 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 17 grün),
API/Events/Severity/Retry-Dead/Spam-Schutz/Fake-E2E dokumentiert, Status FREIGEGEBEN.
```