Modul-15-M09 Control-Anbindung: Implementierung + Live-E2E dokumentiert (alle Szenarien gruen, noch nicht final freigegeben, 20.08.2026)
This commit is contained in:
parent
a491c7b7d8
commit
ed77df65c3
1 changed files with 150 additions and 0 deletions
150
modul-15-m09-anbindung-implementierung.md
Normal file
150
modul-15-m09-anbindung-implementierung.md
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
# Modul-15 → Modul-09 Control-Anbindung — Implementierung & Live-E2E
|
||||
|
||||
**Status: IMPLEMENTIERT + LIVE-E2E VERIFIZIERT (alle Szenarien grün).**
|
||||
**Datum:** 20.08.2026 · **Autor:** Rain Ocampo (Hermes)
|
||||
**Gilt für:** Modul-09-Execution-Service ↔ Modul-15-Monitoring-Control
|
||||
|
||||
> **Freigabe-Hinweis:** Diese Doku dokumentiert den Implementierungs- und E2E-Stand.
|
||||
> Die **finale Freigabe** (M15/M09 als FREIGEGEBEN markieren) erfolgt erst nach
|
||||
> Nutzer-Bestätigung. M16 wird **nicht** begonnen.
|
||||
|
||||
---
|
||||
|
||||
## 1. Umgesetzte Architektur (Variante C)
|
||||
|
||||
**Event-Cache als nicht-kritischer Vorfilter + synchroner HTTP-Final-Check als Autorität.**
|
||||
|
||||
- **M15** = autoritative Safety-/Trading-Control-Instanz.
|
||||
- **M09** sendet neue Orders (OPEN/INCREASE) **nur**, wenn M15 eindeutig `ENABLED` + frisch bestätigt.
|
||||
- **REDUCE/CLOSE/CANCEL** (Risikoabbau) sind **immer** erlaubt — unabhängig vom M15-Status.
|
||||
|
||||
### Kernregel
|
||||
| State | OPEN/INCREASE | REDUCE/CLOSE/CANCEL |
|
||||
|-------|---------------|---------------------|
|
||||
| `ENABLED` | **ERLAUBT** | erlaubt |
|
||||
| `PAUSED` | **VERBOTEN** | erlaubt |
|
||||
| `HALTED` | **VERBOTEN** | erlaubt |
|
||||
| `UNKNOWN` / M15-down / stale | **VERBOTEN (FAIL-CLOSED)** | erlaubt |
|
||||
|
||||
---
|
||||
|
||||
## 2. API-Vertrag M15 → M09 (additiv, kein Breaking Change)
|
||||
|
||||
### `GET /control/trading-state`
|
||||
```json
|
||||
{
|
||||
"trading_status": "ENABLED", // ENABLED|PAUSED|HALTED|UNKNOWN
|
||||
"system_status": "HEALTHY",
|
||||
"state_version": 3, // monoton, je Status-Änderung inkrementiert
|
||||
"timestamp": "2026-08-20T15:30:00Z",
|
||||
"expires_at": "2026-08-20T15:30:00.5Z", // TTL-Horizont (now + 500ms)
|
||||
"ttl_seconds": 0.5,
|
||||
"audit_id": "ctl-..." // M15-interne Audit-Kennung
|
||||
}
|
||||
```
|
||||
Nur `trading_status=ENABLED` **und** `expires_at` in der Zukunft = eindeutige Erlaubnis.
|
||||
Timeout/5xx/503 → **FAIL-CLOSED** (als UNKNOWN behandeln).
|
||||
|
||||
### Event-Kanal (Event-Cache)
|
||||
- Exchange `market.control`, Routing-Key `trading.state` (nur bei Status-Änderung).
|
||||
- M09-Consumer `ControlCacheConsumer` speist den Event-Cache-Vorfilter.
|
||||
- Cache-TTL: 5 s. Stale-Cache → als UNKNOWN behandeln → OPEN/INCREASE blockiert.
|
||||
|
||||
---
|
||||
|
||||
## 3. M09-Implementierung
|
||||
|
||||
### Neue Dateien
|
||||
- `app/control/client.py` — **ControlClient** (Event-Cache-Vorfilter + HTTP-Final-Check, FAIL-CLOSED)
|
||||
- `app/control/__init__.py`
|
||||
- `app/consumer/control_consumer.py` — **ControlCacheConsumer** (`market.control`/`trading.state`)
|
||||
|
||||
### Geänderte Dateien
|
||||
- `app/core/service.py` — Control-Check zwischen Schritt 4 (Order anlegen) und Schritt 5 (`_submit_and_track`); `_apply_control_audit`; `action_type`-Ableitung; Consumer-Start/Stop
|
||||
- `app/core/models.py` — `ExecutionOrder` + Audit-Felder
|
||||
- `app/config.py` — M15-URL/Timeout/TTL/Cache-TTL + Control-Exchange/Routing-Key
|
||||
- `app/storage/storage.py` — INSERT/UPDATE um control_*-Felder; `update_control_audit`
|
||||
- `migrations/001_execution.sql` — Audit-Spalten (idempotent via `ADD COLUMN IF NOT EXISTS`)
|
||||
|
||||
### Audit-Felder je Order
|
||||
`action_type`, `control_status`, `control_state_version`, `control_expires_at`,
|
||||
`control_audit_id`, `control_check_source` (`http`|`cache`|`bypass`|`none`).
|
||||
|
||||
### Parameter
|
||||
- Final-Check-HTTP-Timeout: **500 ms**, Retry: **max 1** sofort.
|
||||
- `expires_at`-TTL (M15): **500 ms**.
|
||||
- Cache-TTL (M09): **5 s**.
|
||||
- Kein Auto-Resume; kein "Senden ohne Check".
|
||||
|
||||
---
|
||||
|
||||
## 4. Live-E2E-Ergebnisse (VPS, 20.08.2026)
|
||||
|
||||
Alle Szenarien auf dem VPS (187.124.31.123) live ausgeführt. **Alle grün.**
|
||||
|
||||
| # | Szenario | Ergebnis |
|
||||
|---|----------|----------|
|
||||
| S1 | **ENABLED-Pfad**: OPEN-Order durch, Auditfelder in DB | **9/9 PASS** |
|
||||
| S2 | **HALTED-Pfad**: M03 gestoppt → OPEN blockiert, kein Broker-Send, reason `CONTROL_HALTED` | **9/9 PASS** |
|
||||
| S3 | **PAUSED-Pfad**: nicht-krit. Service down → OPEN blockiert, reason `CONTROL_PAUSED` | **4/4 PASS** |
|
||||
| S4 | **Risikoabbau bei HALTED**: REDUCE/CLOSE/CANCEL erlaubt (bypass) | **4/4 PASS** |
|
||||
| S5 | **M15-down**: OPEN FAIL-CLOSED (`CONTROL_UNREACHABLE`), REDUCE/CLOSE/CANCEL erlaubt | **7/7 PASS** |
|
||||
| S6 | **Cache-vs-Final-Check**: HTTP HALTED gewinnt über Cache | **2/2 PASS** |
|
||||
| S7 | **Race/TTL**: Doppel-Publish → keine Doppelorder (Idempotenz) | **2/2 PASS** |
|
||||
| S8 | **Event-Cache**: 1 Consumer, 0 Backlog, Reconnect nach RabbitMQ-Restart | **verifiziert** |
|
||||
| S9 | **Recovery**: nach Behebung wieder ENABLED, OPEN erlaubt | **3/3 PASS** |
|
||||
| S10 | **Health/Security**: health/ready 200, keine öffentl. Ports, keine Secrets, keine echten Tracebacks | **verifiziert** |
|
||||
| S11 | **Regression**: bestehender M09-Paper-E2E (Kette 03→09) | **ALLE CHECKS BESTANDEN** |
|
||||
|
||||
### S1-Detail (ENABLED)
|
||||
```
|
||||
control_status=ENABLED
|
||||
control_state_version=1
|
||||
control_expires_at=1787241066.98
|
||||
control_audit_id=ctl-2073194e651040db
|
||||
control_check_source=http
|
||||
action_type=OPEN
|
||||
broker_order_id=PAPER-EXEC-... (Broker-Send erfolgt)
|
||||
status=FILLED
|
||||
```
|
||||
|
||||
### S2-Detail (HALTED)
|
||||
```
|
||||
control_status=HALTED
|
||||
control_state_version=2
|
||||
control_audit_id=ctl-b2cd6303f8754328
|
||||
control_check_source=http
|
||||
action_type=OPEN
|
||||
broker_order_id=None (KEIN Broker-Send)
|
||||
reason_codes=['CONTROL_HALTED']
|
||||
status=FAILED
|
||||
```
|
||||
|
||||
### S5-Detail (M15-down)
|
||||
```
|
||||
reason_codes=['CONTROL_UNREACHABLE']
|
||||
broker_order_id=None (FAIL-CLOSED, kein Broker-Send)
|
||||
REDUCE/CLOSE/CANCEL → control_check_source=bypass (erlaubt)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Verifikation Health/Security
|
||||
|
||||
- M09 + M15 `health` und `health/ready` → **200**.
|
||||
- Keine öffentlichen Ports (nur Docker-intern via `expose:`).
|
||||
- Keine Secrets in Logs/API/DB.
|
||||
- Tracebacks in Logs: ausschließlich pika `AMQPConnectionError` während des
|
||||
kontrollierten RabbitMQ-Restarts (erwartete Reconnect-Logs, keine echten Fehler).
|
||||
|
||||
---
|
||||
|
||||
## 6. Offene Punkte / Freigabe
|
||||
|
||||
- **Finale Freigabe** M15/M09 als FREIGEGEBEN markieren: **ausstehend** (Nutzer-Bestätigung).
|
||||
- M16: **nicht beginnen**.
|
||||
- Credential-Cleanup: temporäre Forgejo-Tokens/Deploy-Keys → count=0 (siehe Git-Commit).
|
||||
|
||||
---
|
||||
|
||||
*Implementierung + Live-E2E abgeschlossen. Finale Freigabe steht aus.*
|
||||
Loading…
Reference in a new issue