Riy777 commited on
Commit
c4b5eae
·
verified ·
1 Parent(s): 744703f

Update trade_manager.py

Browse files
Files changed (1) hide show
  1. trade_manager.py +90 -47
trade_manager.py CHANGED
@@ -1,4 +1,4 @@
1
- # trade_manager.py (V23.2 - GEM-Architect: Sniper with Order Book)
2
 
3
  import asyncio
4
  import uuid
@@ -10,20 +10,24 @@ from typing import List, Dict, Any
10
  class TradeManager:
11
  def __init__(self, r2_service, data_manager, processor):
12
  """
13
- مدير الصفقات: ينفذ الأوامر بناءً على إشارات المعالج المركزي.
 
14
  """
15
  self.r2 = r2_service
16
  self.data_manager = data_manager
17
 
18
- # 🧠 The Central Brain
19
  self.processor = processor
20
 
 
 
 
21
  self.open_positions = {}
22
  self.watchlist = {}
23
  self.sentry_tasks = {}
24
  self.running = True
25
 
26
- # [ 🧠 IQ Stats ]
27
  self.ai_stats = {
28
  "hybrid": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0},
29
  "v2": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0},
@@ -32,7 +36,7 @@ class TradeManager:
32
 
33
  self.execution_lock = asyncio.Lock()
34
 
35
- print(f"🛡️ [TradeManager V23.2] Initialized (L4 OB Enabled).")
36
 
37
  async def initialize_sentry_exchanges(self):
38
  print("🛡️ [TradeManager] Syncing state with R2...")
@@ -48,13 +52,20 @@ class TradeManager:
48
  self.open_positions = {}
49
 
50
  # ==============================================================================
51
- # 🎯 L4 Sniper Logic (Fusion: ML + Order Book)
52
  # ==============================================================================
53
  async def select_and_execute_best_signal(self, oracle_approved_signals: List[Dict[str, Any]]):
 
 
 
 
 
 
54
  if len(self.open_positions) > 0:
55
  print(f"⛔ [TradeManager] Scan aborted. Max positions reached.")
56
  return
57
 
 
58
  if not self.processor.initialized:
59
  await self.processor.initialize()
60
 
@@ -66,76 +77,78 @@ class TradeManager:
66
  if symbol in self.open_positions or symbol in self.watchlist:
67
  continue
68
 
69
- # ⚡ [Parallel Fetch] جلب الشموع + دفتر الطلبات معاً ⚡
70
  ohlcv_task = self.data_manager.get_latest_ohlcv(symbol, '1m', 600)
71
  ob_task = self.data_manager.get_order_book_snapshot(symbol)
72
 
73
- # تنفيذ الطلبين في وقت واحد للسرعة
74
  ohlcv_1m, order_book = await asyncio.gather(ohlcv_task, ob_task)
75
 
76
- # التحقق من البيانات
77
  if not ohlcv_1m or len(ohlcv_1m) < 500:
78
- print(f" -> [L4 Skip] {symbol} (Not enough candle data).")
79
  continue
80
 
81
- if not order_book or 'bids' not in order_book or not order_book['bids']:
82
- print(f" -> [L4 Skip] {symbol} (Empty Order Book).")
83
- continue
84
 
85
- # ⚡ استشارة القناص بالبيانات المدمجة
86
  sniper_result = await self.processor.check_sniper_entry(ohlcv_1m, order_book)
87
 
 
 
 
 
88
  if sniper_result['signal'] == 'BUY':
89
- confidence = sniper_result['confidence_prob']
90
- # تحليل دفتر الطلبات المرفق
91
- ob_info = sniper_result.get('ob_data', {})
92
- ob_reason = ob_info.get('reason', 'N/A')
93
-
94
- print(f" -> [L4 PASS] {symbol} (Conf: {confidence:.2f} | OB: {ob_reason})")
95
 
96
  signal['l2_sniper_result'] = sniper_result
97
  signal['sniper_confidence'] = confidence
98
  sniper_candidates.append(signal)
99
  else:
100
- print(f" -> [L4 REJECT] {symbol} ({sniper_result.get('reason')})")
 
101
 
102
  if not sniper_candidates:
103
- print(" -> [L4 Result] No candidates passed the Sniper & OrderBook check.")
104
  return
105
 
106
- # ترتيب النتائج حسب الثقة
107
  sniper_candidates.sort(key=lambda x: (x['sniper_confidence'], x['final_total_score']), reverse=True)
108
  best_signal = sniper_candidates[0]
109
 
110
  print(f" 🔥 [L4 WINNER] {best_signal['symbol']} selected for execution.")
111
 
112
- # التنفيذ
113
  async with self.execution_lock:
114
  if len(self.open_positions) == 0 and best_signal['symbol'] not in self.open_positions:
115
  await self._execute_entry_from_signal(best_signal['symbol'], best_signal)
116
 
117
  # ==============================================================================
118
- # 🎯 Entry Execution
119
  # ==============================================================================
120
  async def _execute_entry_from_signal(self, symbol, signal_data):
121
  try:
122
  trade_id = str(uuid.uuid4())
123
  current_price = float(signal_data.get('current_price', 0.0))
 
 
124
  if current_price <= 0.0:
125
  current_price = await self.data_manager.get_latest_price_async(symbol)
126
 
 
127
  l2_result = signal_data.get('l2_sniper_result', {})
128
 
129
- # الأهداف (من Oracle)
130
  tp_price = float(signal_data.get('tp_price') or 0.0)
131
  sl_price = float(signal_data.get('sl_price') or 0.0)
132
 
133
- # حماية ضد القيم الصفرية
134
  if tp_price <= 0 or sl_price <= 0:
135
  atr_mock = current_price * 0.02
136
  tp_price = current_price + (atr_mock * 2.0)
137
  sl_price = current_price - (atr_mock * 1.0)
138
 
 
139
  new_trade = {
140
  'id': trade_id,
141
  'symbol': symbol,
@@ -146,23 +159,30 @@ class TradeManager:
146
  'sl_price': sl_price,
147
  'last_update': datetime.now().isoformat(),
148
 
 
 
149
  'l1_score': float(signal_data.get('final_total_score', 0.0)),
150
  'l2_sniper_confidence': float(l2_result.get('confidence_prob', 0.0)),
151
- 'l3_oracle_tp': tp_price,
152
- 'l3_oracle_sl': sl_price,
 
 
 
 
153
  }
154
 
 
155
  self.open_positions[symbol] = new_trade
156
  if self.watchlist: self.watchlist.clear()
157
 
158
- # حفظ في R2
159
  portfolio = await self.r2.get_portfolio_state_async()
160
  if portfolio.get('first_trade_timestamp') is None:
161
  portfolio['first_trade_timestamp'] = new_trade['entry_time']
162
  await self.r2.save_portfolio_state_async(portfolio)
163
  await self.r2.save_open_trades_async(list(self.open_positions.values()))
164
 
165
- # تفعيل الحارس
166
  if symbol in self.sentry_tasks: self.sentry_tasks[symbol].cancel()
167
  self.sentry_tasks[symbol] = asyncio.create_task(self._guardian_loop(symbol))
168
 
@@ -173,7 +193,7 @@ class TradeManager:
173
  traceback.print_exc()
174
 
175
  # ==============================================================================
176
- # 🛡️ Hybrid Sentry (Via Processor)
177
  # ==============================================================================
178
  async def _guardian_loop(self, symbol: str):
179
  print(f"🛡️ [Sentry] Guarding {symbol}...")
@@ -181,7 +201,7 @@ class TradeManager:
181
 
182
  while self.running and symbol in self.open_positions:
183
  try:
184
- await asyncio.sleep(2) # فحص سريع للأسعار
185
 
186
  trade = self.open_positions.get(symbol)
187
  if not trade: break
@@ -189,7 +209,7 @@ class TradeManager:
189
  current_price = await self.data_manager.get_latest_price_async(symbol)
190
  if current_price == 0.0: continue
191
 
192
- # 1. Hard Limits Check
193
  if current_price >= trade['tp_price']:
194
  async with self.execution_lock:
195
  await self._execute_exit(symbol, current_price, "TP_HIT_HARD")
@@ -199,8 +219,9 @@ class TradeManager:
199
  await self._execute_exit(symbol, current_price, "SL_HIT_HARD")
200
  break
201
 
202
- # 2. AI Check (Every 60s)
203
  if time.time() - last_ai_check_time > 60:
 
204
  t1 = self.data_manager.get_latest_ohlcv(symbol, '1m', 300)
205
  t5 = self.data_manager.get_latest_ohlcv(symbol, '5m', 200)
206
  t15 = self.data_manager.get_latest_ohlcv(symbol, '15m', 100)
@@ -208,7 +229,7 @@ class TradeManager:
208
  d1, d5, d15 = await asyncio.gather(t1, t5, t15)
209
 
210
  if d1 and d5 and d15 and len(d1) >= 200:
211
- # استشارة المعالج
212
  decision = self.processor.consult_guardian(d1, d5, d15, trade['entry_price'])
213
 
214
  action = decision.get('action', 'HOLD')
@@ -230,10 +251,11 @@ class TradeManager:
230
  await asyncio.sleep(5)
231
 
232
  # ==============================================================================
233
- # 👻 Ghost Monitor & Stats
234
  # ==============================================================================
235
- def _launch_post_exit_analysis(self, symbol, exit_price, exit_time, position_size_usd, ai_scores=None):
236
- asyncio.create_task(self._analyze_after_exit_task(symbol, exit_price, exit_time, position_size_usd, ai_scores))
 
237
 
238
  def _update_specific_stat(self, key, is_good, usd_impact):
239
  if key not in self.ai_stats: return
@@ -244,33 +266,49 @@ class TradeManager:
244
  else:
245
  self.ai_stats[key]["missed"] += abs(usd_impact)
246
 
247
- async def _analyze_after_exit_task(self, symbol, exit_price, exit_time, position_size_usd, ai_scores):
248
- await asyncio.sleep(900) # 15 min wait
 
 
 
 
 
249
  try:
250
  curr = await self.data_manager.get_latest_price_async(symbol)
251
  if curr == 0: return
252
 
 
 
 
253
  change_pct = (curr - exit_price) / exit_price
254
  usd_impact = change_pct * position_size_usd
255
  is_good_exit = change_pct < 0
256
 
 
257
  self._update_specific_stat("hybrid", is_good_exit, usd_impact)
258
-
259
  if ai_scores:
260
  if ai_scores.get('v2', 0) >= 0.60: self._update_specific_stat("v2", is_good_exit, usd_impact)
261
  if ai_scores.get('v3', 0) >= 0.75: self._update_specific_stat("v3", is_good_exit, usd_impact)
262
 
 
263
  record = {
264
  "symbol": symbol, "exit_price": exit_price, "price_15m": curr,
265
  "usd_impact": usd_impact, "verdict": "SUCCESS" if is_good_exit else "MISS"
266
  }
267
  await self.r2.append_deep_steward_audit(record)
268
 
 
 
 
 
 
 
 
269
  except Exception as e:
270
- print(f"⚠️ [Ghost Error] {e}")
271
 
272
  # ==============================================================================
273
- # 🔴 Exit & Force Exit
274
  # ==============================================================================
275
  async def _execute_exit(self, symbol, price, reason, ai_scores=None):
276
  if symbol not in self.open_positions: return
@@ -283,6 +321,7 @@ class TradeManager:
283
 
284
  trade.update({'status': 'CLOSED', 'exit_price': exit_p, 'exit_reason': reason, 'profit_pct': profit_pct*100})
285
 
 
286
  portfolio = await self.r2.get_portfolio_state_async()
287
  curr_cap = float(portfolio.get('current_capital_usd', 100.0))
288
  pnl_usd = curr_cap * profit_pct
@@ -299,14 +338,15 @@ class TradeManager:
299
  portfolio['total_loss_usd'] = portfolio.get('total_loss_usd', 0) + abs(pnl_usd)
300
  trade['result'] = 'LOSS'
301
 
 
302
  await self.r2.save_portfolio_state_async(portfolio)
303
  await self.r2.save_open_trades_async(list(self.open_positions.values()))
304
  await self.r2.append_to_closed_trades_history(trade)
305
 
306
  print(f"✅ [EXIT] {symbol} | PnL: {trade['profit_pct']:.2f}% | Cap: ${new_cap:.2f}")
307
 
