Riy777 commited on
Commit
10eef0b
·
1 Parent(s): bf478e8

Update trade_manager.py

Browse files
Files changed (1) hide show
  1. trade_manager.py +74 -36
trade_manager.py CHANGED
@@ -1,4 +1,4 @@
1
- # trade_manager.py (Updated to V7.5 - Fixed TP Wipe Bug + Added ATR Trailing Logic)
2
  import asyncio
3
  import json
4
  import time
@@ -17,7 +17,11 @@ except ImportError:
17
 
18
  try:
19
  import ccxt.async_support as ccxtasync
 
 
 
20
  CCXT_ASYNC_AVAILABLE = True
 
21
  except ImportError:
22
  print("❌❌❌ خطأ فادح: فشل استيراد 'ccxt.async_support'. ❌❌❌")
23
  CCXT_ASYNC_AVAILABLE = False
@@ -52,12 +56,10 @@ class TacticalData:
52
  self.indicators_1m = {}
53
  self.last_1m_candle_timestamp = None
54
 
55
- # 🔴 --- START OF CHANGE (V7.5) --- 🔴
56
  # (إضافة مؤشرات 1h لـ ATR Trailing)
57
  self.ohlcv_1h = deque(maxlen=100)
58
  self.indicators_1h = {}
59
  self.last_1h_candle_timestamp = None
60
- # 🔴 --- END OF CHANGE --- 🔴
61
 
62
  self.ohlcv_5m = deque(maxlen=100)
63
  self.last_5m_candle_timestamp = None
@@ -147,8 +149,6 @@ class TacticalData:
147
  if new_candles_added and len(self.ohlcv_1m) >= 26:
148
  self._analyze_1m_indicators()
149
 
150
- # 🔴 --- START OF CHANGE (V7.5) --- 🔴
151
- # (دالة جديدة لحساب مؤشرات 1h لـ ATR)
152
  def add_1h_ohlcv(self, ohlcv_data: List):
153
  """(جديد V7.5) إضافة شموع 1-ساعة (لـ ATR Trailing Stop)"""
154
  if not ohlcv_data:
@@ -167,7 +167,6 @@ class TacticalData:
167
 
168
  if new_candles_added and len(self.ohlcv_1h) >= 20: # (ATR يحتاج 14)
169
  self._analyze_1h_indicators()
170
- # 🔴 --- END OF CHANGE --- 🔴
171
 
172
  def _analyze_1m_indicators(self):
173
  """حساب مؤشرات 1-دقيقة الحقيقية (للدخول)"""
@@ -209,7 +208,6 @@ class TacticalData:
209
  except Exception as e:
210
  self.indicators_1m = {}
211
 
212
- # 🔴 --- START OF CHANGE (V7.5) --- 🔴
213
  def _analyze_1h_indicators(self):
214
  """(جديد V7.5) حساب مؤشرات 1-ساعة (فقط ATR حالياً)"""
215
  if ta is None or len(self.ohlcv_1h) < 15: # (ATR يحتاج 14)
@@ -231,7 +229,6 @@ class TacticalData:
231
 
232
  except Exception as e:
233
  self.indicators_1h = {}
234
- # 🔴 --- END OF CHANGE --- 🔴
235
 
236
  def add_5m_ohlcv(self, ohlcv_data: List):
237
  """(جديد V7.0) إضافة شموع 5-دقائق (لحماية الأرباح)"""
@@ -292,6 +289,14 @@ class TradeManager:
292
  self.polling_interval = 1.5
293
  self.confirmation_polling_interval = 3.0
294
 
 
 
 
 
 
 
 
 
295
  self.sentry_technical_analyzer = AdvancedTechnicalAnalyzer()
296
 
297
  if self.data_manager and self.data_manager.pattern_analyzer:
@@ -311,7 +316,11 @@ class TradeManager:
311
  print("🔄 [Sentry] تهيئة منصات التداول (KuCoin REST ومنصات التأكيد)...")
312
 
313
  print(" [Sentry] تهيئة KuCoin للبيانات العامة (وضع المحاكاة).")
