Riy777 commited on
Commit
82d46a6
·
verified ·
1 Parent(s): e756af2

Update trade_manager.py

Browse files
Files changed (1) hide show
  1. trade_manager.py +125 -44
trade_manager.py CHANGED
@@ -1,4 +1,4 @@
1
- # trade_manager.py (V23.10 - GEM-Architect: Data Depth Fix)
2
 
3
  import asyncio
4
  import uuid
@@ -19,6 +19,9 @@ class TradeManager:
19
  self.sentry_tasks = {}
20
  self.running = True
21
 
 
 
 
22
  self.ai_stats = {
23
  "hybrid": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0},
24
  "v2": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0},
@@ -26,7 +29,7 @@ class TradeManager:
26
  }
27
 
28
  self.execution_lock = asyncio.Lock()
29
- print(f"🛡️ [TradeManager V23.10] Initialized (Data Depth Boosted).")
30
 
31
  async def initialize_sentry_exchanges(self):
32
  print("🛡️ [TradeManager] Syncing state with R2...")
@@ -60,7 +63,7 @@ class TradeManager:
60
  if symbol in self.open_positions or symbol in self.watchlist:
61
  continue
62
 
63
- # [FIX] زيادة البيانات هنا أيضاً للقناص
64
  ohlcv_task = self.data_manager.get_latest_ohlcv(symbol, '1m', 1000)
65
  ob_task = self.data_manager.get_order_book_snapshot(symbol)
66
  ohlcv_1m, order_book = await asyncio.gather(ohlcv_task, ob_task)
@@ -99,15 +102,25 @@ class TradeManager:
99
  await self._execute_entry_from_signal(best_signal['symbol'], best_signal)
100
 
101
  # ==============================================================================
102
- # 🎯 Entry Execution
103
  # ==============================================================================
104
  async def _execute_entry_from_signal(self, symbol, signal_data):
105
  try:
106
  trade_id = str(uuid.uuid4())
 
 
107
  current_price = float(signal_data.get('current_price', 0.0))
108
  if current_price <= 0.0:
109
  current_price = await self.data_manager.get_latest_price_async(symbol)
110
 
 
 
 
 
 
 
 
 
111
  l2_result = signal_data.get('l2_sniper_result', {})
112
  tp_price = float(signal_data.get('tp_price') or 0.0)
113
  sl_price = float(signal_data.get('sl_price') or 0.0)
@@ -129,6 +142,9 @@ class TradeManager:
129
  'strategy': 'Hybrid_Titan_V1',
130
  'l1_score': float(signal_data.get('enhanced_final_score', 0.0)),
131
  'l2_sniper_confidence': float(l2_result.get('confidence_prob', 0.0)),
 
 
 
