Riy777 commited on
Commit
f3ca3ad
·
1 Parent(s): d16eedb

Update trade_manager.py

Browse files
Files changed (1) hide show
  1. trade_manager.py +164 -18
trade_manager.py CHANGED
@@ -1,5 +1,7 @@
1
  import asyncio
2
  import json
 
 
3
  from datetime import datetime, timedelta
4
  from helpers import safe_float_conversion, _apply_patience_logic
5
 
@@ -10,6 +12,8 @@ class TradeManager:
10
  self.data_manager = data_manager
11
  self.monitoring_tasks = {}
12
  self.is_running = False
 
 
13
 
14
  async def open_trade(self, symbol, decision, current_price):
15
  try:
@@ -17,6 +21,7 @@ class TradeManager:
17
  available_capital = portfolio_state.get("current_capital_usd", 0)
18
 
19
  if available_capital < 1:
 
20
  return None
21
 
22
  expected_target_minutes = decision.get('expected_target_minutes', 15)
@@ -42,7 +47,8 @@ class TradeManager:
42
  "expected_target_minutes": expected_target_minutes,
43
  "expected_target_time": expected_target_time,
44
  "is_monitored": True,
45
- "strategy": strategy
 
46
  }
47
 
48
  trades.append(new_trade)
@@ -62,13 +68,21 @@ class TradeManager:
62
  "strategy": strategy
63
  })
64
 
 
65
  return new_trade
66
 
67
  except Exception as e:
 
 
 
 
 
 
68
  raise
69
 
70
  async def close_trade(self, trade_to_close, close_price, reason="إغلاق بالنظام"):
71
  try:
 
72
  trade_to_close['status'] = 'CLOSED'
73
  trade_to_close['close_price'] = close_price
74
  trade_to_close['close_timestamp'] = datetime.now().isoformat()
@@ -122,9 +136,15 @@ class TradeManager:
122
  trades_to_keep = [t for t in open_trades if t.get('id') != trade_to_close.get('id')]
123
  await self.r2_service.save_open_trades_async(trades_to_keep)
124
 
 
 
 
 
 
 
125
  await self.r2_service.save_system_logs_async({
126
  "trade_closed": True,
127
- "symbol": trade_to_close.get('symbol'),
128
  "entry_price": entry_price,
129
  "close_price": close_price,
130
  "pnl_usd": pnl,
@@ -139,13 +159,21 @@ class TradeManager:
139
  if self.learning_engine and self.learning_engine.initialized:
140
  await self.learning_engine.analyze_trade_outcome(trade_to_close, reason)
141
 
 
142
  return True
143
 
144
  except Exception as e:
 
 
 
 
 
 
145
  raise
146
 
147
  async def update_trade(self, trade_to_update, re_analysis_decision):
148
  try:
 
149
  if re_analysis_decision.get('new_stop_loss'):
150
  trade_to_update['stop_loss'] = re_analysis_decision['new_stop_loss']
151
  if re_analysis_decision.get('new_take_profit'):
@@ -175,15 +203,17 @@ class TradeManager:
175
 
176
  await self.r2_service.save_system_logs_async({
177
  "trade_updated": True,
178
- "symbol": trade_to_update.get('symbol'),
179
  "new_expected_minutes": new_expected_minutes,
180
  "action": "UPDATE_TRADE",
181
  "strategy": original_strategy
182
  })
183
 
 
184
  return True
185
 
186
  except Exception as e:
 
187
  raise
188
 
189
  async def immediate_close_trade(self, symbol, close_price, reason="المراقبة الفورية"):
@@ -197,6 +227,7 @@ class TradeManager:
197
  break
198
 
199
  if not trade_to_close:
 
200
  return False
201
 
202
  await self.close_trade(trade_to_close, close_price, reason)
@@ -204,38 +235,87 @@ class TradeManager:
204
  return True
205
 
206
  except Exception as e:
 
207
  return False
208
 
209
  async def start_trade_monitoring(self):
210
  self.is_running = True
 
 
211
  while self.is_running:
212
  try:
213
  open_trades = await self.r2_service.get_open_trades_async()
 
 
214
  for trade in open_trades:
215
  symbol = trade['symbol']
 
 
 
 
 
 
 
 
216
  if symbol not in self.monitoring_tasks:
217
- asyncio.create_task(self._monitor_single_trade(trade))
218
- self.monitoring_tasks[symbol] = trade
 
 
 
 
 
219
 
 
220
  current_symbols = {trade['symbol'] for trade in open_trades}
221
  for symbol in list(self.monitoring_tasks.keys()):
222
  if symbol not in current_symbols:
 
 
 
223
  del self.monitoring_tasks[symbol]
 
 
224
 
225
  await asyncio.sleep(10)
 
226
  except Exception as error:
 
227
  await asyncio.sleep(30)
228
 
229
  async def _monitor_single_trade(self, trade):
230
  symbol = trade['symbol']
231
- while symbol in self.monitoring_tasks and self.is_running:
 
 
 
 
 
 
 
232
  try:
 
 
233
  if not self.data_manager:
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  await asyncio.sleep(15)
235
  continue
236
 
237
- current_price = await self.data_manager.get_latest_price_async(symbol)
238
  if not current_price:
 
 
239
  await asyncio.sleep(15)
240
  continue
241
 
@@ -244,34 +324,77 @@ class TradeManager:
244
  take_profit = trade.get('take_profit')
245
  should_close, close_reason = False, ""
246
 
 
247
  if stop_loss and current_price <= stop_loss:
248
  should_close, close_reason = True, f"وصول وقف الخسارة: {current_price} <= {stop_loss}"
249
  elif take_profit and current_price >= take_profit:
250
  should_close, close_reason = True, f"وصول جني الأرباح: {current_price} >= {take_profit}"
251
 
 
252
  if not should_close and current_price > entry_price:
253
  dynamic_stop = current_price * 0.98
254
  if dynamic_stop > (stop_loss or 0):
255
  trade['stop_loss'] = dynamic_stop
256
 
 
257
  if should_close:
258
  if self.r2_service.acquire_lock():
259
  try:
260
  await self.immediate_close_trade(symbol, current_price, close_reason)
 
 
261
  finally:
262
  self.r2_service.release_lock()
263
 
264
- if symbol in self.monitoring_tasks:
265
- del self.monitoring_tasks[symbol]
 
 
 
 
 
 
 
 
266
  break
267
 
268
  await asyncio.sleep(15)
 
269
  except Exception as error:
 
 
 
 
 
 
 
 
 
 
 
 
 
270
  await asyncio.sleep(30)
271
 
 
 
 
 
 
 
 
 
272
  def stop_monitoring(self):
273
  self.is_running = False
 
 
 
 
 
 
 
274
  self.monitoring_tasks.clear()
 
275
 
276
  async def _archive_closed_trade(self, closed_trade):
277
  try:
@@ -284,12 +407,16 @@ class TradeManager:
284
 
285
  history.append(closed_trade)
286
 
 
 
 
 
287
  data_json = json.dumps(history, indent=2).encode('utf-8')
288
  self.r2_service.s3_client.put_object(
289
  Bucket="trading", Key=key, Body=data_json, ContentType="application/json"
290
  )
291
  except Exception as e:
292
- pass
293
 
294
  async def _update_trade_summary(self, closed_trade):
295
  try:
@@ -302,7 +429,8 @@ class TradeManager:
302
  "total_trades": 0, "winning_trades": 0, "losing_trades": 0,
303
  "total_profit_usd": 0.0, "total_loss_usd": 0.0, "win_percentage": 0.0,
304
  "avg_profit_per_trade": 0.0, "avg_loss_per_trade": 0.0,
305
- "largest_win": 0.0, "largest_loss": 0.0
 
306
  }
307
 
308
  pnl = closed_trade.get('pnl_usd', 0.0)
@@ -327,20 +455,38 @@ class TradeManager:
327
  if summary['losing_trades'] > 0:
328
  summary['avg_loss_per_trade'] = summary['total_loss_usd'] / summary['losing_trades']
329
 
 
 
330
  data_json = json.dumps(summary, indent=2).encode('utf-8')
331
  self.r2_service.s3_client.put_object(
332
  Bucket="trading", Key=key, Body=data_json, ContentType="application/json"
333
  )
334
 
335
  except Exception as e:
336
- pass
337
 
338
  async def get_open_trades(self):
339
- return await self.r2_service.get_open_trades_async()
 
 
 
 
340
 
341
  async def get_trade_by_symbol(self, symbol):
342
- open_trades = await self.get_open_trades()
343
- for trade in open_trades:
344
- if trade['symbol'] == symbol and trade['status'] == 'OPEN':
345
- return trade
346
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import asyncio
2
  import json
3
+ import time
4
+ import traceback
5
  from datetime import datetime, timedelta
6
  from helpers import safe_float_conversion, _apply_patience_logic
7
 
 
12
  self.data_manager = data_manager
13
  self.monitoring_tasks = {}
14
  self.is_running = False
15
+ self.monitoring_errors = {}
16
+ self.max_consecutive_errors = 5
17
 
18
  async def open_trade(self, symbol, decision, current_price):
19
  try:
 
21
  available_capital = portfolio_state.get("current_capital_usd", 0)
22
 
23
  if available_capital < 1:
24
+ print(f"❌ رأس المال غير كافي لفتح صفقة لـ {symbol}: {available_capital}")
25
  return None
26
 
27
  expected_target_minutes = decision.get('expected_target_minutes', 15)
 
47
  "expected_target_minutes": expected_target_minutes,
48
  "expected_target_time": expected_target_time,
49
  "is_monitored": True,
50
+ "strategy": strategy,
51
+ "monitoring_started": False
52
  }