314
- self.kucoin_rest = ccxtasync.kucoin()
 
 
 
 
315
  await self.kucoin_rest.load_markets()
316
  print("✅ [Sentry] منصة REST الأساسية (KuCoin) جاهزة (بيانات عامة فقط).")
317
 
@@ -321,7 +330,8 @@ class TradeManager:
321
  print(f" [Sentry] تهيئة منصات التأكيد (Confirmation Exchanges): {', '.join(confirmation_exchange_ids)}")
322
  for ex_id in confirmation_exchange_ids:
323
  try:
324
- exchange = getattr(ccxtasync, ex_id)()
 
325
  await exchange.load_markets()
326
  self.confirmation_exchanges[ex_id] = exchange
327
  print(f" ✅ [Sentry] منصة التأكيد {ex_id} جاهزة (REST).")
@@ -466,8 +476,15 @@ class TradeManager:
466
  if symbol not in self.tactical_data_cache:
467
  self.tactical_data_cache[symbol] = TacticalData(symbol)
468
 
 
 
 
 
 
 
469
  tasks_to_gather = [
470
- self._poll_kucoin_data(symbol),
 
471
  self._poll_confirmation_data(symbol),
472
  self._run_tactical_analysis_loop(symbol, strategy_hint)
473
  ]
@@ -486,8 +503,13 @@ class TradeManager:
486
  if symbol in self.tactical_data_cache:
487
  del self.tactical_data_cache[symbol]
488
 
489
- async def _poll_kucoin_data(self, symbol):
490
- """(محدث V7.5) حلقة Polling لبيانات KuCoin (تتضمن 1m, 5m, 1h)"""
 
 
 
 
 
491
  while self.is_running:
492
  try:
493
  if not self.kucoin_rest:
@@ -500,7 +522,6 @@ class TradeManager:
500
  'trades': asyncio.create_task(self.kucoin_rest.fetch_trades(symbol, since=int((time.time() - 60) * 1000), limit=50)),
501
  'ohlcv_1m': asyncio.create_task(self.kucoin_rest.fetch_ohlcv(symbol, '1m', limit=50)),
502
  'ohlcv_5m': asyncio.create_task(self.kucoin_rest.fetch_ohlcv(symbol, '5m', limit=50)),
503
- # (إضافة 1h لـ ATR Trailing Stop)
504
  'ohlcv_1h': asyncio.create_task(self.kucoin_rest.fetch_ohlcv(symbol, '1h', limit=50))
505
  }
506
 
@@ -509,14 +530,20 @@ class TradeManager:
509
  if symbol not in self.tactical_data_cache:
510
  continue
511
 
 
512
  if not tasks['ob'].exception():
513
  self.tactical_data_cache[symbol].set_order_book(tasks['ob'].result())
 
 
 
514
 
515
  if not tasks['trades'].exception():
516
  trades = tasks['trades'].result()
517
  trades.sort(key=lambda x: x['timestamp'])
518
  for trade in trades:
519
  self.tactical_data_cache[symbol].add_trade(trade)
 
 
520
 
521
  if not tasks['ohlcv_1m'].exception():
522
  self.tactical_data_cache[symbol].add_1m_ohlcv(tasks['ohlcv_1m'].result())
@@ -524,21 +551,34 @@ class TradeManager:
524
  if not tasks['ohlcv_5m'].exception():
525
  self.tactical_data_cache[symbol].add_5m_ohlcv(tasks['ohlcv_5m'].result())
526
 
527
- # 🔴 --- START OF CHANGE (V7.5) --- 🔴
528
  if not tasks['ohlcv_1h'].exception():
529
  self.tactical_data_cache[symbol].add_1h_ohlcv(tasks['ohlcv_1h'].result())
530
- # 🔴 --- END OF CHANGE --- 🔴
531
 
532
- await asyncio.sleep(self.polling_interval)
 
 
 
 
 
 
 
 
 
 
 
 
 
533
 
