Compare commits

...
3 Commits
Author SHA1 Message Date
cbazzaandClaude Sonnet 5 73a6b43ec9 Update draws, retrain models and generate tips through Sep 11
Also refreshes the Superzahl frequency analysis with the latest draws.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 10:02:20 +02:00
cbazzaandClaude Sonnet 5 d2c9369e3f Rewrite README to match the actual active system
Same problem as Eurojackpot had: described the old 3-strategy system
(PURE-AI/PURE-PATTERN/ENSEMBLE) and crontab scheduling, neither of
which reflects what's actually running. Rewritten to describe the
current 4-strategy generator, the EV-optimized quality score, the
launchd schedule (update/weekly/watchdog), and the new backup
retention. Links to ARCHITECTURE.md for details.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 10:02:10 +02:00
cbazzaandClaude Sonnet 5 612334dc1a Add watchdog for missed launchd runs, fix backup path bug + retention
Watchdog (scripts/utils/watchdog.py, com.lotto.watchdog.plist, daily
21:30) checks whether the latest draw or the trained models are older
than the launchd schedule allows and sends a Telegram alert - this
would have caught the Aug 15 update run that silently never fired
(the Mac was presumably asleep; launchd does not retry missed
StartCalendarInterval jobs), instead of that only surfacing on the
next manual check.

Also fixes a real path bug in LottoAPIUpdater.create_backup(): since
self.data_file already points into data/, joining "data","backups"
wrote backups to data/data/backups/ instead of data/backups/ - 73
backups (6.3MB) had been piling up there undetected for months, with
data/data/ only ever gitignored rather than the root cause fixed.
Merges the misplaced backups into the correct location and adds
retention (keep last 30) so backups no longer grow unbounded; applied
the same retention to Eurojackpot's updater in a matching commit
there, which didn't have the path bug but had the same unbounded
growth (85 backups, 2.4MB).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 10:02:04 +02:00
17 changed files with 459 additions and 10329 deletions

No files matched your search