53
 
54
  trades.append(new_trade)
 
68
  "strategy": strategy
69
  })
70
 
71
+ print(f"✅ تم فتح صفقة جديدة لـ {symbol} باستراتيجية {strategy}")
72
  return new_trade
73
 
74
  except Exception as e:
75
+ print(f"❌ فشل فتح صفقة لـ {symbol}: {e}")
76
+ await self.r2_service.save_system_logs_async({
77
+ "trade_open_failed": True,
78
+ "symbol": symbol,
79
+ "error": str(e)
80
+ })
81
  raise
82
 
83
  async def close_trade(self, trade_to_close, close_price, reason="إغلاق بالنظام"):
84
  try:
85
+ symbol = trade_to_close.get('symbol')
86
  trade_to_close['status'] = 'CLOSED'
87
  trade_to_close['close_price'] = close_price
88
  trade_to_close['close_timestamp'] = datetime.now().isoformat()
 
136
  trades_to_keep = [t for t in open_trades if t.get('id') != trade_to_close.get('id')]
137
  await self.r2_service.save_open_trades_async(trades_to_keep)
138
 
139
+ # إزالة من المهام النشطة
140
+ if symbol in self.monitoring_tasks:
141
+ del self.monitoring_tasks[symbol]
142
+ if symbol in self.monitoring_errors:
143
+ del self.monitoring_errors[symbol]
144
+
145
  await self.r2_service.save_system_logs_async({
146
  "trade_closed": True,
147
+ "symbol": symbol,
148
  "entry_price": entry_price,
149
  "close_price": close_price,
150
  "pnl_usd": pnl,
 
159
  if self.learning_engine and self.learning_engine.initialized:
160
  await self.learning_engine.analyze_trade_outcome(trade_to_close, reason)
161
 
162
+ print(f"✅ تم إغلاق صفقة {symbol} - السبب: {reason} - الربح: {pnl_percent:+.2f}%")
163
  return True
164
 
165
  except Exception as e:
166
+ print(f"❌ فشل إغلاق صفقة {trade_to_close.get('symbol')}: {e}")
167
+ await self.r2_service.save_system_logs_async({
168
+ "trade_close_failed": True,
169
+ "symbol": trade_to_close.get('symbol'),
170
+ "error": str(e)
171
+ })
172
  raise
173
 
174
  async def update_trade(self, trade_to_update, re_analysis_decision):
175
  try:
176
+ symbol = trade_to_update.get('symbol')
177
  if re_analysis_decision.get('new_stop_loss'):
178
  trade_to_update['stop_loss'] = re_analysis_decision['new_stop_loss']
179
  if re_analysis_decision.get('new_take_profit'):
 
203
 
204
  await self.r2_service.save_system_logs_async({
205
  "trade_updated": True,
206
+ "symbol": symbol,
207
  "new_expected_minutes": new_expected_minutes,
208
  "action": "UPDATE_TRADE",
209
  "strategy": original_strategy
210
  })
211
 
212
+ print(f"✅ تم تحديث صفقة {symbol}")
213
  return True
214
 
215
  except Exception as e:
216
+ print(f"❌ فشل تحديث صفقة {trade_to_update.get('symbol')}: {e}")
217
  raise
218
 
219
  async def immediate_close_trade(self, symbol, close_price, reason="المراقبة الفورية"):
 
227
  break
228
 
229
  if not trade_to_close:
230
+ print(f"⚠️ لم يتم العثور على صفقة مفتوحة لـ {symbol}")
231
  return False
232
 
233
  await self.close_trade(trade_to_close, close_price, reason)
 
235
  return True
236
 
237
  except Exception as e:
238
+ print(f"❌ فشل الإغلاق الفوري لـ {symbol}: {e}")
239
  return False
240
 
241
  async def start_trade_monitoring(self):
242
  self.is_running = True
243
+ print("🔍 بدء مراقبة الصفقات...")
244
+
245
  while self.is_running:
246
  try:
247
  open_trades = await self.r2_service.get_open_trades_async()
248
+ current_time = time.time()
249
+
250
  for trade in open_trades:
251
  symbol = trade['symbol']
252
+
253
+ # تخطي الصفقات التي تجاوزت حد الأخطاء
254
+ if self.monitoring_errors.get(symbol, 0) >= self.max_consecutive_errors:
255
+ if symbol in self.monitoring_tasks:
256
+ del self.monitoring_tasks[symbol]
257
+ continue
258
+
259
+ # بدء المراقبة إذا لم تكن نشطة
260
  if symbol not in self.monitoring_tasks:
261
+ task = asyncio.create_task(self._monitor_single_trade(trade))
262
+ self.monitoring_tasks[symbol] = {
263
+ 'task': task,
264
+ 'start_time': current_time,
265
+ 'trade': trade
266
+ }
267
+ trade['monitoring_started'] = True
268
 
269
+ # تنظيف المهام المنتهية
270
  current_symbols = {trade['symbol'] for trade in open_trades}
271
  for symbol in list(self.monitoring_tasks.keys()):
272
  if symbol not in current_symbols:
273
+ task_info = self.monitoring_tasks[symbol]
274
+ if not task_info['task'].done():
275
+ task_info['task'].cancel()
276
  del self.monitoring_tasks[symbol]
277
+ if symbol in self.monitoring_errors:
278
+ del self.monitoring_errors[symbol]
279
 
280
  await asyncio.sleep(10)
281
+
282
  except Exception as error:
283
+ print(f"❌ خطأ في مراقبة الصفقات: {error}")
284
  await asyncio.sleep(30)
285
 
286
  async def _monitor_single_trade(self, trade):
287
  symbol = trade['symbol']
288
+ max_monitoring_time = 3600 # أقصى وقت مراقبة: ساعة واحدة
289
+
290
+ print(f"🔍 بدء مراقبة الصفقة: {symbol}")
291
+
292
+ while (symbol in self.monitoring_tasks and
293
+ self.is_running and
294
+ self.monitoring_errors.get(symbol, 0) < self.max_consecutive_errors):
295
+
296
  try:
297
+ start_time = time.time()
298
+
299
  if not self.data_manager:
300
+ print(f"⚠️ DataManager غير متوفر لـ {symbol}")
301
+ await asyncio.sleep(15)
302
+ continue
303
+
304
+ # الحصول على السعر الحالي مع مهلة زمنية
305
+ try:
306
+ current_price = await asyncio.wait_for(
307
+ self.data_manager.get_latest_price_async(symbol),
308
+ timeout=10
309
+ )
310
+ except asyncio.TimeoutError:
311
+ print(f"⏰ مهلة انتظار السعر لـ {symbol}")
312
+ self._increment_monitoring_error(symbol)
313
  await asyncio.sleep(15)
314
  continue
315
 
 
316
  if not current_price:
317
+ print(f"⚠️ لم يتم الحصول على سعر لـ {symbol}")
318
+ self._increment_monitoring_error(symbol)
319
  await asyncio.sleep(15)
320
  continue
321
 
 
324
  take_profit = trade.get('take_profit')
325
  should_close, close_reason = False, ""
326
 
327
+ # التحقق من شروط الإغلاق
328
  if stop_loss and current_price <= stop_loss:
329
  should_close, close_reason = True, f"وصول وقف الخسارة: {current_price} <= {stop_loss}"
330
  elif take_profit and current_price >= take_profit:
331
  should_close, close_reason = True, f"وصول جني الأرباح: {current_price} >= {take_profit}"
332
 
333
+ # تحديث وقف الخسارة الديناميكي
334
  if not should_close and current_price > entry_price:
335
  dynamic_stop = current_price * 0.98
336
  if dynamic_stop > (stop_loss or 0):
337
  trade['stop_loss'] = dynamic_stop
338
 
339
+ # إغلاق الصفقة إذا لزم الأمر
340
  if should_close:
341
  if self.r2_service.acquire_lock():
342
  try:
343
  await self.immediate_close_trade(symbol, current_price, close_reason)
344
+ except Exception as close_error:
345
+ print(f"❌ فشل الإغلاق التلقائي لـ {symbol}: {close_error}")
346
  finally:
347
  self.r2_service.release_lock()
348
 
349
+ break
350
+
351
+ # إعادة تعيين عداد الأخطاء عند النجاح
352
+ if symbol in self.monitoring_errors:
353
+ self.monitoring_errors[symbol] = 0
354
+
355
+ # التحقق من وقت المراقبة الطويل
356
+ monitoring_duration = time.time() - start_time
357
+ if monitoring_duration > max_monitoring_time:
358
+ print(f"🕒 انتهى وقت مراقبة الصفقة {symbol}")
359
  break
360
 
361
  await asyncio.sleep(15)
362
+
363
  except Exception as error:
364
+ error_count = self._increment_monitoring_error(symbol)
365
+ print(f"❌ خطأ في مراقبة {symbol} (الخطأ #{error_count}): {error}")
366
+
367
+ if error_count >= self.max_consecutive_errors:
368
+ print(f"🚨 إيقاف مراقبة {symbol} بسبب الأخطاء المتتالية")
369
+ await self.r2_service.save_system_logs_async({
370
+ "monitoring_stopped": True,
371
+ "symbol": symbol,
372
+ "error_count": error_count,
373
+ "error": str(error)
374
+ })
375
+ break
376
+
377
  await asyncio.sleep(30)
378
 
379
+ print(f"🛑 توقيف مراقبة الصفقة: {symbol}")
380
+
381
+ def _increment_monitoring_error(self, symbol):
382
+ if symbol not in self.monitoring_errors:
383
+ self.monitoring_errors[symbol] = 0
384
+ self.monitoring_errors[symbol] += 1
385
+ return self.monitoring_errors[symbol]
386
+
387
  def stop_monitoring(self):
388
  self.is_running = False
389
+ print("🛑 إيقاف جميع مهام المراقبة...")
390
+
391
+ for symbol, task_info in self.monitoring_tasks.items():
392
+ if not task_info['task'].done():
393
+ task_info['task'].cancel()
394
+ print(f"✅ تم إلغاء مهمة مراقبة {symbol}")
395
+
396
  self.monitoring_tasks.clear()
397
+ self.monitoring_errors.clear()
398
 
399
  async def _archive_closed_trade(self, closed_trade):
400
  try:
 
407
 
408
  history.append(closed_trade)
409
 
410
+ # حفظ آخر 1000 صفقة فقط
411
+ if len(history) > 1000:
412
+ history = history[-1000:]
413
+
414
  data_json = json.dumps(history, indent=2).encode('utf-8')
415
  self.r2_service.s3_client.put_object(
416
  Bucket="trading", Key=key, Body=data_json, ContentType="application/json"
417
  )
418
  except Exception as e:
419
+ print(f"❌ فشل أرشفة الصفقة: {e}")
420
 
421
  async def _update_trade_summary(self, closed_trade):
422
  try:
 
429
  "total_trades": 0, "winning_trades": 0, "losing_trades": 0,
430
  "total_profit_usd": 0.0, "total_loss_usd": 0.0, "win_percentage": 0.0,
431
  "avg_profit_per_trade": 0.0, "avg_loss_per_trade": 0.0,
432
+ "largest_win": 0.0, "largest_loss": 0.0,
433
+ "last_updated": datetime.now().isoformat()
434
  }
435
 
436
  pnl = closed_trade.get('pnl_usd', 0.0)
 
455
  if summary['losing_trades'] > 0:
456
  summary['avg_loss_per_trade'] = summary['total_loss_usd'] / summary['losing_trades']
457
 
458
+ summary['last_updated'] = datetime.now().isoformat()
459
+
460
  data_json = json.dumps(summary, indent=2).encode('utf-8')
461
  self.r2_service.s3_client.put_object(
462
  Bucket="trading", Key=key, Body=data_json, ContentType="application/json"
463
  )
464
 
465
  except Exception as e:
466
+ print(f"❌ فشل تحديث ملخص التداول: {e}")
467
 
468
  async def get_open_trades(self):
469
+ try:
470
+ return await self.r2_service.get_open_trades_async()
471
+ except Exception as e:
472
+ print(f"❌ فشل جلب الصفقات المفتوحة: {e}")
473
+ return []
474
 
475
  async def get_trade_by_symbol(self, symbol):
476
+ try:
477
+ open_trades = await self.get_open_trades()
478
+ for trade in open_trades:
479
+ if trade['symbol'] == symbol and trade['status'] == 'OPEN':
480
+ return trade
481
+ return None
482
+ except Exception as e:
483
+ print(f"❌ فشل البحث عن صفقة {symbol}: {e}")
484
+ return None
485
+
486
+ def get_monitoring_status(self):
487
+ return {
488
+ 'is_running': self.is_running,
489
+ 'active_tasks': len(self.monitoring_tasks),
490
+ 'monitoring_errors': dict(self.monitoring_errors),
491
+ 'max_consecutive_errors': self.max_consecutive_errors
492
+ }