132
  'decision_data': {
133
  'components': signal_data.get('components', {}),
134
  'sniper_analysis': l2_result,
@@ -140,73 +156,101 @@ class TradeManager:
140
  self.open_positions[symbol] = new_trade
141
  if self.watchlist: self.watchlist.clear()
142
 
143
- portfolio = await self.r2.get_portfolio_state_async()
144
  if portfolio.get('first_trade_timestamp') is None:
145
  portfolio['first_trade_timestamp'] = new_trade['entry_time']
146
  await self.r2.save_portfolio_state_async(portfolio)
 
147
  await self.r2.save_open_trades_async(list(self.open_positions.values()))
148
 
149
  if symbol in self.sentry_tasks: self.sentry_tasks[symbol].cancel()
150
  self.sentry_tasks[symbol] = asyncio.create_task(self._guardian_loop(symbol))
151
 
152
- print(f"✅ [ENTRY] {symbol} @ {current_price} | TP: {tp_price} | SL: {sl_price}")
153
 
154
  except Exception as e:
155
  print(f"❌ [Entry Error] {symbol}: {e}")
156
  traceback.print_exc()
157
 
158
  # ==============================================================================
159
- # 🛡️ Hybrid Sentry (FIXED SECTION)
160
  # ==============================================================================
161
  async def _guardian_loop(self, symbol: str):
162
- print(f"🛡️ [Sentry] Guarding {symbol}...")
163
  last_ai_check_time = 0
164
 
165
  while self.running and symbol in self.open_positions:
166
  try:
167
- await asyncio.sleep(2)
 
 
168
  trade = self.open_positions.get(symbol)
169
  if not trade: break
170
 
171
- current_price = await self.data_manager.get_latest_price_async(symbol)
172
- if current_price == 0.0: continue
173
-
174
- # Hard TP/SL Check
175
- if current_price >= trade['tp_price']:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  async with self.execution_lock:
177
- await self._execute_exit(symbol, current_price, "TP_HIT_HARD")
 
178
  break
179
- if current_price <= trade['sl_price']:
 
 
 
180
  async with self.execution_lock:
181
- await self._execute_exit(symbol, current_price, "SL_HIT_HARD")
 
182
  break
183
 
184
- # AI Guardian Check
 
 
185
  if time.time() - last_ai_check_time > 60:
186
- # [CRITICAL FIX] زيادة عدد الشموع لتجاوز EMA200 Warm-up
187
- t1 = self.data_manager.get_latest_ohlcv(symbol, '1m', 1000) # كان 300
188
- t5 = self.data_manager.get_latest_ohlcv(symbol, '5m', 500) # كان 200
189
- t15 = self.data_manager.get_latest_ohlcv(symbol, '15m', 500) # كان 100
190
  d1, d5, d15 = await asyncio.gather(t1, t5, t15)
191
 
192
- # التحقق من أن البيانات كافية بعد الحسابات
193
  if d1 and d5 and d15 and len(d1) >= 500:
194
  decision = self.processor.consult_guardian(d1, d5, d15, trade['entry_price'])
195
  action = decision.get('action', 'HOLD')
196
  scores = decision.get('scores', {})
197
 
198
- # طباعة التشخيص (اختياري لملف التداول)
199
- # v2_val = int(scores.get('v2', 0.0) * 100)
200
- # v3_val = int(scores.get('v3', 0.0) * 100)
201
- # print(f" 🕵️ [Sentry check] {symbol} | V2:{v2_val} V3:{v3_val} | {action}")
202
-
203
  if action in ['EXIT_HARD', 'EXIT_SOFT']:
204
  print(f"🤖 [Guardian] {action}: {decision.get('reason')}")
205
  async with self.execution_lock:
206
- await self._execute_exit(symbol, current_price, f"AI_{action}", ai_scores=scores)
 
207
  break
208
  last_ai_check_time = time.time()
 
 
209
  self.open_positions[symbol]['last_update'] = datetime.now().isoformat()
 
210
  except asyncio.CancelledError: break
211
  except Exception as e:
212
  print(f"❌ [Sentry Error] {symbol}: {e}")
@@ -253,41 +297,75 @@ class TradeManager:
253
  print(f"⚠️ [Ghost/Learning Error] {e}")
254
 
255
  # ==============================================================================
256
- # 🔴 Exit Logic
257
  # ==============================================================================
258
  async def _execute_exit(self, symbol, price, reason, ai_scores=None):
259
  if symbol not in self.open_positions: return
260
  try:
261
  trade = self.open_positions.pop(symbol)
262
- entry = float(trade['entry_price'])
263
- exit_p = float(price)
264
- profit_pct = ((exit_p - entry) / entry)
265
 
266
- trade.update({'status': 'CLOSED', 'exit_price': exit_p, 'exit_reason': reason, 'profit_pct': profit_pct*100})
267
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  portfolio = await self.r2.get_portfolio_state_async()
269
- curr_cap = float(portfolio.get('current_capital_usd', 100.0))
270
- pnl_usd = curr_cap * profit_pct
271
- new_cap = curr_cap + pnl_usd
 
272
 
273
  portfolio['current_capital_usd'] = new_cap
274
  portfolio['total_trades'] = portfolio.get('total_trades', 0) + 1
275
- if pnl_usd >= 0:
 
276
  portfolio['winning_trades'] = portfolio.get('winning_trades', 0) + 1
277
- portfolio['total_profit_usd'] = portfolio.get('total_profit_usd', 0) + pnl_usd
278
  trade['result'] = 'WIN'
279
  else:
280
  portfolio['losing_trades'] = portfolio.get('losing_trades', 0) + 1
281
- portfolio['total_loss_usd'] = portfolio.get('total_loss_usd', 0) + abs(pnl_usd)
282
  trade['result'] = 'LOSS'
283
 
284
  await self.r2.save_portfolio_state_async(portfolio)
285
  await self.r2.save_open_trades_async(list(self.open_positions.values()))
286
  await self.r2.append_to_closed_trades_history(trade)
287
 
288
- print(f"✅ [EXIT] {symbol} | PnL: {trade['profit_pct']:.2f}% | Cap: ${new_cap:.2f}")
289
 
290
- self._launch_post_exit_analysis(symbol, exit_p, trade.get('exit_time'), curr_cap, ai_scores, trade)
291
 
292
  if symbol in self.sentry_tasks:
293
  self.sentry_tasks[symbol].cancel()
@@ -295,7 +373,10 @@ class TradeManager:
295
 
296
  except Exception as e:
297
  print(f"❌ [Exit Error] {e}")
298
- self.open_positions[symbol] = trade
 
 
 
299
 
300
  async def force_exit_by_manager(self, symbol, reason):
301
  p = await self.data_manager.get_latest_price_async(symbol)
 
1
+ # trade_manager.py (V24.0 - GEM-Architect: Realistic Sim & Fees)
2
 
3
  import asyncio
4
  import uuid
 
19
  self.sentry_tasks = {}
20
  self.running = True
21
 
22
+ # إعدادات الرسوم (0.1% لكل عملية)
23
+ self.FEE_RATE = 0.001
24
+
25
  self.ai_stats = {
26
  "hybrid": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0},
27
  "v2": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0},
 
29
  }
30
 
31
  self.execution_lock = asyncio.Lock()
32
+ print(f"🛡️ [TradeManager V24.0] Initialized (High/Low Sim + Fees Enabled).")
33
 
34
  async def initialize_sentry_exchanges(self):
35
  print("🛡️ [TradeManager] Syncing state with R2...")
 
63
  if symbol in self.open_positions or symbol in self.watchlist:
64
  continue
65
 
66
+ # التحقق من القناص مع بيانات كافية
67
  ohlcv_task = self.data_manager.get_latest_ohlcv(symbol, '1m', 1000)
68
  ob_task = self.data_manager.get_order_book_snapshot(symbol)
69
  ohlcv_1m, order_book = await asyncio.gather(ohlcv_task, ob_task)
 
102
  await self._execute_entry_from_signal(best_signal['symbol'], best_signal)
103
 
104
  # ==============================================================================
105
+ # 🎯 Entry Execution (With Fee Calculation)
106
  # ==============================================================================
107
  async def _execute_entry_from_signal(self, symbol, signal_data):
108
  try:
109
  trade_id = str(uuid.uuid4())
110
+
111
+ # تحديد سعر الدخول
112
  current_price = float(signal_data.get('current_price', 0.0))
113
  if current_price <= 0.0:
114
  current_price = await self.data_manager.get_latest_price_async(symbol)
115
 
116
+ # جلب حجم المحفظة لحساب الرسوم الأولية
117
+ portfolio = await self.r2.get_portfolio_state_async()
118
+ current_capital = float(portfolio.get('current_capital_usd', 100.0))
119
+
120
+ # حساب الرسوم المتوقعة للدخول
121
+ # ملاحظة: نفترض أننا ندخل بكامل رأس المال المتاح في المحاكاة الحالية
122
+ entry_fee_usd = current_capital * self.FEE_RATE
123
+
124
  l2_result = signal_data.get('l2_sniper_result', {})
125
  tp_price = float(signal_data.get('tp_price') or 0.0)
126
  sl_price = float(signal_data.get('sl_price') or 0.0)
 
142
  'strategy': 'Hybrid_Titan_V1',
143
  'l1_score': float(signal_data.get('enhanced_final_score', 0.0)),
144
  'l2_sniper_confidence': float(l2_result.get('confidence_prob', 0.0)),