308
- if "AI_" in reason:
309
- self._launch_post_exit_analysis(symbol, exit_p, trade.get('exit_time'), curr_cap, ai_scores)
310
 
311
  if symbol in self.sentry_tasks:
312
  self.sentry_tasks[symbol].cancel()
@@ -314,18 +354,21 @@ class TradeManager:
314
 
315
  except Exception as e:
316
  print(f"❌ [Exit Error] {e}")
317
- self.open_positions[symbol] = trade # استعادة الصفقة في الذاكرة عند الخطأ
318
 
319
  async def force_exit_by_manager(self, symbol, reason):
 
320
  p = await self.data_manager.get_latest_price_async(symbol)
321
  async with self.execution_lock:
322
  await self._execute_exit(symbol, p, reason)
323
 
324
  async def start_sentry_loops(self):
 
325
  for symbol in list(self.open_positions.keys()):
326
  if symbol not in self.sentry_tasks or self.sentry_tasks[symbol].done():
327
  self.sentry_tasks[symbol] = asyncio.create_task(self._guardian_loop(symbol))
328
 
329
  async def stop_sentry_loops(self):
 
330
  self.running = False
331
  for task in self.sentry_tasks.values(): task.cancel()
 
1
+ # trade_manager.py (V23.8 - GEM-Architect: Full Integration with Sniper 60/40 & Learning Hub)
2
 
3
  import asyncio
4
  import uuid
 
10
  class TradeManager:
11
  def __init__(self, r2_service, data_manager, processor):
12
  """
13
+ مدير الصفقات: المسؤول التنفيذي.
14
+ ينسق بين البيانات (Data)، العقل (Processor)، ومركز التعلم (Learning Hub).
15
  """
16
  self.r2 = r2_service
17
  self.data_manager = data_manager
18
 
19
+ # 🧠 The Central Brain (Titan, Oracle, Sniper, Guardian)
20
  self.processor = processor
21
 
22
+ # 🎓 Learning Hub (سيتم حقنه من app.py)
23
+ self.learning_hub = None
24
+
25
  self.open_positions = {}
26
  self.watchlist = {}
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},
 
36
 
37
  self.execution_lock = asyncio.Lock()
38
 
39
+ print(f"🛡️ [TradeManager V23.8] Initialized (Sniper 60/40 & Learning Ready).")
40
 
41
  async def initialize_sentry_exchanges(self):
42
  print("🛡️ [TradeManager] Syncing state with R2...")
 
52
  self.open_positions = {}
53
 
54
  # ==============================================================================
55
+ # 🎯 L4 Sniper Logic (Fusion: ML + Order Book 60/40)
56
  # ==============================================================================
57
  async def select_and_execute_best_signal(self, oracle_approved_signals: List[Dict[str, Any]]):
58
+ """
59
+ مرحلة الفرز النهائي (القناص):
60
+ - تجلب شموع الدقيقة + دفتر الطلبات.
61
+ - ترسلها للمعالج ليطبق معادلة (0.6*ML + 0.4*OB).
62
+ - تختار الأفضل وتنفذه.
63
+ """
64
  if len(self.open_positions) > 0:
65
  print(f"⛔ [TradeManager] Scan aborted. Max positions reached.")
66
  return
67
 
68
+ # التأكد من جاهزية المعالج
69
  if not self.processor.initialized:
70
  await self.processor.initialize()
71
 
 
77
  if symbol in self.open_positions or symbol in self.watchlist:
78
  continue
79
 
80
+ # ⚡ [Parallel Fetch] جلب الشموع + دفتر الطلبات معاً للسرعة
81
  ohlcv_task = self.data_manager.get_latest_ohlcv(symbol, '1m', 600)
82
  ob_task = self.data_manager.get_order_book_snapshot(symbol)
83
 
 
84
  ohlcv_1m, order_book = await asyncio.gather(ohlcv_task, ob_task)
85
 
86
+ # التحقق من سلامة البيانات
87
  if not ohlcv_1m or len(ohlcv_1m) < 500:
88
+ print(f" -> [L4 Skip] {symbol} (Not enough 1m candles).")
89
  continue
90
 
