Riy777 commited on
Commit
bdb23a4
·
verified ·
1 Parent(s): 6863ef9

Update trade_manager.py

Browse files
Files changed (1) hide show
  1. trade_manager.py +55 -79
trade_manager.py CHANGED
@@ -1,7 +1,8 @@
1
- # trade_manager.py (V22.4 - GEM-Architect: Full Logic + Multi-Stats + 300 Candle Fix)
2
  import asyncio
3
  import json
4
  import uuid
 
5
  import traceback
6
  from datetime import datetime
7
  from typing import List, Dict, Any
@@ -26,22 +27,18 @@ class TradeManager:
26
  self.sentry_tasks = {}
27
  self.running = True
28
 
29
- # [ 🧠 IQ Stats ] مخزن الإحصائيات (سجلات فقط - المال في R2)
30
- # Hybrid: كل الصفقات التي أغلقها الذكاء الاصطناعي
31
- # V2: إحصائيات خاصة عندما كان V2 مرتفعاً
32
- # V3: إحصائيات خاصة عندما كان V3 مرتفعاً
33
  self.ai_stats = {
34
  "hybrid": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0},
35
  "v2": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0},
36
  "v3": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0}
37
  }
38
 
39
- # للإبقاء على التوافق مع الكود القديم الذي قد يطلب ds_stats مباشرة
40
  self.ds_stats = self.ai_stats["hybrid"]
41
 
42
  self.execution_lock = asyncio.Lock()
43
 
44
- print(f"🛡️ [TradeManager V22.4] Initialized. Hybrid Guardian Active (Limit: 300).")
45
 
46
  async def initialize_sentry_exchanges(self):
47
  print("🛡️ [TradeManager] Initializing and syncing state with R2...")
@@ -195,14 +192,12 @@ class TradeManager:
195
  self.watchlist.clear()
196
 
197
  # ============================================================
198
- # 🕒 تسجيل وقت أول صفقة في تاريخ النظام (للعداد)
199
  # ============================================================
200
  portfolio = await self.r2.get_portfolio_state_async()
201
  if portfolio.get('first_trade_timestamp') is None:
202
  portfolio['first_trade_timestamp'] = new_trade['entry_time']
203
  await self.r2.save_portfolio_state_async(portfolio)
204
- print(f"🕒 [System Info] تم تسجيل توقيت أول صفقة في النظام: {new_trade['entry_time']}")
205
- # ============================================================
206
 
207
  await self.r2.save_open_trades_async(list(self.open_positions.values()))
208
 
@@ -212,7 +207,6 @@ class TradeManager:
212
  self.sentry_tasks[symbol] = asyncio.create_task(self._guardian_loop(symbol))
213
 
214
  print(f"✅ [ENTRY EXECUTED] {symbol} | Price: {current_price:.8f} | TP: {tp_price:.8f} | SL: {sl_price:.8f}")
215
- print(f" -> L2 Score: {new_trade['l1_score']:.2f}, L4 Conf: {new_trade['l2_sniper_confidence']:.2f}")
216
 
217
  except Exception as e:
218
  print(f"❌ [TradeManager] فشل فادح أثناء تنفيذ الدخول لـ {symbol}: {e}")
@@ -266,15 +260,18 @@ class TradeManager:
266
  await self.select_and_execute_best_signal(signals_from_watchlist)
267
 
268
  # ==============================================================================
269
- # 🛡️ دوال حارس الخروج (Hybrid Sentry)
270
  # ==============================================================================
271
  async def _guardian_loop(self, symbol: str):
272
- print(f"🛡️ [Sentry Activated] بدء الحراسة لـ {symbol} (فحص كل 60 ثانية).")
 
 
273
 
274
  while self.running and symbol in self.open_positions:
275
  try:
276
- # 🚀 1. الانتظار لمدة 60 ثانية
277
- await asyncio.sleep(60)
 
278
 
279
  trade = self.open_positions.get(symbol)
280
  if not trade: break
@@ -282,71 +279,66 @@ class TradeManager:
282
  current_price = await self.data_manager.get_latest_price_async(symbol)
283
  if current_price == 0.0: continue
284
 
285
- # 🚀 2. فحص الحدود الصارمة (Hard Limits) - أمان أخير
286
  if current_price >= trade['tp_price']:
287
- print(f"✅ [Sentry EXIT] {symbol} (Hard TP Hit) at {current_price:.8f}")
288
  async with self.execution_lock:
289
  await self._execute_exit(symbol, current_price, "TP_HIT_HARD")
290
  break
291
 
292
  if current_price <= trade['sl_price']:
293
- print(f"🛑 [Sentry EXIT] {symbol} (Hard SL Hit) at {current_price:.8f}")
294
  async with self.execution_lock:
295
  await self._execute_exit(symbol, current_price, "SL_HIT_HARD")
296
  break
297
 
298
- # 🚀 3. [ 🧠 ] Hybrid DeepSteward (V2 + V3 Dual Brain)
299
- if self.deep_steward and self.deep_steward.initialized:
300
- # --- [تعديل جوهري] زيادة الحد إلى 300 شمعة لتجنب أخطاء النموذج ---
301
- task1 = self.data_manager.get_latest_ohlcv(symbol, '1m', 300)
302
- task5 = self.data_manager.get_latest_ohlcv(symbol, '5m', 200)
303
- task15 = self.data_manager.get_latest_ohlcv(symbol, '15m', 100)
304
-
305
- d1, d5, d15 = await asyncio.gather(task1, task5, task15)
306
-
307
- # التأكد من وجود بيانات كافية (V2 يحتاج 64 شمعة، المؤشرات تحتاج 200)
308
- if d1 and d5 and d15 and len(d1) >= 200:
309
- # استشارة الحارس الهجين
310
- decision = self.deep_steward.analyze_position(d1, d5, d15, trade['entry_price'])
311
- action = decision.get('action', 'HOLD')
312
- scores = decision.get('scores', {}) # درجات V2 و V3 للتدقيق
313
 
314
- if action == 'EXIT_HARD':
315
- print(f"🤖 [Hybrid Guardian] 🚨 خروج طارئ! {decision['reason']}")
316
- async with self.execution_lock:
317
- await self._execute_exit(symbol, current_price, "AI_HARD_EXIT", ai_scores=scores)
318
- break
319
 
320
- elif action == 'EXIT_SOFT':
321
- print(f"🤖 [Hybrid Guardian] ⚠️ تحذير خروج! {decision['reason']}")
322
- # هنا يمكن إضافة منطق لتشديد الستوب بدلاً من الخروج المباشر
323
- async with self.execution_lock:
324
- await self._execute_exit(symbol, current_price, "AI_SOFT_EXIT", ai_scores=scores)
325
- break
326
-
327
- else:
328
- print(f"⚠️ [Sentry] بيانات الحارس الهجين غير مكتملة لـ {symbol} (Rows: {len(d1) if d1 else 0}). تخطي الدورة.")
329
- else:
330
- pass # لا يوجد نموذج فعال
331
-
332
- self.open_positions[symbol]['last_update'] = datetime.now().isoformat()
 
 
 
 
 
 
 
 
 
333
 
334
  except asyncio.CancelledError:
335
  print(f"🛡️ [Sentry STOP] تم إيقاف الحارس لـ {symbol}.")
336
  break
337
  except Exception as e:
338
  print(f"❌ [Sentry ERROR] فشل مؤقت لـ {symbol}: {e}")
339
- await asyncio.sleep(10)
340
 
341
  # ==============================================================================
342
- # 👻 المراقب الشبحي (Ghost Monitor) - [محدث لتوزيع الإحصائيات]
343
  # ==============================================================================
344
  def _launch_post_exit_analysis(self, symbol, exit_price, exit_time, position_size_usd, ai_scores=None):
345
- """يطلق مهمة خلفية لمراقبة السعر بعد الخروج وحساب الأثر بالدولار"""
346
  asyncio.create_task(self._analyze_after_exit_task(symbol, exit_price, exit_time, position_size_usd, ai_scores))
347
 
348
  def _update_specific_stat(self, key, is_good, usd_impact):
349
- """دالة مساعدة لتحديث إحصائيات محفظة معينة (Hybrid, V2, V3)"""
350
  if key not in self.ai_stats: return
351
  self.ai_stats[key]["total"] += 1
352
  if is_good:
@@ -356,41 +348,36 @@ class TradeManager:
356
  self.ai_stats[key]["missed"] += abs(usd_impact)
357
 
358
  async def _analyze_after_exit_task(self, symbol, exit_price, exit_time, position_size_usd, ai_scores):
