Created comprehensive database maintenance tools: 1. database_cleanup.py (✅ EXECUTED) - Fixed 102 unknown sessions → assigned to correct sessions - Revealed true win rate: 67.8% (not 18.5%!) - Created automatic backup before changes 2. cleanup_historical_trades.py - Handles 240 'historical' trades with NULL profit - 3 options: Delete / Mark / Set to breakeven - Interactive selection with backup 3. setup_automated_backup.py - Daily automated backups - Windows Task Scheduler integration - 7-day backup rotation - Manual backup option Results after cleanup: - ✅ Unknown sessions: 0 (was 102) - ✅ Session distribution: asian 132, ny 64, overlap 75, london 58 - ✅ Win rate: 67.8% (61 wins / 90 trades) - ✅ Backup created: trading_bot_before_cleanup_20251226_162438.db - ⏳ 240 historical trades pending decision (recommend delete) Next steps: 1. Run cleanup_historical_trades.py (option 1: delete) 2. Setup automated backups via Task Scheduler 3. Re-analyze performance with correct session data
249 lines
6.9 KiB
Python
249 lines
6.9 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
🗑️ Historical Trades Cleanup
|
|
Behandelt die 240 'historical' Trades mit NULL profit
|
|
"""
|
|
|
|
import sqlite3
|
|
import shutil
|
|
from datetime import datetime
|
|
|
|
DB_PATH = "trading_bot.db"
|
|
BACKUP_DIR = "backups"
|
|
|
|
def create_backup():
|
|
"""Backup erstellen"""
|
|
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
|
backup_path = f"{BACKUP_DIR}/trading_bot_before_historical_cleanup_{timestamp}.db"
|
|
shutil.copy(DB_PATH, backup_path)
|
|
print(f"✅ Backup: {backup_path}")
|
|
return backup_path
|
|
|
|
def analyze_historical_trades(conn):
|
|
"""Analysiere historical trades"""
|
|
print("\n" + "="*80)
|
|
print("📊 ANALYSE: Historical Trades")
|
|
print("="*80)
|
|
|
|
cursor = conn.cursor()
|
|
|
|
# Basic info
|
|
cursor.execute("""
|
|
SELECT
|
|
COUNT(*) as total,
|
|
MIN(entry_time) as first_trade,
|
|
MAX(entry_time) as last_trade,
|
|
COUNT(DISTINCT DATE(entry_time)) as trading_days
|
|
FROM trades
|
|
WHERE status = 'historical' AND net_profit IS NULL
|
|
""")
|
|
|
|
total, first, last, days = cursor.fetchone()
|
|
print(f"\nTotal: {total} Trades")
|
|
print(f"Zeitraum: {first} bis {last}")
|
|
print(f"Trading Days: {days}")
|
|
|
|
# Session breakdown
|
|
cursor.execute("""
|
|
SELECT session, COUNT(*) as count
|
|
FROM trades
|
|
WHERE status = 'historical' AND net_profit IS NULL
|
|
GROUP BY session
|
|
ORDER BY count DESC
|
|
""")
|
|
|
|
print("\nSession Breakdown:")
|
|
for session, count in cursor.fetchall():
|
|
print(f" {session:10} {count:3} Trades")
|
|
|
|
# Quality breakdown
|
|
cursor.execute("""
|
|
SELECT
|
|
quality,
|
|
COUNT(*) as count,
|
|
ROUND(AVG(confidence), 1) as avg_conf
|
|
FROM trades
|
|
WHERE status = 'historical' AND net_profit IS NULL
|
|
GROUP BY quality
|
|
""")
|
|
|
|
print("\nQuality Breakdown:")
|
|
for quality, count, conf in cursor.fetchall():
|
|
print(f" {quality if quality else 'NULL':12} {count:3} Trades (avg conf: {conf}%)")
|
|
|
|
return total
|
|
|
|
def option_delete_historical(conn):
|
|
"""Option 1: Lösche alle historical trades"""
|
|
print("\n" + "="*80)
|
|
print("🗑️ OPTION 1: Alle historical Trades LÖSCHEN")
|
|
print("="*80)
|
|
|
|
cursor = conn.cursor()
|
|
|
|
cursor.execute("DELETE FROM trades WHERE status = 'historical' AND net_profit IS NULL")
|
|
deleted = cursor.rowcount
|
|
|
|
print(f"✅ Gelöscht: {deleted} Trades")
|
|
|
|
conn.commit()
|
|
|
|
def option_mark_as_invalid(conn):
|
|
"""Option 2: Markiere als invalid statt löschen"""
|
|
print("\n" + "="*80)
|
|
print("🏷️ OPTION 2: Als 'invalid' markieren")
|
|
print("="*80)
|
|
|
|
cursor = conn.cursor()
|
|
|
|
cursor.execute("""
|
|
UPDATE trades
|
|
SET status = 'invalid_historical'
|
|
WHERE status = 'historical' AND net_profit IS NULL
|
|
""")
|
|
updated = cursor.rowcount
|
|
|
|
print(f"✅ Markiert: {updated} Trades als 'invalid_historical'")
|
|
|
|
conn.commit()
|
|
|
|
def option_set_zero_profit(conn):
|
|
"""Option 3: Setze net_profit = 0 (als breakeven)"""
|
|
print("\n" + "="*80)
|
|
print("💰 OPTION 3: net_profit = 0 setzen (Breakeven)")
|
|
print("="*80)
|
|
|
|
cursor = conn.cursor()
|
|
|
|
cursor.execute("""
|
|
UPDATE trades
|
|
SET net_profit = 0.0,
|
|
profit = 0.0,
|
|
profit_pct = 0.0,
|
|
exit_reason = 'historical_migration_breakeven'
|
|
WHERE status = 'historical' AND net_profit IS NULL
|
|
""")
|
|
updated = cursor.rowcount
|
|
|
|
print(f"✅ Updated: {updated} Trades auf Breakeven gesetzt")
|
|
|
|
conn.commit()
|
|
|
|
def verify_cleanup(conn):
|
|
"""Verifiziere Cleanup"""
|
|
print("\n" + "="*80)
|
|
print("✅ VERIFICATION")
|
|
print("="*80)
|
|
|
|
cursor = conn.cursor()
|
|
|
|
# Count historical
|
|
cursor.execute("SELECT COUNT(*) FROM trades WHERE status = 'historical'")
|
|
historical = cursor.fetchone()[0]
|
|
|
|
# Count NULL profits
|
|
cursor.execute("SELECT COUNT(*) FROM trades WHERE net_profit IS NULL")
|
|
null_profits = cursor.fetchone()[0]
|
|
|
|
# Count invalid
|
|
cursor.execute("SELECT COUNT(*) FROM trades WHERE status = 'invalid_historical'")
|
|
invalid = cursor.fetchone()[0]
|
|
|
|
print(f"Historical Trades: {historical}")
|
|
print(f"NULL Profits: {null_profits}")
|
|
print(f"Invalid Historical: {invalid}")
|
|
|
|
# Performance
|
|
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(SUM(net_profit), 2) as total_profit
|
|
FROM trades
|
|
WHERE net_profit IS NOT NULL
|
|
""")
|
|
|
|
total, wins, losses, profit = cursor.fetchone()
|
|
win_rate = (wins / total * 100) if total > 0 else 0
|
|
|
|
print(f"\nGesamt Performance (nur valide Trades):")
|
|
print(f" Total: {total}")
|
|
print(f" Wins: {wins} ({win_rate:.1f}%)")
|
|
print(f" Losses: {losses}")
|
|
print(f" Total Profit: ${profit}")
|
|
|
|
def main():
|
|
print("="*80)
|
|
print("🗑️ HISTORICAL TRADES CLEANUP")
|
|
print("="*80)
|
|
|
|
# Backup
|
|
backup_path = create_backup()
|
|
|
|
# Connect
|
|
conn = sqlite3.connect(DB_PATH)
|
|
|
|
try:
|
|
# Analyze
|
|
total = analyze_historical_trades(conn)
|
|
|
|
# Ask user
|
|
print("\n" + "="*80)
|
|
print("❓ AUSWAHL")
|
|
print("="*80)
|
|
print("\nWas soll mit den 240 historical Trades passieren?")
|
|
print()
|
|
print("1️⃣ LÖSCHEN - Alle historical trades permanent entfernen")
|
|
print(" Pro: Saubere Datenbank")
|
|
print(" Con: Daten unwiederbringlich weg")
|
|
print()
|
|
print("2️⃣ MARKIEREN - Als 'invalid_historical' markieren (behalten aber ausblenden)")
|
|
print(" Pro: Daten bleiben erhalten")
|
|
print(" Con: Nimmt Speicherplatz")
|
|
print()
|
|
print("3️⃣ BREAKEVEN - net_profit = 0 setzen (als Breakeven-Trades behandeln)")
|
|
print(" Pro: Fließen in Statistik ein")
|
|
print(" Con: Verfälscht Performance-Daten")
|
|
print()
|
|
print("4️⃣ ABBRECHEN - Nichts tun, Trades behalten wie sie sind")
|
|
print()
|
|
|
|
choice = input("Ihre Wahl (1-4): ").strip()
|
|
|
|
if choice == "1":
|
|
option_delete_historical(conn)
|
|
elif choice == "2":
|
|
option_mark_as_invalid(conn)
|
|
elif choice == "3":
|
|
option_set_zero_profit(conn)
|
|
elif choice == "4":
|
|
print("\n⏸️ Abgebrochen - Keine Änderungen")
|
|
return
|
|
else:
|
|
print(f"\n❌ Ungültige Wahl: {choice}")
|
|
return
|
|
|
|
# Verify
|
|
verify_cleanup(conn)
|
|
|
|
print("\n" + "="*80)
|
|
print("✅ CLEANUP ABGESCHLOSSEN")
|
|
print("="*80)
|
|
print(f"\nBackup: {backup_path}")
|
|
|
|
except Exception as e:
|
|
print(f"\n❌ ERROR: {e}")
|
|
conn.rollback()
|
|
|
|
# Restore backup
|
|
print(f"Restore Backup: {backup_path}")
|
|
shutil.copy(backup_path, DB_PATH)
|
|
print("✅ Backup wiederhergestellt")
|
|
|
|
finally:
|
|
conn.close()
|
|
|
|
if __name__ == "__main__":
|
|
main()
|