Files
turf_saas/agent_turf.py
2026-04-25 17:18:43 +02:00

348 lines
10 KiB
Python
Executable File
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Agent Turf Autonome - Prédictions quotidiennes
Usage: python3 agent_turf.py [--dry-run]
"""
import sqlite3
import subprocess
import sys
import json
import os
from datetime import datetime, timedelta
from pathlib import Path
DB_PATH = "/home/h3r7/turf_scraper/turf.db"
LOG_FILE = "/home/h3r7/turf_scraper/logs/agent_turf.log"
VPS_HOST = "10.0.1.1"
def log(msg: str):
"""Log avec timestamp"""
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
line = f"[{timestamp}] {msg}"
print(line)
os.makedirs(os.path.dirname(LOG_FILE), exist_ok=True)
with open(LOG_FILE, "a") as f:
f.write(line + "\n")
def sync_database():
"""Sync base de données depuis VPS"""
log("=== SYNC DATABASE ===")
# Check remote size
cmd = f"sshpass -p 'Cronstadt35*' ssh -o StrictHostKeyChecking=no h3r7@{VPS_HOST} stat -c %s {DB_PATH}"
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
if result.returncode != 0:
log(f"ERROR: Cannot connect to VPS")
return False
remote_size = int(result.stdout.strip())
local_size = os.path.getsize(DB_PATH) if os.path.exists(DB_PATH) else 0
if remote_size == local_size:
log(f"Database up to date ({remote_size} bytes)")
return True
log(f"Syncing database (remote: {remote_size}, local: {local_size})...")
cmd = f"sshpass -p 'Cronstadt35*' scp -o StrictHostKeyChecking=no h3r7@{VPS_HOST}:{DB_PATH} {DB_PATH}.tmp"
result = subprocess.run(cmd, shell=True, capture_output=True)
if result.returncode == 0:
os.rename(DB_PATH + ".tmp", DB_PATH)
log(f"Database synced successfully")
return True
else:
log(f"ERROR: Sync failed")
return False
def generate_predictions():
"""Génère les prédictions du jour"""
log("=== GENERATE PREDICTIONS ===")
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
today = datetime.now().strftime("%Y-%m-%d")
# Get today's races
cursor.execute(
"""
SELECT DISTINCT c.date_programme, c.num_reunion, c.num_course,
c.libelle, c.heure_depart_str, r.hippodrome_court
FROM pmu_courses c
JOIN pmu_reunions r ON r.date_programme = c.date_programme AND r.num_reunion = c.num_reunion
WHERE c.date_programme = ? AND r.pays_code = 'FRA'
ORDER BY c.heure_depart_str
""",
(today,),
)
races = cursor.fetchall()
log(f"Found {len(races)} races today")
predictions = []
for race in races:
date, num_reunion, num_course, libelle, heure, hippodrome = race
# Get scoring
cursor.execute(
"""
SELECT horse_name, horse_number, score, cote, rang_scoring
FROM scoring
WHERE date = ? AND race_name LIKE ?
ORDER BY rang_scoring
LIMIT 4
""",
(date, f"%{libelle}%"),
)
scores = cursor.fetchall()
if scores:
pred = {
"race": f"{heure} - {hippodrome} - {libelle}",
"top4": [dict(s) for s in scores],
}
predictions.append(pred)
log(
f" {pred['race'][:50]} -> {', '.join([s['horse_name'] for s in scores[:3]])}"
)
conn.close()
return predictions
def save_recommendations(predictions):
"""Sauvegarde les recommandations en base"""
log("=== SAVE RECOMMENDATIONS ===")
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
today = datetime.now().strftime("%Y-%m-%d")
# Clear today's recommendations
cursor.execute("DELETE FROM recommendations WHERE date = ?", (today,))
for pred in predictions:
race_name = pred["race"]
# Add top 4
for i, horse in enumerate(pred["top4"]):
if i < 4: # TOP 4
cursor.execute(
"""
INSERT INTO recommendations
(date, race_name, type_pari, cheval1, numero1, cote, mise, confiance, justification)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
today,
race_name,
f"top{i + 1}",
horse["horse_name"],
horse["horse_number"],
horse["cote"],
2.0,
f"TOP {i + 1}",
f"Score: {horse['score']}, Rang scoring: {horse['rang_scoring']}",
),
)
# Add simple winner (top 1)
top1 = pred["top4"][0]
cursor.execute(
"""
INSERT INTO recommendations
(date, race_name, type_pari, cheval1, numero1, cote, mise, confiance, justification)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
today,
race_name,
"simple_gagnant",
top1["horse_name"],
top1["horse_number"],
top1["cote"],
2.0,
"🔥 FORTE" if top1["score"] > 60 else "✅ BONNE",
f"Score: {top1['score']}, Favorite prediction",
),
)
conn.commit()
conn.close()
log(f"Saved {len(predictions)} race recommendations")
def notify_telegram(predictions):
"""Envoie les prédictions du jour via Telegram"""
import urllib.request
config_path = "/home/h3r7/turf_scraper/config_telegram.json"
if not os.path.exists(config_path):
log("No Telegram config found, skipping notification")
return
try:
with open(config_path, "r") as f:
cfg = json.load(f)
token = cfg.get("token", "")
chat_id = cfg.get("chat_id", "")
if not token or not chat_id:
log("Telegram config missing token or chat_id")
return
except Exception as e:
log(f"Error reading Telegram config: {e}")
return
today = datetime.now().strftime("%d/%m/%Y")
lines = [f"🏇 *Prédictions Turf — {today}*", ""]
if not predictions:
lines.append("Aucune prédiction disponible aujourd'hui.")
else:
for pred in predictions:
race_label = pred.get("race", "Course inconnue")
top4 = pred.get("top4", [])
lines.append(f"📍 *{race_label}*")
for i, horse in enumerate(top4, 1):
name = horse.get("horse_name", "?")
score = horse.get("score", 0)
cote = horse.get("cote", "?")
icon = "🥇" if i == 1 else ("🥈" if i == 2 else ("🥉" if i == 3 else "4"))
lines.append(f" {icon} {name} — score: {score} — cote: {cote}")
lines.append("")
text = "\n".join(lines)
url = f"https://api.telegram.org/bot{token}/sendMessage"
payload = json.dumps({
"chat_id": chat_id,
"text": text,
"parse_mode": "Markdown"
}).encode("utf-8")
try:
req = urllib.request.Request(
url,
data=payload,
headers={"Content-Type": "application/json"},
method="POST"
)
with urllib.request.urlopen(req, timeout=10) as resp:
result = json.loads(resp.read().decode("utf-8"))
if result.get("ok"):
log(f"Telegram notification sent ({len(predictions)} races)")
else:
log(f"Telegram API error: {result}")
except Exception as e:
log(f"Error sending Telegram notification: {e}")
def run_backtest_yesterday():
"""Lance le backtest sur les résultats d'hier"""
log("=== RUN BACKTEST ===")
yesterday = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")
# Check if we have results for yesterday
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
cursor.execute(
"""
SELECT COUNT(*) FROM pmu_partants
WHERE date_programme = ? AND ordre_arrivee > 0
""",
(yesterday,),
)
count = cursor.fetchone()[0]
conn.close()
if count > 0:
log(f"Running backtest for {yesterday}...")
# Run backtest script
cmd = f"cd /home/h3r7/turf_scraper && python3 backtest_analyzer.py --date {yesterday}"
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
if result.returncode == 0:
log("Backtest completed")
else:
log(f"Backtest failed: {result.stderr}")
else:
log(f"No results for {yesterday}, skipping backtest")
def update_paris_results():
"""Met à jour les paris avec les résultats PMU"""
log("=== UPDATE PARIS RESULTS ===")
today = datetime.now().strftime("%Y-%m-%d")
cmd = (
f"cd /home/h3r7/turf_scraper && python3 update_paris_results.py --date {today}"
)
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
if result.returncode == 0:
log(f"Paris updated: {result.stdout.strip()}")
else:
log(f"Paris update failed: {result.stderr}")
def main():
"""Point d'entrée principal"""
dry_run = "--dry-run" in sys.argv
log("=" * 50)
log("🤖 AGENT TURF AUTONOME - DEMARRAGE")
log("=" * 50)
try:
# 1. Sync database
if not dry_run:
sync_database()
# 2. Generate predictions
predictions = generate_predictions()
# 3. Save recommendations
if not dry_run and predictions:
save_recommendations(predictions)
# 3b. Send Telegram notification
if not dry_run:
notify_telegram(predictions)
# 4. Create paris from recommendations
if not dry_run and predictions:
update_paris_results()
# 5. Run backtest on yesterday
if not dry_run:
run_backtest_yesterday()
log("=" * 50)
log("✅ AGENT TURF - TERMINÉ")
log("=" * 50)
except Exception as e:
log(f"❌ ERREUR: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == "__main__":
main()