#!/usr/bin/env python3 """ 🔧 Database Cleanup Script Behebt Daten-Inkonsistenzen in trading_bot.db """ import sqlite3 import shutil from datetime import datetime import os # ========================================== # CONFIGURATION # ========================================== DB_PATH = "trading_bot.db" BACKUP_DIR = "backups" # ========================================== # BACKUP FUNCTION # ========================================== def create_backup(): """Erstellt Backup vor Cleanup""" if not os.path.exists(BACKUP_DIR): os.makedirs(BACKUP_DIR) timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') backup_path = f"{BACKUP_DIR}/trading_bot_before_cleanup_{timestamp}.db" shutil.copy(DB_PATH, backup_path) print(f"✅ Backup erstellt: {backup_path}") return backup_path # ========================================== # CLEANUP FUNCTIONS # ========================================== def fix_session_unknown(conn): """ Behebt 'unknown' Sessions basierend auf entry_time Sessions (UTC): - Asian: 23:00-08:00 - London: 08:00-16:00 - NY: 13:00-22:00 - Overlap: 13:00-16:00 """ print("\n" + "="*80) print("1️⃣ FIXING UNKNOWN SESSIONS") print("="*80) cursor = conn.cursor() # Hole alle unknown session trades cursor.execute(""" SELECT id, ticket, entry_time FROM trades WHERE session = 'unknown' OR session IS NULL """) unknown_trades = cursor.fetchall() print(f"Gefunden: {len(unknown_trades)} Trades mit unknown session") fixed = 0 for trade_id, ticket, entry_time in unknown_trades: # Parse entry_time dt = datetime.fromisoformat(entry_time.replace('Z', '+00:00')) hour = dt.hour # Bestimme Session basierend auf UTC Hour if 23 <= hour or hour < 8: session = 'asian' elif 8 <= hour < 13: session = 'london' elif 13 <= hour < 16: session = 'overlap' elif 16 <= hour < 22: session = 'ny' else: session = 'ny' # 22-23 = NY tail # Update cursor.execute(""" UPDATE trades SET session = ? WHERE id = ? """, (session, trade_id)) fixed += 1 conn.commit() print(f"✅ Fixed: {fixed} Sessions") # Verify cursor.execute("SELECT COUNT(*) FROM trades WHERE session = 'unknown'") remaining = cursor.fetchone()[0] print(f"Verbleibend: {remaining} unknown sessions") def fix_null_profits(conn): """ Analysiert und behebt NULL profits Problem: 240 'historical' Trades haben NULL profit Vermutlich: Alte Trades die nicht korrekt migriert wurden """ print("\n" + "="*80) print("2️⃣ FIXING NULL PROFITS") print("="*80) cursor = conn.cursor() # Hole alle NULL profit trades cursor.execute(""" SELECT id, ticket, entry_price, exit_price, volume, status FROM trades WHERE net_profit IS NULL """) null_trades = cursor.fetchall() print(f"Gefunden: {len(null_trades)} Trades mit NULL profit") # Analyse: Warum NULL? cursor.execute(""" SELECT COUNT(*) as total, SUM(CASE WHEN entry_price = 0 THEN 1 ELSE 0 END) as zero_entry, SUM(CASE WHEN exit_price IS NULL THEN 1 ELSE 0 END) as no_exit, SUM(CASE WHEN status = 'historical' THEN 1 ELSE 0 END) as historical FROM trades WHERE net_profit IS NULL """) stats = cursor.fetchone() print(f"\nAnalyse:") print(f" Total NULL profits: {stats[0]}") print(f" Entry Price = 0: {stats[1]}") print(f" Keine Exit Price: {stats[2]}") print(f" Status = historical: {stats[3]}") # Decision print("\n⚠️ ENTSCHEIDUNG NÖTIG:") print(" Option 1: Alle 'historical' Trades mit NULL profit LÖSCHEN") print(" Option 2: Profit = 0 setzen (als Breakeven behandeln)") print(" Option 3: Trades behalten wie sie sind (ignorieren)") # Für jetzt: Option 3 (safe) print("\n➡️ AKTION: Trades werden markiert aber NICHT gelöscht") print(" Grund: Vermutlich alte Migrations-Daten") print(" Empfehlung: Manuell reviewen und entscheiden") # Markiere sie in einem neuen Feld (falls gewünscht) # Für jetzt: Nur Info def fix_status_field(conn): """ Analysiert Status-Field und fügt win/loss Klassifikation hinzu Aktuell: - 'closed' = Trade ist abgeschlossen - 'historical' = Alte Trades Wir brauchen: win/loss Status basierend auf net_profit """ print("\n" + "="*80) print("3️⃣ STATUS FIELD ANALYSE") print("="*80) cursor = conn.cursor() # Check ob exit_reason Feld existiert cursor.execute("PRAGMA table_info(trades)") columns = [col[1] for col in cursor.fetchall()] print(f"Verfügbare Felder: {', '.join(columns)}") # Count by status cursor.execute(""" SELECT status, COUNT(*) as count, SUM(CASE WHEN net_profit > 0 THEN 1 ELSE 0 END) as wins, SUM(CASE WHEN net_profit < 0 THEN 1 ELSE 0 END) as losses, SUM(CASE WHEN net_profit IS NULL THEN 1 ELSE 0 END) as nulls FROM trades GROUP BY status """) results = cursor.fetchall() print("\nStatus Breakdown:") for row in results: status, count, wins, losses, nulls = row print(f" {status:12} | Total: {count:3} | Wins: {wins:3} | Losses: {losses:3} | NULL: {nulls:3}") # Info: exit_reason kann verwendet werden um win/loss zu tracken print("\n💡 INFO:") print(" - 'closed' Trades haben net_profit (wins/losses)") print(" - 'historical' Trades haben NULL profit (alte Daten)") print(" - exit_reason Feld kann für Klassifikation genutzt werden") def add_backup_automation(conn): """ Info über Backup-Automation """ print("\n" + "="*80) print("4️⃣ BACKUP AUTOMATION SETUP") print("="*80) print("Empfehlung: Tägliche automatische Backups") print("\nMöglichkeiten:") print(" 1. Windows Task Scheduler (täglich um 00:00)") print(" 2. Python Script mit Scheduler") print(" 3. Manuell vor wichtigen Änderungen") print("\nAktuell: Backup vor jedem Cleanup (manuell)") def verify_fixes(conn): """ Verifiziert die durchgeführten Fixes """ print("\n" + "="*80) print("5️⃣ VERIFICATION") print("="*80) cursor = conn.cursor() # Check unknown sessions cursor.execute("SELECT COUNT(*) FROM trades WHERE session = 'unknown'") unknown = cursor.fetchone()[0] print(f"Unknown Sessions: {unknown} (Ziel: 0)") # Check session distribution cursor.execute(""" SELECT session, COUNT(*) as count FROM trades GROUP BY session ORDER BY count DESC """) print("\nSession Distribution:") for session, count in cursor.fetchall(): print(f" {session:10} {count:3} Trades") # Check NULL profits cursor.execute("SELECT COUNT(*) FROM trades WHERE net_profit IS NULL") null_profits = cursor.fetchone()[0] print(f"\nNULL Profits: {null_profits}") # Performance nach Cleanup cursor.execute(""" SELECT COUNT(*) as total, SUM(CASE WHEN net_profit > 0 THEN 1 ELSE 0 END) as wins, SUM(CASE WHEN net_profit < 0 THEN 1 ELSE 0 END) as losses, ROUND(AVG(CASE WHEN net_profit > 0 THEN net_profit END), 2) as avg_win, ROUND(AVG(CASE WHEN net_profit < 0 THEN net_profit END), 2) as avg_loss FROM trades WHERE net_profit IS NOT NULL """) total, wins, losses, avg_win, avg_loss = cursor.fetchone() if total > 0: win_rate = (wins / total * 100) if total > 0 else 0 print(f"\nPerformance (nur Trades mit Profit-Daten):") print(f" Total: {total}") print(f" Wins: {wins} ({win_rate:.1f}%)") print(f" Losses: {losses}") print(f" Avg Win: ${avg_win}") print(f" Avg Loss: ${avg_loss}") # ========================================== # MAIN # ========================================== def main(): print("="*80) print("🔧 DATABASE CLEANUP SCRIPT") print("="*80) print() # Check if DB exists if not os.path.exists(DB_PATH): print(f"❌ ERROR: {DB_PATH} nicht gefunden!") return print(f"Database: {DB_PATH}") print(f"Size: {os.path.getsize(DB_PATH) / 1024:.2f} KB") print() # Create backup backup_path = create_backup() # Connect conn = sqlite3.connect(DB_PATH) try: # Run cleanup functions fix_session_unknown(conn) fix_null_profits(conn) fix_status_field(conn) add_backup_automation(conn) verify_fixes(conn) print("\n" + "="*80) print("✅ CLEANUP ABGESCHLOSSEN") print("="*80) print(f"\nBackup: {backup_path}") print("Database wurde aktualisiert!") except Exception as e: print(f"\n❌ ERROR: {e}") print("Rollback...") conn.rollback() # Restore backup print(f"Stelle Backup wieder her: {backup_path}") shutil.copy(backup_path, DB_PATH) print("✅ Backup wiederhergestellt") finally: conn.close() if __name__ == "__main__": main()