91
+ # دفتر الطلبات فارغ؟ (نكمل ولكن الدرجة ستتأثر)
92
+ if not order_book:
93
+ print(f" ⚠️ [L4 Warn] {symbol} (Empty Order Book). Proceeding with ML only...")
94
 
95
+ # ⚡ استشارة القناص (Processor -> SniperEngine)
96
  sniper_result = await self.processor.check_sniper_entry(ohlcv_1m, order_book)
97
 
98
+ # استخراج بيانات التقرير
99
+ reason_str = sniper_result.get('reason', 'N/A')
100
+ confidence = sniper_result.get('confidence_prob', 0.0) # هذه هي الدرجة الموزونة النهائية
101
+
102
  if sniper_result['signal'] == 'BUY':
103
+ print(f" -> 🟢 [L4 PASS] {symbol} | Score: {confidence:.2f} | {reason_str}")
 
 
 
 
 
104
 
105
  signal['l2_sniper_result'] = sniper_result
106
  signal['sniper_confidence'] = confidence
107
  sniper_candidates.append(signal)
108
  else:
109
+ # طباعة سبب الرفض (سيوضح ما إذا كان السبب هو النموذج أو الدفتر)
110
+ print(f" -> 🔴 [L4 REJECT] {symbol} | {reason_str}")
111
 
112
  if not sniper_candidates:
113
+ print(" -> [L4 Result] No candidates passed the Sniper (60/40) check.")
114
  return
115
 
116
+ # ترتيب النتائج حسب الدرجة النهائية (الأعلى ثقة)
117
  sniper_candidates.sort(key=lambda x: (x['sniper_confidence'], x['final_total_score']), reverse=True)
118
  best_signal = sniper_candidates[0]
119
 
120
  print(f" 🔥 [L4 WINNER] {best_signal['symbol']} selected for execution.")
121
 
122
+ # التنفيذ الفعلي
123
  async with self.execution_lock:
124
  if len(self.open_positions) == 0 and best_signal['symbol'] not in self.open_positions:
125
  await self._execute_entry_from_signal(best_signal['symbol'], best_signal)
126
 
127
  # ==============================================================================
128
+ # 🎯 Entry Execution (تنفيذ الدخول)
129
  # ==============================================================================
130
  async def _execute_entry_from_signal(self, symbol, signal_data):
131
  try:
132
  trade_id = str(uuid.uuid4())
133
  current_price = float(signal_data.get('current_price', 0.0))
134
+
135
+ # حماية: إذا السعر 0، اطلبه مرة أخرى
136
  if current_price <= 0.0:
137
  current_price = await self.data_manager.get_latest_price_async(symbol)
138
 
139
+ # بيانات القناص (للتخزين)
140
  l2_result = signal_data.get('l2_sniper_result', {})
141
 
142
+ # الأهداف (من Oracle أو حساب تلقائي)
143
  tp_price = float(signal_data.get('tp_price') or 0.0)
144
  sl_price = float(signal_data.get('sl_price') or 0.0)
145
 
 
146
  if tp_price <= 0 or sl_price <= 0:
147
  atr_mock = current_price * 0.02
148
  tp_price = current_price + (atr_mock * 2.0)
149
  sl_price = current_price - (atr_mock * 1.0)
150
 
151
+ # بناء كائن الصفقة
152
  new_trade = {
153
  'id': trade_id,
154
  'symbol': symbol,
 
159
  'sl_price': sl_price,
160
  'last_update': datetime.now().isoformat(),
161
 
162
+ # تخزين البيانات الوصفية للتعلم لاحقاً
163
+ 'strategy': 'Hybrid_Titan_V1',
164
  'l1_score': float(signal_data.get('final_total_score', 0.0)),
165
  'l2_sniper_confidence': float(l2_result.get('confidence_prob', 0.0)),
166
+ 'decision_data': { # بيانات مفصلة لمركز التعلم
167
+ 'components': signal_data.get('components', {}),
168
+ 'sniper_analysis': l2_result,
169
+ 'oracle_tp': tp_price,
170
+ 'oracle_sl': sl_price
171
+ }
172
  }
173
 
174
+ # الحفظ والتحديث
175
  self.open_positions[symbol] = new_trade
176
  if self.watchlist: self.watchlist.clear()
177
 
