Files
Place-Order-Trading-Bot/database_cleanup.py
T

322 lines
9.2 KiB
Python
Raw Normal View History

2025-12-26 16:28:07 +01:00
#!/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()