534
- except ccxtasync.RateLimitExceeded as e:
535
- print(f"⏳ [Sentry Polling] {symbol} KuCoin Rate Limit Exceeded: {e}. زيادة فترة الانتظار...")
536
- await asyncio.sleep(10)
537
  except asyncio.CancelledError:
538
  raise
 
539
  except Exception as e:
 
540
  print(f"⚠️ [Sentry Polling] خ��أ في {symbol} KuCoin data polling: {e}")
541
- await asyncio.sleep(5)
 
 
 
542
 
543
  async def _poll_confirmation_data(self, symbol):
544
  if not self.confirmation_exchanges:
@@ -574,15 +614,22 @@ class TradeManager:
574
  for trade in trades:
575
  self.tactical_data_cache[symbol].add_confirmation_trade(ex_id, trade)
576
 
577
- except ccxtasync.RateLimitExceeded:
578
- print(f"⏳ [Sentry Conf] {ex_id} Rate Limit لـ {symbol}. الانتظار...")
579
- await asyncio.sleep(15)
 
 
 
 
 
 
 
580
  except asyncio.CancelledError:
581
  raise
582
  except Exception as e:
 
583
  pass
584
 
585
- # 🔴 --- START OF CHANGE (V7.5) --- 🔴
586
  async def _run_tactical_analysis_loop(self, symbol: str, strategy_hint: str):
587
  """(محدث V7.5) (دماغ الحارس) يشغل التحليل التكتيكي + ATR Trailing"""
588
  while self.is_running:
@@ -677,7 +724,6 @@ class TradeManager:
677
  except asyncio.CancelledError:
678
  raise
679
  except Exception as e: print(f"❌ [Sentry] خطأ في حلقة التحليل التكتيكي لـ {symbol}: {e}"); traceback.print_exc()
680
- # 🔴 --- END OF CHANGE --- 🔴
681
 
682
 
683
  def _check_entry_trigger(self, symbol: str, strategy_hint: str, data: Dict, all_weights: Dict) -> bool:
@@ -753,18 +799,15 @@ class TradeManager:
753
  default=None
754
  )
755
 
756
- # 🔴 --- START OF CHANGE (V7.5) --- 🔴
757
  # (التحقق فقط إذا كانت القيمة أكبر من صفر)
758
  if hard_stop and hard_stop > 0 and current_price_for_sl and current_price_for_sl <= hard_stop:
759
  return f"Strategic Stop Loss hit: {current_price_for_sl} <= {hard_stop}"
760
 
761
  if take_profit and take_profit > 0 and current_price_for_tp and current_price_for_tp >= take_profit:
762
  return f"Strategic Take Profit hit: {current_price_for_tp} >= {take_profit}"
763
- # 🔴 --- END OF CHANGE --- 🔴
764
 
765
  return None
766
 
767
- # 🔴 --- START OF CHANGE (V7.5) --- 🔴
768
  async def _check_atr_trailing_stop(self, trade: Dict, snapshot: Dict, current_price: float) -> str:
769
  """(جديد V7.5) يحسب ويحدث وقف الخسارة المتحرك ATR"""
770
 
@@ -787,7 +830,7 @@ class TradeManager:
787
  # (السعر الحالي - (قيمة ATR * المضاعف))
788
  new_trailing_stop = current_price - (atr_1h * atr_multiplier)
789
 
790
- # 4. جلب وقف الخسارة الحالي (الديناميكي)
791
  current_dynamic_sl = trade.get('dynamic_stop_loss', 0)
792
 
793
  # 5. منطق التحديث (لا تخفض الوقف أبداً)
@@ -809,7 +852,6 @@ class TradeManager:
809
  except Exception as e:
810
  print(f"❌ [Sentry ATR] {trade['symbol']}: Error calculating ATR stop: {e}")
811
  return None
812
- # 🔴 --- END OF CHANGE --- 🔴
813
 
814
 
815
  def _create_dataframe_5m(self, candles: List) -> pd.DataFrame:
@@ -1132,7 +1174,6 @@ class TradeManager:
1132
  finally:
1133
  if self.r2_service.lock_acquired: self.r2_service.release_lock()
1134
 
