trading-system-docs/notes/trading/system-docs/modul-06-signal-ranking.md

9.4 KiB
Raw Blame History

Modul-06-Signal-Ranking — Betriebsdokumentation

Erstellt: 20.08.2026 (Rain Ocampo) · Status: Freigegeben (E2E+Idempotenz+Reconnect+Doku grün)

Zweck

Signal-Ranking — konsumiert SIGNAL_DETECTED (Modul-05) + OHLCV (Modul-03), bewertet die Signalqualität deterministisch (Score 0100, vollständig nachvollziehbar und konfigurierbar), persistiert das Ergebnis und publiziert SIGNAL_RANKED. Pipeline: SIGNAL_DETECTED (Modul-05) → Signal-Ranking (signal_quality_v1) → PostgreSQL (signal_ranking) → SIGNAL_RANKED Deterministische, regelbasierte Ranking-Engine — bewusst OHNE KI/ML. Keine Positionsgröße, kein Risk Management, keine Broker-/Order-Ausführung (Trade-Freigabe macht später der Risk Manager). Modular aufgebaut, damit später eine ML/KI-Engine als ZUSÄTZLICHE Ranking-Engine registriert werden kann.

Container

Attribut Wert
Name Modul-06-Signal-Ranking
Image signal-ranking:0.1.0 (lokal gebaut)
Port 55006NUR intern (expose, nicht öffentlich)
Netzwerk trading-modules (bridge)
Build-Context /opt/trading-modules/modul06-signal-ranking/
Restart unless-stopped
Healthcheck healthy

Deployment

cd /opt/trading-modules
docker compose build modul-06-signal-ranking
docker compose up -d --no-deps --force-recreate modul-06-signal-ranking

Environment Variables (Compose)

