Riy777 commited on
Commit
c8bad4a
·
1 Parent(s): e73bb85

Update trade_manager.py

Browse files
Files changed (1) hide show
  1. trade_manager.py +20 -26
trade_manager.py CHANGED
@@ -1,4 +1,4 @@
1
- # trade_manager.py (Updated to V5.8 - Added Asyncio Lock for Watchlist)
2
  import asyncio
3
  import json
4
  import time
@@ -133,9 +133,7 @@ class TradeManager:
133
  self.sentry_tasks = {}
134
  self.tactical_data_cache = {}
135
 
136
- # 🔴 --- START OF CHANGE (FIX RACE CONDITION) --- 🔴
137
- self.sentry_lock = asyncio.Lock() # إضافة قفل لحماية Watchlist
138
- # 🔴 --- END OF CHANGE --- 🔴
139
 
140
  self.kucoin_rest = None # (المنصة الأساسية)
141
  self.confirmation_exchanges = {} # (المنصات الثانوية للتأكيد)
@@ -189,8 +187,7 @@ class TradeManager:
189
  for ex in self.confirmation_exchanges.values(): await ex.close()
190
  raise
191
 
192
- # 🔴 --- START OF CHANGE (FIX RACE CONDITION) --- 🔴
193
- async def start_sentry_and_monitoring_loops(self):
194
  """الحلقة الرئيسية للحارس (Sentry) ومراقب الخروج (Exit Monitor)."""
195
  self.is_running = True
196
  print(f"✅ [Sentry] بدء حلقات المراقبة التكتيكية (Layer 2 - API Polling)...")
@@ -236,7 +233,6 @@ class TradeManager:
236
 
237
  except Exception as error:
238
  print(f"❌ [Sentry] خطأ في الحلقة الرئيسية: {error}"); traceback.print_exc(); await asyncio.sleep(60)
239
- # 🔴 --- END OF CHANGE --- 🔴
240
 
241
  async def stop_sentry_loops(self):
242
  """(محدث) إيقاف جميع مهام المراقبة وإغلاق جميع اتصالات REST"""
@@ -259,13 +255,11 @@ class TradeManager:
259
  print("✅ [Sentry] تم إغلاق اتصالات التداول (REST).")
260
  except Exception as e: print(f"⚠️ [Sentry] خطأ أثناء إغلاق الاتصالات: {e}")
261
 
262
- # 🔴 --- START OF CHANGE (FIX RACE CONDITION) --- 🔴
263
- async def update_sentry_watchlist(self, candidates: List[Dict]):
264
  """تحديث قائمة المراقبة التي يستخدمها الحارس (Sentry)."""
265
  async with self.sentry_lock: # (استخدام القفل قبل الكتابة)
266
  self.sentry_watchlist = {c['symbol']: c for c in candidates}
267
  print(f"ℹ️ [Sentry] تم تحديث Watchlist. عدد المرشحين: {len(self.sentry_watchlist)}")
268
- # 🔴 --- END OF CHANGE --- 🔴
269
 
270
  def get_sentry_status(self):
271
  """(محدث) لواجهة برمجة التطبيقات /system-status"""
@@ -399,7 +393,7 @@ class TradeManager:
399
 
400
 
401
  async def _run_tactical_analysis_loop(self, symbol: str, strategy_hint: str):
402
- """(محدث) (دماغ الحارس) يشغل التحليل التكتيكي كل ثانية."""
403
  while self.is_running:
404
  await asyncio.sleep(1) # (التحليل السريع كل ثانية)
405
  try:
@@ -419,9 +413,7 @@ class TradeManager:
419
  current_price = tactical_data.order_book['bids'][0][0] if tactical_data.order_book and tactical_data.order_book.get('bids') and len(tactical_data.order_book['bids']) > 0 else None
420
  if current_price: await self.immediate_close_trade(symbol, current_price, f"Tactical Exit: {exit_reason}")
421
  else:
422
- # (منطق الدخول - محدث)
423
- # (التحقق من قائمة المراقبة يتم الآن في الحلقة الرئيسية)
424
- # (لكننا نحتاج إلى التحقق مرة أخرى هنا لضمان عدم حدوث سباق حالات)
425
  async with self.sentry_lock:
426
  is_still_on_watchlist = symbol in self.sentry_watchlist
427
 
@@ -442,8 +434,9 @@ class TradeManager:
442
  raise # تمرير الإلغاء
443
  except Exception as e: print(f"❌ [Sentry] خطأ في حلقة التحليل التكتيكي لـ {symbol}: {e}"); traceback.print_exc()
444
 
 
445
  def _check_entry_trigger(self, symbol: str, strategy_hint: str, data: Dict) -> bool:
446
- """(محدث V5.7) إصلاح منطق الزناد للسماح باستراتيجيات الانعكاس."""
447
 
448
  rsi = data.get('rsi_1m_approx', 50)
449
  cvd_kucoin = data.get('cvd_kucoin', 0)
@@ -452,9 +445,10 @@ class TradeManager:
452
  cvd_conf_agg = data.get('cvd_confirmation_aggregate', 0)
453
  cvd_conf_sources_count = len(data.get('cvd_confirmation_sources', {}))
454
 
455
- # (تم حذف شرط CVD العام من هنا)
456
 
457
- if strategy_hint == 'breakout_momentum':
 
458
  # استراتيجية الزخم *تتطلب* تدفقاً إيجابياً
459
  if cvd_kucoin <= 0:
460
  return False # (لا يوجد زخم إذا كان CVD سلبياً)
@@ -463,18 +457,16 @@ class TradeManager:
463
  if (rsi > 55):
464
  # (الفيتو المخفf: نوقف فقط إذا كانت المنصات الأخرى تبيع ضدنا بقوة)
465
  if cvd_conf_sources_count > 0 and cvd_conf_agg < (cvd_kucoin * -0.5):
466
- print(f" [Trigger Hold] {symbol} Breakout: KuCoin CVD ({cvd_kucoin:.0f}) إيجابي، لكن منصات التأكيد ({cvd_conf_agg:.0f}) تبيع بقوة.")
467
  return False
468
 
469
- print(f" [Trigger] {symbol} Breakout: K_CVD={cvd_kucoin:.0f}, C_CVD_Agg={cvd_conf_agg:.0f}, RSI={rsi:.1f}");
470
  return True
471
 
472
  elif strategy_hint == 'mean_reversion':
473
  # استراتيجية الانعكاس *لا* تتطلب CVD إيجابي
474
  # (الشروط المخففة: RSI < 35)
475
  if (rsi < 35):
476
- # (اختياري: يمكننا إضافة شرط أن يكون CVD قد بدأ في التحسن،
477
- # ولكن في الوقت الحالي، RSI < 35 هو زناد كافٍ)
478
  print(f" [Trigger] {symbol} Reversion: RSI={rsi:.1f}, K_CVD={cvd_kucoin:.0f} (CVD check ignored)")
479
  return True
480
 
@@ -486,15 +478,18 @@ class TradeManager:
486
  return True
487
 
488
  return False
489
- # 🔴 --- END OF LOGIC FIX (V5.7) --- 🔴
490
 
491
  def _check_exit_trigger(self, trade: Dict, data: Dict) -> str: # (لا تغيير هنا)
492
  """يحدد ما إذا كان يجب الخروج من صفقة مفتوحة تكتيكياً."""
493
  rsi = data.get('rsi_1m_approx', 50); cvd_kucoin = data.get('cvd_kucoin', 0); symbol = trade['symbol']
494
- if trade.get('strategy') == 'breakout_momentum':
495
- if rsi < 40 and cvd_kucoin < 0: return "Momentum reversal (RSI < 40 + CVD Negative)"
 
 
496
  if trade.get('strategy') == 'mean_reversion':
497
  if rsi > 75: return "Mean Reversion Target Hit (RSI > 75)"
 
498
  if symbol in self.tactical_data_cache and self.tactical_data_cache[symbol].order_book:
499
  ob = self.tactical_data_cache[symbol].order_book
500
  if ob and ob.get('bids') and len(ob['bids']) > 0: # (تحقق إضافي)
@@ -627,7 +622,6 @@ class TradeManager:
627
  await self.r2_service.save_open_trades_async(trades_to_keep)
628
 
629
  # (لا حاجة لإزالة المهمة يدوياً، الحلقة الرئيسية ستفعل ذلك)