359
- # ⏳ ننتظر 15 دقيقة (900 ثانية)
360
  await asyncio.sleep(900)
361
 
362
  try:
363
  current_price = await self.data_manager.get_latest_price_async(symbol)
364
  if current_price == 0: return
365
 
366
- # هل كان الخروج صحيحاً؟
367
  change_pct = (current_price - exit_price) / exit_price
368
  usd_impact = change_pct * position_size_usd
369
- is_good_exit = change_pct < 0 # جيد إذا السعر هبط بعد الخروج
370
 
371
- # 1. تحديث إحصائيات النظام الهجين (الكل)
372
  self._update_specific_stat("hybrid", is_good_exit, usd_impact)
373
 
374
- # 2. توزيع الفضل (Attribution) لـ V2 و V3
375
  if ai_scores:
376
  v2_score = ai_scores.get('v2', 0.0)
377
  v3_score = ai_scores.get('v3', 0.0)
378
 
379
- # V2 ينسب له الفضل إذا كانت درجة الخطر لديه عالية (فتح البوابة)
380
- if v2_score >= 0.60:
381
  self._update_specific_stat("v2", is_good_exit, usd_impact)
382
 
383
- # V3 ينسب له الفضل إذا كانت درجة الثقة لديه عالية (ضغط الزناد)
384
- if v3_score >= 0.75:
385
  self._update_specific_stat("v3", is_good_exit, usd_impact)
386
 
387
- # إعداد السجل لـ R2
388
  audit_record = {
389
  "symbol": symbol,
390
  "exit_time": exit_time,
391
  "exit_price": exit_price,
392
- "price_15m_later": current_price,
393
- "change_15m_pct": change_pct * 100,
394
  "usd_impact": usd_impact,
395
  "verdict": "SUCCESS (Saved Loss)" if is_good_exit else "MISS (Lost Profit)",
396
  "ai_scores": ai_scores if ai_scores else {},
@@ -401,7 +388,7 @@ class TradeManager:
401
  print(f"👻 [Ghost Monitor] {symbol}: {audit_record['verdict']} | Impact: ${usd_impact:.2f}")
402
 
403
  except Exception as e:
404
- print(f"⚠️ [Ghost Error] فشل تحليل ما بعد الخروج لـ {symbol}: {e}")
405
 
406
  # ==============================================================================
407
  # 🔴 دالة الخروج المحاسبية (Accounting Exit Logic)
@@ -412,31 +399,25 @@ class TradeManager:
412
  return
413
 
414
  try:
415
- # 1. استخراج بيانات الصفقة
416
  trade = self.open_positions.pop(symbol)
417
-
418
  entry_price = float(trade['entry_price'])
419
  exit_price = float(price)
420
 
421
- # 2. حساب نسبة الربح
422
  raw_profit_pct = (exit_price - entry_price) / entry_price
423
  profit_pct_display = raw_profit_pct * 100
424
 
425
- # 3. تحديث بيانات الصفقة
426
  trade['status'] = 'CLOSED'
427
  trade['exit_price'] = exit_price
428
  trade['exit_time'] = datetime.now().isoformat()
429
  trade['exit_reason'] = reason
430
  trade['profit_pct'] = profit_pct_display
431
 
432
- # 4. 💰 [المحاسبة] تحديث المحفظة (محفظة واحدة فقط)
433
  portfolio = await self.r2.get_portfolio_state_async()
434
  current_capital = float(portfolio.get('current_capital_usd', 100.0))
435
 
436
  pnl_usd = current_capital * raw_profit_pct
437
  new_capital = current_capital + pnl_usd
438
 
439
- # تحديث الإحصائيات التراكمية
440
  portfolio['current_capital_usd'] = new_capital
441
  portfolio['total_trades'] = portfolio.get('total_trades', 0) + 1
442
 
@@ -449,25 +430,21 @@ class TradeManager:
449
  portfolio['total_loss_usd'] = portfolio.get('total_loss_usd', 0.0) + abs(pnl_usd)
450
  trade['result'] = 'LOSS'
451
 
452
- # تحديث نسبة الفوز
453
  total_t = portfolio['total_trades']
454
  if total_t > 0:
455
  portfolio['win_rate'] = (portfolio['winning_trades'] / total_t) * 100
456
 
457
  trade['pnl_usd'] = pnl_usd
458
 
459
- # 5. حفظ البيانات في R2
460
  await self.r2.save_portfolio_state_async(portfolio)
461
  await self.r2.save_open_trades_async(list(self.open_positions.values()))
462
  await self.r2.append_to_closed_trades_history(trade)
463
 
464
  print(f"✅ [EXIT EXECUTED] {symbol} | Reason: {reason} | PnL: {profit_pct_display:.2f}% (${pnl_usd:.2f}) | New Cap: ${new_capital:.2f}")
465
 
466
- # 6. [ 🧠 ] إطلاق المراقب الشبحي إذا كان الخروج بسبب الذكاء الاصطناعي
467
  if "AI_" in reason:
468
  self._launch_post_exit_analysis(symbol, exit_price, trade['exit_time'], current_capital, ai_scores)
469
 
470
- # 7. تنظيف مهام الحارس
471
  if symbol in self.sentry_tasks:
472
  self.sentry_tasks[symbol].cancel()
473
  del self.sentry_tasks[symbol]
@@ -478,7 +455,6 @@ class TradeManager:
478
  if symbol not in self.open_positions:
479
  self.open_positions[symbol] = trade
480
 
481
- # [جديد] دالة للخروج القسري من واجهة المستخدم
482
  async def force_exit_by_manager(self, symbol, reason):
483
  print(f"⚠️ [Manager Force Exit] طلب خروج إداري لـ {symbol} بسبب: {reason}")
484
  current_price = await self.data_manager.get_latest_price_async(symbol)
 
1
+ # trade_manager.py (V22.5 - GEM-Architect: Real-Time Hard Limits Fix)
2
  import asyncio
3
  import json
4
  import uuid
5
+ import time
6
  import traceback
7
  from datetime import datetime
8
  from typing import List, Dict, Any
 
27
  self.sentry_tasks = {}
28
  self.running = True
29
 
30
+ # [ 🧠 IQ Stats ] مخزن الإحصائيات
 
 
 
31
  self.ai_stats = {
32
  "hybrid": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0},
33
  "v2": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0},
