Compare commits
1 Commits
819b6c2232
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad890d23b0 |
@@ -7,4 +7,6 @@ RUN pip install --no-cache-dir -r requirements.txt
|
|||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
ENV PYTHONPATH=/app
|
||||||
|
|
||||||
CMD ["python", "dashboard/server.py"]
|
CMD ["python", "dashboard/server.py"]
|
||||||
|
|||||||
171
README.md
171
README.md
@@ -1,20 +1,140 @@
|
|||||||
# Trading Data Daemon
|
# 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
|
## 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
|
## 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).
|
Fünf Microservices, orchestriert via Docker Compose:
|
||||||
- `daemon.py`: Der Orchestrator, der die Daten abruft und speichert.
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ 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
|
## Installation und Setup
|
||||||
|
|
||||||
### 1. QuestDB (Timeseries DB) starten
|
### 1. QuestDB starten (via Docker Compose)
|
||||||
Am einfachsten via Docker Compose:
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
```
|
```
|
||||||
@@ -25,26 +145,35 @@ QuestDB ist dann unter `http://localhost:9000` erreichbar.
|
|||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Systemd Service einrichten
|
### 3. Manuell starten
|
||||||
Kopiere die Dateien nach `/etc/systemd/system/`:
|
```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
|
```bash
|
||||||
sudo cp systemd/trading-daemon.service /etc/systemd/system/
|
sudo cp systemd/trading-daemon.service /etc/systemd/system/
|
||||||
sudo cp systemd/trading-daemon.timer /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 daemon-reload
|
||||||
sudo systemctl enable --now trading-daemon.timer
|
sudo systemctl enable --now trading-daemon.timer
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. Manuell testen
|
## Umgebungsvariablen
|
||||||
```bash
|
|
||||||
python3 daemon.py
|
| Variable | Default | Beschreibung |
|
||||||
```
|
|----------|---------|-------------|
|
||||||
|
| `DB_USER` | `admin` | QuestDB Benutzername |
|
||||||
|
| `DB_PASSWORD` | `quest` | QuestDB Passwort |
|
||||||
|
| `DB_HOST` | `questdb` | QuestDB Hostname (Docker-intern) |
|
||||||
|
|
||||||
## Erweiterung
|
## 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)
|
||||||
|
|||||||
Reference in New Issue
Block a user