5.8 KiB
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
{
"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-Keytrading.state(nur bei Status-Änderung). - M09-Consumer
ControlCacheConsumerspeist 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__.pyapp/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/Stopapp/core/models.py—ExecutionOrder+ Audit-Felderapp/config.py— M15-URL/Timeout/TTL/Cache-TTL + Control-Exchange/Routing-Keyapp/storage/storage.py— INSERT/UPDATE um control_*-Felder;update_control_auditmigrations/001_execution.sql— Audit-Spalten (idempotent viaADD 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
healthundhealth/ready→ 200. - Keine öffentlichen Ports (nur Docker-intern via
expose:). - Keine Secrets in Logs/API/DB.
- Tracebacks in Logs: ausschließlich pika
AMQPConnectionErrorwä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.