Variable Wert (Default) Zweck
PG_HOST Modul-01-PostgreSQL Docker-interner Servicename
PG_PORT 5432 intern
PG_USER/PASSWORD/DB trading aus .env-Defaults
RABBITMQ_HOST Modul-02-RabbitMQ Docker-interner Servicename
RABBITMQ_PORT 5672 intern
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-05 ──signal.detected──▶ RankingConsumer (ranking.input)
                                        │
                                        ▼
                        OHLCV (Modul-03, intern 55003/history/{symbol})
                                        │
                                        ▼
                        RankingEngine (deterministisch, Registry)
                        signal_quality_v1 (9 Komponenten, Score 0100)
                                        │
                            ┌───────────┴───────────┐
                            ▼                       ▼
                    signal_ranking (PG)     SIGNAL_RANKED
                    (eigene Tabelle)        → market.rankings / signal.ranked
  • Consumer (app/consumer/consumer.py): bindet Exchange market.signals, Routing strategy.signal.detected; durable Queue ranking.input; manuelles Ack erst nach erfolgreicher Verarbeitung; Reconnect mit Backoff + conn.close(); queue_delete beim Start (entfernt verwaiste/Zombie-Consumer).
  • MarketData-Client (app/marketdata/client.py): liest OHLCV über interne FastAPI Modul-03 (http://Modul-03-Market-Data:55003/history/{symbol}); hoher Lookback, damit die letzten geschlossenen Kerzen enthalten sind.
  • Engine/Register (app/ranking/engine.py): modular — Ranking-Engines via .name/.version/rank() registriert; KI/ML später ergänzbar.
  • Storage (app/storage/storage.py): idempotent via partiellen Unique-Index auf source_signal_id.
  • Publisher (app/publisher/publisher.py): frische Verbindung je Publish + conn.close() im finally (verhindert ConnectionResetError durch RabbitMQ-Closed-Verbindungen).
  • Service (app/core/service.py): Pipeline Event → OHLCV → Ranking → speichern + publizieren; robuste Payload-Extraktion; setzt source_signal_id aus dem Event-Top-Level, falls die Engine es nicht aus dem inneren payload ziehen konnte.

Ranking-Engine V1 — signal_quality_v1 (app/ranking/signal_quality_v1.py)

Deterministische Qualitätsbewertung. 9 Komponenten, Score 0100, vollständig nachvollziehbar und konfigurierbar. Jede Komponente liefert 0..max; Summe = total_score. Komponenten werden einzeln in der DB gespeichert (comp_*).

Komponente Max Bewertet
regime 20 Market-Regime-Qualität / Confidence
trend 15 Trendstärke
pullback 15 Pullback-Qualität
trigger 10 Entry-Bestätigung
rr 10 Chance/Risiko-Verhältnis
volatility 10 Volatilitätszustand
distance 10 Distanz Entry→Stop
data_quality 5 Datenqualität / Candle-Anzahl
consistency 5 Setup-Konsistenz

Klassifizierung: total_score >= eligible_thresholdeligible; darunter weak (wird trotzdem gespeichert).

Zentrale Schwellenwerte (app/config.py, env-overridable):

Parameter Default Bedeutung
max_score 100 Score-Obergrenze
eligible_threshold 70 Score ≥ 70 → eligible
ranking_lookback 500 max. Kerzen für Trend/Volatilität (Modul-03 liefert die N ältesten, daher hoch)
min_candles_required 60 unter dieser Zahl → Datenqualität-Punktabzug
full_candles_target 250 ab dieser Zahl → volle Datenqualität
pullback_depth_min/max 0.5 / 4.0 Abstand Entry zu Trend-Referenz in ATR
vol_ideal_lo/hi 0.005 / 0.030 ATR %-Fenster für "ideale" Volatilität

Keine Trade-Freigabe — nur Qualitätsbewertung.

Datenbank (Modul-01-PostgreSQL)

Eigene Tabelle public.signal_ranking (bestehende unangetastet):

ranking_id UUID, source_signal_id TEXT, source_event_id TEXT, correlation_id TEXT,
timestamp TIMESTAMPTZ, symbol TEXT, asset_class TEXT, provider TEXT, timeframe TEXT,
ranking_name TEXT, ranking_version TEXT, ranking_class TEXT (eligible/weak),
total_score DOUBLE PRECISION, comp_regime/comp_trend/comp_pullback/comp_trigger/comp_rr/
comp_volatility/comp_distance/comp_data_quality/comp_consistency DOUBLE PRECISION,
details JSONB, reasons JSONB
  • Unique (partiell): uq_signal_ranking_src auf (symbol, timeframe, source_signal_id) WHERE source_signal_id IS NOT NULL → Idempotenz (source_signal_id nie doppelt).
  • Migration: migrations/001_signal_ranking.sql (idempotent, löscht nichts).

RabbitMQ (Modul-02)

Exchange Typ Routing Event
market.signals topic strategy.signal.detected (eingang) SIGNAL_DETECTED
market.rankings topic signal.ranked (ausgang) SIGNAL_RANKED

Interne API

Endpoint Zweck
GET /health Liveness (200) + Komponentenstatus im Body
GET /health/ready Readiness (503 wenn PG/RabbitMQ/Modul-03 down)
GET /rankings/{symbol} Rankings eines Symbols
GET /rankings/latest/{symbol} Neuestes Ranking eines Symbols
GET /ranking-engines Registrierte Ranking-Engines

End-to-End-Test (20.08.2026, final)

Kette verifiziert: Modul-03 Ingest → market.data.ready → Modul-04 → market.regime.ready → Modul-05 → SIGNAL_DETECTED → Modul-06 Consumer → signal_quality_v1signal_rankingSIGNAL_RANKED.

Fall Ranking Score Class DB SIGNAL_RANKED
M5LONG (TREND_UP) eligible 72.2 eligible 1 1
M5SHORT (TREND_DOWN) eligible 73.6 eligible 1 1
M5NOPULL (kein Pullback) 0 0
M5RANGE (Range) 0 0

Verifizierte Eigenschaften:

  • Gültiger LONG/SHORT → genau 1 SIGNAL_RANKED; NOPULL/RANGE → 0 Ranking + 0 Event.
  • DB-Eintrag je gültigem Signal vorhanden; source_signal_id korrekt übernommen (z.B. effd782c..., 42dd2042...).
  • total_score immer 0100; Summe aller Komponenten == total_score.
  • eligible/weak korrekt gemäß Threshold (72.2/73.6 ≥ 70 → eligible).
  • Idempotenz: identisches SIGNAL_DETECTED erneut → kein zweiter DB-Eintrag, kein zweites SIGNAL_RANKED (count=1).
  • RabbitMQ-Reconnect: kontrollierter Neustart → Modul-04/05/06 verbinden automatisch (Backoff 1s→2s→4s→8s), je exakt 1 Consumer, keine Zombies, keine verlorenen Events.
  • Logs: keine unbehandelten Tracebacks nach Reconnect. Health/Readiness {postgresql:true, rabbitmq:true, market_data:true, consumer_ready:true}.

Bugs behoben während E2E (20.08.2026)

Bug Fix
source_signal_id leer Service setzt source_signal_id aus dem Event-Top-Level, falls die Engine es nicht aus dem inner-Payload ziehen konnte
OHLCV-Lookback zu klein (120) ranking_lookback auf 500 erhöht — Modul-03 /history?limit=N liefert die N ältesten Kerzen; zu kleiner Lookback → Engine bewertete auf veralteten Kerzen (Konsistenz/Trend/Pullback schief)
direction-Spalte existierte nicht E2E-Query auf details/Modell angepasst

Offene Punkte

  • Downstream-Consumer für signal.ranked (Modul-07+ — noch NICHT begonnen)
  • KI/ML-Ranking-Engine über Registry ergänzbar
  • Trade-Freigabe später durch Risk Manager (noch nicht gebaut)

Geändert von: Rain Ocampo
Datum: 20.08.2026
Grund: Modul-06-Dokumentation angelegt (Signal-Ranking signal_quality_v1, signal_ranking-Schema, Events, E2E final grün).