178
+ # R2 Update
179
  portfolio = await self.r2.get_portfolio_state_async()
180
  if portfolio.get('first_trade_timestamp') is None:
181
  portfolio['first_trade_timestamp'] = new_trade['entry_time']
182
  await self.r2.save_portfolio_state_async(portfolio)
183
  await self.r2.save_open_trades_async(list(self.open_positions.values()))
184
 
185
+ # تشغيل الحارس
186
  if symbol in self.sentry_tasks: self.sentry_tasks[symbol].cancel()
187
  self.sentry_tasks[symbol] = asyncio.create_task(self._guardian_loop(symbol))
188
 
 
193
  traceback.print_exc()
194
 
195
  # ==============================================================================
196
+ # 🛡️ Hybrid Sentry (حارس الخروج)
197
  # ==============================================================================
198
  async def _guardian_loop(self, symbol: str):
199
  print(f"🛡️ [Sentry] Guarding {symbol}...")
 
201
 
202
  while self.running and symbol in self.open_positions:
203
  try:
204
+ await asyncio.sleep(2) # فحص سريع كل ثانيتين
205
 
206
  trade = self.open_positions.get(symbol)
207
  if not trade: break
 
209
  current_price = await self.data_manager.get_latest_price_async(symbol)
210
  if current_price == 0.0: continue
211
 
212
+ # 1. الفحص الصارم للأهداف (Hard Limits) - الأولوية القصوى
213
  if current_price >= trade['tp_price']:
214
  async with self.execution_lock:
215
  await self._execute_exit(symbol, current_price, "TP_HIT_HARD")
 
219
  await self._execute_exit(symbol, current_price, "SL_HIT_HARD")
220
  break
221
 
222
+ # 2. فحص الذكاء الاصطناعي (كل 60 ثانية)
223
  if time.time() - last_ai_check_time > 60:
224
+ # جلب بيانات متعددة الأطر للحارس
225
  t1 = self.data_manager.get_latest_ohlcv(symbol, '1m', 300)
226
  t5 = self.data_manager.get_latest_ohlcv(symbol, '5m', 200)
227
  t15 = self.data_manager.get_latest_ohlcv(symbol, '15m', 100)
 
229
  d1, d5, d15 = await asyncio.gather(t1, t5, t15)
230
 
231
  if d1 and d5 and d15 and len(d1) >= 200:
232
+ # استشارة المعالج (الذي يستشير الحارس)
233
  decision = self.processor.consult_guardian(d1, d5, d15, trade['entry_price'])
234
 
235
  action = decision.get('action', 'HOLD')
 
251
  await asyncio.sleep(5)
252
 
253
  # ==============================================================================
254
+ # 👻 Ghost Monitor & Learning Stats
255
  # ==============================================================================
256
+ def _launch_post_exit_analysis(self, symbol, exit_price, exit_time, position_size_usd, ai_scores=None, trade_obj=None):
257
+ # تشغيل مهمة الخلفية للتعلم
258
+ asyncio.create_task(self._analyze_after_exit_task(symbol, exit_price, exit_time, position_size_usd, ai_scores, trade_obj))
259
 
260
  def _update_specific_stat(self, key, is_good, usd_impact):
261
  if key not in self.ai_stats: return
 
266
  else:
267
  self.ai_stats[key]["missed"] += abs(usd_impact)
268
 
269
+ async def _analyze_after_exit_task(self, symbol, exit_price, exit_time, position_size_usd, ai_scores, trade_obj):
270
+ """
271
+ تحليل ما بعد الخروج:
272
+ 1. يراقب السعر بعد 15 دقيقة (Ghost Monitor).
273
+ 2. يرسل الصفقة لمركز التعلم (Learning Hub).
274
+ """
275
+ await asyncio.sleep(900) # انتظار 15 دقيقة
276
  try:
277
  curr = await self.data_manager.get_latest_price_async(symbol)
278
  if curr == 0: return
279
 
280
+ # هل كان الخروج جيداً؟
281
+ # إذا بعنا والسعر نزل -> جيد (وفرنا خسارة)
282
+ # إذا بعنا والسعر صعد -> سيء (فوتنا ربح)
283
  change_pct = (curr - exit_price) / exit_price
284
  usd_impact = change_pct * position_size_usd