1135
- # 🔴 --- START OF CHANGE (V7.5) --- 🔴
1136
  async def update_trade_strategy(self, trade_to_update, re_analysis_decision):
1137
  """
1138
  (محدث V7.5)
@@ -1177,9 +1218,7 @@ class TradeManager:
1177
  except Exception as e:
1178
  print(f"❌ (Explorer) فشل تحديث استراتيجية {symbol}: {e}");
1179
  raise
1180
- # 🔴 --- END OF CHANGE --- 🔴
1181
 
1182
- # 🔴 --- START OF CHANGE (V7.5) --- 🔴
1183
  async def _update_trade_dynamic_sl_in_r2(self, trade_id: str, new_dynamic_sl: float):
1184
  """(جديد V7.5) دالة مساعدة لتحديث الوقف الديناميكي فقط في R2"""
1185
  try:
@@ -1198,7 +1237,6 @@ class TradeManager:
1198
 
1199
  except Exception as e:
1200
  print(f"❌ [Sentry ATR] فشل حفظ الوقف المتحرك الجديد في R2: {e}")
1201
- # 🔴 --- END OF CHANGE --- 🔴
1202
 
1203
  async def _archive_closed_trade(self, closed_trade):
1204
  try:
@@ -1237,4 +1275,4 @@ class TradeManager:
1237
  except Exception as e: print(f"❌ Failed to get trade by symbol {symbol}: {e}"); return None
1238
 
1239
 
1240
- print(f"✅ Trade Manager loaded - V7.5 (Fixed TP Wipe Bug + ATR Trailing Logic) (ccxt.async_support: {CCXT_ASYNC_AVAILABLE})")
 
1
+ # trade_manager.py (Updated to V7.6 - Exponential Backoff for Timeouts)
2
  import asyncio
3
  import json
4
  import time
 
17
 
18
  try:
19
  import ccxt.async_support as ccxtasync
20
+ # 🔴 --- START OF CHANGE (V7.6) --- 🔴
21
+ # (نحتاج لاستيراد الأخطاء للتعامل معها)
22
+ from ccxt.base.errors import RequestTimeout, RateLimitExceeded
23
  CCXT_ASYNC_AVAILABLE = True
24
+ # 🔴 --- END OF CHANGE --- 🔴
25
  except ImportError:
26
  print("❌❌❌ خطأ فادح: فشل استيراد 'ccxt.async_support'. ❌❌❌")
27
  CCXT_ASYNC_AVAILABLE = False
 
56
  self.indicators_1m = {}
57
  self.last_1m_candle_timestamp = None
58
 
 
59
  # (إضافة مؤشرات 1h لـ ATR Trailing)
60
  self.ohlcv_1h = deque(maxlen=100)
61
  self.indicators_1h = {}
62
  self.last_1h_candle_timestamp = None
 
63
 
64
  self.ohlcv_5m = deque(maxlen=100)
65
  self.last_5m_candle_timestamp = None
 
149
  if new_candles_added and len(self.ohlcv_1m) >= 26:
150
  self._analyze_1m_indicators()
151
 
 
 
152
  def add_1h_ohlcv(self, ohlcv_data: List):
153
  """(جديد V7.5) إضافة شموع 1-ساعة (لـ ATR Trailing Stop)"""
154
  if not ohlcv_data:
 
167
 
168
  if new_candles_added and len(self.ohlcv_1h) >= 20: # (ATR يحتاج 14)
169
  self._analyze_1h_indicators()
 
170
 
171
  def _analyze_1m_indicators(self):
172
  """حساب مؤشرات 1-دقيقة الحقيقية (للدخول)"""
 
208
  except Exception as e:
209
  self.indicators_1m = {}
210
 
 
211
  def _analyze_1h_indicators(self):
212
  """(جديد V7.5) حساب مؤشرات 1-ساعة (فقط ATR حالياً)"""
213
  if ta is None or len(self.ohlcv_1h) < 15: # (ATR يحتاج 14)
 
229
 
230
  except Exception as e:
231
  self.indicators_1h = {}
 
232
 
233
  def add_5m_ohlcv(self, ohlcv_data: List):
234
  """(جديد V7.0) إضافة شموع 5-دقائق (لحماية الأرباح)"""
 
289
  self.polling_interval = 1.5
290
  self.confirmation_polling_interval = 3.0
291
 
292
+ # 🔴 --- START OF CHANGE (V7.6) --- 🔴
293
+ # (زيادة المهلة الافتراضية للاتصال العام)
294
+ self.kucoin_rest_config = {
295
+ 'enableRateLimit': True,
296
+ 'timeout': 60000, # (زيادة المهلة إلى 60 ثانية)
297
+ }
298
+ # 🔴 --- END OF CHANGE --- 🔴
299
+
300
  self.sentry_technical_analyzer = AdvancedTechnicalAnalyzer()
301
 
302
  if self.data_manager and self.data_manager.pattern_analyzer:
 
316
  print("🔄 [Sentry] تهيئة منصات التداول (KuCoin REST ومنصات التأكيد)...")
317
 
318
  print(" [Sentry] تهيئة KuCoin للبيانات العامة (وضع المحاكاة).")
319
+ # 🔴 --- START OF CHANGE (V7.6) --- 🔴
320
+ # (استخدام الإعدادات الجديدة مع المهلة الممددة)
321
+ self.kucoin_rest = ccxtasync.kucoin(self.kucoin_rest_config)
322
+ # 🔴 --- END OF CHANGE --- 🔴
323
+
324
  await self.kucoin_rest.load_markets()
325
  print("✅ [Sentry] منصة REST الأساسية (KuCoin) جاهزة (بيانات عامة فقط).")
326
 
 
330
  print(f" [Sentry] تهيئة منصات التأكيد (Confirmation Exchanges): {', '.join(confirmation_exchange_ids)}")
331
  for ex_id in confirmation_exchange_ids:
332
  try:
333
+ # (تطبيق نفس المهلة الممددة على منصات التأكيد أيضاً)
334
+ exchange = getattr(ccxtasync, ex_id)(self.kucoin_rest_config)
335
  await exchange.load_markets()
336
  self.confirmation_exchanges[ex_id] = exchange
337
  print(f" ✅ [Sentry] منصة التأكيد {ex_id} جاهزة (REST).")
 
476
  if symbol not in self.tactical_data_cache:
477
  self.tactical_data_cache[symbol] = TacticalData(symbol)
478
 
479
+ # 🔴 --- START OF CHANGE (V7.6) --- 🔴
480
+ # (إضافة متغيرات الترا��ع الأسي الخاصة بهذه المهمة)
481
+ kucoin_backoff_delay = 5.0 # (البدء بـ 5 ثوانٍ)
482
+ MAX_BACKOFF = 60.0 # (الحد الأقصى 60 ثانية)
483
+ # 🔴 --- END OF CHANGE --- 🔴
484
+
485
  tasks_to_gather = [
486
+ # (تمرير متغيرات التراجع)
487
+ self._poll_kucoin_data(symbol, kucoin_backoff_delay, MAX_BACKOFF),
488
  self._poll_confirmation_data(symbol),
489
  self._run_tactical_analysis_loop(symbol, strategy_hint)
490
  ]
 
503
  if symbol in self.tactical_data_cache:
504
  del self.tactical_data_cache[symbol]
505
 
506
+ # 🔴 --- START OF CHANGE (V7.6) --- 🔴
507
+ # (تعديل الدالة لتقبل متغيرات التراجع)
508
+ async def _poll_kucoin_data(self, symbol, kucoin_backoff_delay, MAX_BACKOFF):
509
+ """(محدث V7.6) حلقة Polling لبيانات KuCoin (مع التراجع الأسي)"""
510
+
511
+ base_polling_interval = self.polling_interval # (الفترة العادية)
512
+
513
  while self.is_running:
514
  try:
515
  if not self.kucoin_rest:
 
522
  'trades': asyncio.create_task(self.kucoin_rest.fetch_trades(symbol, since=int((time.time() - 60) * 1000), limit=50)),
523
  'ohlcv_1m': asyncio.create_task(self.kucoin_rest.fetch_ohlcv(symbol, '1m', limit=50)),
524
  'ohlcv_5m': asyncio.create_task(self.kucoin_rest.fetch_ohlcv(symbol, '5m', limit=50)),
 
525
  'ohlcv_1h': asyncio.create_task(self.kucoin_rest.fetch_ohlcv(symbol, '1h', limit=50))
526
  }
527
 
 
530
  if symbol not in self.tactical_data_cache:
531
  continue
532
 
533
+ # (التحقق من الأخطاء بشكل فردي لتجنب انهيار الحلقة)
534
  if not tasks['ob'].exception():
535
  self.tactical_data_cache[symbol].set_order_book(tasks['ob'].result())
536
+ else:
537
+ # (رمي الخطأ ليتم التقاطه أدناه)
538
+ raise tasks['ob'].exception()
539
 
540
  if not tasks['trades'].exception():
541
  trades = tasks['trades'].result()
542
  trades.sort(key=lambda x: x['timestamp'])
543
  for trade in trades:
544
  self.tactical_data_cache[symbol].add_trade(trade)
545
+ else:
546
+ raise tasks['trades'].exception()
547
 
548
  if not tasks['ohlcv_1m'].exception():
549
  self.tactical_data_cache[symbol].add_1m_ohlcv(tasks['ohlcv_1m'].result())
 
551
  if not tasks['ohlcv_5m'].exception():
552
  self.tactical_data_cache[symbol].add_5m_ohlcv(tasks['ohlcv_5m'].result())
553
 
 
554
  if not tasks['ohlcv_1h'].exception():
555
  self.tactical_data_cache[symbol].add_1h_ohlcv(tasks['ohlcv_1h'].result())
 
556
 
557
+ # (نجاح! إعادة تعيين التراجع والانتظار العادي)
558
+ kucoin_backoff_delay = base_polling_interval # (إعادة تعيين)
559
+ await asyncio.sleep(base_polling_interval)
560
+
561
+ except RateLimitExceeded as e:
562
+ print(f"⏳ [Sentry Polling] {symbol} KuCoin Rate Limit Exceeded: {e}. زيادة فترة الانتظار (30 ثانية)...")
563
+ await asyncio.sleep(30)
564
+ # (لا نزيد التراجع الأسي هنا، ننتظر فقط)
565
+
566
+ except RequestTimeout as e:
567
+ print(f"⏳ [Sentry Polling] {symbol} KuCoin Request Timeout: {e}. التراجع الأسي (الانتظار {kucoin_backoff_delay:.1f} ثانية)...")
568
+ await asyncio.sleep(kucoin_backoff_delay)
569
+ # (مضاعفة فترة الانتظار للمرة القادمة، بحد أقصى)
570
+ kucoin_backoff_delay = min(kucoin_backoff_delay * 2, MAX_BACKOFF)
571
 
 
 
 
572
  except asyncio.CancelledError:
573
  raise
574
+
575
  except Exception as e:
576
+ # (التقاط أخطاء أخرى (مثل 404 Not Found أو أخطاء التحليل))
577
  print(f"⚠️ [Sentry Polling] خ��أ في {symbol} KuCoin data polling: {e}")
578
+ # (إعادة تعيين التراجع عند الأخطاء العامة)
579
+ kucoin_backoff_delay = base_polling_interval
580
+ await asyncio.sleep(10) # (انتظار أطول من 5 ثوانٍ)
581
+ # 🔴 --- END OF CHANGE --- 🔴
582
 
583
  async def _poll_confirmation_data(self, symbol):
584
  if not self.confirmation_exchanges:
 
614
  for trade in trades:
615
  self.tactical_data_cache[symbol].add_confirmation_trade(ex_id, trade)
616
 
617
+ # 🔴 --- START OF CHANGE (V7.6) --- 🔴
618
+ # (التعامل مع الأخطاء الشائعة هنا أيضاً)
619
+ except RateLimitExceeded:
620
+ print(f"⏳ [Sentry Conf] {ex_id} Rate Limit لـ {symbol}. الانتظار (30 ثانية)...")
621
+ await asyncio.sleep(30)
622
+ except RequestTimeout:
623
+ print(f"⏳ [Sentry Conf] {ex_id} Timeout لـ {symbol}. الانتظار (15 ثانية)...")
624
+ await asyncio.sleep(15)
625
+ # 🔴 --- END OF CHANGE --- 🔴
626
+
627
  except asyncio.CancelledError:
628
  raise
629
  except Exception as e:
630
+ # (نتجاهل الأخطاء بصمت هنا حتى لا نوقف الحلقة الرئيسية)
631
  pass
632
 
 
633
  async def _run_tactical_analysis_loop(self, symbol: str, strategy_hint: str):
634
  """(محدث V7.5) (دماغ الحارس) يشغل التحليل التكتيكي + ATR Trailing"""
635
  while self.is_running:
 
724
  except asyncio.CancelledError:
725
  raise
726
  except Exception as e: print(f"❌ [Sentry] خطأ في حلقة التحليل التكتيكي لـ {symbol}: {e}"); traceback.print_exc()
 
727
 
728
 
729
  def _check_entry_trigger(self, symbol: str, strategy_hint: str, data: Dict, all_weights: Dict) -> bool:
 
799
  default=None
800
  )
801
 
 
802
  # (التحقق فقط إذا كانت القيمة أكبر من صفر)
803
  if hard_stop and hard_stop > 0 and current_price_for_sl and current_price_for_sl <= hard_stop:
804
  return f"Strategic Stop Loss hit: {current_price_for_sl} <= {hard_stop}"
805
 
806
  if take_profit and take_profit > 0 and current_price_for_tp and current_price_for_tp >= take_profit:
807
  return f"Strategic Take Profit hit: {current_price_for_tp} >= {take_profit}"
 
808
 
809
  return None
810
 
 
811
  async def _check_atr_trailing_stop(self, trade: Dict, snapshot: Dict, current_price: float) -> str:
812
  """(جديد V7.5) يحسب ويحدث وقف الخسارة المتحرك ATR"""
813
 
 
830
  # (السعر الحالي - (قيمة ATR * المضاعف))
831
  new_trailing_stop = current_price - (atr_1h * atr_multiplier)
832
 
833
+ # 4. جلب وقف الخsارة الحالي (الديناميكي)
834
  current_dynamic_sl = trade.get('dynamic_stop_loss', 0)
835
 
836
  # 5. منطق التحديث (لا تخفض الوقف أبداً)
 
852
  except Exception as e:
853
  print(f"❌ [Sentry ATR] {trade['symbol']}: Error calculating ATR stop: {e}")
854
  return None
 
855
 
856
 
857
  def _create_dataframe_5m(self, candles: List) -> pd.DataFrame:
 
1174
  finally:
1175
  if self.r2_service.lock_acquired: self.r2_service.release_lock()
1176
 
 
1177
  async def update_trade_strategy(self, trade_to_update, re_analysis_decision):
1178
  """
1179
  (محدث V7.5)
 
1218
  except Exception as e:
1219
  print(f"❌ (Explorer) فشل تحديث استراتيجية {symbol}: {e}");
1220
  raise
 
1221
 
 
1222
  async def _update_trade_dynamic_sl_in_r2(self, trade_id: str, new_dynamic_sl: float):
1223
  """(جديد V7.5) دالة مساعدة لتحديث الوقف الديناميكي فقط في R2"""
1224
  try:
 
1237
 
1238
  except Exception as e:
1239
  print(f"❌ [Sentry ATR] فشل حفظ الوقف المتحرك الجديد في R2: {e}")
 
1240
 
1241
  async def _archive_closed_trade(self, closed_trade):
1242
  try:
 
1275
  except Exception as e: print(f"❌ Failed to get trade by symbol {symbol}: {e}"); return None
1276
 
1277
 
1278
+ print(f"✅ Trade Manager loaded - V7.6 (Exponential Backoff) (ccxt.async_support: {CCXT_ASYNC_AVAILABLE})")