34
  "v3": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0}
35
  }
36
 
 
37
  self.ds_stats = self.ai_stats["hybrid"]
38
 
39
  self.execution_lock = asyncio.Lock()
40
 
41
+ print(f"🛡️ [TradeManager V22.5] Initialized. Real-Time Hard Limits Enabled.")
42
 
43
  async def initialize_sentry_exchanges(self):
44
  print("🛡️ [TradeManager] Initializing and syncing state with R2...")
 
192
  self.watchlist.clear()
193
 
194
  # ============================================================
195
+ # 🕒 تسجيل وقت أول صفقة
196
  # ============================================================
197
  portfolio = await self.r2.get_portfolio_state_async()
198
  if portfolio.get('first_trade_timestamp') is None:
199
  portfolio['first_trade_timestamp'] = new_trade['entry_time']
200
  await self.r2.save_portfolio_state_async(portfolio)
 
 
201
 
202
  await self.r2.save_open_trades_async(list(self.open_positions.values()))
203
 
 
207
  self.sentry_tasks[symbol] = asyncio.create_task(self._guardian_loop(symbol))
208
 
209
  print(f"✅ [ENTRY EXECUTED] {symbol} | Price: {current_price:.8f} | TP: {tp_price:.8f} | SL: {sl_price:.8f}")
 
210
 
211
  except Exception as e:
212
  print(f"❌ [TradeManager] فشل فادح أثناء تنفيذ الدخول لـ {symbol}: {e}")
 
260
  await self.select_and_execute_best_signal(signals_from_watchlist)
261
 
262
  # ==============================================================================
263
+ # 🛡️ دوال حارس الخروج (Hybrid Sentry) - [CRITICAL FIX: Real-Time Checks]
264
  # ==============================================================================
265
  async def _guardian_loop(self, symbol: str):
266
+ print(f"🛡️ [Sentry Activated] بدء الحراسة لـ {symbol} (وضع الاستجابة الفورية).")
267
+
268
+ last_ai_check_time = 0
269
 
270
  while self.running and symbol in self.open_positions:
271
  try:
272
+ # [FAST LANE] المسار السريع: فحص الحدود الصارمة كل 2 ثانية
273
+ # هذا يضمن عدم تفويت الهدف إذا تحرك السعر بسرعة
274
+ await asyncio.sleep(2)
275
 
