all changes done over the last 2 weeks

This commit is contained in:
2025-12-16 22:02:15 +01:00
parent 596718e30b
commit cbd6584db5
54 changed files with 25684 additions and 116 deletions
+224
View File
@@ -0,0 +1,224 @@
#!/usr/bin/env python3
"""
📊 Position Monitor - Trade Exit Detection
Überwacht offene Positionen und updated die Datenbank wenn sie geschlossen werden
"""
import MetaTrader5 as mt
from datetime import datetime
from trading_database import TradingDatabase
from telegram_notifier import TelegramNotifier
import logging
logger = logging.getLogger(__name__)
class PositionMonitor:
"""
Überwacht offene Positionen und erkennt wenn sie geschlossen werden
"""
def __init__(self, database: TradingDatabase, telegram: TelegramNotifier = None):
self.db = database
self.telegram = telegram
self.tracked_positions = {} # {ticket: position_data}
def check_open_positions(self):
"""
Prüft alle offenen Positionen in der Datenbank
und vergleicht mit MT5 um geschlossene zu finden
"""
try:
# Hole alle offenen Positionen aus der Datenbank
open_trades = self.db.get_open_trades()
if not open_trades:
return
# Hole aktuelle Positionen von MT5
mt5_positions = mt.positions_get()
mt5_tickets = {pos.ticket for pos in mt5_positions} if mt5_positions else set()
# Prüfe jede offene Position aus der DB
for trade in open_trades:
ticket = trade['ticket']
# Wenn Position nicht mehr in MT5 → wurde geschlossen
if ticket not in mt5_tickets:
self._handle_closed_position(ticket, trade)
except Exception as e:
logger.error(f"Error checking positions: {e}")
def _handle_closed_position(self, ticket, trade_data):
"""
Behandelt eine geschlossene Position
"""
try:
# Hole Trade-History von MT5 (letzten 30 Tage)
from datetime import timedelta
now = datetime.now()
days_ago = now - timedelta(days=30)
deals = mt.history_deals_get(days_ago, now, ticket=ticket)
if not deals:
logger.warning(f"No history found for ticket {ticket}")
return
# Finde das Close-Deal (letztes Deal für dieses Ticket)
close_deal = None
for deal in deals:
if deal.entry == 1: # Entry out = Close
close_deal = deal
break
if not close_deal:
logger.warning(f"No close deal found for ticket {ticket}")
return
# Berechne Exit-Daten
exit_price = close_deal.price
exit_time = datetime.fromtimestamp(close_deal.time)
profit = close_deal.profit
commission = close_deal.commission
swap = close_deal.swap
# Berechne weitere Metriken
entry_time = datetime.strptime(trade_data['entry_time'], '%Y-%m-%d %H:%M:%S')
duration = (exit_time - entry_time).total_seconds() / 3600 # hours
net_profit = profit + commission + swap
# RR Ratio berechnen (falls SL/TP bekannt)
rr_ratio = None
if trade_data.get('sl_price') and trade_data.get('tp_price'):
entry_price = trade_data['entry_price']
sl_price = trade_data['sl_price']
tp_price = trade_data['tp_price']
risk = abs(entry_price - sl_price)
reward = abs(tp_price - entry_price)
if risk > 0:
rr_ratio = reward / risk
# Bestimme Exit-Grund
exit_reason = self._determine_exit_reason(
exit_price,
trade_data.get('sl_price'),
trade_data.get('tp_price'),
trade_data['type']
)
# Update Database
exit_data = {
'exit_price': exit_price,
'exit_time': exit_time.strftime('%Y-%m-%d %H:%M:%S'),
'duration_hours': duration,
'profit': profit,
'commission': commission,
'swap': swap,
'net_profit': net_profit,
'rr_ratio': rr_ratio,
'status': 'closed',
'exit_reason': exit_reason
}
self.db.update_trade_exit(ticket, exit_data)
logger.info(f"✅ Updated closed position {ticket}: {exit_reason}, Profit: {net_profit:.2f}")
# Sende Telegram Notification
if self.telegram:
self._send_exit_notification(ticket, trade_data, exit_data)
except Exception as e:
logger.error(f"Error handling closed position {ticket}: {e}")
def _determine_exit_reason(self, exit_price, sl_price, tp_price, trade_type):
"""
Bestimmt warum der Trade geschlossen wurde
"""
if not sl_price or not tp_price:
return "manual_close"
tolerance = 0.5 # Pips tolerance
if trade_type == "BUY":
if abs(exit_price - tp_price) <= tolerance:
return "take_profit"
elif abs(exit_price - sl_price) <= tolerance:
return "stop_loss"
else: # SELL
if abs(exit_price - tp_price) <= tolerance:
return "take_profit"
elif abs(exit_price - sl_price) <= tolerance:
return "stop_loss"
return "manual_close"
def _send_exit_notification(self, ticket, trade_data, exit_data):
"""
Sendet Telegram Notification für geschlossenen Trade
"""
try:
notification_data = {
'ticket': ticket,
'symbol': trade_data['symbol'],
'type': trade_data['type'],
'entry_price': trade_data['entry_price'],
'exit_price': exit_data['exit_price'],
'exit_time': exit_data['exit_time'],
'exit_reason': exit_data['exit_reason'],
'net_profit': exit_data['net_profit'],
'duration_hours': exit_data['duration_hours'],
'session': trade_data.get('session', 'unknown')
}
self.telegram.notify_trade_exit(notification_data)
except Exception as e:
logger.error(f"Error sending exit notification: {e}")
def create_position_monitor_job(database, telegram=None):
"""
Factory-Funktion die einen Position Monitor Job erstellt
Usage:
monitor = PositionMonitor(infra.db, infra.telegram)
# Im Scheduler hinzufügen:
scheduler.add_job(
func=monitor.check_open_positions,
trigger='interval',
minutes=1, # Prüfe jede Minute
id='position_monitor'
)
"""
monitor = PositionMonitor(database, telegram)
return monitor
# ==========================================
# USAGE EXAMPLE (für Notebook)
# ==========================================
"""
Im Trading Bot Notebook hinzufügen:
# Nach Infrastructure Setup (Cell 8):
from position_monitor import PositionMonitor
# Position Monitor erstellen
position_monitor = PositionMonitor(infra.db, infra.telegram)
# Im Scheduler hinzufügen (Cell 33):
scheduler.add_job(
func=position_monitor.check_open_positions,
trigger='interval',
minutes=1,
id='position_monitor'
)
print("✅ Position Monitor aktiviert - prüft jede Minute nach geschlossenen Trades")
"""