C3H HUMAN REVIEW WAVE 1: migriert 16 freigegebene HR-A PAIR_MIGRATION_SAFE Paare. Pro Pair: Root (representation=source) + Canonical (representation=canonical, derived_from=Root-ID) atomar. Body byte-genau unveraendert. IDs stabil aus C3 Mapping Preview v2. modul-12/18/19/vps.md NICHT angefasst. Knowledge schema v1. (Red Queen)
4.8 KiB
4.8 KiB
| knowledge_schema | id | type | role | representation | state | derived_from | _organized |
|---|---|---|---|---|---|---|---|
| 1 | object/e2beb080-49cc-0b05-64fb-291440fe9abb | journal | module | canonical | current | object/0b393153-53e5-99fe-1098-e8bb4e0b6c7c | true |
Modul-10: Trade-Journal
Status: FREIGEGEBEN (20.08.2026)
Deterministisches, append-only Trade-Journal. Konsumiert die
Execution-Events (ORDER_SUBMITTED/ORDER_FILLED/ORDER_REJECTED/ORDER_FAILED)
aus Modul-09, validiert die Herkunftskette hart (FAIL-CLOSED) und dokumentiert
jeden Trade in einer Audit-Trail-konformen, idempotenten Struktur.
Ergebnis wird als TRADE_RECORDED-Event publiziert.
Sicherheit & Audit:
- Keine KI/ML, keine Orderausführung, keine Risikoberechnung — reines Dokumentieren
- Append-only:
trade_event_historyist unveränderlich (nur INSERT, kein UPDATE/DELETE) - Herkunftskette wird validiert (Signal → Ranking → Risk → Portfolio → Execution)
- Bei inkonsistenter Kette → Trade als
INCONSISTENTmarkiert (FAIL-CLOSED), nicht stumm verworfen TRADE_RECORDEDoptional: ACK nach persistenter Speicherung
Architektur
ORDER_* (market.execution, Modul-09)
│
▼
JournalConsumer (journal.input, Consumer-Fix von Anfang an)
│
▼
Herkunfts-Validierung (FAIL-CLOSED)
│ Kette 05→06→07→08→09 nachvollziehbar?
│ provenance_consistent in metadata
▼
JournalService
│ erstes Event → Trade anlegen + TRADE_RECORDED
│ Folge-Event → append-only Event + Status-Update (bestehenden Trade)
▼
JournalStorage (idempotent, Unique-Index)
│ trade_journal + trade_event_history
▼
RabbitMQPublisher (market.journal)
TRADE_RECORDED (trade.recorded)
Service-/Storage-Grenze (vereinheitlicht)
Storage gibt IMMER TradeJournal zurück — nie ein dict.
- Einzige Konvertierungsstelle
_row_to_trade(DB-Zeile → Modell) get_trade_by_execution()→Optional[TradeJournal]- Der Service arbeitet ausschließlich mit Modellen (kein dict/Model-Mix je Codepfad)
- Dadurch ist
trade_idauf jedem Rückgabewert garantiert verfügbar - (Fix gegen
'dict' object has no attribute 'trade_id'→ NACK/Requeue)
Verarbeitung
- Erstes Event (ORDER_SUBMITTED) → Trade anlegen,
TRADE_RECORDEDpublizieren - Folge-Event (ORDER_FILLED u.a.) → bestehenden Trade als Status-Update verarbeiten: append-only Event + Status-Update, KEIN neuer Trade, kein zweites TRADE_RECORDED
- Bei FILLED:
entry_filled/filled_at/Status aktualisiert (Trade öffnen/aktualisieren) - Eingehende Events werden nicht doppelt verarbeitet (idempotent, Unique-Index)
Idempotenz
- Unique-Index auf
(execution_id, event_id)→ jedes Event exakt einmal provenance_consistentinmetadata(jsonb), nicht als SpalteTRADE_RECORDEDnur beim ersten Event pro Trade- Kein Doppel-Journal bei Re-Publish desselben Events
DB-Tabellen
trade_journal: trade_id, execution_id, source_portfolio_decision_id, Herkunftsketten-IDs (signal/ranking/risk/portfolio), symbol, asset_class, strategy, direction, timeframe, entry_requested, entry_filled, stop_loss, target, quantity, risk_amount, execution_mode, broker_order_id, status, provenance_consistent (in metadata), created_at, updated_attrade_event_history: execution_id, event_id, event_type, payload, created_at (append-only, unveränderlich)
RabbitMQ
- Exchange
market.journal(topic, durable) - Routing:
trade.recorded - Queue
journal.input(durable), gebunden anmarket.execution/order.* - Consumer-Fix von Anfang an: dauerhafte Verbindung,
process_data_events(time_limit=1.0)in innerer Schleife, KEIN queue_delete bei normalen Reconnects, Backoff 1s→2s→4s→8s→16s→30s - ACK erst nach persistenter Speicherung; bei Fehler NACK/Requeue
Tests
- 39/39 Unit-Tests (test_journal.py): Anlage, Status-Update, Idempotenz, FAIL-CLOSED-Herkunftskette, append-only, exakt 1 TRADE_RECORDED, Reconnect
- Gezielter Fix-Test 9/9 (test_fix_update.py): SUBMITTED→Journal,
FILLED→Update, Rückgabe ist TradeJournal (kein dict),
trade_idvorhanden, genau 1 Trade, append-only (2 Events), idempotent, kein Doppel-Journal - E2E 7/7 Checks (Kette 03→10): Journal-Eintrag in DB, exakt 1 Trade, Pflichtfelder + Herkunftskette konsistent, append-only Event-Historie (SUBMITTED+FILLED), exakt 1 TRADE_RECORDED, Idempotenz nach Re-Publish
- Reconnect-Test: RabbitMQ gestoppt → Backoff 1→2→4→8→16s → wieder
verbunden,
journal.input1 Consumer, 0 Messages, kein Zombie - Log-Check: 0 ERROR, 0 Traceback, ACK auf ORDER_SUBMITTED + ORDER_FILLED
Deploy
- Container
Modul-10-Trade-Journal, Port 55010 nur Docker-intern (kein öffentlicher Host-Port) - Health/Readiness 200, PG + RabbitMQ erreichbar, Consumer bereit
- Env:
EXECUTION_MODE=PAPER,TRADING_ENABLED=false