Dokumentation Modul-04-Market-Regime (Regime-Engine, market_regime, E2E freigegeben) (Rain Ocampo, 20.08.2026)

This commit is contained in:
Rain Ocampo 2026-08-20 03:47:04 +00:00
parent 7b94d6018d
commit 65a0d5521b

152
modul-04-market-regime.md Normal file
View file

@ -0,0 +1,152 @@
# Modul-04-Market-Regime — Betriebsdokumentation
> Erstellt: 20.08.2026 (Rain Ocampo) · Status: ✅ Freigegeben (E2E bestanden)
## Zweck
Erster **Consumer** der Market-Data-Events von Modul-03. Pipeline:
`MARKET_DATA_READY / MARKET_CANDLE_CLOSED (Modul-03) → Regime-Berechnung → PostgreSQL (market_regime) → MARKET_REGIME_READY`
**Deterministische, regelbasierte Engine — bewusst OHNE KI/ML.**
## Container
| Attribut | Wert |
|----------|------|
| Name | `Modul-04-Market-Regime` |
| Image | `market-regime:1.0.0` (lokal gebaut) |
| Port | **55004****NUR intern** (`expose`, nicht öffentlich) |
| Netzwerk | `trading-modules` (bridge) |
| Build-Context | `/opt/trading-modules/modul04-market-regime/` |
| Restart | `unless-stopped` |
| Healthcheck | ✅ `healthy` |
## Deployment
```bash
cd /opt/trading-modules
docker compose build modul-04-market-regime
docker compose up -d --no-deps --force-recreate modul-04-market-regime
```
## 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`** |
| `LOG_LEVEL` | `INFO` | Strukturiertes Logging |
Keine festen IPs — nur Docker-interne Hostnamen. Creds via Compose env + Defaults.
## Architektur
```
Modul-03 ──market.data.ready / market.data.candle.closed──▶ RegimeConsumer
│ (bindet beide Routing-Keys)
RegimeEngine (deterministisch)
EMA / ADX / ATR / Slope / Preisstruktur
┌───────────┴───────────┐
▼ ▼
market_regime (PG) MARKET_REGIME_READY
(16 Spalten) → market.regime.ready
```
- **Consumer** (`app/consumer/consumer.py`): bindet `market.data.ready` + `market.data.candle.closed`; durable Queue `market-regime.input`; manuelles Ack; Reconnect mit Backoff; **schließt alte Verbindung beim Reconnect** (verhindert Consumer-Leak/Nachrichtenverlust).
- **Engine** (`app/regime/engine.py`): deterministisch, ohne KI.
- **Storage** (`app/storage/storage.py`): idempotent via partiellen Unique-Index.
- **Publisher** (`app/publisher/publisher.py`): publiziert `MARKET_REGIME_READY` auf `market.regime` (Routing `market.regime.ready`).
- **History-Client** (`app/marketdata/client.py`): liest OHLCV über interne FastAPI Modul-03 (`http://Modul-03-Market-Data:55003/history/{symbol}`).
## Regime-Engine (`app/regime/engine.py`)
Deterministische Regel-Engine (Version `1.0.0`), 7 Regime:
`TREND_UP, TREND_DOWN, RANGE, HIGH_VOLATILITY, LOW_VOLATILITY, TRANSITION, UNKNOWN`
**Indikatoren & Metriken:**
| Indikator | Fenster/Param | Zweck |
|-----------|---------------|-------|
| EMA fast/slow | 10 / 30 | Trendrichtung (EMA-Flanken-Differenz) |
| ADX | 14 | Trendstärke (≥20 = echter Trend) |
| ATR | 14 | Volatilität (absolut + Ratio + Perzentil) |
| Slope | 20 | normierte Steigung der Close-Linie |
| Preisstruktur | — | higher_highs / lower_lows / range |
**Zentrale Schwellenwerte** (`app/config.py`, env-overridable):
| Parameter | Default | Bedeutung |
|-----------|---------|-----------|
| `min_candles_required` | 30 | UNKNOWN, wenn weniger Daten |
| `regime_lookback` | 60 | max. Kerzen für Berechnung |
| `trend_min_ema_gap` | 0.02 | |EMA_fast-EMA_slow|/close ≥ → Trend |
| `adx_trend_threshold` | 20.0 | ADX ≥ → echter Trend |
| `slope_up/down_threshold` | 0.05 / -0.05 | normierte Steigung |
| `range_atr_ratio` | 0.02 | ATR/close darunter = Range |
| `atr_high_vol_multiplier` | 1.5 | ATR jetzt > hist_mean × → HIGH_VOL |
| `atr_low_vol_multiplier` | 0.6 | ATR jetzt < hist_mean × LOW_VOL |
| `high_vol_atr_ratio` | 0.03 | ATR/close ≥ → starke Vol |
| `low_vol_atr_ratio` | 0.008 | ATR/close ≤ → geringe Vol |
| `slope_threshold` | 0.01 | |Slope| darunter = seitwärts |
| `transition_min_events` | 3 | Events für TRANSITION |
## Datenbank (Modul-01-PostgreSQL)
Tabelle `public.market_regime` (16 Spalten, eigene Tabelle — bestehende unangetastet):
```sql
symbol TEXT, asset_class TEXT, timeframe TEXT, provider TEXT,
regime TEXT, confidence INTEGER (0-100),
trend_strength DOUBLE PRECISION, volatility_state TEXT,
timestamp TIMESTAMPTZ, indicators_json JSONB,
candles_used INTEGER, version TEXT,
source_event_id TEXT, correlation_id TEXT,
data_ts TIMESTAMPTZ, data_ts_end TIMESTAMPTZ
```
- **Unique (partiell):** `uq_market_regime_src` auf `(symbol, timeframe, source_event_id)` **WHERE source_event_id IS NOT NULL** → Idempotenz.
- Migration: `migrations/001_market_regime.sql` (idempotent, löscht nichts).
## RabbitMQ (Modul-02)
| Exchange | Typ | Routing | Event |
|----------|-----|---------|-------|
| `market.data` | topic | `market.data.ready` (eingang) | MARKET_DATA_READY |
| `market.data` | topic | `market.data.candle.closed` (eingang) | MARKET_CANDLE_CLOSED |
| `market.regime` | topic | `market.regime.ready` (**ausgang**) | MARKET_REGIME_READY |
## Interne API
| Endpoint | Zweck |
|----------|-------|
| `GET /health` | Liveness (200 immer) + Komponentenstatus im Body |
| `GET /health/ready` | Readiness (503 wenn PG/RabbitMQ/Modul-03 down) |
| `GET /regime/{symbol}` | Regime-Einträge abfragen |
| `GET /regime/latest/{symbol}` | Letztes Regime eines Symbols |
## End-to-End-Test (20.08.2026, final, nach Rebuild) ✅
Kette verifiziert: Modul-03 Ingest → `market.data.ready` → Modul-04 Consumer → RegimeEngine → `market_regime``MARKET_REGIME_READY` auf `market.regime.ready`.
| Fall | Regime | Conf | candles | version | Event | DB |
|------|--------|------|---------|---------|-------|----|
| M4TREND_UP (40) | TREND_UP | 100 | 40 | 1.0.0 | genau 1 | ✅ |
| M4TREND_DN (40) | TREND_DOWN | 100 | 40 | 1.0.0 | genau 1 | ✅ |
| M4RANGE (40) | LOW_VOLATILITY (Range) | 60 | 40 | 1.0.0 | genau 1 | ✅ |
| M4HIGHVOL (40) | HIGH_VOLATILITY | 75 | 40 | 1.0.0 | genau 1 | ✅ |
| M4UNKNOWN (5) | UNKNOWN | 20 | 5 | 1.0.0 | genau 1 | ✅ |
| M4IDEMPOT (40) | TREND_UP | 100 | 40 | 1.0.0 | genau 1 | ✅ |
**Idempotenz:** dasselbe Quell-Event (`source_event_id`) erneut → **kein zweiter Datensatz**, kein Doppel-Event. ✅
**Logs:** keine Errors/Tracebacks. Health `{postgresql:true, rabbitmq:true, market_data_ready:true}`. ✅
## Bugs behoben während E2E (20.08.2026)
| Bug | Fix |
|-----|-----|
| `can't adapt type 'dict'` (JSONB) | `json.dumps(ind.model_dump(mode="json"))` |
| `tuple index out of range` (16/15) | `version` in INSERT-VALUES ergänzt |
| `ON CONFLICT` + partieller Index Fehler | `WHERE source_event_id IS NOT NULL` in Klausel |
| `model_dump(default=...)` TypeError | `default`-Kwarg entfernt (`model_dump(mode="json")`) |
| Consumer-Verbindungs-Leak | `conn.close()` bei Reconnect → kein Message-Leak |
## Offene Punkte
- Consumer-Downstream für `market.regime.ready` (Modul-05+)
- Bestätigte TRANSITION-Detektion mit echten Folgedaten
---
```
Geändert von: Rain Ocampo
Datum: 20.08.2026
Grund: Modul-04-Dokumentation angelegt (Regime-Engine, market_regime-Schema, Events, E2E freigegeben).
```