diff --git a/Dockerfile.dashboard b/Dockerfile.dashboard index 5b22dd4..1216dbc 100644 --- a/Dockerfile.dashboard +++ b/Dockerfile.dashboard @@ -7,4 +7,6 @@ RUN pip install --no-cache-dir -r requirements.txt COPY . . +ENV PYTHONPATH=/app + CMD ["python", "dashboard/server.py"] diff --git a/README.md b/README.md index 8886434..1c2e133 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,140 @@ # Trading Data Daemon -Ein modularer Daemon zum Herunterladen und Speichern von Handelsdaten von verschiedenen Börsen in einer Time-Series-Datenbank. +Ein modularer Daemon zum Herunterladen und Speichern von Handelsdaten von verschiedenen deutschen Börsen in QuestDB (Time-Series-Datenbank). ## Unterstützte Exchanges -- **European Investor Exchange (EIX)**: Lädt tägliche Kursblatt-CSVs herunter. -- **Lang & Schwarz (LS)**: Fragt die heutigen Trades über deren JSON/CSV-RPC ab. + +- **European Investor Exchange (EIX)** — Streaming-Verarbeitung (große CSV-Dateien) +- **Lang & Schwarz (LS)** — JSON/CSV-RPC API +- **Deutsche Börse** — Xetra, Frankfurt, Quotrix +- **Gettex** — Bayerische Börse (MUNC/MUND) +- **Stuttgart** — MiFIR II Delayed Data +- **Börsenag** — Düsseldorf (DUSA/DUSB/DUSC/DUSD), Hamburg (HAMA/HAMB), Hannover (HANA/HANB) ## Architektur -- `src/exchanges/base.py`: Basisklasse für neue Börsen (einfach erweiterbar). -- `src/database/questdb_client.py`: Speichert Daten in QuestDB via Influx Line Protocol (ILP). -- `daemon.py`: Der Orchestrator, der die Daten abruft und speichert. + +Fünf Microservices, orchestriert via Docker Compose: + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ QuestDB │ +│ (9000=HTTP, 8812=PostgreSQL, 9009=ILP) │ +└──────────┬──────────────┬──────────────┬──────────────┬─────────┘ + │ │ │ │ + ┌──────┴──────┐ ┌─────┴─────┐ ┌──────┴──────┐ ┌─────┴─────┐ + │ fetcher │ │ analytics │ │ metadata │ │ dashboard │ + │ daemon.py │ │ worker │ │ fetcher │ │ :8080 │ + └─────────────┘ └───────────┘ └─────────────┘ └───────────┘ +``` + +- **Fetcher (`daemon.py`):** Orchestrator. Holt Trades von allen Börsen täglich um 23:00. Streaming für EIX, Batch für alle anderen. Deduplizierung via Hash-Cache. +- **Analytics Worker (`src/analytics/worker.py`):** Berechnet aggregierte Tabellen für Zeiträume: 7, 30, 42, 69, 180, 365 Tage. +- **Metadata Fetcher (`src/metadata/fetcher.py`):** Reichert ISINs mit Firmen-/Sektordaten an (OpenFIGI API, yfinance). +- **Dashboard (`dashboard/server.py`):** FastAPI-Server mit REST-Endpunkten und statischem UI aus `dashboard/public/`. + +## Datenbank-Schema + +QuestDB speichert alle Daten via Influx Line Protocol (ILP) mit Nanosekunden-Präzision. + +### `trades` + +Rohe Handelsdaten aller Börsen. Geschrieben vom Fetcher. + +| Spalte | Typ | Beschreibung | +|--------|-----|-------------| +| `timestamp` | timestamp | Zeitpunkt des Trades | +| `exchange` | symbol (tag) | Börsenname (z.B. `XETRA`, `LS`, `GETTEX`) | +| `symbol` | symbol (tag) | Wertpapiername | +| `isin` | symbol (tag) | ISIN-Kennung (z.B. `DE000BAY0017`) | +| `price` | double | Handelspreis | +| `quantity` | double | Handelsvolumen (Stückzahl) | + +### `analytics_exchange_daily` + +Tägliche Aggregationen pro Börse mit Moving Averages. Geschrieben vom Analytics Worker. + +| Spalte | Typ | Beschreibung | +|--------|-----|-------------| +| `timestamp` | timestamp | Tag der Aggregation | +| `exchange` | symbol (tag) | Börsenname | +| `trade_count` | long | Anzahl Trades am Tag | +| `volume` | double | Gesamtvolumen (Summe von Preis × Menge) | +| `ma{N}_count` | double | N-Tage Moving Average der Trade-Anzahl | +| `ma{N}_volume` | double | N-Tage Moving Average des Volumens | + +*N = 7, 30, 42, 69, 180, 365* + +### `analytics_daily_summary` + +Tagesübergreifende Zusammenfassung aller Börsen. Geschrieben vom Analytics Worker. + +| Spalte | Typ | Beschreibung | +|--------|-----|-------------| +| `timestamp` | timestamp | Tag der Zusammenfassung | +| `total_trades` | long | Gesamtanzahl Trades über alle Börsen | +| `total_volume` | double | Gesamtvolumen über alle Börsen | +| `unique_assets` | long | Anzahl verschiedener gehandelter ISINs | + +### `analytics_stock_trends` + +Trendanalyse pro ISIN mit prozentualen Veränderungen. Geschrieben vom Analytics Worker. + +| Spalte | Typ | Beschreibung | +|--------|-----|-------------| +| `timestamp` | timestamp | Analysedatum | +| `isin` | symbol (tag) | ISIN-Kennung | +| `trade_count` | long | Gesamtanzahl Trades im Zeitraum | +| `volume` | double | Gesamtvolumen im Zeitraum | +| `count_change_pct` | double | Prozentuale Änderung der Trade-Anzahl (1. vs. 2. Hälfte) | +| `volume_change_pct` | double | Prozentuale Änderung des Volumens | +| `period_days` | long | Zeitraum in Tagen (7/30/42/69/180/365) | + +### `analytics_volume_changes` + +Volumen- und Anzahländerungen pro Börse mit Trendklassifizierung. Geschrieben vom Analytics Worker. + +| Spalte | Typ | Beschreibung | +|--------|-----|-------------| +| `timestamp` | timestamp | Analysedatum | +| `exchange` | symbol (tag) | Börsenname | +| `trend` | symbol (tag) | Trendklasse (s.u.) | +| `trade_count` | long | Gesamtanzahl Trades im Zeitraum | +| `volume` | double | Gesamtvolumen im Zeitraum | +| `count_change_pct` | double | Prozentuale Änderung der Trade-Anzahl | +| `volume_change_pct` | double | Prozentuale Änderung des Volumens | +| `period_days` | long | Zeitraum in Tagen | + +**Trendklassen:** `mehr_trades_mehr_volumen`, `mehr_trades_weniger_volumen`, `weniger_trades_mehr_volumen`, `weniger_trades_weniger_volumen`, `stabil` + +### `analytics_custom` + +Vorberechnete Custom-Analytics für den Dashboard-Graphen-Builder. Geschrieben vom Analytics Worker. + +| Spalte | Typ | Beschreibung | +|--------|-----|-------------| +| `timestamp` | timestamp | Tag der Berechnung | +| `y_axis` | symbol (tag) | Metrik-Typ (`volume`, `trade_count`, `avg_price`) | +| `group_by` | symbol (tag) | Gruppierung (`exchange`, `isin`, `date`) | +| `exchange_filter` | symbol (tag) | Exchange-Filter (Börsenname oder `all`) | +| `group_value` | string | Wert der Gruppierung (z.B. Börsenname, ISIN) | +| `y_value` | double | Berechneter Metrik-Wert | + +### `metadata` + +ISIN-Stammdaten (Firmenname, Land, Sektor). Geschrieben vom Metadata Fetcher. + +| Spalte | Typ | Beschreibung | +|--------|-----|-------------| +| `timestamp` | timestamp | Zeitpunkt der letzten Aktualisierung | +| `isin` | symbol (tag) | ISIN-Kennung | +| `name` | string | Firmen-/Wertpapiername | +| `country` | string | Ländercode oder -name | +| `continent` | string | Kontinent | +| `sector` | string | Branchenklassifizierung | ## Installation und Setup -### 1. QuestDB (Timeseries DB) starten -Am einfachsten via Docker Compose: +### 1. QuestDB starten (via Docker Compose) ```bash docker-compose up -d ``` @@ -25,26 +145,35 @@ QuestDB ist dann unter `http://localhost:9000` erreichbar. pip install -r requirements.txt ``` -### 3. Systemd Service einrichten -Kopiere die Dateien nach `/etc/systemd/system/`: +### 3. Manuell starten +```bash +python3 daemon.py # Fetcher +python -m src.analytics.worker # Analytics Worker +python src/metadata/fetcher.py # Metadata Fetcher +python dashboard/server.py # Dashboard (Port 8000) +``` +### 4. Systemd Service (optional) ```bash sudo cp systemd/trading-daemon.service /etc/systemd/system/ sudo cp systemd/trading-daemon.timer /etc/systemd/system/ -``` - -Pfade in `trading-daemon.service` müssen ggf. angepasst werden (aktuell auf `/Users/melchiorreimers/...` gesetzt). - -Dienste aktivieren: -```bash sudo systemctl daemon-reload sudo systemctl enable --now trading-daemon.timer ``` -### 4. Manuell testen -```bash -python3 daemon.py -``` +## Umgebungsvariablen + +| Variable | Default | Beschreibung | +|----------|---------|-------------| +| `DB_USER` | `admin` | QuestDB Benutzername | +| `DB_PASSWORD` | `quest` | QuestDB Passwort | +| `DB_HOST` | `questdb` | QuestDB Hostname (Docker-intern) | ## Erweiterung -Um eine neue Börse hinzuzufügen, erstelle einfach eine neue Klasse in `src/exchanges/`, die von `BaseExchange` erbt und implementiere `fetch_latest_trades()`. Füge sie dann in `daemon.py` zur Liste hinzu. + +Um eine neue Börse hinzuzufügen: + +1. Erstelle eine neue Klasse in `src/exchanges/`, die von `BaseExchange` erbt +2. Implementiere `fetch_latest_trades()` (gibt `List[Trade]` zurück) +3. Implementiere die `name`-Property +4. Registriere in `daemon.py` unter `STREAMING_EXCHANGES` (große Daten) oder `STANDARD_EXCHANGES` (Batch)