fix: enhanced_trailing_stop, dynamic_threshold_optimizer, signal_cache

enhanced_trailing_stop.py:
- mt -> mt5 (all occurrences)
- Add pandas + timezone imports at file top
- Fix UTC bug: datetime.fromtimestamp(..., tz=timezone.utc).replace(tzinfo=None)
- Fetch symbol_info once per call, reuse for point (was called twice)
- cleanup_closed_positions: handle None from positions_get()
- Remove pandas import from inside function body

dynamic_threshold_optimizer.py:
- Fix SQL injection: replace f-string session filter with parameterized query (?)
- Add logging module, replace all print() with logger calls
- Use context manager (with sqlite3.connect()) to prevent connection leak on exception
- save_thresholds_to_config: add try/except with logger.error

signal_cache.py:
- Fix bare except -> except Exception in _cleanup_old_entries

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-12 11:48:38 +02:00
co-authored by Claude Sonnet 4.6
parent 4e45db967b
commit f37e7adcf3
3 changed files with 51 additions and 46 deletions
+32 -30
View File
@@ -12,10 +12,13 @@ FEATURES:
import sqlite3 import sqlite3
import pandas as pd import pandas as pd
import logging
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Dict, Optional, Tuple from typing import Dict, Optional, Tuple
import json import json
logger = logging.getLogger(__name__)
class DynamicThresholdOptimizer: class DynamicThresholdOptimizer:
""" """
@@ -58,10 +61,9 @@ class DynamicThresholdOptimizer:
'overlap': 70 'overlap': 70
} }
print(f"Dynamic Threshold Optimizer initialized") logger.info(f"Dynamic Threshold Optimizer initialized"
print(f" Lookback: {lookback_trades} trades") f"lookback={lookback_trades}, target_wr={target_win_rate*100:.0f}%, "
print(f" Target Win Rate: {target_win_rate*100:.1f}%") f"range={min_threshold}%-{max_threshold}%")
print(f" Range: {min_threshold}% - {max_threshold}%")
def get_recent_performance(self, session: Optional[str] = None) -> Dict: def get_recent_performance(self, session: Optional[str] = None) -> Dict:
""" """
@@ -74,10 +76,7 @@ class DynamicThresholdOptimizer:
Dict mit Performance-Metriken Dict mit Performance-Metriken
""" """
try: try:
conn = sqlite3.connect(self.db_path) base_query = """
# Query für letzte N Trades
query = f"""
SELECT SELECT
confidence, confidence,
session, session,
@@ -86,14 +85,15 @@ class DynamicThresholdOptimizer:
FROM trades FROM trades
WHERE status = 'closed' WHERE status = 'closed'
""" """
params: list = []
if session: if session:
query += f" AND session = '{session}'" base_query += " AND session = ?"
params.append(session)
base_query += " ORDER BY exit_time DESC LIMIT ?"
params.append(self.lookback_trades)
query += f" ORDER BY exit_time DESC LIMIT {self.lookback_trades}" with sqlite3.connect(self.db_path) as conn:
df = pd.read_sql_query(base_query, conn, params=params)
df = pd.read_sql_query(query, conn)
conn.close()
if df.empty: if df.empty:
return { return {
@@ -105,10 +105,10 @@ class DynamicThresholdOptimizer:
} }
trades = len(df) trades = len(df)
wins = df['win'].sum() wins = int(df['win'].sum())
win_rate = wins / trades if trades > 0 else 0.0 win_rate = wins / trades if trades > 0 else 0.0
avg_confidence = df['confidence'].mean() avg_confidence = float(df['confidence'].mean())
total_profit = df['net_profit'].sum() total_profit = float(df['net_profit'].sum())
return { return {
'trades': trades, 'trades': trades,
@@ -121,7 +121,7 @@ class DynamicThresholdOptimizer:
} }
except Exception as e: except Exception as e:
print(f"Error getting performance: {e}") logger.error(f"Error getting performance: {e}")
return { return {
'trades': 0, 'trades': 0,
'win_rate': 0.0, 'win_rate': 0.0,
@@ -322,10 +322,12 @@ class DynamicThresholdOptimizer:
} }
} }
with open(config_file, 'w') as f: try:
json.dump(config, f, indent=2) with open(config_file, 'w') as f:
json.dump(config, f, indent=2)
print(f"Thresholds saved to: {config_file}") logger.info(f"Thresholds saved to: {config_file}")
except Exception as e:
logger.error(f"Failed to save thresholds: {e}")
# ========================================== # ==========================================
@@ -344,9 +346,9 @@ def auto_optimize_thresholds(optimizer: DynamicThresholdOptimizer,
Returns: Returns:
Optimization Results Optimization Results
""" """
print(f"\n{'='*70}") logger.info(f"\n{'='*70}")
print(f"🔄 AUTO-OPTIMIZATION STARTED - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") logger.info(f"🔄 AUTO-OPTIMIZATION STARTED - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"{'='*70}\n") logger.info(f"{'='*70}\n")
results = optimizer.optimize_all_sessions() results = optimizer.optimize_all_sessions()
@@ -357,16 +359,16 @@ def auto_optimize_thresholds(optimizer: DynamicThresholdOptimizer,
change_emoji = "🔽" if info['change'] < 0 else ("🔼" if info['change'] > 0 else "➡️") change_emoji = "🔽" if info['change'] < 0 else ("🔼" if info['change'] > 0 else "➡️")
print(f"{session.upper():8s}: {info['old_threshold']}% → {info['new_threshold']}% " logger.info(f"{session.upper():8s}: {info['old_threshold']}% → {info['new_threshold']}% "
f"{change_emoji} | WR: {info['win_rate']*100:.1f}% ({info['recent_trades']} trades)") f"{change_emoji} | WR: {info['win_rate']*100:.1f}% ({info['recent_trades']} trades)")
if apply_changes: if apply_changes:
optimizer.save_thresholds_to_config() optimizer.save_thresholds_to_config()
print("\n✅ Changes applied and saved!") logger.info("\n✅ Changes applied and saved!")
else: else:
print("\n⚠️ Dry-run mode - changes NOT applied") logger.info("\n⚠️ Dry-run mode - changes NOT applied")
print(f"\n{'='*70}\n") logger.info(f"\n{'='*70}\n")
return results return results
@@ -434,4 +436,4 @@ print("✅ Auto-optimization scheduled (daily at midnight)")
if __name__ == "__main__": if __name__ == "__main__":
# Test # Test
optimizer = DynamicThresholdOptimizer() optimizer = DynamicThresholdOptimizer()
print(optimizer.generate_report()) logger.info(optimizer.generate_report())
+18 -15
View File
@@ -11,8 +11,9 @@ IMPROVEMENTS:
5. Multi-tier Profit Locking 5. Multi-tier Profit Locking
""" """
import MetaTrader5 as mt import MetaTrader5 as mt5
from datetime import datetime, timedelta import pandas as pd
from datetime import datetime, timedelta, timezone
from typing import Tuple, Optional, Dict from typing import Tuple, Optional, Dict
import logging import logging
@@ -132,15 +133,18 @@ class EnhancedTrailingStopManager:
entry_price = position.price_open entry_price = position.price_open
current_sl = position.sl current_sl = position.sl
tp = position.tp tp = position.tp
entry_time = datetime.fromtimestamp(position.time) entry_time = datetime.fromtimestamp(position.time, tz=timezone.utc).replace(tzinfo=None)
# Current Price # Current Price — fetch tick and symbol info once each
symbol_info = mt.symbol_info_tick(position.symbol) tick = mt5.symbol_info_tick(position.symbol)
if not symbol_info: if not tick:
return False, None, "No symbol info" return False, None, "No symbol info"
current_price = symbol_info.bid if position_type == 0 else symbol_info.ask current_price = tick.bid if position_type == 0 else tick.ask
point = mt.symbol_info(position.symbol).point sym_info = mt5.symbol_info(position.symbol)
if not sym_info:
return False, None, "No symbol info"
point = sym_info.point
# Calculate progress # Calculate progress
if position_type == 0: # BUY if position_type == 0: # BUY
@@ -318,7 +322,7 @@ class EnhancedTrailingStopManager:
""" """
try: try:
request = { request = {
"action": mt.TRADE_ACTION_SLTP, "action": mt5.TRADE_ACTION_SLTP,
"position": position.ticket, "position": position.ticket,
"symbol": position.symbol, "symbol": position.symbol,
"sl": new_sl, "sl": new_sl,
@@ -327,9 +331,9 @@ class EnhancedTrailingStopManager:
"comment": "Enhanced Trailing" "comment": "Enhanced Trailing"
} }
result = mt.order_send(request) result = mt5.order_send(request)
if result.retcode == mt.TRADE_RETCODE_DONE: if result.retcode == mt5.TRADE_RETCODE_DONE:
logger.info(f"✅ Enhanced Trailing Stop updated for #{position.ticket}") logger.info(f"✅ Enhanced Trailing Stop updated for #{position.ticket}")
logger.info(f" Old SL: {position.sl:.5f}") logger.info(f" Old SL: {position.sl:.5f}")
logger.info(f" New SL: {new_sl:.5f}") logger.info(f" New SL: {new_sl:.5f}")
@@ -345,7 +349,7 @@ class EnhancedTrailingStopManager:
def cleanup_closed_positions(self): def cleanup_closed_positions(self):
"""Entfernt geschlossene Positions aus Tier-Tracking""" """Entfernt geschlossene Positions aus Tier-Tracking"""
open_tickets = {pos.ticket for pos in mt.positions_get()} open_tickets = {pos.ticket for pos in (mt5.positions_get() or [])}
closed_tickets = set(self.position_tiers.keys()) - open_tickets closed_tickets = set(self.position_tiers.keys()) - open_tickets
for ticket in closed_tickets: for ticket in closed_tickets:
@@ -387,7 +391,7 @@ def create_enhanced_position_monitor(
- Time-based Breakeven - Time-based Breakeven
""" """
try: try:
positions = mt.positions_get(symbol=symbol) positions = mt5.positions_get(symbol=symbol)
if not positions: if not positions:
return return
@@ -398,9 +402,8 @@ def create_enhanced_position_monitor(
# Get current ATR # Get current ATR
atr_value = None atr_value = None
try: try:
rates = mt.copy_rates_from_pos(symbol, mt.TIMEFRAME_M5, 0, 20) rates = mt5.copy_rates_from_pos(symbol, mt5.TIMEFRAME_M5, 0, 20)
if rates is not None: if rates is not None:
import pandas as pd
df = pd.DataFrame(rates) df = pd.DataFrame(rates)
df['tr'] = df[['high', 'low', 'close']].apply( df['tr'] = df[['high', 'low', 'close']].apply(
lambda x: max(x['high'] - x['low'], lambda x: max(x['high'] - x['low'],
+1 -1
View File
@@ -84,7 +84,7 @@ class SignalCache:
age_hours = (now - timestamp).total_seconds() / 3600 age_hours = (now - timestamp).total_seconds() / 3600
if age_hours > MAX_CACHE_AGE_HOURS: if age_hours > MAX_CACHE_AGE_HOURS:
to_remove.append(ticket) to_remove.append(ticket)
except: except Exception:
to_remove.append(ticket) to_remove.append(ticket)
for ticket in to_remove: for ticket in to_remove: