trading-system-docs/notes/trading/system-docs/modul-03-market-data.md
Red Queen 022c30e537 feat(tolaria): migrate C3H HR wave 1 — 16 safe root↔canonical pairs to schema v1
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)
2026-08-25 19:19:02 +00:00

5.2 KiB

knowledge_schema id type role representation state derived_from _organized
1 object/cf54abfc-4c8b-7477-1b4f-21e76ca7b055 arch module canonical current object/1761a726-55af-f97b-d509-84aa4577f102 true

Modul-03-Market-Data — Betriebsdokumentation

Erstellt: 20.08.2026 (Rain Ocampo) · Status: In Betrieb (healthy)

Zweck

Zentrale Marktdatenquelle des Trading-Systems. Pipeline: Marktdaten → Validierung → Normalisierung → PostgreSQL → RabbitMQ-Event Keine Strategie, keine Orders, keine KI — nur zuverlässige Marktdaten.

Container

Attribut Wert
Name Modul-03-Market-Data
Image market-data:0.1.0 (lokal gebaut)
Port 55003NUR intern (expose, nicht öffentlich)
Netzwerk trading-modules (bridge)
Build-Context /opt/trading-modules/modul03-market-data/
Restart unless-stopped
Healthcheck healthy

Deployment

cd /opt/trading-modules
docker compose build modul-03-market-data
docker compose up -d modul-03-market-data

Environment Variables (Compose)

Variable Wert (Default) Zweck
PG_HOST Modul-01-PostgreSQL Docker-interner Servicename
PG_PORT 5432 intern (Host: 55432)
PG_USER/PASSWORD/DB trading aus .env-Defaults
RABBITMQ_HOST Modul-02-RabbitMQ Docker-interner Servicename
RABBITMQ_PORT 5672 intern (Host: 55672)
RABBITMQ_USER/PASSWORD/VHOST trading vhost trading
DATA_PROVIDER noop noop / demo / später Broker
LOG_LEVEL INFO Strukturiertes Logging

Keine festen IPs — nur Docker-interne Hostnamen. Creds via Compose env + ${VAR:-default}.

Datenbank (Modul-01-PostgreSQL)

Tabelle public.ohlcv:

symbol TEXT, asset_class TEXT, provider TEXT, timeframe TEXT,
ts TIMESTAMPTZ, open/high/low/close DOUBLE PRECISION, volume DOUBLE PRECISION,
created_at TIMESTAMPTZ DEFAULT now(), id BIGSERIAL PRIMARY KEY

Finale Constraints & Indizes (Stand 20.08.2026)

Index Typ
uq_ohlcv_provider_symbol_tf_ts UNIQUE (provider, symbol, timeframe, ts)
ohlcv_pkey UNIQUE (id)
idx_ohlcv_symbol_tf (symbol, timeframe)
idx_ohlcv_symbol_tf_ts (symbol, timeframe, ts DESC)
idx_ohlcv_provider_symbol_tf_ts (provider, symbol, timeframe, ts DESC)

Der Unique-Index inkludiert den Provider — langfristig werden mehrere Provider/Broker unterstützt (gleiche Symbol+Timeframe+Timestamp können von verschiedenen Quellen kommen).

Migrationen: /app/migrations/001_ohlcv.sql + 002_unique_provider.sql (idempotent, löschen nichts; automatisch via ensure_schema()/glob angewendet).

RabbitMQ (Modul-02)

  • Exchange: market.data (topic, durable)
  • vhost: trading (wichtig!)
  • Routing-Keys:
Routing-Key Event-Typ Wann
market.data.ready MARKET_DATA_READY Batch/Import — EIN Event pro Batch, candle_count + batch:true
market.data.candle.closed MARKET_CANDLE_CLOSED Live — pro abgeschlossener Kerze EIN Event, OHLCV im payload
  • Eventschema v1.0: event_id, event_type, event_version, timestamp, symbol, asset_class, timeframe, provider + payload.

Interne API

Endpoint Zweck
GET /health Liveness (200 immer) + Komponentenstatus im Body
GET /health/ready Readiness (503 wenn PG/RabbitMQ down)
POST /ingest Kerzen einspeisen (JSON: {"candles":[...]})
GET /prices/{symbol} Letzte Kurse
GET /history/{symbol}?timeframe= Historische OHLCV

Provider-Adapter

app/providers/providers.py:

  • DataProvider (ABC) — abstrakte Schnittstelle fetch_ohlcv(), health()
  • NoopProvider — keine Datenquelle konfiguriert (Default)
  • DemoProvider — synthetische OHLCV-Daten für Tests
  • Neue Broker = neue Klasse, umschalten via DATA_PROVIDER env → keine harte Anbieter-Kopplung

Validierung (app/validation/validator.py)

  • Timestamp gültig (UTC, nicht Zukunft, nicht zu alt/stale)
  • OHLC-Werte > 0
  • High ≥ Low, High ≥ Open/Close, Low ≤ Open/Close
  • Duplikat-Erkennung (Storage + DB-Unique-Index)

End-to-End-Test (20.08.2026, nach Provider-Constraint-Upgrade)

  • Batch-Pfad: 4 GOOG-Kerzen → saved:4, GENAU EIN MARKET_DATA_READY (routing market.data.ready, candle_count:4). 3 NVDA → saved:3, EIN Event.
  • Live-Pfad: 1 AMZN-Candle → MARKET_CANDLE_CLOSED (routing market.data.candle.closed, OHLCV im payload).
  • Provider-Duplikat: identische NVDA-Kerze erneut → duplicate, kein Insert, kein Event.
  • Port-Sicherheit: 55003 von außen (http://187.124.31.123:55003/health) → nicht erreichbar
  • Datenbestand final: AAPL 5, GOOG 4, NVDA 3, EURUSD 3, TSLA 4, MSFT 1, AMZN 1.

Offene Punkte

  • Echter Broker-/Datenprovider-Adapter (Interface bereit, noop/demo Defaults)
  • Zusätzliche Daten (Bid/Ask/Spread, Ticks, Fundamentaldaten) — vorbereitet
  • Consumer für market.data.ready / market.data.candle.closed (Modul-04 ff.)

Geändert von: Rain Ocampo
Datum: 20.08.2026
Grund: Modul-03-Dokumentation angelegt (Provider-Constraint, Event-Trennung, Port-Nicht-Exposition).