SoarCast
A paragliding forecast for each launch. Go, Flyable, Marginal or Not flyable for the next 7 days, with the hours that work, windgrams, trip planning and alerts. Free.
翻译准备中
尚未收集仓库信息。
尚未检测到更新。GitHub 发布版本后会显示在这里。
制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗Ein selbstlernender KI-Tipper für die Kicktipp-Runde. Konzept: siehe concept.md. Website: https://elsuk-kicktipp-automat.github.io/
Stand: Phase 2-4 im Test-/Härtungsbetrieb – Statistik-Engine (OpenLigaDB +
ELO + Quoten, Dixon-Coles-Modell, Kicktipp-Punkteoptimierung, Backtesting),
LLM-Begründungsschicht (Claude Opus 5.5), Paper-Betting, Astro-Website, Hash-Versiegelung,
GitHub-Actions-Automatisierung und verifizierte Kicktipp-Abgabe per Playwright.
Der WM-2026-Testbetrieb lief bis 19.07.2026; seit dem 14.08.2026 läuft die
Pipeline auf der Bundesliga 2026/27 (erster Anstoß 28.08.2026). Die
WM-Daten bleiben unter data/ liegen und sind im Site-Archiv nachprüfbar.
wm26 (durchgehend gepflegt, Gruppenphase bis
Finale, stabile Team-IDs). Teams werden trotzdem über normalisierte Namen
identifiziert – bei anderen Community-Ligen (z.B. Vereinswettbewerbe) sind
Namensvarianten wie „Bosnien-Herzegowina" vs. „Bosnien und Herzegowina" die Norm.team_type in
config.yaml): clubelo.com für Vereine (mit historischen
Ständen pro Stichtag) und eloratings.net für
Nationalteams (nur aktueller Stand). Namens-Zuordnung: data/mappings/.neutral_venue: true abgeschaltet) und ELO-Differenz. Der ELO-Koeffizient
wird mitgeschätzt und zum Prior regularisiert – so trägt ELO die Prognose,
solange wenig Spieldaten da sind (WM-Gruppenphase), und die gefitteten
Teamstärken übernehmen mit wachsender Datenmenge. Output pro Spiel: die
vollständige Wahrscheinlichkeitsmatrix aller Ergebnisse von 0:0 bis 6:6.wm26 sauber getrennt
von „nach Verlängerung" und „nach Elfmeterschießen") – ein Unentschieden ist
ein gültiger und tippbarer Ausgang.engine/market.py, Gewicht konfigurierbar über odds.market_weight).
Ergebnis wird auf einen Blend aus Markt- und Modellmeinung optimiert, die
relative Form (Dixon-Coles-rho) bleibt vom Modell bestimmt. Cache pro
Kalendertag hält den Verbrauch weit unter dem Freikontingent.tipico_de, falls verfügbar; sonst wird der Durchschnitt der
gelieferten Bookmaker-Quoten genutzt. Der Einsatz wird per konservativem
Fractional-Kelly aus der rohen Modellwahrscheinlichkeit vor Markt-Blend
berechnet und bei 100 EUR gedeckelt (paper_betting in config.yaml).CLAUDE_CODE_OAUTH_TOKEN (claude setup-token, ein Jahr gültig). Vor dem
Versiegeln wird geprüft, dass der Text den Tipp nennt und keine dritte
Mannschaft; ohne CLI/Token/Kontingent oder nach zwei verworfenen Texten
springt die Template-Begründung ein. Zusätzlich prüft der News-Check Kicker,
Sportschau und die BILD-News-Sitemap auf aktuelle Schlagzeilen zu den Teams,
ebenfalls mit Claude; nur wenn Claude nicht erreichbar ist, springt
Groq (Free Tier, openai/gpt-oss-120b) ein.
Eine daraus abgeleitete Tipp-Anpassung läuft nur als Schattentipp mit und
ändert den offiziellen Tipp nicht. Die gelesenen Meldungen und der
Token-Verbrauch aller LLM-Aufrufe eines Spiels (factors.llm_usage) werden
nach Anstoß veröffentlicht.Voraussetzung: Python ≥ 3.11
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# Tests (laufen ohne Netzwerk)
python -m pytest tests/
# Tipps für die nächste anstehende Runde -> data/predictions/ (gitignored!)
python -m engine.cli predict
# Tipps versiegeln: Hash öffentlich, Klartext verschlüsselt (braucht SEAL_SECRET)
python -m engine.cli seal
# Saison-Bonusfragen beantworten und versiegeln (einmal pro Saison)
python -m engine.cli bonus
# Tipps nach Anstoß enthüllen (Spieltipps, Kombi und Bonusfragen)
python -m engine.cli unseal
# Abrechnung der enthüllten Tipps gegen die realen Ergebnisse -> data/results/
python -m engine.cli evaluate
# Backtests -> data/backtests/ (--mode club | national | all)
python -m engine.cli backtest
Alle erzeugten Daten sind menschenlesbares JSON im Repo (data/) – das Repo
ist die Datenbank. API-Antworten werden unter data/cache/ (gitignored) gecacht.
Klartext-Tipps liegen nie vor Anstoß im öffentlichen Repo
(data/predictions/ ist gitignored). Stattdessen (concept.md §5):
(Teams, Anstoß, Tipp, Begründung, Paper-Bet, Salt) veröffentlicht
(data/matchdays/); der Klartext liegt Fernet-verschlüsselt in
data/sealed/*.enc. Schlüssel: SEAL_SECRET (GitHub Actions Secret /
lokale .env). Der Commit-Zeitstempel beweist den Zeitpunkt.GitHub Actions führt die Jobs aus (.github/workflows/); der Zeitgeber ist
bewusst cron-job.org per workflow_dispatch. GitHubs eigene Schedule-Events
waren im produktiven Betrieb nicht zuverlässig genug und sind deaktiviert.
| Workflow | Zeitplan | Aufgabe |
|---|---|---|
spieltag.yml | extern stündlich | predict (Spiele im 4h-Fenster vor Anstoß) → seal → Fairness-Commit → Kicktipp-Abgabe → Abgabe-Quittung → Deadline-Audit |
unseal.yml | extern alle 5 min | fällige Tipps ab Anstoß enthüllen + abrechnen (früher Abbruch ohne fällige Spiele) |
deploy-site.yml | bei Daten-/Site-Änderungen | Astro-Build → GitHub Pages |
Deploys laufen über main. Manuelle Spieltags-/Unseal-Läufe auf main
triggern bei geänderten Daten anschließend den Site-Deploy.
Der Abgabe-Audit prüft am Ende jedes Spieltag-Laufs den aktuellen OpenLigaDB-
Spielplan gegen die versiegelten Paarungen und data/submissions/. Ein nach
einer Trigger-Lücke bereits begonnenes Spiel ohne bestätigte Abgabe macht den
Lauf dadurch ausdrücklich rot. Die Quittungen enthalten nur Paarung und
Prüfzeitpunkt, niemals den noch versiegelten Tippwert.
K.o.-Pläne mit Platzhaltern („Sieger SF 12") werden unterstützt: Sobald Nachzügler-Paarungen feststehen, versiegelt der nächste Lauf sie als weiteren Batch derselben Runde.
Kicktipp-Runden stellen neben den Spieltipps Saisonfragen (4 Punkte je richtiger
Antwort), die alle vor dem ersten Anstoß beantwortet sein müssen. Der Automat
beantwortet sie in engine/bonus.py und versiegelt sie wie jeden anderen Tipp –
bis zur Frist ist nur der Hash öffentlich.
Die Antworten kommen aus zwei klar getrennten Quellen, die auch auf der Website ausgewiesen werden:
| Frage | Quelle |
|---|---|
| Deutscher Meister | Saisonsimulation |
| Herbstmeister | Saisonsimulation (Tabelle nach Spieltag 17) |
| Plätze 16–18 | Saisonsimulation |
| Verein des Torschützenkönigs | LLM |
| Erster Trainerwechsel | LLM |
engine/season.py würfelt dafür die Restsaison 10.000 Mal komplett aus – je
Simulation ein Ergebnis pro offenem Spiel aus der Dixon-Coles-Matrix, daraus
eine Abschlusstabelle – und zählt aus, wie oft ein Verein oben oder unten
landet. Bereits gespielte Partien gehen mit ihrem echten Ergebnis in jede
Simulation ein, die Prognose wird also im Saisonverlauf schärfer. Fester Seed:
dieselbe Datenlage muss dieselbe Antwort ergeben, sonst wäre eine versiegelte
Antwort nicht nachvollziehbar.
Für die letzten beiden Fragen hat das Projekt keine Datenquelle – es gibt weder Spieler- noch Trainerdaten. Dort entscheidet Claude Opus 5.5 (Ersatz: Groq) mit seinem Weltwissen und bekommt die Modellprognose als Kontext. Das ist die einzige Stelle, an der die LLM-Schicht eine echte Abgabe bestimmt statt nur im Schatten mitzulaufen; ist kein LLM erreichbar, greift eine dokumentierte Heuristik (stärkste Offensive bzw. größte Abstiegsgefahr unter den etablierten Vereinen).
Abgerechnet wird automatisch, sobald die nötige Tabelle steht (Herbstmeister nach Spieltag 17, Meister und Abstieg am Saisonende). Torschützenkönig und Trainerwechsel bleiben dauerhaft offen – dafür liefert OpenLigaDB nichts.
Die Fragetexte der eigenen Runde stehen in data/mappings/bonus_questions.json.
Eine Frage ohne Eintrag lässt die Abgabe laut fehlschlagen statt sie still
zu überspringen: Ein stiller Fehler würde erst am Saisonende auffallen, wenn
nichts mehr zu retten ist.
Astro-Site unter site/, deployed auf
https://elsuk-kicktipp-automat.github.io/:
Spieltag (versiegelte/enthüllte Tipps), Archiv, Bilanz
(Live-Punkte + Backtests), Wie ich denke (Modell & Hash-Verifikation).
Die Site wird statisch gebaut; ein kleiner Versions-Check (site-version.json)
lädt nach einem neuen Pages-Deploy einmal hart neu, damit Navigation zwischen
Spieltag, Archiv und Bilanz nicht auf einer alten HTML-Version hängen bleibt.
cd site && npm install && npm run dev # lokale Vorschau
data/backtests/club.json bzw. national.json.| Backtest | Spiele | Punkte | Ø/Spiel | ELO-Favorit 2:1 | immer 1:1 |
|---|---|---|---|---|---|
| Bundesliga 2023/24 | 306 | 408 | 1,333 | 395 | 244 |
| Bundesliga 2024/25 | 306 | 383 | 1,252 | 379 | 206 |
| Bundesliga 2025/26 | 306 | 418 | 1,366 | 437 | 218 |
| Bundesliga gesamt | 918 | 1209 | 1,317 | 1211 | 668 |
| WM 2026 (out-of-sample) | 82 | 138 | 1,683 | 123 | 72 |
Befunde:
config.yaml)competition / leagues / season – aktiver Wettbewerb (Bundesliga: bl1,
Saison-Schlüssel 2026 = Spielzeit 2026/27)team_type – club (clubelo.com) oder national (eloratings.net)neutral_venue – Heimvorteil abschalten (Turnier auf neutralem Platz)odds.sport_key – Wettbewerb bei The Odds API; bei jedem Wechsel mitziehenkicktipp.points – Punkteschema der Runde; advance = Zusatzfrage
„Wer kommt weiter?" bei K.o.-Spielen (separat ausgewiesen, 0 = deaktiviert)model.* – Zeitgewichtung, Regularisierung, Tor-Raster, ELO-Priorbacktest.* – Parameter der beiden Backtest-ModiBei einem Wettbewerbswechsel zusätzlich prüfen: die drei Namens-Mappings unter
data/mappings/ (clubs.json für ClubElo, odds_teams.json für The Odds API,
kicktipp_teams.json für die Tippabgabe-Seite) – jede Quelle schreibt Vereine
anders, und ein fehlender Eintrag fällt still aus (Quoten) oder lässt den Lauf
fehlschlagen (Kicktipp). Die Tipprunde selbst steht im Secret KICKTIPP_RUNDE,
nicht in der config.
data/versions.json hält fest, wann sich geändert hat, wie Tipp, Begründung
oder Wette entstehen. Die Website zeigt die Liste unter „Wie ich denke" und
die Version an jedem Spiel; predict.py schreibt die letzte Version an jeden
Tipp (factors.method_version).
Bei einer solchen Änderung einen Eintrag am Ende ergänzen: fortlaufende
version, since_utc (ab wann Tipps danach entstehen), model_version
(die Kennung aus engine/predict.py), title und changes. Änderungen an
Darstellung oder Betrieb gehören nicht hinein. tests/test_versions.py
schlägt fehl, wenn die Kennung im Code geändert wird, ohne dass ein Eintrag
dazukommt.
engine/ Python-Engine
cli.py Einstiegspunkt: predict / bonus / seal / unseal / evaluate / backtest
predict.py Prognose der nächsten Runde -> data/predictions/ (gitignored)
seal.py Hash-Versiegelung + Entsiegelung nach Anstoß
evaluate.py Punkteabrechnung -> data/results/
backtest.py Backtests (club + national) -> data/backtests/
season.py Monte-Carlo-Saisonsimulation (Tabellenprognose)
bonus.py Saison-Bonusfragen der Runde -> data/bonus/
model.py Dixon-Coles-Poisson mit ELO-Term
market.py Quoten-Blending der Wahrscheinlichkeitsmatrix (Vorhersagezeit)
paper_betting.py theoretische Wetten, Einsatzlogik, Abrechnung
llm.py LLM-Aufrufe (Claude, Ersatz Groq): Begründung, News-Anpassung, Verbrauch
kicktipp_bot.py Playwright-Abgabe bei kicktipp.de mit Verifikation
optimizer.py Kicktipp-Punktelogik + EV-Optimierung + Baselines
teams.py Team-Identität über normalisierte Namen
versions.py Versionshistorie der Methode (data/versions.json)
sources/
openligadb.py Spielplan/Ergebnisse mit Cache
elo.py ELO-Adapter (clubelo.com | eloratings.net)
odds.py Quoten-Adapter (The Odds API) mit Cache + Entviggen
tests/ pytest-Suite (ohne Netzwerkzugriff lauffähig)
data/ JSON-„Datenbank" (cache/ ist gitignored)
matchdays/ öffentliche Spieltags-Dateien (Hashes bzw. Enthülltes)
sealed/ verschlüsselte Klartext-Tipps bis zum Anstoß
submissions/ tippwertfreie Quittungen serverseitig bestätigter Abgaben
bonus/ Saison-Bonusfragen (Hash bis zur Frist, dann Antworten)
mappings/ Namens-Zuordnung OpenLigaDB -> ELO-/Quoten-/Kicktipp-Namen
und Bonus-Fragetexte
versions.json Versionshistorie der Methode (von Hand gepflegt)
site/ Astro-Website (GitHub Pages)
.github/workflows/ GitHub Actions (Spieltag, Entsiegeln, Site-Deploy)
config.yaml Wettbewerb, Punkteschema, Modell- und Backtest-Parameter
.env.example Vorlage für Secrets späterer Phasen (Phase 1 braucht keine)