276
  trade = self.open_positions.get(symbol)
277
  if not trade: break
 
279
  current_price = await self.data_manager.get_latest_price_async(symbol)
280
  if current_price == 0.0: continue
281
 
282
+ # 1. فحص الحدود الصارمة (Hard Limits) - أولوية قصوى
283
  if current_price >= trade['tp_price']:
284
+ print(f"✅ [Sentry FAST] {symbol} (Target Hit) at {current_price:.8f}")
285
  async with self.execution_lock:
286
  await self._execute_exit(symbol, current_price, "TP_HIT_HARD")
287
  break
288
 
289
  if current_price <= trade['sl_price']:
290
+ print(f"🛑 [Sentry FAST] {symbol} (Stop Loss Hit) at {current_price:.8f}")
291
  async with self.execution_lock:
292
  await self._execute_exit(symbol, current_price, "SL_HIT_HARD")
293
  break
294
 
295
+ # 🐌 [SLOW LANE] المسار البطيء: فحص الذكاء الاصطناعي كل 60 ثانية
296
+ if time.time() - last_ai_check_time > 60:
297
+ if self.deep_steward and self.deep_steward.initialized:
298
+ # جلب البيانات اللازمة (تم تثبيت الحد على 300)
299
+ task1 = self.data_manager.get_latest_ohlcv(symbol, '1m', 300)
300
+ task5 = self.data_manager.get_latest_ohlcv(symbol, '5m', 200)
301
+ task15 = self.data_manager.get_latest_ohlcv(symbol, '15m', 100)
 
 
 
 
 
 
 
 
302
 
303
+ d1, d5, d15 = await asyncio.gather(task1, task5, task15)
 
 
 
 
304
 
305
+ if d1 and d5 and d15 and len(d1) >= 200:
306
+ decision = self.deep_steward.analyze_position(d1, d5, d15, trade['entry_price'])
307
+ action = decision.get('action', 'HOLD')
308
+ scores = decision.get('scores', {})
309
+
310
+ if action == 'EXIT_HARD':
311
+ print(f"🤖 [Hybrid Guardian] 🚨 خروج طارئ! {decision['reason']}")
312
+ async with self.execution_lock:
313
+ await self._execute_exit(symbol, current_price, "AI_HARD_EXIT", ai_scores=scores)
314
+ break
315
+
316
+ elif action == 'EXIT_SOFT':
317
+ print(f"🤖 [Hybrid Guardian] ⚠️ تحذير خروج! {decision['reason']}")
318
+ async with self.execution_lock:
319
+ await self._execute_exit(symbol, current_price, "AI_SOFT_EXIT", ai_scores=scores)
320
+ break
321
+ else:
322
+ # لو البيانات ناقصة، ننتظر الد��رة القادمة
323
+ pass
324
+
325
+ last_ai_check_time = time.time()
326
+ self.open_positions[symbol]['last_update'] = datetime.now().isoformat()
327
 
328
  except asyncio.CancelledError:
329
  print(f"🛡️ [Sentry STOP] تم إيقاف الحارس لـ {symbol}.")
330
  break
331
  except Exception as e:
332
  print(f"❌ [Sentry ERROR] فشل مؤقت لـ {symbol}: {e}")
333
+ await asyncio.sleep(5) # انتظار قصير عند الخطأ
334
 
335
  # ==============================================================================
336
+ # 👻 المراقب الشبحي (Ghost Monitor)
337
  # ==============================================================================
338
  def _launch_post_exit_analysis(self, symbol, exit_price, exit_time, position_size_usd, ai_scores=None):
 
339
  asyncio.create_task(self._analyze_after_exit_task(symbol, exit_price, exit_time, position_size_usd, ai_scores))
340
 
341
  def _update_specific_stat(self, key, is_good, usd_impact):
 
342
  if key not in self.ai_stats: return
343
  self.ai_stats[key]["total"] += 1
344
  if is_good:
 
348
  self.ai_stats[key]["missed"] += abs(usd_impact)
349
 
350
  async def _analyze_after_exit_task(self, symbol, exit_price, exit_time, position_size_usd, ai_scores):
 
351
  await asyncio.sleep(900)
352
 
353
  try:
354
  current_price = await self.data_manager.get_latest_price_async(symbol)
355
  if current_price == 0: return