145
+ # تخزين بيانات الرسوم ورأس المال عند الدخول
146
+ 'entry_capital': current_capital,
147
+ 'entry_fee_usd': entry_fee_usd,
148
  'decision_data': {
149
  'components': signal_data.get('components', {}),
150
  'sniper_analysis': l2_result,
 
156
  self.open_positions[symbol] = new_trade
157
  if self.watchlist: self.watchlist.clear()
158
 
 
159
  if portfolio.get('first_trade_timestamp') is None:
160
  portfolio['first_trade_timestamp'] = new_trade['entry_time']
161
  await self.r2.save_portfolio_state_async(portfolio)
162
+
163
  await self.r2.save_open_trades_async(list(self.open_positions.values()))
164
 
165
  if symbol in self.sentry_tasks: self.sentry_tasks[symbol].cancel()
166
  self.sentry_tasks[symbol] = asyncio.create_task(self._guardian_loop(symbol))
167
 
168
+ print(f"✅ [ENTRY] {symbol} @ {current_price} | Cap: ${current_capital:.2f} | Fee: ${entry_fee_usd:.3f}")
169
 
170
  except Exception as e:
171
  print(f"❌ [Entry Error] {symbol}: {e}")
172
  traceback.print_exc()
173
 
174
  # ==============================================================================
175
+ # 🛡️ Hybrid Sentry (Updated: High/Low Candle Check)
176
  # ==============================================================================
177
  async def _guardian_loop(self, symbol: str):
178
+ print(f"🛡️ [Sentry] Guarding {symbol} (High/Low Simulation Mode)...")
179
  last_ai_check_time = 0
180
 
181
  while self.running and symbol in self.open_positions:
182
  try:
183
+ # انتظار قصير للتحديث
184
+ await asyncio.sleep(1)
185
+
186
  trade = self.open_positions.get(symbol)
187
  if not trade: break
188
 
189
+ # -------------------------------------------------------------
190
+ # 1. جلب بيانات الشموع للتحقق من الذيول (Wicks)
191
+ # -------------------------------------------------------------
192
+ # نجلب آخر شمعتين لضمان تغطية الشمعة الحالية والمكتملة حديثاً
193
+ ohlcv = await self.data_manager.get_latest_ohlcv(symbol, '1m', limit=2)
194
+ current_ticker_price = await self.data_manager.get_latest_price_async(symbol)
195
+
196
+ if not ohlcv:
197
+ continue
198
+
199
+ # الشمعة الأخيرة (الحالية)
200
+ current_candle = ohlcv[-1]
201
+ # [timestamp, open, high, low, close, volume]
202
+ candle_high = current_candle[2]
203
+ candle_low = current_candle[3]
204
+
205
+ # استخدام السعر الأعلى بين التيكر والشمعة لضمان عدم تفويت أي قمة لحظية
206
+ max_price_seen = max(current_ticker_price, candle_high)
207
+ min_price_seen = min(current_ticker_price, candle_low)
208
+
209
+ # -------------------------------------------------------------
210
+ # 2. التحقق من الأهداف (Simulated Limit Orders)
211
+ # -------------------------------------------------------------
212
+
213
+ # أ) تحقق من الهدف (Take Profit)
214
+ if max_price_seen >= trade['tp_price']:
215
+ print(f"🎯 [TP HIT] Price {max_price_seen} touched Target {trade['tp_price']}")
216
  async with self.execution_lock:
217
+ # الخروج بسعر الهدف تماماً كما يفعل الأمر المعلق
218
+ await self._execute_exit(symbol, trade['tp_price'], "TP_HIT_SIMULATED")
219
  break
220
+
221
+ # ب) تحقق من وقف الخسارة (Stop Loss)
222
+ if min_price_seen <= trade['sl_price']:
223
+ print(f"🛑 [SL HIT] Price {min_price_seen} touched Stop {trade['sl_price']}")
224
  async with self.execution_lock:
225
+ # الخروج بسعر الوقف
226
+ await self._execute_exit(symbol, trade['sl_price'], "SL_HIT_SIMULATED")
227
  break
228
 
229
+ # -------------------------------------------------------------
230
+ # 3. التدقيق الذكي (AI Guardian)
231
+ # -------------------------------------------------------------
232
  if time.time() - last_ai_check_time > 60:
233
+ t1 = self.data_manager.get_latest_ohlcv(symbol, '1m', 1000)
234
+ t5 = self.data_manager.get_latest_ohlcv(symbol, '5m', 500)
235
+ t15 = self.data_manager.get_latest_ohlcv(symbol, '15m', 500)
 
236
  d1, d5, d15 = await asyncio.gather(t1, t5, t15)
237
 
 
238
  if d1 and d5 and d15 and len(d1) >= 500:
239
  decision = self.processor.consult_guardian(d1, d5, d15, trade['entry_price'])
240
  action = decision.get('action', 'HOLD')
241
  scores = decision.get('scores', {})
242
 
 
 
 
 
 
243
  if action in ['EXIT_HARD', 'EXIT_SOFT']:
244
  print(f"🤖 [Guardian] {action}: {decision.get('reason')}")
245
  async with self.execution_lock:
246
+ # في حالة الخروج الذكي، نستخدم السعر الحالي للتنفيذ (Market Order)
247
+ await self._execute_exit(symbol, current_ticker_price, f"AI_{action}", ai_scores=scores)
248
  break
249
  last_ai_check_time = time.time()
250
+
251
+ # تحديث وقت آخر نشاط للصفقة
252
  self.open_positions[symbol]['last_update'] = datetime.now().isoformat()
253
+
254
  except asyncio.CancelledError: break
255
  except Exception as e:
256
  print(f"❌ [Sentry Error] {symbol}: {e}")
 
297
  print(f"⚠️ [Ghost/Learning Error] {e}")
298
 
299
  # ==============================================================================
300
+ # 🔴 Exit Logic (Net PnL with Fees)
301
  # ==============================================================================
302
  async def _execute_exit(self, symbol, price, reason, ai_scores=None):
303
  if symbol not in self.open_positions: return
304
  try:
305
  trade = self.open_positions.pop(symbol)
 
 
 
306
 
307
+ entry_price = float(trade['entry_price'])
308
+ exit_price = float(price)
309
+
310
+ # استرجاع رأس المال عند الدخول ورسوم الدخول
311
+ entry_capital = float(trade.get('entry_capital', 100.0)) # Fallback if missing
312
+ entry_fee_usd = float(trade.get('entry_fee_usd', 0.0))
313
+
314
+ # حساب قيمة الخروج قبل خصم رسوم الخروج
315
+ # معادلة التغير: (سعر الخروج / سعر الدخول) * رأس المال
316
+ exit_value_gross = (exit_price / entry_price) * entry_capital
317
+
318
+ # حساب رسوم الخروج
319
+ exit_fee_usd = exit_value_gross * self.FEE_RATE
320
+
321
+ # القيمة الصافية النهائية (Net Value)
322
+ net_exit_value = exit_value_gross - exit_fee_usd
323
+
324
+ # صافي الربح/الخسارة بالدولار (يشمل خصم رسوم الدخول والخروج)
325
+ # Net PnL = القيمة النهائية - (رأس المال الأصلي + رسوم الدخول المدفوعة سابقاً)
326
+ # ملاحظة: رسوم الدخول خُصمت ضمنياً لأننا نستخدم رأس المال الكامل، لذا الحسبة الأدق:
327
+ # الربح الصافي = ما تبقى في اليد (Net Exit) - ما كان في اليد قبل الدخول (Entry Capital)
328
+ net_pnl_usd = net_exit_value - entry_capital
329
+
330
+ # نسبة الربح الصافي
331
+ net_profit_pct = (net_pnl_usd / entry_capital) * 100
332
+
333
+ # تحديث بيانات الصفقة
334
+ trade.update({
335
+ 'status': 'CLOSED',
336
+ 'exit_price': exit_price,
337
+ 'exit_reason': reason,
338
+ 'profit_pct': net_profit_pct,
339
+ 'net_pnl_usd': net_pnl_usd,
340
+ 'fees_paid_usd': entry_fee_usd + exit_fee_usd
341
+ })
342
+
343
+ # تحديث المحفظة
344
  portfolio = await self.r2.get_portfolio_state_async()
345
+ current_total_cap = float(portfolio.get('current_capital_usd', 100.0))
346
+
347
+ # رأس المال الجديد = رأس المال القديم + صافي الربح/الخسارة
348
+ new_cap = current_total_cap + net_pnl_usd
349
 
350
  portfolio['current_capital_usd'] = new_cap
351
  portfolio['total_trades'] = portfolio.get('total_trades', 0) + 1
352
+
353
+ if net_pnl_usd >= 0:
354
  portfolio['winning_trades'] = portfolio.get('winning_trades', 0) + 1
355
+ portfolio['total_profit_usd'] = portfolio.get('total_profit_usd', 0) + net_pnl_usd
356
  trade['result'] = 'WIN'
357
  else:
358
  portfolio['losing_trades'] = portfolio.get('losing_trades', 0) + 1
359
+ portfolio['total_loss_usd'] = portfolio.get('total_loss_usd', 0) + abs(net_pnl_usd)
360
  trade['result'] = 'LOSS'
361
 
362
  await self.r2.save_portfolio_state_async(portfolio)
363
  await self.r2.save_open_trades_async(list(self.open_positions.values()))
364
  await self.r2.append_to_closed_trades_history(trade)
365
 
366
+ print(f"✅ [EXIT] {symbol} | Net PnL: {net_profit_pct:.2f}% (${net_pnl_usd:.2f}) | Fees: ${entry_fee_usd+exit_fee_usd:.3f} | Cap: ${new_cap:.2f}")
367
 
368
+ self._launch_post_exit_analysis(symbol, exit_price, trade.get('exit_time'), entry_capital, ai_scores, trade)
369
 
370
  if symbol in self.sentry_tasks:
371
  self.sentry_tasks[symbol].cancel()
 
373
 
374
  except Exception as e:
375
  print(f"❌ [Exit Error] {e}")
376
+ traceback.print_exc()
377
+ # في حالة الخطأ نعيد الصفقة للقائمة لتجنب ضياعها
378
+ if symbol not in self.open_positions:
379
+ self.open_positions[symbol] = trade
380
 
381
  async def force_exit_by_manager(self, symbol, reason):
382
  p = await self.data_manager.get_latest_price_async(symbol)