285
  is_good_exit = change_pct < 0
286
 
287
+ # تحديث إحصائيات الواجهة
288
  self._update_specific_stat("hybrid", is_good_exit, usd_impact)
 
289
  if ai_scores:
290
  if ai_scores.get('v2', 0) >= 0.60: self._update_specific_stat("v2", is_good_exit, usd_impact)
291
  if ai_scores.get('v3', 0) >= 0.75: self._update_specific_stat("v3", is_good_exit, usd_impact)
292
 
293
+ # تسجيل التدقيق في R2
294
  record = {
295
  "symbol": symbol, "exit_price": exit_price, "price_15m": curr,
296
  "usd_impact": usd_impact, "verdict": "SUCCESS" if is_good_exit else "MISS"
297
  }
298
  await self.r2.append_deep_steward_audit(record)
299
 
300
+ # 🎓 إرسال البيانات لمركز التعلم (The Self-Evolving Step) 🎓
301
+ if self.learning_hub and trade_obj:
302
+ # نحدث كائن الصفقة بالنتيجة النهائية
303
+ trade_obj['pnl_percent'] = trade_obj.get('profit_pct', 0.0)
304
+ # نرسلها للمدير التعليمي ليوزعها (أوزان + تدريب)
305
+ await self.learning_hub.analyze_trade_and_learn(trade_obj, trade_obj.get('exit_reason', 'UNKNOWN'))
306
+
307
  except Exception as e:
308
+ print(f"⚠️ [Ghost/Learning Error] {e}")
309
 
310
  # ==============================================================================
311
+ # 🔴 Exit Logic (تنفيذ الخروج)
312
  # ==============================================================================
313
  async def _execute_exit(self, symbol, price, reason, ai_scores=None):
314
  if symbol not in self.open_positions: return
 
321
 
322
  trade.update({'status': 'CLOSED', 'exit_price': exit_p, 'exit_reason': reason, 'profit_pct': profit_pct*100})
323
 
324
+ # المحاسبة
325
  portfolio = await self.r2.get_portfolio_state_async()
326
  curr_cap = float(portfolio.get('current_capital_usd', 100.0))
327
  pnl_usd = curr_cap * profit_pct
 
338
  portfolio['total_loss_usd'] = portfolio.get('total_loss_usd', 0) + abs(pnl_usd)
339
  trade['result'] = 'LOSS'
340
 
341
+ # الحفظ
342
  await self.r2.save_portfolio_state_async(portfolio)
343
  await self.r2.save_open_trades_async(list(self.open_positions.values()))
344
  await self.r2.append_to_closed_trades_history(trade)
345
 
346
  print(f"✅ [EXIT] {symbol} | PnL: {trade['profit_pct']:.2f}% | Cap: ${new_cap:.2f}")
347
 
348
+ # إطلاق التحليل اللاحق (Learning & Ghost)
349
+ self._launch_post_exit_analysis(symbol, exit_p, trade.get('exit_time'), curr_cap, ai_scores, trade)
350
 
351
  if symbol in self.sentry_tasks:
352
  self.sentry_tasks[symbol].cancel()
 
354
 
355
  except Exception as e:
356
  print(f"❌ [Exit Error] {e}")
357
+ self.open_positions[symbol] = trade # استعادة الصفقة في حال فشل الخروج
358
 
359
  async def force_exit_by_manager(self, symbol, reason):
360
+ """الخروج اليدوي القسري"""
361
  p = await self.data_manager.get_latest_price_async(symbol)
362
  async with self.execution_lock:
363
  await self._execute_exit(symbol, p, reason)
364
 
365
  async def start_sentry_loops(self):
366
+ """إعادة تشغيل المراقبة للصفقات الموجودة عند بدء النظام"""
367
  for symbol in list(self.open_positions.keys()):
368
  if symbol not in self.sentry_tasks or self.sentry_tasks[symbol].done():
369
  self.sentry_tasks[symbol] = asyncio.create_task(self._guardian_loop(symbol))
370
 
371
  async def stop_sentry_loops(self):
372
+ """إيقاف جميع الحراس عند الإغلاق"""
373
  self.running = False
374
  for task in self.sentry_tasks.values(): task.cancel()