-1
View File
@@ -59,7 +59,6 @@ data/backups/
data/*.backup* data/*.backup*
data/generated_tips/weekly_lotto_tips_*.csv data/generated_tips/weekly_lotto_tips_*.csv
data/performance_reports/ data/performance_reports/
data/data/
# graphify Code-Graph (regenerierbar via /graphify --update) # graphify Code-Graph (regenerierbar via /graphify --update)
graphify-out/ graphify-out/
+123 -356
View File
@@ -1,41 +1,57 @@
# Lotto 6aus49 Analyse & Generator # Lotto 6aus49 Analyse & Generator
Automatisiertes System zur Analyse von Lotto 6aus49-Daten und Generierung von KI-gestützten Tipps mit Telegram-Benachrichtigungen. Automatisiertes System zur Analyse von Lotto 6aus49-Daten und Generierung von KI-gestützten Tipps mit Telegram-Benachrichtigungen. Schwesterprojekt von [Eurojackpot](../Eurojackpot) — gleiche Architektur, angepasst an das Lotto-Format (6 Zahlen aus 49 + 1 Superzahl).
Eine detaillierte Architektur- und Datenfluss-Beschreibung steht in [documentation/ARCHITECTURE.md](documentation/ARCHITECTURE.md).
## 🎲 Features ## 🎲 Features
- **🧠 AI/ML-basierte Tipp-Generierung**: Random Forest, Gradient Boosting & Neural Networks - **🧠 AI/ML-basierte Tipp-Generierung**: Random Forest + LSTM (PyTorch) Hybrid-Predictor
- **🎨 Pattern-Analyse**: Historische Verteilungsmuster (NNMMHH, etc.) - **🎨 Pattern-Analyse**: Historische Verteilungsmuster (N/M/H-Zonen)
- **⚡ Hybrid-Optimization**: Multi-objektive Optimierung mit 4 Strategien - **⚡ 4-Strategien-System**: HYBRID-OPT, BALANCED-SPREAD, HIGH-EV, SOFT-CONTRARIAN mit adaptiver Gewichtung
- **📚 Real-Time Learning**: Kontinuierliche Anpassung der Modelle - **💰 EV-Optimierung**: Quality-Score gewichtet gezielt auf unpopuläre (aber gleich wahrscheinliche) Zahlenkombinationen, um den Gewinnanteil im Trefferfall zu erhöhen
- **🤖 Automatisierung**: Wöchentliche Tipp-Generierung via Cron - **📚 Real-Time Learning**: Kontinuierliches Retraining bei neuen Ziehungen
- **📲 Telegram-Benachrichtigungen**: Automatische Tipps direkt aufs Smartphone - **🤖 Automatisierung**: Tipp-Generierung & Datenupdate via launchd, mit täglichem Watchdog gegen verpasste Läufe
- **🔄 Data-Updates**: 3 verschiedene Methoden (API, Web-Scraping, Manuelle Eingabe) - **📲 Telegram-Benachrichtigungen**: Automatische Tipps direkt aufs Smartphone, plus Watchdog-Alarm bei Problemen
- **🔄 Data-Updates**: API-Kette (Lottoland → GitHub-Archiv → lottoAPI) mit Web-Scraping- und manuellem Fallback
## 📁 Verzeichnisstruktur ## 📁 Verzeichnisstruktur
``` ```text
Lotto/ Lotto/
├── README.md # Diese Datei ├── README.md
├── config/ # Konfigurationsdateien ├── config/
│ ├── notifications.json # Telegram/Email Config │ └── notifications.json # Telegram/Email Config
│ └── notifications.json.example # Beispiel-Konfiguration ├── data/
├── data/ # Generierte Daten │ ├── AlleLottozahlen.csv # Ziehungshistorie
│ └── generated_tips/ # Hier landen die generierten Tipps │ ├── backups/ # CSV-Backups (automatisch rotiert, letzte 30)
│ ├── ultimate_ml_models/ # Trainierte Modelle (RF + LSTM)
│ └── generated_tips/ # Generierte Tipps
├── scripts/ ├── scripts/
│ ├── generators/ # Tipp-Generatoren │ ├── generators/
│ │ └── ultimate_ai_ml_hybrid_generator.py │ │ └── ultimate_ai_ml_hybrid_generator.py # aktiver Generator
│ ├── automation/ # Automatisierungs-Scripts │ ├── automation/
│ │ └── weekly_tip_generator.py │ │ ├── auto_update_and_learn.py # Update + Retraining
│ ├── analysis/ # Analyse-Scripts │ │ ├── weekly_tip_generator.py # Tipp-Generierung
│ └── utils/ # Hilfsfunktionen │ │ ├── com.lotto.update.plist
│ ├── notifier.py # Benachrichtigungs-System │ │ ├── com.lotto.weekly.plist
│ ├── update_from_api.py # API-basiertes Update │ │ └── com.lotto.watchdog.plist
│ ├── update_from_web.py # Web-Scraping Update │ └── utils/
│ └── simple_update.py # Manuelle Eingabe │ ├── notifier.py # Telegram/Email
├── results/ # Auswertungen & Grafiken │ ├── update_from_api.py # API-Updater (aktiv)
├── logs/ # Log-Dateien │ ├── update_from_web.py # Web-Scraping-Fallback (nicht automatisiert)
└── setup_cron.sh # Cron-Job Einrichtung │ ├── simple_update.py # Manuelle Eingabe
│ ├── watchdog.py # Prüft Daten-/Modell-Aktualität, alarmiert per Telegram
│ ├── validate_csv.py # manuelles Utility
│ ├── verify_draws.py # manuelles Utility
│ └── health_check.py # manuelles Utility
├── documentation/
│ └── ARCHITECTURE.md
├── results/ # Auswertungen (z.B. Superzahl-Statistik)
├── run_tip_generator.sh
├── run_update_and_learn.sh
├── run_watchdog.sh
└── logs/
``` ```
## 🚀 Quick Start ## 🚀 Quick Start
@@ -43,19 +59,16 @@ Lotto/
### 1. Installation ### 1. Installation
```bash ```bash
# Python-Abhängigkeiten installieren python3 -m venv venv
pip install pandas numpy scikit-learn requests source venv/bin/activate
pip install pandas numpy scikit-learn requests torch
# Optional: Deep Learning
pip install tensorflow
``` ```
### 2. Telegram-Bot einrichten ### 2. Telegram-Bot einrichten
1. Erstelle einen Bot mit [@BotFather](https://t.me/botfather) 1. Erstelle einen Bot mit [@BotFather](https://t.me/botfather)
2. Kopiere den Bot-Token 2. Kopiere den Bot-Token, hole deine Chat-ID (z.B. mit [@userinfobot](https://t.me/userinfobot))
3. Hole deine Chat-ID (z.B. mit [@userinfobot](https://t.me/userinfobot)) 3. Konfiguriere `config/notifications.json` (siehe [TELEGRAM_BOT_SETUP.md](TELEGRAM_BOT_SETUP.md)):
4. Konfiguriere `config/notifications.json`:
```json ```json
{ {
@@ -67,360 +80,114 @@ pip install tensorflow
} }
``` ```
### 3. Manuell Tipps generieren Test-Benachrichtigung senden:
```bash
cd scripts/automation
python weekly_tip_generator.py --force --num-tips 10
```
### 4. Telegram-Bot einrichten (optional)
Richte einen eigenen Lotto-Bot für Benachrichtigungen ein:
```bash
# Anleitung lesen
cat TELEGRAM_BOT_SETUP.md
# Bot-Token in config/notifications.json eintragen
# Siehe detaillierte Anleitung in TELEGRAM_BOT_SETUP.md
```
**Kurzanleitung**:
1. Erstelle Bot mit @BotFather auf Telegram
2. Trage Bot-Token in `config/notifications.json` ein
3. Setze `"enabled": true`
### 5. Automatisierung einrichten
```bash
# Cron-Job erstellen (Dienstag & Freitag um 9 Uhr)
./setup_cron.sh
```
## 📊 Verwendung
### Tipps manuell generieren
```bash
# Standard: 10 Tipps
python scripts/automation/weekly_tip_generator.py --force
# Anzahl Tipps anpassen
python scripts/automation/weekly_tip_generator.py --force --num-tips 20
# Nur Historie anzeigen
python scripts/automation/weekly_tip_generator.py --history
```
### Generator direkt aufrufen
```bash
cd scripts/generators
python ultimate_ai_ml_hybrid_generator.py
```
### Test-Benachrichtigung senden
```bash ```bash
python scripts/utils/notifier.py --test python scripts/utils/notifier.py --test
``` ```
### 3. Tipps manuell generieren
```bash
python scripts/automation/weekly_tip_generator.py --force --num-tips 10
# Nur Historie anzeigen
python scripts/automation/weekly_tip_generator.py --history
```
### 4. Daten manuell aktualisieren + Modelle neu trainieren
```bash
./run_update_and_learn.sh
```
## 🧠 Generator-Strategien ## 🧠 Generator-Strategien
Der Ultimate AI/ML Hybrid Generator verwendet **4 verschiedene Strategien**: Der Ultimate AI/ML Hybrid Generator (`scripts/generators/ultimate_ai_ml_hybrid_generator.py`) verteilt pro Lauf 10 Tipps über 4 Strategien, deren Gewichtung sich anhand der bisherigen Trefferleistung selbst anpasst (Startwerte in Klammern):
1. **PURE-AI** (30%): Basierend ausschließlich auf Machine Learning Predictions 1. **HYBRID-OPT** (40%): AI-Score + historisches Pattern-Gewicht kombiniert
2. **PURE-PATTERN** (25%): Historische Pattern-Analyse (NNMMHH-Kombinationen) 2. **BALANCED-SPREAD** (30%): erzwingt Verteilung über N/M/H-Zonen, kontrollierter Summenbereich
3. **HYBRID-OPT** (30%): Multi-objektive Optimierung (AI + Pattern + Diversity) 3. **HIGH-EV** (20%): 2–3 Zahlen >31, meidet empirisch belegte populäre Einzelzahlen — auf EV statt Trefferwahrscheinlichkeit optimiert
4. **ENSEMBLE** (15%): Best-of-all kombiniert alle Ansätze 4. **SOFT-CONTRARIAN** (10%): bevorzugt in den letzten 30 Ziehungen unterrepräsentierte Zahlen
### Ausgabe-Beispiel: ### Warum EV statt Trefferwahrscheinlichkeit?
``` Lotto-Ziehungen sind unabhängige Zufallsereignisse — kein Modell kann die
Nr 6 Numbers SZ Strategy AI-Score Pattern-W Confidence Quality Trefferwahrscheinlichkeit über den statistischen Erwartungswert heben. Der
---------------------------------------------------------------------------------------- Quality-Score optimiert deshalb bewusst auf **Expected Value**: unpopuläre
1 7-14-21-28-35-42 3 PURE-AI 0.7234 0.3456 0.7123 ⭐ 0.756 Zahlenkombinationen haben bei einem Treffer weniger Mitgewinner und damit eine
2 2-11-19-27-36-45 7 HYBRID-OPT 0.6891 0.4123 0.6987 🌟 0.689 höhere Auszahlung. Details und Quellen dazu in
3 5-12-23-31-38-47 6 PURE-PATTERN 0.5234 0.5678 0.6234 🌟 0.621 [documentation/ARCHITECTURE.md](documentation/ARCHITECTURE.md#3-design-entscheidung-quality-score--ev-optimierung).
...
## 🔄 Daten aktualisieren
Der automatisierte Workflow (`auto_update_and_learn.py`) nutzt
`LottoAPIUpdater.fetch_from_all_apis()`: Lottoland zuerst (liefert die
neueste Ziehung meist noch am Ziehungstag), GitHub-Archiv für die Historie.
Für manuelle Läufe stehen zusätzlich bereit:
```bash
# Web-Scraping-Fallback (lotto.de) - nicht in der Automatisierung verdrahtet
python scripts/utils/update_from_web.py
# Letzter Fallback: manuelle Eingabe / CSV-Import
python scripts/utils/simple_update.py
``` ```
## 📈 Performance Tracking Jedes Update erstellt automatisch ein Backup unter `data/backups/` und
rotiert alte Backups (behält die letzten 30).
Alle Generierungen werden in `data/generated_tips/generation_history.json` getrackt: ## 🐶 Watchdog
```json `scripts/utils/watchdog.py` läuft täglich (`com.lotto.watchdog.plist`) und
{ prüft, ob die letzte Ziehung in den Daten älter als 5 Tage ist bzw. ob die
"generations": [ Modelle noch auf einem älteren Stand trainiert sind als die neueste Ziehung.
{ Das fängt genau den Fall ab, dass ein `StartCalendarInterval`-Job verpasst
"timestamp": "2024-11-27T09:00:00", wurde (z.B. weil der Rechner im Schlaf war) — launchd holt das selbst nicht
"num_tips": 10, nach. Bei einem Problem kommt ein Telegram-Alarm, sonst bleibt es still.
"file": "weekly_lotto_tips_20241127_090000.csv",
"avg_confidence": 0.6834,
"avg_quality": 0.6521
}
]
}
```
## 🤖 Automatisierung ## 🤖 Automatisierung
### Cron-Job Zeitplan Läuft über **launchd** (`~/Library/LaunchAgents/`), nicht crontab:
Der `setup_cron.sh` Script richtet folgende Zeitpunkte ein: | Job | Plist | Zeitplan |
| --- | --- | --- |
- **Dienstag 09:00**: Tipps für Mittwoch-Ziehung | Update & Learning | `com.lotto.update.plist` | Mi + Sa, 20:00 Uhr |
- **Freitag 09:00**: Tipps für Samstag-Ziehung | Tipp-Generierung | `com.lotto.weekly.plist` | Di + Fr, 21:00 Uhr |
| Watchdog | `com.lotto.watchdog.plist` | täglich, 21:30 Uhr |
### Cron-Job manuell einrichten
```bash
crontab -e
```
Füge hinzu:
```bash
# Lotto 6aus49 - Dienstag & Freitag 09:00
0 9 * * 2,5 cd /path/to/Lotto && python3 scripts/automation/weekly_tip_generator.py >> logs/weekly_tips.log 2>&1
```
## 📲 Telegram-Benachrichtigungen ## 📲 Telegram-Benachrichtigungen
Bei erfolgreicher Generierung erhältst du automatisch: Bei erfolgreicher Tipp-Generierung bzw. nach jeder ausgewerteten Ziehung
verschickt `LottoNotifier` automatisch eine Zusammenfassung (bester Tipp, Ø
Confidence/Quality, Trefferauswertung). Der Watchdog nutzt denselben Kanal
für Alarme.
- ⭐ **Bester Tipp** (höchste Confidence) ## 📈 Performance Tracking
- 📊 **Statistiken** (Ø Confidence & Quality)
- 🏆 **Top 3 Tipps** nach Confidence sortiert
Beispiel-Nachricht: Alle Generierungen werden in `data/generated_tips/generation_history.json`
protokolliert, alle Ziehungsauswertungen in `data/learning_log.json`.
``` ## 🗂️ Datenformat
🎲 LOTTO 6AUS49 - NEUE TIPPS GENERIERT
========================================
📅 Generiert: 2024-11-27 09:00 `data/AlleLottozahlen.csv` (Semikolon-getrennt):
📊 Anzahl Tipps: 10
⭐ BESTER TIPP:
🎯 Zahlen: 07 - 14 - 21 - 28 - 35 - 42
🌟 Superzahl: 3
📈 Confidence: 0.7234
🎨 Strategie: PURE-AI
🍀 Viel Glück!
```
## 🔧 Konfiguration
### notifications.json
```json
{
"telegram": {
"enabled": true,
"bot_token": "YOUR_BOT_TOKEN",
"chat_id": "YOUR_CHAT_ID"
},
"email": {
"enabled": false,
"smtp_server": "smtp.gmail.com",
"smtp_port": 587,
"smtp_user": "",
"smtp_password": "",
"from_email": "",
"to_email": ""
}
}
```
## 🎰 Lotto 6aus49 Regeln
- **6 Zahlen** aus 50 (1-49)
- **1 Superzahl** aus 10 (0-9)
- Ziehungen: **Mittwoch** und **Samstag**
- Quoten: [www.lotto.de](https://www.lotto.de)
## 📝 Datenformat
CSV-Datei `AlleLottozahlen.csv`:
```csv ```csv
datum;Z1;Z2;Z3;Z4;Z5;Z6;SZ datum;Z1;Z2;Z3;Z4;Z5;Z6;SZ
2024-11-23;7;14;21;28;35;42;3 2026-08-08;2;8;22;30;38;43;4
2024-11-20;2;11;19;27;36;45;7
...
``` ```
## 🔄 Daten Aktualisieren ## Hinweise
Das System bietet **3 verschiedene Methoden** zum Aktualisieren der Lottozahlen: - Lotto 6aus49: 6 Zahlen aus 49 + 1 Superzahl (0–9), Ziehungen Mi + Sa
- Keine Gewinngarantie — statistisches Analyse-Tool, keine Vorhersagekraft
### Methode 1: API-Update (Empfohlen) über den Zufalls-Erwartungswert hinaus (siehe Abschnitt "Warum EV statt
Trefferwahrscheinlichkeit?")
Automatisches Laden von öffentlichen Lotto-APIs: - Bot-Token niemals öffentlich teilen
```bash
cd scripts/utils
python update_from_api.py
```
**Vorteile:**
- ✅ Automatisch
- ✅ Schnell
- ✅ Keine manuelle Eingabe
**Hinweis:** Funktioniert nur wenn APIs verfügbar sind.
### Methode 2: Web-Scraping
Scrapt aktuelle Ziehungen direkt von lotto.de:
```bash
cd scripts/utils
python update_from_web.py
```
Optionen:
```bash
# Mehr Seiten laden (mehr historische Daten)
python update_from_web.py --pages 10
# Spezifische Datei
python update_from_web.py /pfad/zur/datei.csv 5
```
**Vorteile:**
- ✅ Offizielle Quelle (lotto.de)
- ✅ Zuverlässig
- ✅ Viele historische Daten
**Nachteile:**
- ⚠️ Kann bei Struktur-Änderungen der Website fehlschlagen
### Methode 3: Manuelle Eingabe (Fallback)
Für den Fall dass API und Web-Scraping nicht funktionieren:
```bash
cd scripts/utils
python simple_update.py
```
**Zwei Modi:**
1. **Einzelne Ziehung eingeben:**
```
Format: YYYY-MM-DD Z1 Z2 Z3 Z4 Z5 Z6 SZ
Beispiel: 2024-11-27 7 14 21 28 35 42 3
```
2. **CSV-Import:**
```csv
datum;Z1;Z2;Z3;Z4;Z5;Z6;SZ
2024-11-27;7;14;21;28;35;42;3
2024-11-23;2;11;19;27;36;45;7
```
**Vorteile:**
- ✅ Funktioniert immer
- ✅ Volle Kontrolle
- ✅ CSV-Batch-Import möglich
### Automatisches Backup
Alle Update-Methoden erstellen **automatisch ein Backup** vor der Änderung:
- Gespeichert in: `Lotto/data/backups/`
- Format: `AlleLottozahlen.csv.backup_YYYYMMDD_HHMMSS`
### Update-Workflow Empfehlung
1. **Versuche zuerst API:**
```bash
python scripts/utils/update_from_api.py
```
2. **Fallback auf Web-Scraping:**
```bash
python scripts/utils/update_from_web.py
```
3. **Letzter Fallback - Manuelle Eingabe:**
```bash
python scripts/utils/simple_update.py
```
### Nach dem Update
Nach dem erfolgreichen Update der Daten:
```bash
# Neue Tipps generieren (mit automatischem Retraining)
python scripts/automation/weekly_tip_generator.py --force
# Oder direkt den Generator aufrufen
python scripts/generators/ultimate_ai_ml_hybrid_generator.py
```
### 🤖 Automatisches ML-Retraining
Das System verwendet **intelligentes Retraining**:
- ✅ **Automatische Erkennung**: Prüft ob CSV neuer als ML-Cache
- ✅ **Smart Caching**: Nutzt Cache wenn Daten unverändert
- ✅ **Auto-Retrain**: Trainiert neu wenn neue Daten verfügbar
**So funktioniert es:**
1. Nach `update_from_api.py` wird CSV aktualisiert
2. Beim nächsten Generator-Start: CSV-Timestamp > Cache-Timestamp
3. ✅ Automatisches Retraining mit neuen Daten
4. Neue ML-Modelle berücksichtigen aktuelle Ziehungen
**Manuelles Retraining erzwingen:**
```bash
# Cache löschen für komplettes Retraining
rm -rf ultimate_ml_models/
python scripts/generators/ultimate_ai_ml_hybrid_generator.py
```
## 🛠️ Entwicklung
### Neue Strategie hinzufügen
1. Öffne `scripts/generators/ultimate_ai_ml_hybrid_generator.py`
2. Füge neue Strategie in `strategy_weights` hinzu
3. Implementiere `_generate_<strategy>_tip()` Methode
4. Update `_generate_tips_by_strategy()`
### Logging aktivieren
```python
import logging
logging.basicConfig(
filename='logs/generator.log',
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
```
## ⚠️ Hinweise
- **Keine Gewinngarantie**: Dies ist ein statistisches Analyse-Tool
- **Historische Daten**: Vergangene Ziehungen garantieren keine zukünftigen Ergebnisse
- **Verantwortungsvoll spielen**: Lotto ist Glücksspiel
- **Bot-Token**: Niemals öffentlich teilen!
## 📜 Lizenz
Dieses Projekt ist für private Zwecke. Keine kommerzielle Nutzung.
## 🙋 Support ## 🙋 Support
Bei Fragen oder Problemen: 1. Logs prüfen: `logs/update_stdout.log`, `logs/stdout.log`, `logs/watchdog_stdout.log`
1. Check die Logs: `logs/weekly_tips.log`
2. Test-Benachrichtigung: `python scripts/utils/notifier.py --test` 2. Test-Benachrichtigung: `python scripts/utils/notifier.py --test`
3. Historie prüfen: `python scripts/automation/weekly_tip_generator.py --history` 3. Historie prüfen: `python scripts/automation/weekly_tip_generator.py --history`
+4
View File
@@ -3154,3 +3154,7 @@ datum;Z1;Z2;Z3;Z4;Z5;Z6;SZ
2026-08-19;3;23;31;33;35;47;6 2026-08-19;3;23;31;33;35;47;6
2026-08-22;7;16;22;28;30;31;4 2026-08-22;7;16;22;28;30;31;4
2026-08-26;6;15;30;33;42;49;1 2026-08-26;6;15;30;33;42;49;1
2026-08-29;9;18;28;39;41;48;6
2026-09-02;8;19;23;32;44;45;8
2026-09-05;6;11;28;31;37;47;5
2026-09-09;7;24;28;30;39;41;5
1 datum Z1 Z2 Z3 Z4 Z5 Z6 SZ
3154 2026-08-19 3 23 31 33 35 47 6
3155 2026-08-22 7 16 22 28 30 31 4
3156 2026-08-26 6 15 30 33 42 49 1
3157 2026-08-29 9 18 28 39 41 48 6
3158 2026-09-02 8 19 23 32 44 45 8
3159 2026-09-05 6 11 28 31 37 47 5
3160 2026-09-09 7 24 28 30 39 41 5
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -629,6 +629,34 @@
"file": "weekly_lotto_tips_20260828_210030.csv", "file": "weekly_lotto_tips_20260828_210030.csv",
"avg_confidence": 0.5506374712982418, "avg_confidence": 0.5506374712982418,
"avg_quality": 0.5772785715969829 "avg_quality": 0.5772785715969829
},
{
"timestamp": "2026-09-01T21:00:51.165743",
"num_tips": 10,
"file": "weekly_lotto_tips_20260901_210051.csv",
"avg_confidence": 0.5179789527048461,
"avg_quality": 0.5936163803435738
},
{
"timestamp": "2026-09-04T21:08:30.163353",
"num_tips": 10,
"file": "weekly_lotto_tips_20260904_210830.csv",
"avg_confidence": 0.4558949537384773,
"avg_quality": 0.5771370784104253
},
{
"timestamp": "2026-09-08T21:11:41.032574",
"num_tips": 10,
"file": "weekly_lotto_tips_20260908_211141.csv",
"avg_confidence": 0.43113063183079586,
"avg_quality": 0.5745755401219569
},
{
"timestamp": "2026-09-11T21:00:21.363480",
"num_tips": 10,
"file": "weekly_lotto_tips_20260911_210021.csv",
"avg_confidence": 0.44672937432051574,
"avg_quality": 0.5866481687048961
} }
] ]
} }
+52
View File
@@ -948,6 +948,58 @@
"main": 0.7, "main": 0.7,
"sz_rate": 0.1 "sz_rate": 0.1
} }
},
{
"timestamp": "2026-08-29T20:00:52.886607",
"draw_date": "2026-08-29",
"evaluation": {
"main": 2,
"sz": true,
"tip": 5
},
"avg_matches": {
"main": 0.8,
"sz_rate": 0.1
}
},
{
"timestamp": "2026-09-02T20:00:54.961352",
"draw_date": "2026-09-02",
"evaluation": {
"main": 2,
"sz": false,
"tip": 1
},
"avg_matches": {
"main": 1.1,
"sz_rate": 0.1
}
},
{
"timestamp": "2026-09-05T20:00:53.963202",
"draw_date": "2026-09-05",
"evaluation": {
"main": 2,
"sz": false,
"tip": 6
},
"avg_matches": {
"main": 0.6,
"sz_rate": 0.1
}
},
{
"timestamp": "2026-09-09T20:00:55.140109",
"draw_date": "2026-09-09",
"evaluation": {
"main": 1,
"sz": true,
"tip": 7
},
"avg_matches": {
"main": 0.4,
"sz_rate": 0.1
}
} }
] ]
} }
@@ -1,9 +1,9 @@
{ {
"trained_at": "2026-08-26T20:00:56.283583", "trained_at": "2026-09-09T20:00:55.132856",
"num_samples": 3135, "num_samples": 3139,
"num_features": 20, "num_features": 20,
"final_loss": 0.3730317278753353, "final_loss": 0.3722805441180362,
"final_val_loss": 0.3733683332800865, "final_val_loss": 0.37351479232311247,
"best_val_loss": 0.37311964482069016, "best_val_loss": 0.3732968047261238,
"epochs_trained": 28 "epochs_trained": 30
} }
+54 -54
View File
@@ -1,61 +1,61 @@
{ {
"adjustments": { "adjustments": {
"1": -0.19478554069866538, "1": -0.2106701973228925,
"2": -0.18901075949778243, "2": -0.20501004841262846,
"3": -0.17621167205923433, "3": -0.1924650292512798,
"4": -0.18956571514574574, "4": -0.20555398791384874,
"5": -0.17727980514372696, "5": -0.1935119593606463,
"6": -0.20113063443288826, "6": -0.20203896277790986,
"7": -0.11518430063362944, "7": -0.11772408162401678,
"8": -0.18146617313838923, "8": -0.182839102735047,
"9": -0.18759303973870778, "9": -0.1889182285893702,
"10": -0.1677611820598764, "10": -0.18418228569837258,
"11": -0.18219933065355118, "11": -0.1834834548324124,
"12": -0.20960960726910235, "12": -0.22519999876913804,
"13": -0.16373141392375076, "13": -0.18023251047211447,
"14": -0.22788450336984212, "14": -0.2431121290562519,
"15": -0.11295326493095584, "15": -0.13046233309416472,
"16": -0.16515777429003534, "16": -0.1816305568728396,
"17": -0.20387467951219515, "17": -0.219578912192085,
"18": -0.20533691607503754, "18": -0.20630988011957557,
"19": -0.19401957406253612, "19": -0.19514331238199498,
"20": -0.06410859904192305, "20": -0.08258725821483424,
"21": -0.2189476922004348, "21": -0.2343527180513774,
"22": -0.18703774486077154, "22": -0.2030761991014364,
"23": -0.17986662678637222, "23": -0.18127130817689097,
"24": -0.19143450263033077, "24": -0.19246067903363895,
"25": -0.15615639580581434, "25": -0.17280786024659375,
"26": -0.1629012047154243, "26": -0.17941878133115902,
"27": -0.16573545711626494, "27": -0.1821967724064882,
"28": -0.14097169337511936, "28": -0.11344696423263387,
"29": -0.19412106797494957, "29": -0.21001891471456352,
"30": -0.15821678362881567, "30": -0.15990234834240233,
"31": -0.1806700907184887, "31": -0.18198457107372512,
"32": -0.14317751187959615, "32": -0.1453104905226412,
"33": -0.18911248952822005, "33": -0.2051097590511604,
"34": -0.24300222383281544, "34": -0.25792975521862355,
"35": -0.14889768383078938, "35": -0.16569323732909239,
"36": -0.19962377282817678, "36": -0.2154123881285409,
"37": -0.12131189581893032, "37": -0.1238046659849216,
"38": -0.23006634008932061, "38": -0.24525065522729406,
"39": -0.14892185306375724, "39": -0.1360896842813413,
"40": -0.19554822180621415, "40": -0.21141773882959256,
"41": -0.2004735679542188, "41": -0.18661807188758955,
"42": -0.1605865902425092, "42": -0.1771501131113919,
"43": -0.16395664875494143, "43": -0.1804532742794294,
"44": -0.16636537531663315, "44": -0.16803806329101365,
"45": -0.22780245811738084, "45": -0.2282555893180233,
"46": -0.2110493342300891, "46": -0.22661114643098554,
"47": -0.17439901746633996, "47": -0.17583798175724874,
"48": -0.16614824262473232, "48": -0.16789912140710272,
"49": -0.15277862794792954 "49": -0.1694971427674608
}, },
"stats": { "stats": {
"incorrect": 2838, "incorrect": 3010,
"correct": 396, "correct": 420,
"cycles": 66, "cycles": 70,
"last_update": "2026-08-26T20:00:56.286878" "last_update": "2026-09-09T20:00:55.138048"
}, },
"learning_rate": 0.1, "learning_rate": 0.1,
"last_saved": "2026-08-26T20:00:56.286936" "last_saved": "2026-09-09T20:00:55.138109"
} }
Binary file not shown.
+14 -14
View File
@@ -1,25 +1,25 @@
# Häufigste Superzahlen – Lotto 6aus49 # Häufigste Superzahlen – Lotto 6aus49
Stand: 3.152 Ziehungen (inkl. Ziehung vom 15.08.2026) Stand: 3.155 Ziehungen (inkl. Ziehung vom 26.08.2026)
| Rang | Superzahl | Häufigkeit | Anteil | | Rang | Superzahl | Häufigkeit | Anteil |
|---|---|---|---| | --- | --- | --- | --- |
| 1 | **7** | 342× | 10,85% | | 1 | **7** | 342× | 10,84% |
| 2 | **5** | 333× | 10,56% | | 2 | **5** | 333× | 10,55% |
| 3 | **4** | 327× | 10,37% | | 3 | **4** | 328× | 10,40% |
| 4 | **6** | 318× | 10,09% | | 4 | **6** | 319× | 10,11% |
| 5 | **3** | 317× | 10,06% | | 5 | **3** | 317× | 10,05% |
| 6 | **0** | 311× | 9,87% | | 6 | **0** | 311× | 9,86% |
| 7 | **1** | 306× | 9,71% | | 7 | **1** | 307× | 9,73% |
| 8 | **2** | 306× | 9,71% | | 8 | **2** | 306× | 9,70% |
| 9 | **8** | 299× | 9,49% | | 9 | **8** | 299× | 9,48% |
| 10 | **9** | 293× | 9,30% | | 10 | **9** | 293× | 9,29% |
## Einordnung ## Einordnung
Bei theoretisch exakt gleicher Wahrscheinlichkeit (10,0% pro Ziffer) liegt die Bei theoretisch exakt gleicher Wahrscheinlichkeit (10,0% pro Ziffer) liegt die
erwartete Zufallsschwankung bei ±0,5 Prozentpunkten über 3.152 Ziehungen — die erwartete Zufallsschwankung bei ±0,5 Prozentpunkten über 3.155 Ziehungen — die
gemessene Spanne (9,30%–10,85%) liegt genau in diesem Rauschband. Das ist gemessene Spanne (9,29%–10,84%) liegt genau in diesem Rauschband. Das ist
**kein Beleg dafür, dass die 7 "häufiger kommt"**, sondern reine **kein Beleg dafür, dass die 7 "häufiger kommt"**, sondern reine
Stichprobenstreuung, da jede Ziehung unabhängig und gleichverteilt ist. Stichprobenstreuung, da jede Ziehung unabhängig und gleichverteilt ist.
+4
View File
@@ -0,0 +1,4 @@
#!/bin/bash
cd "/Users/sebastianfrohlich/Projekte/Lotto"
source venv/bin/activate
python scripts/utils/watchdog.py
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.lotto.watchdog</string>
<key>ProgramArguments</key>
<array>
<string>/Users/sebastianfrohlich/Projekte/Lotto/run_watchdog.sh</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>21</integer>
<key>Minute</key>
<integer>30</integer>
</dict>
<key>WorkingDirectory</key>
<string>/Users/sebastianfrohlich/Projekte/Lotto</string>
<key>StandardOutPath</key>
<string>/Users/sebastianfrohlich/Projekte/Lotto/logs/watchdog_stdout.log</string>
<key>StandardErrorPath</key>
<string>/Users/sebastianfrohlich/Projekte/Lotto/logs/watchdog_stderr.log</string>
<key>RunAtLoad</key>
<false/>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
<key>LANG</key>
<string>de_DE.UTF-8</string>
</dict>
<key>LaunchOnlyOnce</key>
<false/>
<key>Nice</key>
<integer>10</integer>
</dict>
</plist>
+30
View File
@@ -265,6 +265,36 @@ class LottoNotifier:
# TODO: Email-Versand implementieren wenn gewünscht # TODO: Email-Versand implementieren wenn gewünscht
print("⚠️ Email-Benachrichtigung noch nicht implementiert") print("⚠️ Email-Benachrichtigung noch nicht implementiert")
def send_alert(self, message: str) -> bool:
"""Sendet eine formlose Warn-/Alarm-Nachricht (z.B. vom Watchdog)."""
if not self.config['telegram']['enabled']:
print("⚠️ Telegram nicht aktiviert in config/notifications.json")
return False
try:
bot_token = self.config['telegram']['bot_token']
chat_id = self.config['telegram']['chat_id']
url = f"https://api.telegram.org/bot{bot_token}/sendMessage"
data = {
"chat_id": chat_id,
"text": message,
"parse_mode": "Markdown"
}
response = requests.post(url, data=data, timeout=10)
if response.status_code == 200:
print("✅ Alarm-Benachrichtigung gesendet")
return True
else:
print(f"⚠️ Telegram-Fehler: {response.status_code}")
return False
except Exception as e:
print(f"⚠️ Alarm-Benachrichtigung fehlgeschlagen: {e}")
return False
def send_test_notification(self): def send_test_notification(self):
"""Sendet Test-Benachrichtigung.""" """Sendet Test-Benachrichtigung."""
if self.config['telegram']['enabled']: if self.config['telegram']['enabled']:
+21 -2
View File
@@ -91,7 +91,7 @@ class LottoAPIUpdater:
return False return False
def create_backup(self) -> bool: def create_backup(self) -> bool:
"""Erstellt Backup.""" """Erstellt Backup und behält nur die letzten KEEP_BACKUPS Stück."""
if not os.path.exists(self.data_file): if not os.path.exists(self.data_file):
return True return True
@@ -99,7 +99,10 @@ class LottoAPIUpdater:
print("\n💾 Erstelle Backup...") print("\n💾 Erstelle Backup...")
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_dir = os.path.join(os.path.dirname(self.data_file), "data", "backups") # self.data_file liegt bereits unter .../data/ - vorher wurde hier
# zusätzlich "data" angehängt, was seit Monaten ungenutzt nach
# .../data/data/backups/ statt .../data/backups/ geschrieben hat.
backup_dir = os.path.join(os.path.dirname(self.data_file), "backups")
os.makedirs(backup_dir, exist_ok=True) os.makedirs(backup_dir, exist_ok=True)
filename = os.path.basename(self.data_file) filename = os.path.basename(self.data_file)
@@ -108,12 +111,28 @@ class LottoAPIUpdater:
shutil.copy2(self.data_file, self.backup_file) shutil.copy2(self.data_file, self.backup_file)
print(f" ✅ Backup: {os.path.basename(self.backup_file)}") print(f" ✅ Backup: {os.path.basename(self.backup_file)}")
self._prune_old_backups(backup_dir, filename)
return True return True
except Exception as e: except Exception as e:
print(f" ⚠️ Backup-Fehler: {e}") print(f" ⚠️ Backup-Fehler: {e}")
return False return False
@staticmethod
def _prune_old_backups(backup_dir: str, filename: str, keep: int = 30) -> None:
"""Behält nur die KEEP neuesten Backups, löscht den Rest."""
pattern = f"{filename}.backup_"
backups = sorted(
(f for f in os.listdir(backup_dir) if f.startswith(pattern)),
reverse=True,
)
for old in backups[keep:]:
try:
os.remove(os.path.join(backup_dir, old))
except OSError:
pass
def fetch_from_api(self, api_name: str) -> List[Dict]: def fetch_from_api(self, api_name: str) -> List[Dict]:
""" """
Holt Daten von spezifischer API. Holt Daten von spezifischer API.
+77
View File
@@ -0,0 +1,77 @@
#!/usr/bin/env python3
"""
Watchdog für die Lotto-Automatisierung.
Prüft die Aktualität von Ziehungsdaten und trainierten Modellen anhand des
Ziehungskalenders (Mi + Sa) und meldet sich per Telegram, wenn etwas länger
stillsteht als der launchd-Zeitplan erlaubt. Das fängt genau den Fall ab, in
dem ein StartCalendarInterval-Job verpasst wurde (z.B. weil der Mac im Schlaf
war) - launchd holt das selbst nicht nach, und ohne diesen Check fällt es
sonst erst bei der nächsten manuellen Nachfrage auf.
Aufruf: python3 scripts/utils/watchdog.py
Gedacht für einen täglichen launchd-Job (siehe com.lotto.watchdog.plist).
"""
import os
import sys
from datetime import datetime
import pandas as pd
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from notifier import LottoNotifier
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
DATA_FILE = os.path.join(PROJECT_ROOT, "data", "AlleLottozahlen.csv")
MODEL_FILE = os.path.join(PROJECT_ROOT, "data", "ultimate_ml_models", "trained_models.pkl")
# Lotto zieht Mi + Sa -> maximal 4 Tage zwischen zwei Ziehungen. 5 Tage
# Toleranz lassen dem Update-Job (selber Abend, 20 Uhr) Luft, ohne echte
# Ausfälle zu verpassen.
MAX_DAYS_SINCE_DRAW = 5
def check() -> list[str]:
"""Prüft Daten- und Modell-Aktualität, gibt gefundene Probleme zurück."""
problems = []
df = pd.read_csv(DATA_FILE, sep=';')
last_draw = pd.to_datetime(df['datum'], format='%Y-%m-%d').max().date()
days_since_draw = (datetime.now().date() - last_draw).days
if days_since_draw > MAX_DAYS_SINCE_DRAW:
problems.append(
f"Letzte Ziehung in den Daten ist {last_draw.isoformat()} "
f"({days_since_draw} Tage her) - der automatische Update-Lauf "
f"scheint ausgefallen zu sein."
)
if os.path.exists(MODEL_FILE):
model_date = datetime.fromtimestamp(os.path.getmtime(MODEL_FILE)).date()
if model_date < last_draw and (datetime.now().date() - model_date).days > 1:
problems.append(
f"Modelle sind vom {model_date.isoformat()}, aber die neueste "
f"Ziehung ({last_draw.isoformat()}) wurde noch nicht eingelernt."
)
else:
problems.append("Modell-Datei trained_models.pkl fehlt komplett.")
return problems
def main():
problems = check()
if not problems:
print("✅ Watchdog: alles aktuell.")
return
print("⚠️ Watchdog hat Probleme gefunden:")
for p in problems:
print(f" - {p}")
message = "🐶 *LOTTO WATCHDOG - PROBLEM ERKANNT*\n\n" + "\n".join(f"⚠️ {p}" for p in problems)
LottoNotifier().send_alert(message)
if __name__ == "__main__":
main()