356
 
 
357
  change_pct = (current_price - exit_price) / exit_price
358
  usd_impact = change_pct * position_size_usd
359
+ is_good_exit = change_pct < 0
360
 
361
+ # 1. تحديث الهجين
362
  self._update_specific_stat("hybrid", is_good_exit, usd_impact)
363
 
364
+ # 2. تحديث المكونات
365
  if ai_scores:
366
  v2_score = ai_scores.get('v2', 0.0)
367
  v3_score = ai_scores.get('v3', 0.0)
368
 
369
+ if v2_score >= 0.60: # V2 ساهم
 
370
  self._update_specific_stat("v2", is_good_exit, usd_impact)
371
 
372
+ if v3_score >= 0.75: # V3 ساهم
 
373
  self._update_specific_stat("v3", is_good_exit, usd_impact)
374
 
 
375
  audit_record = {
376
  "symbol": symbol,
377
  "exit_time": exit_time,
378
  "exit_price": exit_price,
379
+ "price_15m_later": current_price,
380
+ "change_15m_pct": change_pct * 100,
381
  "usd_impact": usd_impact,
382
  "verdict": "SUCCESS (Saved Loss)" if is_good_exit else "MISS (Lost Profit)",
383
  "ai_scores": ai_scores if ai_scores else {},
 
388
  print(f"👻 [Ghost Monitor] {symbol}: {audit_record['verdict']} | Impact: ${usd_impact:.2f}")
389
 
390
  except Exception as e:
391
+ print(f"⚠️ [Ghost Error] {symbol}: {e}")
392
 
393
  # ==============================================================================
394
  # 🔴 دالة الخروج المحاسبية (Accounting Exit Logic)
 
399
  return
400
 
401
  try:
 
402
  trade = self.open_positions.pop(symbol)
 
403
  entry_price = float(trade['entry_price'])
404
  exit_price = float(price)
405
 
 
406
  raw_profit_pct = (exit_price - entry_price) / entry_price
407
  profit_pct_display = raw_profit_pct * 100
408
 
 
409
  trade['status'] = 'CLOSED'
410
  trade['exit_price'] = exit_price
411
  trade['exit_time'] = datetime.now().isoformat()
412
  trade['exit_reason'] = reason
413
  trade['profit_pct'] = profit_pct_display
414
 
 
415
  portfolio = await self.r2.get_portfolio_state_async()
416
  current_capital = float(portfolio.get('current_capital_usd', 100.0))
417
 
418
  pnl_usd = current_capital * raw_profit_pct
419
  new_capital = current_capital + pnl_usd
420
 
 
421
  portfolio['current_capital_usd'] = new_capital
422
  portfolio['total_trades'] = portfolio.get('total_trades', 0) + 1
423
 
 
430
  portfolio['total_loss_usd'] = portfolio.get('total_loss_usd', 0.0) + abs(pnl_usd)
431
  trade['result'] = 'LOSS'
432
 
 
433
  total_t = portfolio['total_trades']
434
  if total_t > 0:
435
  portfolio['win_rate'] = (portfolio['winning_trades'] / total_t) * 100
436
 
437
  trade['pnl_usd'] = pnl_usd
438
 
 
439
  await self.r2.save_portfolio_state_async(portfolio)
440
  await self.r2.save_open_trades_async(list(self.open_positions.values()))
441
  await self.r2.append_to_closed_trades_history(trade)
442
 
443
  print(f"✅ [EXIT EXECUTED] {symbol} | Reason: {reason} | PnL: {profit_pct_display:.2f}% (${pnl_usd:.2f}) | New Cap: ${new_capital:.2f}")
444
 
 
445
  if "AI_" in reason:
446
  self._launch_post_exit_analysis(symbol, exit_price, trade['exit_time'], current_capital, ai_scores)
447
 
 
448
  if symbol in self.sentry_tasks:
449
  self.sentry_tasks[symbol].cancel()
450
  del self.sentry_tasks[symbol]
 
455
  if symbol not in self.open_positions:
456
  self.open_positions[symbol] = trade
457
 
 
458
  async def force_exit_by_manager(self, symbol, reason):
459
  print(f"⚠️ [Manager Force Exit] طلب خروج إداري لـ {symbol} بسبب: {reason}")
460
  current_price = await self.data_manager.get_latest_price_async(symbol)