630
- # if symbol in self.sentry_tasks: print(f"ℹ️ [Sentry] الصفقة {symbol} أغلقت، ستتوقف المراقبة.")
631
 
632
  await self.r2_service.save_system_logs_async({
633
  "trade_closed": True, "symbol": symbol, "pnl_usd": pnl, "pnl_percent": pnl_percent,
@@ -715,4 +709,4 @@ class TradeManager:
715
  except Exception as e: print(f"❌ Failed to get trade by symbol {symbol}: {e}"); return None
716
 
717
 
718
- print(f"✅ Trade Manager loaded - V5.8 (Added Watchlist Lock) (ccxt.async_support: {CCXT_ASYNC_AVAILABLE})")
 
1
+ # trade_manager.py (Updated to V5.9 - Added trend_following to Triggers)
2
  import asyncio
3
  import json
4
  import time
 
133
  self.sentry_tasks = {}
134
  self.tactical_data_cache = {}
135
 
136
+ self.sentry_lock = asyncio.Lock() # (إصلاح V5.8)
 
 
137
 
138
  self.kucoin_rest = None # (المنصة الأساسية)
139
  self.confirmation_exchanges = {} # (المنصات الثانوية للتأكيد)
 
187
  for ex in self.confirmation_exchanges.values(): await ex.close()
188
  raise
189
 
190
+ async def start_sentry_and_monitoring_loops(self): # (محدث V5.8)
 
191
  """الحلقة الرئيسية للحارس (Sentry) ومراقب الخروج (Exit Monitor)."""
192
  self.is_running = True
193
  print(f"✅ [Sentry] بدء حلقات المراقبة التكتيكية (Layer 2 - API Polling)...")
 
233
 
234
  except Exception as error:
235
  print(f"❌ [Sentry] خطأ في الحلقة الرئيسية: {error}"); traceback.print_exc(); await asyncio.sleep(60)
 
236
 
237
  async def stop_sentry_loops(self):
238
  """(محدث) إيقاف جميع مهام المراقبة وإغلاق جميع اتصالات REST"""
 
255
  print("✅ [Sentry] تم إغلاق اتصالات التداول (REST).")
256
  except Exception as e: print(f"⚠️ [Sentry] خطأ أثناء إغلاق الاتصالات: {e}")
257
 
258
+ async def update_sentry_watchlist(self, candidates: List[Dict]): # (محدث V5.8)
 
259
  """تحديث قائمة المراقبة التي يستخدمها الحارس (Sentry)."""
260
  async with self.sentry_lock: # (استخدام القفل قبل الكتابة)
261
  self.sentry_watchlist = {c['symbol']: c for c in candidates}
262
  print(f"ℹ️ [Sentry] تم تحديث Watchlist. عدد المرشحين: {len(self.sentry_watchlist)}")
 
263
 
264
  def get_sentry_status(self):
265
  """(محدث) لواجهة برمجة التطبيقات /system-status"""
 
393
 
394
 
395
  async def _run_tactical_analysis_loop(self, symbol: str, strategy_hint: str):
396
+ """(محدث V5.8) (دماغ الحارس) يشغل التحليل التكتيكي كل ثانية."""
397
  while self.is_running:
398
  await asyncio.sleep(1) # (التحليل السريع كل ثانية)
399
  try:
 
413
  current_price = tactical_data.order_book['bids'][0][0] if tactical_data.order_book and tactical_data.order_book.get('bids') and len(tactical_data.order_book['bids']) > 0 else None
414
  if current_price: await self.immediate_close_trade(symbol, current_price, f"Tactical Exit: {exit_reason}")
415
  else:
416
+ # (منطق الدخول - محدث V5.8)
 
 
417
  async with self.sentry_lock:
418
  is_still_on_watchlist = symbol in self.sentry_watchlist
419
 
 
434
  raise # تمرير الإلغاء
435
  except Exception as e: print(f"❌ [Sentry] خطأ في حلقة التحليل التكتيكي لـ {symbol}: {e}"); traceback.print_exc()
436
 
437
+ # 🔴 --- START OF CHANGE (LOGIC FIXED V5.9) --- 🔴
438
  def _check_entry_trigger(self, symbol: str, strategy_hint: str, data: Dict) -> bool:
439
+ """(محدث V5.9) إضافة trend_following إلى منطق الزناد."""
440
 
441
  rsi = data.get('rsi_1m_approx', 50)
442
  cvd_kucoin = data.get('cvd_kucoin', 0)
 
445
  cvd_conf_agg = data.get('cvd_confirmation_aggregate', 0)
446
  cvd_conf_sources_count = len(data.get('cvd_confirmation_sources', {}))
447
 
448
+ # (تم حذف شرط CVD العام من هنا - إصلاح V5.7)
449
 
450
+ # (إصلاح V5.9: دمج trend_following مع breakout_momentum)
451
+ if strategy_hint in ['breakout_momentum', 'trend_following']:
452
  # استراتيجية الزخم *تتطلب* تدفقاً إيجابياً
453
  if cvd_kucoin <= 0:
454
  return False # (لا يوجد زخم إذا كان CVD سلبياً)
 
457
  if (rsi > 55):
458
  # (الفيتو المخفf: نوقف فقط إذا كانت المنصات الأخرى تبيع ضدنا بقوة)
459
  if cvd_conf_sources_count > 0 and cvd_conf_agg < (cvd_kucoin * -0.5):
460
+ print(f" [Trigger Hold] {symbol} Breakout/Trend: KuCoin CVD ({cvd_kucoin:.0f}) إيجابي، لكن منصات التأكيد ({cvd_conf_agg:.0f}) تبيع بقوة.")
461
  return False
462
 
463
+ print(f" [Trigger] {symbol} Breakout/Trend: K_CVD={cvd_kucoin:.0f}, C_CVD_Agg={cvd_conf_agg:.0f}, RSI={rsi:.1f}");
464
  return True
465
 
466
  elif strategy_hint == 'mean_reversion':
467
  # استراتيجية الانعكاس *لا* تتطلب CVD إيجابي
468
  # (الشروط المخففة: RSI < 35)
469
  if (rsi < 35):
 
 
470
  print(f" [Trigger] {symbol} Reversion: RSI={rsi:.1f}, K_CVD={cvd_kucoin:.0f} (CVD check ignored)")
471
  return True
472
 
 
478
  return True
479
 
480
  return False
481
+ # 🔴 --- END OF CHANGE (LOGIC FIXED V5.9) --- 🔴
482
 
483
  def _check_exit_trigger(self, trade: Dict, data: Dict) -> str: # (لا تغيير هنا)
484
  """يحدد ما إذا كان يجب الخروج من صفقة مفتوحة تكتيكياً."""
485
  rsi = data.get('rsi_1m_approx', 50); cvd_kucoin = data.get('cvd_kucoin', 0); symbol = trade['symbol']
486
+
487
+ # (تحديث منطق الخروج ليشمل trend_following)
488
+ if trade.get('strategy') in ['breakout_momentum', 'trend_following']:
489
+ if rsi < 40 and cvd_kucoin < 0: return "Momentum/Trend reversal (RSI < 40 + CVD Negative)"
490
  if trade.get('strategy') == 'mean_reversion':
491
  if rsi > 75: return "Mean Reversion Target Hit (RSI > 75)"
492
+
493
  if symbol in self.tactical_data_cache and self.tactical_data_cache[symbol].order_book:
494
  ob = self.tactical_data_cache[symbol].order_book
495
  if ob and ob.get('bids') and len(ob['bids']) > 0: # (تحقق إضافي)
 
622
  await self.r2_service.save_open_trades_async(trades_to_keep)
623
 
624
  # (لا حاجة لإزالة المهمة يدوياً، الحلقة الرئيسية ستفعل ذلك)
 
625
 
626
  await self.r2_service.save_system_logs_async({
627
  "trade_closed": True, "symbol": symbol, "pnl_usd": pnl, "pnl_percent": pnl_percent,
 
709
  except Exception as e: print(f"❌ Failed to get trade by symbol {symbol}: {e}"); return None
710
 
711
 
712
+ print(f"✅ Trade Manager loaded - V5.9 (Fixed trend_following Trigger) (ccxt.async_support: {CCXT_ASYNC_AVAILABLE})")